|
@ -33,3 +33,5 @@ obj
|
|||
*.scss.ts
|
||||
|
||||
packages/
|
||||
samples/react-teams-send-notification/MyNotes.md
|
||||
samples/react-teams-send-notification/teams/teams.zip
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
# title of the sample
|
||||
# Title of the sample
|
||||
|
||||
> This is how you want the sample to appear in the samples browser.
|
||||
> When naming your sample, try to give it a friendly name that describes what it does. Avoid using terms like `SharePoint` and `WebPart` -- because that's what all the samples in this repo is all about. Also, don't use `React`, `Angular`, `JavaScript`, etc. in your sample title -- unless that's what the sample is about.
|
||||
|
||||
## Summary
|
||||
|
||||
|
@ -6,16 +9,15 @@ Short summary on functionality and used technologies.
|
|||
|
||||
> Please provide a high-quality screenshot of your web parts below. It should be stored in a folder called `assets`.
|
||||
> If possible, use a resolution of 1920x1080.
|
||||
> If your web part uses a placeholder screen and requires the user to configure it, please use a screenshot of the web part as it appears after it has been configured.
|
||||
> If your web part uses a placeholder screen and requires the user to configure it, please use a screenshot of the web part as it appears **after** it has been configured.
|
||||
> You can add as many screen shots as you'd like to help users understand your web part without having to download it and install it.
|
||||
> DELETE THIS PARAGRAPH BEFORE SUBMITTING
|
||||
|
||||
|
||||
![picture of the web part in action](assets/preview.png)
|
||||
|
||||
## Used SharePoint Framework Version
|
||||
|
||||
![1.11.0](https://img.shields.io/badge/version-1.11.0-green.svg)
|
||||
![SPFx 1.11.0](https://img.shields.io/badge/version-1.11.0-green.svg)
|
||||
|
||||
## Applies to
|
||||
|
||||
|
|
|
@ -3450,8 +3450,8 @@ inherits@2.0.1:
|
|||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
|
||||
|
||||
ini@^1.3.4, ini@~1.3.0:
|
||||
version "1.3.4"
|
||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
|
||||
version "1.3.7"
|
||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84"
|
||||
|
||||
inpath@~1.0.2:
|
||||
version "1.0.2"
|
||||
|
|
|
@ -15,7 +15,7 @@ extensions:
|
|||
createdDate: 2/15/2020 12:00:00 AM
|
||||
---
|
||||
|
||||
# AngularJS Greeting client-side web part
|
||||
# AngularJS Greeting
|
||||
|
||||
## Summary
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ extensions:
|
|||
- AngularJS
|
||||
createdDate: 2/16/2017 12:00:00 AM
|
||||
---
|
||||
## Angular MS Graph Web Part Built with Angular v1.x
|
||||
# Angular MS Graph Web Part Built with Angular v1.x
|
||||
|
||||
## Summary
|
||||
This is a sample MS Graph web part that connects to Microsoft Graph and pulls SharePoint information from your
|
||||
|
|
|
@ -20,7 +20,7 @@ extensions:
|
|||
Set of sample web parts illustrating how to use Angular Elements in the SharePoint Framework.
|
||||
|
||||
## Used SharePoint Framework Version
|
||||
![drop](https://img.shields.io/badge/drop-1.4.1-green.svg)
|
||||
![SPFx 1.4.1](https://img.shields.io/badge/spfx-1.4.1-green.svg)
|
||||
|
||||
## Applies to
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ extensions:
|
|||
- jQuery
|
||||
createdDate: 5/1/2017 12:00:00 AM
|
||||
---
|
||||
## Bootstrap Slider Built with jQuery v1.x and Boostrap v3.x
|
||||
# Bootstrap Slider Built with jQuery v1.x and Boostrap v3.x
|
||||
|
||||
## Summary
|
||||
Sample bootstrap slider which pulls the slides from a list inside the SharePoint site. The list is automatically deployed once the app is installed in the SharePoint site.
|
||||
|
|
|
@ -13,7 +13,7 @@ extensions:
|
|||
- Handlebars
|
||||
createdDate: 3/5/2017 12:00:00 AM
|
||||
---
|
||||
## SPFx Sample with Handlebars.js
|
||||
# SPFx Sample with Handlebars.js
|
||||
|
||||
This sample demonstrate how to set up SPFx to use [Handlebars](http://handlebarsjs.com) through [webpack loader](https://webpack.github.io/docs/loaders.html).
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ extensions:
|
|||
- JQuery
|
||||
createdDate: 1/1/2016 12:00:00 AM
|
||||
---
|
||||
# JQuery, Photopile.JS & Office UI Fabric Client-Side Web Part
|
||||
# JQuery, Photopile.JS & Office UI Fabric Client-Side Web Part
|
||||
|
||||
## Summary
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ extensions:
|
|||
- SharePoint Framework
|
||||
createdDate: 5/15/2017 12:00:00 AM
|
||||
---
|
||||
# Display List JavaScript Client-Side Web Part
|
||||
# Display List
|
||||
|
||||
## Summary
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ extensions:
|
|||
- react
|
||||
createdDate: 8/1/2017 12:00:00 AM
|
||||
---
|
||||
# Display Employee Spotlight JavaScript Client-Side Web Part
|
||||
# Employee Spotlight
|
||||
|
||||
## Summary
|
||||
Simple Web Part that demonstrates the use of SharePoint Framework for show casing Employee Spotlight. The web part pulls data from a configured list and User Profile service.
|
||||
|
|
|
@ -4348,8 +4348,8 @@ inherits@^2.0.3:
|
|||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
|
||||
ini@^1.3.4, ini@~1.3.0:
|
||||
version "1.3.4"
|
||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
|
||||
version "1.3.7"
|
||||
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84"
|
||||
|
||||
inline-style-prefixer@^3.0.6:
|
||||
version "3.0.8"
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# GitHub Badge
|
||||
# GitHub Badge (JavaScript version)
|
||||
|
||||
## Summary
|
||||
Displays information from GitHub for a specified user.
|
||||
![drop](./assets/1.png)
|
||||
|
||||
## Used SharePoint Framework Version
|
||||
![drop](https://img.shields.io/badge/version-GA-green.svg)
|
||||
![SPFx 1.8.0](https://img.shields.io/badge/version-1.8.0-green.svg)
|
||||
|
||||
## Applies to
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# SPFx My Flows Web Part
|
||||
# My Flows
|
||||
|
||||
## Summary
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ extensions:
|
|||
- JQuery
|
||||
createdDate: 1/1/2016 12:00:00 AM
|
||||
---
|
||||
# Embed a PowerBI report in a Client-Side Web Part
|
||||
# Embed a PowerBI Report
|
||||
|
||||
## Summary
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# JS Property Controls SVG
|
||||
# Dynamic Scalable Vector Graphics (SVG) image using propertie
|
||||
|
||||
## Summary
|
||||
An SPFx webpart that displays a Scalable Vector Graphics (SVG) image using properties to customize how it is rendered. The webpart utilizes the PnP SPFx Property Controls package (specifially the SpinButton and ColorPicker) to set these properties.
|
||||
|
|
|
@ -5,7 +5,14 @@
|
|||
"id": "5d6f4a5a-9d2b-4a93-a283-16b8f5ea75d6",
|
||||
"version": "1.5.0.0",
|
||||
"includeClientSideAssets": true,
|
||||
"skipFeatureDeployment": true
|
||||
"skipFeatureDeployment": true,
|
||||
"developer": {
|
||||
"name": "Contoso",
|
||||
"privacyUrl": "https://contoso.com/privacy",
|
||||
"termsOfUseUrl": "https://contoso.com/terms-of-use",
|
||||
"websiteUrl": "https://contoso.com/my-app",
|
||||
"mpnId": "000000"
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/workbench-customizer.sppkg"
|
||||
|
|
|
@ -13,7 +13,7 @@ extensions:
|
|||
- Knockout
|
||||
createdDate: 1/1/2016 12:00:00 AM
|
||||
---
|
||||
# Sample Web Part implementing dependent properties in Property Pane
|
||||
# Dependent Property Pane Properties
|
||||
|
||||
## Summary
|
||||
Sample Web Part illustrating
|
||||
|
|
|
@ -13,7 +13,7 @@ extensions:
|
|||
- Knockout
|
||||
createdDate: 3/1/2017 12:00:00 AM
|
||||
---
|
||||
# Sample showing the use of @pnp/sp library with Knockoutjs
|
||||
# Using @pnp/sp with Knockoutjs
|
||||
|
||||
## Summary
|
||||
|
||||
|
|
|
@ -6342,12 +6342,6 @@
|
|||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
|
@ -8083,9 +8077,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
||||
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
||||
"dev": true
|
||||
},
|
||||
"inpath": {
|
||||
|
|
|
@ -13,7 +13,7 @@ extensions:
|
|||
- knockout
|
||||
createdDate: 1/1/2016 12:00:00 AM
|
||||
---
|
||||
# Taxonomy Web Part
|
||||
# Taxonomy
|
||||
|
||||
## Summary
|
||||
Sample Web Part illustrating
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
# Edit Application Customizers
|
||||
|
||||
## Summary
|
||||
|
||||
This web part will allow users to view/update application customizers properties across any web where the current user has access to. This web part can be helpful when we require to update the properties for application customizer without using any PowerShell script or cli tool.
|
||||
|
||||
![Web part in action](assets/react-all-applicationcustomizers.gif?raw=true "Webpart in action")
|
||||
|
||||
## Idea behind this web part
|
||||
|
||||
- SPFx Application customizer can be used to add scripts, and add custom html to well known placeholder(header and footer)
|
||||
- We can use properties to pass data to Application customizers to make solution customizable.
|
||||
- To update properties of application customizer there is no UI based solution.
|
||||
- To update the title, details and other information of application customizer we use either PowerShell script or cli tool.
|
||||
- This webpart can be used at a central location where all the users have access and if they require to update title, description and properties.
|
||||
|
||||
## Features
|
||||
|
||||
- Webpart to view/update Application Customizers registered for a selected web
|
||||
- Provides two different UI Accordion or List based(configurable)
|
||||
- Provides a dropdown to select the web from where we would require to fetch application customizers
|
||||
- Allows to update application customizer properties which makes it easy to make re-useable application customizers
|
||||
|
||||
|
||||
## Used SharePoint Framework Version
|
||||
|
||||
![SPFx 1.11](https://img.shields.io/badge/version-1.11-green.svg)
|
||||
|
||||
## Applies to
|
||||
|
||||
- [SharePoint Framework](https://aka.ms/spfx)
|
||||
- [Microsoft 365 tenant](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-developer-tenant)
|
||||
|
||||
> Get your own free development tenant by subscribing to [Microsoft 365 developer program](http://aka.ms/o365devprogram)
|
||||
|
||||
## Solution
|
||||
|
||||
Solution|Author(s)
|
||||
--------|---------
|
||||
react-edit-applicationcustomizers | [Kunj Sangani](https://www.linkedin.com/in/kunj-sangani/) and [Siddharth Vaghasia](https://www.linkedin.com/in/siddharthvaghasia)
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0|October 16, 2020|Initial release
|
||||
|
||||
## 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.**
|
||||
|
||||
---
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
||||
- Clone this repository
|
||||
- Ensure that you are at the solution folder
|
||||
- in the command-line run:
|
||||
- **npm install**
|
||||
- **gulp serve**
|
||||
|
||||
|
||||
For any issue or help, Buzz us on twitter:([sanganikunj](https://twitter.com/sanganikunj)) or ([siddh_me](https://twitter.com/siddh_me/))
|
||||
|
||||
> Sharing is caring!
|
||||
|
||||
## References
|
||||
|
||||
- [Getting started with SharePoint Framework](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-developer-tenant)
|
||||
- [Building for Microsoft teams](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/build-for-teams-overview)
|
||||
- [Use Microsoft Graph in your solution](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/using-microsoft-graph-apis)
|
||||
- [Publish SharePoint Framework applications to the Marketplace](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/publish-to-marketplace-overview)
|
||||
- [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - Guidance, tooling, samples and open-source controls for your Microsoft 365 development
|
||||
|
||||
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-edit-applicationcustomizers" />
|
After Width: | Height: | Size: 2.3 MiB |
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||
"version": "2.0",
|
||||
"bundles": {
|
||||
"application-customizers-web-part": {
|
||||
"components": [
|
||||
{
|
||||
"entrypoint": "./lib/webparts/applicationCustomizers/ApplicationCustomizersWebPart.js",
|
||||
"manifest": "./src/webparts/applicationCustomizers/ApplicationCustomizersWebPart.manifest.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"externals": {},
|
||||
"localizedResources": {
|
||||
"ApplicationCustomizersWebPartStrings": "lib/webparts/applicationCustomizers/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": "react-all-application-customizers",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||
"solution": {
|
||||
"name": "react-all-application-customizers-client-side-solution",
|
||||
"id": "f560c809-b5a8-4320-abef-224f27d2d0f0",
|
||||
"version": "1.0.0.0",
|
||||
"includeClientSideAssets": true,
|
||||
"isDomainIsolated": false,
|
||||
"developer": {
|
||||
"name": "",
|
||||
"websiteUrl": "",
|
||||
"privacyUrl": "",
|
||||
"termsOfUseUrl": "",
|
||||
"mpnId": ""
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/react-all-application-customizers.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'));
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"name": "react-all-application-customizers",
|
||||
"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": {
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@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/sp": "^2.0.10",
|
||||
"ace-builds": "^1.4.12",
|
||||
"office-ui-fabric-react": "6.214.0",
|
||||
"react": "16.8.5",
|
||||
"react-ace": "^9.1.3",
|
||||
"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,35 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||
"id": "05567ead-69c5-4c36-958c-613a21cce18c",
|
||||
"alias": "ApplicationCustomizersWebPart",
|
||||
"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": "ApplicationCustomizers"
|
||||
},
|
||||
"description": {
|
||||
"default": "ApplicationCustomizers description"
|
||||
},
|
||||
"officeFabricIconFontName": "CustomizeToolbar",
|
||||
"properties": {
|
||||
"description": "ApplicationCustomizers",
|
||||
"designType": "List"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import { Version } from '@microsoft/sp-core-library';
|
||||
import {
|
||||
IPropertyPaneConfiguration,
|
||||
PropertyPaneTextField,
|
||||
PropertyPaneChoiceGroup
|
||||
} from '@microsoft/sp-property-pane';
|
||||
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
|
||||
|
||||
import * as strings from 'ApplicationCustomizersWebPartStrings';
|
||||
import ApplicationCustomizers from './components/ApplicationCustomizers';
|
||||
import { IApplicationCustomizersProps } from './components/IApplicationCustomizersProps';
|
||||
import { sp } from "@pnp/sp/presets/all";
|
||||
|
||||
|
||||
export interface IApplicationCustomizersWebPartProps {
|
||||
description: string;
|
||||
designType: string;
|
||||
}
|
||||
|
||||
export default class ApplicationCustomizersWebPart extends BaseClientSideWebPart<IApplicationCustomizersWebPartProps> {
|
||||
|
||||
protected onInit(): Promise<void> {
|
||||
|
||||
return super.onInit().then(_ => {
|
||||
|
||||
// other init code may be present
|
||||
|
||||
sp.setup({
|
||||
spfxContext: this.context
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public render(): void {
|
||||
const element: React.ReactElement<IApplicationCustomizersProps> = React.createElement(
|
||||
ApplicationCustomizers,
|
||||
{
|
||||
description: this.properties.description,
|
||||
context: this.context,
|
||||
designType: this.properties.designType
|
||||
}
|
||||
);
|
||||
|
||||
ReactDom.render(element, this.domElement);
|
||||
}
|
||||
|
||||
protected onDispose(): void {
|
||||
ReactDom.unmountComponentAtNode(this.domElement);
|
||||
}
|
||||
|
||||
protected get dataVersion(): Version {
|
||||
return Version.parse('1.0');
|
||||
}
|
||||
|
||||
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||
return {
|
||||
pages: [
|
||||
{
|
||||
header: {
|
||||
description: strings.PropertyPaneDescription
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
groupName: strings.BasicGroupName,
|
||||
groupFields: [
|
||||
PropertyPaneTextField('description', {
|
||||
label: strings.DescriptionFieldLabel
|
||||
}),
|
||||
PropertyPaneChoiceGroup('designType', {
|
||||
label: strings.DesignFieldLabel,
|
||||
options: [
|
||||
{ key: 'Accordion', checked: true, text: 'Accordion', iconProps: { officeFabricIconFontName: 'AutoFillTemplate' } },
|
||||
{ key: 'List', text: 'List', iconProps: { officeFabricIconFontName: 'GroupedList' } }
|
||||
]
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
@import '~office-ui-fabric-react/dist/sass/References.scss';
|
||||
|
||||
.applicationCustomizers {
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0px auto;
|
||||
}
|
||||
|
||||
.row {
|
||||
@include ms-Grid-row;
|
||||
padding: 20px;
|
||||
}
|
||||
.column2 {
|
||||
@include ms-Grid-col;
|
||||
@include ms-lg2;
|
||||
@include ms-xl2;
|
||||
margin-top: 5px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.column {
|
||||
@include ms-Grid-col;
|
||||
@include ms-lg10;
|
||||
@include ms-xl10;
|
||||
margin-top: 5px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.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;
|
||||
margin-top: 10px;
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
:global{
|
||||
#workbenchPageContent{
|
||||
max-width: 1200px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,446 @@
|
|||
import * as React from 'react';
|
||||
import styles from './ApplicationCustomizers.module.scss';
|
||||
import { IApplicationCustomizersProps } from './IApplicationCustomizersProps';
|
||||
import { assign } from '@microsoft/sp-lodash-subset';
|
||||
import ApplicationCustomizersService from "../service/ApplicationCustomizersService";
|
||||
import { Dropdown, DropdownMenuItemType, IDropdownOption, IDropdownStyles } from 'office-ui-fabric-react/lib/Dropdown';
|
||||
import MuiAccordion from '@material-ui/core/Accordion';
|
||||
import MuiAccordionSummary from '@material-ui/core/AccordionSummary';
|
||||
import MuiAccordionDetails from '@material-ui/core/AccordionDetails';
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import { DefaultButton, TextField, thProperties, Dialog, DialogFooter, DialogType, List, mergeStyleSets, getFocusStyle, ITheme, getTheme, IconButton, Panel, PanelType } from 'office-ui-fabric-react';
|
||||
import AceEditor from "react-ace";
|
||||
|
||||
import "ace-builds/src-noconflict/mode-json";
|
||||
import "ace-builds/src-noconflict/theme-github";
|
||||
|
||||
const Accordion = withStyles({
|
||||
root: {
|
||||
border: '1px solid rgba(0, 0, 0, .125)',
|
||||
boxShadow: 'none',
|
||||
'&:not(:last-child)': {
|
||||
borderBottom: 0,
|
||||
},
|
||||
'&:before': {
|
||||
display: 'none',
|
||||
},
|
||||
'&$expanded': {
|
||||
margin: 'auto',
|
||||
},
|
||||
},
|
||||
expanded: {},
|
||||
})(MuiAccordion);
|
||||
|
||||
const AccordionSummary = withStyles({
|
||||
root: {
|
||||
backgroundColor: 'rgba(0, 0, 0, .03)',
|
||||
borderBottom: '1px solid rgba(0, 0, 0, .125)',
|
||||
marginBottom: -1,
|
||||
minHeight: 56,
|
||||
'&$expanded': {
|
||||
minHeight: 56,
|
||||
},
|
||||
},
|
||||
content: {
|
||||
'&$expanded': {
|
||||
margin: '12px 0',
|
||||
},
|
||||
},
|
||||
expanded: {},
|
||||
})(MuiAccordionSummary);
|
||||
|
||||
const AccordionDetails = withStyles((themes) => ({
|
||||
root: {
|
||||
padding: themes.spacing(2),
|
||||
display: 'block'
|
||||
},
|
||||
}))(MuiAccordionDetails);
|
||||
const theme: ITheme = getTheme();
|
||||
const { palette, semanticColors, fonts } = theme;
|
||||
const classNames = mergeStyleSets({
|
||||
container: {
|
||||
overflow: 'auto',
|
||||
maxHeight: 500,
|
||||
},
|
||||
itemCell: [
|
||||
getFocusStyle(theme, { inset: -1 }),
|
||||
{
|
||||
minHeight: 54,
|
||||
padding: 10,
|
||||
boxSizing: 'border-box',
|
||||
borderBottom: `1px solid ${semanticColors.bodyDivider}`,
|
||||
display: 'flex',
|
||||
selectors: {
|
||||
'&:hover': { background: palette.neutralLight },
|
||||
},
|
||||
},
|
||||
],
|
||||
itemImage: {
|
||||
flexShrink: 0,
|
||||
},
|
||||
itemContent: {
|
||||
marginLeft: 10,
|
||||
overflow: 'hidden',
|
||||
flexGrow: 1,
|
||||
},
|
||||
itemName: [
|
||||
fonts.xLarge,
|
||||
{
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
color: 'black'
|
||||
},
|
||||
],
|
||||
itemIndex: {
|
||||
fontSize: fonts.small.fontSize,
|
||||
marginBottom: 10,
|
||||
color: 'black'
|
||||
},
|
||||
chevron: {
|
||||
alignSelf: 'center',
|
||||
marginLeft: 10,
|
||||
color: palette.neutralTertiary,
|
||||
fontSize: fonts.large.fontSize,
|
||||
flexShrink: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const applicationCustomizersService = new ApplicationCustomizersService();
|
||||
|
||||
export interface IApplicationCustomizersState {
|
||||
selectedItem: IDropdownOption;
|
||||
dropdownSites: IDropdownOption[];
|
||||
expanded: string | false;
|
||||
allCustomizers: any;
|
||||
previousEditIndex?: number;
|
||||
editJSON?: { Title: string; Description: string; ClientSideComponentProperties: any };
|
||||
hideDialog: boolean;
|
||||
dialogContentProps?: any;
|
||||
isPanelOpen: boolean;
|
||||
itemInEdit?: number;
|
||||
isViewPanelOpen: boolean;
|
||||
viewJSON?: {
|
||||
Title: string; Description: string; ClientSideComponentId: any;
|
||||
ClientSideComponentProperties: any; Id: any;
|
||||
};
|
||||
}
|
||||
|
||||
export default class ApplicationCustomizers extends React.Component<IApplicationCustomizersProps, IApplicationCustomizersState> {
|
||||
|
||||
constructor(props: IApplicationCustomizersProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
selectedItem: undefined,
|
||||
dropdownSites: undefined,
|
||||
expanded: 'panel1',
|
||||
allCustomizers: [],
|
||||
previousEditIndex: undefined,
|
||||
hideDialog: true,
|
||||
isPanelOpen: false,
|
||||
isViewPanelOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public componentDidMount() {
|
||||
applicationCustomizersService.fetchAllApplictionCustomizers(this.props.context.pageContext.web.absoluteUrl)
|
||||
.then((allCustomizers) => {
|
||||
allCustomizers = allCustomizers.map((cus) => { return assign(cus, { inEdit: false }); });
|
||||
this.setState({ allCustomizers: allCustomizers });
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
applicationCustomizersService.getAllSiteCollection()
|
||||
.then((allSites) => {
|
||||
let dropdownSites = allSites.PrimarySearchResults.map((val) => {
|
||||
val['key'] = val['SPSiteUrl'];
|
||||
val['text'] = `${val['Title']} - ${val['SPSiteUrl']}`;
|
||||
return val;
|
||||
});
|
||||
this.setState({ dropdownSites: dropdownSites });
|
||||
});
|
||||
}
|
||||
|
||||
private onChange = (event: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
|
||||
this.setState({ selectedItem: item });
|
||||
applicationCustomizersService.fetchAllApplictionCustomizers(item.key as string)
|
||||
.then((allCustomizers) => {
|
||||
allCustomizers = allCustomizers.map((cus) => { return assign(cus, { inEdit: false }); });
|
||||
this.setState({ allCustomizers: allCustomizers });
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
|
||||
public handleChange = (panel: string) => (event: React.ChangeEvent<{}>, newExpanded: boolean) => {
|
||||
this.setState({ expanded: newExpanded ? panel : false });
|
||||
}
|
||||
|
||||
public editCustomApplication = (index: number, inEdit: boolean) => {
|
||||
let allCustomizers = this.state.allCustomizers;
|
||||
allCustomizers[index].inEdit = inEdit;
|
||||
if (this.state.previousEditIndex !== undefined && inEdit) {
|
||||
allCustomizers[this.state.previousEditIndex].inEdit = false;
|
||||
}
|
||||
if (inEdit) {
|
||||
this.setState({
|
||||
isPanelOpen: this.props.designType === "List" ? true : false,
|
||||
itemInEdit: index,
|
||||
editJSON: {
|
||||
Title: allCustomizers[index].Title,
|
||||
Description: allCustomizers[index].Description,
|
||||
ClientSideComponentProperties: allCustomizers[index].ClientSideComponentProperties
|
||||
}
|
||||
});
|
||||
}
|
||||
if (!inEdit) {
|
||||
this.setState({ isPanelOpen: false });
|
||||
}
|
||||
this.setState({ allCustomizers: allCustomizers, previousEditIndex: index });
|
||||
}
|
||||
|
||||
public onChangeJSON = (obj: string, newValue: string) => {
|
||||
let { editJSON } = this.state;
|
||||
editJSON[obj] = newValue;
|
||||
this.setState({ editJSON });
|
||||
}
|
||||
|
||||
public updateCustomizer = (index: number) => {
|
||||
let webURL = this.state.selectedItem ? this.state.selectedItem.key : this.props.context.pageContext.web.absoluteUrl;
|
||||
let { allCustomizers } = this.state;
|
||||
applicationCustomizersService.updateApplicationCustomizer(webURL, this.state.allCustomizers[index].Id, this.state.editJSON)
|
||||
.then(() => {
|
||||
allCustomizers[index].inEdit = false;
|
||||
this.setState({
|
||||
allCustomizers: allCustomizers,
|
||||
hideDialog: false,
|
||||
isPanelOpen: false,
|
||||
dialogContentProps: {
|
||||
type: DialogType.normal,
|
||||
title: 'Updated Successfully',
|
||||
closeButtonAriaLabel: 'Close',
|
||||
subText: 'Your Customizer is updated. Please refresh the page to look at the changes?'
|
||||
}
|
||||
});
|
||||
}).catch((err) => {
|
||||
this.setState({
|
||||
hideDialog: false,
|
||||
dialogContentProps: {
|
||||
type: DialogType.normal,
|
||||
title: 'Updat Error',
|
||||
closeButtonAriaLabel: 'Close',
|
||||
subText: 'There was some error while updating you customizer. Please try again'
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private toggleHideDialog = () => {
|
||||
applicationCustomizersService.fetchAllApplictionCustomizers(this.state.selectedItem ?
|
||||
this.state.selectedItem.key as string : this.props.context.pageContext.web.absoluteUrl)
|
||||
.then((allCustomizers) => {
|
||||
allCustomizers = allCustomizers.map((cus) => { return assign(cus, { inEdit: false }); });
|
||||
this.setState({ allCustomizers: allCustomizers, hideDialog: true });
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
|
||||
private viewCustomApplication = (index: number) => {
|
||||
let { allCustomizers } = this.state;
|
||||
this.setState({
|
||||
isViewPanelOpen: true,
|
||||
viewJSON: {
|
||||
Title: allCustomizers[index].Title,
|
||||
ClientSideComponentId: allCustomizers[index].ClientSideComponentId,
|
||||
ClientSideComponentProperties: allCustomizers[index].ClientSideComponentProperties,
|
||||
Description: allCustomizers[index].Description,
|
||||
Id: allCustomizers[index].Id
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private onRenderCell = (item: any, index: number, isScrolling: boolean): JSX.Element => {
|
||||
return (
|
||||
<div className={classNames.itemCell} data-is-focusable={true}>
|
||||
<div className={classNames.itemContent}>
|
||||
<div className={classNames.itemName}>{item.Title}</div>
|
||||
<div className={classNames.itemIndex}>{item.Description}</div>
|
||||
</div>
|
||||
<IconButton iconProps={{ iconName: 'View' }} onClick={() => { this.viewCustomApplication(index); }} title="View" ariaLabel="View"></IconButton>
|
||||
<IconButton iconProps={{ iconName: 'Edit' }} onClick={() => { this.editCustomApplication(index, true); }} title="Edit" ariaLabel="Edit"></IconButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public render(): React.ReactElement<IApplicationCustomizersProps> {
|
||||
return (
|
||||
<div className={styles.applicationCustomizers}>
|
||||
<div className={styles.container}>
|
||||
<div className={styles.row}>
|
||||
<h1>{this.props.description}</h1>
|
||||
</div>
|
||||
<div className={styles.row}>
|
||||
<Dropdown
|
||||
label="Select Web"
|
||||
selectedKey={this.state.selectedItem ? this.state.selectedItem.key : undefined}
|
||||
onChange={this.onChange}
|
||||
placeholder="Select an option"
|
||||
options={this.state.dropdownSites}
|
||||
/>
|
||||
</div>
|
||||
{this.props.designType === "Accordion" && this.state.allCustomizers.length !== 0 &&
|
||||
<div className={styles.row}>
|
||||
{this.state.allCustomizers.map((customizer, index) => {
|
||||
return (
|
||||
<Accordion square expanded={this.state.expanded === `panel${index + 1}`} onChange={this.handleChange(`panel${index + 1}`)}>
|
||||
<AccordionSummary aria-controls="panel1d-content" id="panel1d-header">
|
||||
<div>{customizer.Title}{customizer.Description && ` - ${customizer.Description}`}</div>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
{!customizer.inEdit ?
|
||||
<div>
|
||||
<div className={styles.column2}>Component ID</div>
|
||||
<div className={styles.column}>{customizer.ClientSideComponentId}</div>
|
||||
<div className={styles.column2}>ID</div>
|
||||
<div className={styles.column}>{customizer.Id}</div>
|
||||
<div className={styles.column2}>Properties</div>
|
||||
<div className={styles.column}>{customizer.ClientSideComponentProperties}</div>
|
||||
</div> :
|
||||
<div>
|
||||
<div className={styles.column2}>Title</div>
|
||||
<div className={styles.column}><TextField value={this.state.editJSON.Title}
|
||||
onChange={(ev, newVal) => {
|
||||
this.onChangeJSON("Title", newVal);
|
||||
}} />
|
||||
</div>
|
||||
<div className={styles.column2}>Description</div>
|
||||
<div className={styles.column}><TextField value={this.state.editJSON.Description} multiline rows={3}
|
||||
onChange={(ev, newVal) => {
|
||||
this.onChangeJSON("Description", newVal);
|
||||
}} /></div>
|
||||
<div className={styles.column2}>Properties</div>
|
||||
|
||||
<div className={styles.column}>
|
||||
<AceEditor
|
||||
placeholder="Placeholder Text"
|
||||
mode="json"
|
||||
theme="github"
|
||||
onChange={(val) => { this.onChangeJSON("ClientSideComponentProperties", val); }}
|
||||
fontSize={14}
|
||||
style={{ height: 200, width: 790 }}
|
||||
showPrintMargin={true}
|
||||
showGutter={true}
|
||||
highlightActiveLine={false}
|
||||
value={this.state.editJSON.ClientSideComponentProperties}
|
||||
setOptions={{
|
||||
enableBasicAutocompletion: true,
|
||||
enableLiveAutocompletion: false,
|
||||
enableSnippets: false,
|
||||
showLineNumbers: true,
|
||||
tabSize: 2,
|
||||
}} />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
{!customizer.inEdit ?
|
||||
<DefaultButton className={styles.button} text="Edit" onClick={() => { this.editCustomApplication(index, true); }} /> :
|
||||
[<DefaultButton className={styles.button} text="Update" onClick={() => { this.updateCustomizer(index); }} />,
|
||||
<DefaultButton style={{ marginLeft: 10, marginTop: 10 }} text="Cancel" onClick={() => { this.editCustomApplication(index, false); }} />]
|
||||
}
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
}
|
||||
{this.props.designType === "List" && this.state.allCustomizers.length !== 0 &&
|
||||
<div className={styles.row}>
|
||||
<List items={this.state.allCustomizers} onRenderCell={this.onRenderCell} />
|
||||
</div>
|
||||
}
|
||||
{this.state.allCustomizers.length === 0 &&
|
||||
<div className={styles.row}>We did not find any Application Customizers for the selected web</div>
|
||||
}
|
||||
<Dialog
|
||||
hidden={this.state.hideDialog}
|
||||
onDismiss={() => this.toggleHideDialog()}
|
||||
dialogContentProps={this.state.dialogContentProps}
|
||||
>
|
||||
<DialogFooter>
|
||||
<DefaultButton onClick={() => this.toggleHideDialog()} text="Cancel" />
|
||||
</DialogFooter>
|
||||
</Dialog>
|
||||
<Panel
|
||||
headerText="Edit Application Customizer"
|
||||
isOpen={this.state.isPanelOpen}
|
||||
onDismiss={() => this.setState({ isPanelOpen: false })}
|
||||
closeButtonAriaLabel="Close"
|
||||
type={PanelType.large}
|
||||
>
|
||||
{this.state.editJSON &&
|
||||
<div className={styles.applicationCustomizers}>
|
||||
<div className={styles.column2}>Title</div>
|
||||
<div className={styles.column}><TextField value={this.state.editJSON.Title}
|
||||
onChange={(ev, newVal) => {
|
||||
this.onChangeJSON("Title", newVal);
|
||||
}} />
|
||||
</div>
|
||||
<div className={styles.column2}>Description</div>
|
||||
<div className={styles.column}><TextField value={this.state.editJSON.Description} multiline rows={3}
|
||||
onChange={(ev, newVal) => {
|
||||
this.onChangeJSON("Description", newVal);
|
||||
}} /></div>
|
||||
<div className={styles.column2}>Properties</div>
|
||||
|
||||
<div className={styles.column}>
|
||||
<AceEditor
|
||||
placeholder="Placeholder Text"
|
||||
mode="json"
|
||||
theme="github"
|
||||
onChange={(val) => { this.onChangeJSON("ClientSideComponentProperties", val); }}
|
||||
fontSize={14}
|
||||
style={{ height: 200, width: 800 }}
|
||||
showPrintMargin={true}
|
||||
showGutter={true}
|
||||
highlightActiveLine={false}
|
||||
value={this.state.editJSON.ClientSideComponentProperties}
|
||||
setOptions={{
|
||||
enableBasicAutocompletion: true,
|
||||
enableLiveAutocompletion: false,
|
||||
enableSnippets: false,
|
||||
showLineNumbers: true,
|
||||
tabSize: 2,
|
||||
}} />
|
||||
</div>
|
||||
<DefaultButton style={{ marginLeft: 10, marginTop: 10 }} className={styles.button} text="Update" onClick={() => { this.updateCustomizer(this.state.itemInEdit); }} />
|
||||
<DefaultButton style={{ marginLeft: 10, marginTop: 10 }} text="Cancel" onClick={() => { this.editCustomApplication(this.state.itemInEdit, false); }} />
|
||||
</div>}
|
||||
</Panel>
|
||||
<Panel
|
||||
headerText="View Application Customizer"
|
||||
isOpen={this.state.isViewPanelOpen}
|
||||
onDismiss={() => this.setState({ isViewPanelOpen: false })}
|
||||
closeButtonAriaLabel="Close"
|
||||
type={PanelType.medium}
|
||||
>{this.state.viewJSON &&
|
||||
<div className={styles.applicationCustomizers}>
|
||||
<div className={styles.column2}>Title</div>
|
||||
<div className={styles.column}>{this.state.viewJSON.Title}</div>
|
||||
<div className={styles.column2}>Description</div>
|
||||
<div className={styles.column}>{this.state.viewJSON.Description ? this.state.viewJSON.Description : 'null'}</div>
|
||||
<div className={styles.column2}>ComponentID</div>
|
||||
<div className={styles.column}>{this.state.viewJSON.ClientSideComponentId}</div>
|
||||
<div className={styles.column2}>ID</div>
|
||||
<div className={styles.column}>{this.state.viewJSON.Id}</div>
|
||||
<div className={styles.column2}>Properties</div>
|
||||
<div className={styles.column}>{this.state.viewJSON.ClientSideComponentProperties}</div>
|
||||
</div>}</Panel>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { WebPartContext } from "@microsoft/sp-webpart-base";
|
||||
|
||||
export interface IApplicationCustomizersProps {
|
||||
description: string;
|
||||
context: WebPartContext;
|
||||
designType: string;
|
||||
}
|
8
samples/react-Edit-ApplicationCustomizer/src/webparts/applicationCustomizers/loc/en-us.js
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
define([], function () {
|
||||
return {
|
||||
"PropertyPaneDescription": "",
|
||||
"BasicGroupName": "",
|
||||
"DescriptionFieldLabel": "Title",
|
||||
"DesignFieldLabel": "Choose design"
|
||||
}
|
||||
});
|
11
samples/react-Edit-ApplicationCustomizer/src/webparts/applicationCustomizers/loc/mystrings.d.ts
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
declare interface IApplicationCustomizersWebPartStrings {
|
||||
PropertyPaneDescription: string;
|
||||
BasicGroupName: string;
|
||||
DescriptionFieldLabel: string;
|
||||
DesignFieldLabel: string;
|
||||
}
|
||||
|
||||
declare module 'ApplicationCustomizersWebPartStrings' {
|
||||
const strings: IApplicationCustomizersWebPartStrings;
|
||||
export = strings;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import { sp } from "@pnp/sp";
|
||||
import "@pnp/sp/webs";
|
||||
import "@pnp/sp/user-custom-actions";
|
||||
import { ISearchQuery } from "@pnp/sp/search";
|
||||
import { Web } from "@pnp/sp/webs";
|
||||
|
||||
|
||||
export default class ApplicationCustomizersService {
|
||||
|
||||
/**
|
||||
* fetchAllApplictionCustomizers
|
||||
*/
|
||||
public fetchAllApplictionCustomizers = async (webURL: string) => {
|
||||
let web = Web(webURL);
|
||||
let response;
|
||||
try {
|
||||
response = await web.userCustomActions();
|
||||
console.log(response);
|
||||
//let temp = await sp.site.userCustomActions();
|
||||
//console.log(temp);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
response = error;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* getAllSiteCollection
|
||||
*/
|
||||
public getAllSiteCollection = async () => {
|
||||
let response;
|
||||
try {
|
||||
response = await sp.search(<ISearchQuery>{
|
||||
Querytext: "contentclass:STS_Site",
|
||||
SelectProperties: ["Title", "SPSiteUrl", "WebTemplate"],
|
||||
RowLimit: 1000,
|
||||
TrimDuplicates: true
|
||||
});
|
||||
console.log(response.PrimarySearchResults);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
response = error;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* updateApplicationCustomizer
|
||||
*/
|
||||
public updateApplicationCustomizer = async (webURL: string | number, selectedID: string, updateJSON: any) => {
|
||||
let web = Web(webURL as string);
|
||||
let response;
|
||||
try {
|
||||
response = await web.userCustomActions.getById(selectedID).update(updateJSON);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
After Width: | Height: | Size: 1.2 KiB |
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
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
# SPFx Dynamic Accordion - FAQ Builder web part
|
||||
# Dynamic Accordion - FAQ Builder
|
||||
|
||||
## Summary
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
|||
- Adds a collapsible accordion section to an Office 365 SharePoint page or Teams Tab.
|
||||
- Ideal for displaying FAQs.
|
||||
- When adding the web part, you'll be prompted to select a list from a property panel dropdown (target list must be created with FAQ type Question and Answer.).
|
||||
- The web part expects a choice type that will be used as Category.
|
||||
- The web part expects a column called **Category** of type choice that will be used as the category.
|
||||
- The web part will automatically load all the properties in two dropdowns. One for Accordion Title and One for Accordion Content that must be html type.
|
||||
- This will generate an accordion with one section for each item in the list.
|
||||
- Modifications/deletions/additions to the list items in the target list of an added web part are automatically reflected on the page.
|
||||
|
@ -23,8 +23,8 @@
|
|||
- When creating the columns, select "Multiple lines of text". Rich text is now supported within the Content column.
|
||||
|
||||
![Create list for use with the Accordion](./assets/FAQsList.png)
|
||||
![FAQ list Template for use with the Accordion](./assets/FAQsList.stp)
|
||||
![FAQ Site Script use with the Accordion](./assets/FAQsList.json)
|
||||
[FAQ list Template for use with the Accordion](./assets/FAQsList.stp)
|
||||
[FAQ Site Script use with the Accordion](./assets/FAQsList.json)
|
||||
|
||||
**2) Add the Dynamic Accordion Section web part to your page & select your list, category, title and content columns. Click Apply and Publish:**
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# SPFx Accordion Section FAQ Builder web part
|
||||
# Accordion Section -- FAQ Builder
|
||||
|
||||
## Summary
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ extensions:
|
|||
createdDate: 1/8/2018 12:00:00 AM
|
||||
---
|
||||
|
||||
## Using React Accordion plugin with SPFx
|
||||
# Using React Accordion plugin with SPFx
|
||||
|
||||
## Summary
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ extensions:
|
|||
- React
|
||||
createdDate: 11/28/2018 12:00:00 AM
|
||||
---
|
||||
# Image Gallery Web Part Built with Adaptive Cards
|
||||
# Image Gallery Built with Adaptive Cards
|
||||
|
||||
## Summary
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# react-adaptivecards-hooks
|
||||
# Adaptive Cards using React Hooks
|
||||
|
||||
## Summary
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ extensions:
|
|||
- react
|
||||
createdDate: 04/24/2020 12:00:00 AM
|
||||
---
|
||||
## SPFx webpart to add JS and CSS reference on Modern Pages via SPFx application customizer extension
|
||||
# Add JS and CSS reference on Modern Pages via SPFx application customizer extension
|
||||
|
||||
This repo is a react based SPFx web part and extension that allows users to add/modify/delete custom js and css file references using SPFx application customizer extension all modern pages within SP online site. This web part provides an interface to JS and CSS file references so that we don't have to modify code when we need to change references or add new references in the future. As part of security measures, this actions on web part can be only accessed by users who have Manage web permission on site.
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# react-admin-sc-catalog-pnpjs - Site Collection App Catalogs Summary View.
|
||||
# Site Collection App Catalog Summary View.
|
||||
|
||||
## Summary
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# React Aggregated Calendar Webpart
|
||||
# Aggregated Calendar
|
||||
|
||||
## Summary
|
||||
This is a sample webpart developed using React Framework to gather the aggregated events from the multiple calendars from multiple sites using Full Calendar from fullcalendar.io
|
||||
|
|
|
@ -13,7 +13,7 @@ extensions:
|
|||
- react
|
||||
createdDate: 8/1/2017 12:00:00 AM
|
||||
---
|
||||
# SPFx React app settings webpart #
|
||||
# App Settings
|
||||
|
||||
## Summary
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ extensions:
|
|||
- react
|
||||
createdDate: 5/10/2020 12:00:00 AM
|
||||
---
|
||||
# React AppInsights Dashboard
|
||||
# Azure Application Insights Dashboard
|
||||
|
||||
## Summary
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ extensions:
|
|||
- react
|
||||
createdDate: 5/1/2017 12:00:00 AM
|
||||
---
|
||||
# React sample showing the use of @pnp/js with Async / Await
|
||||
# Using @pnp/js with Async / Await
|
||||
|
||||
## Summary
|
||||
This webpart demonstrates how to use [PnPJS](https://pnp.github.io/pnpjs/) with Async functions into the SharePoint Framework as well as integrating [PnP JS and SPFx Logging systems](https://blog.josequinto.com/2017/04/30/how-to-integrate-pnp-js-core-and-sharepoint-framework-logging-systems/). Real example querying SharePoint library to show document sizes.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# SPFx Avatar
|
||||
# Avatar Editor
|
||||
|
||||
## Summary
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
## Local Azure Function and SPFx Web Part Development to consume third party APIs
|
||||
# Local Azure Function and SPFx Web Part Development to consume third party APIs
|
||||
|
||||
This sample shows how to consume third-party APIs through an Azure Functions by a Web Part. In this scenario, Vimeo is the representative third party API.
|
||||
This project contains two separate project folders:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# React Birthdays Web Part
|
||||
# Birthdays
|
||||
|
||||
## Summary
|
||||
The Web Part Birthdays shows the upcoming birthdays in the company, the web part reads birthdays from a list located on the tenant's root site with title "Birthdays."
|
||||
|
@ -17,7 +17,7 @@ But you can synchronize the Birthdays list with other applications HR Systems, o
|
|||
|
||||
|
||||
## Used SharePoint Framework Version
|
||||
![drop](https://img.shields.io/badge/version-1.8.2-green.svg)
|
||||
![1.8.2](https://img.shields.io/badge/version-1.8.2-green.svg)
|
||||
|
||||
## Applies to
|
||||
|
||||
|
|
|
@ -0,0 +1,214 @@
|
|||
# SharePoint web part sample with bot framework - Secure
|
||||
|
||||
## Summary
|
||||
|
||||
[Web parts](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/overview-client-side-web-parts) is a special kind of SharePoint controls that can be supported by the [Bot Framework](https://dev.botframework.com). This sample will show you how to embed a Bot Framework bot into a SharePoint web site with security consideration.
|
||||
|
||||
There are two parts included in this sample:
|
||||
|
||||
1. An echo bot sample
|
||||
1. A web part sample
|
||||
|
||||
The web part sample embeds the echo bot by using a webchat. As web part code is running on client side, [web chat security](https://blog.botframework.com/2018/09/01/using-webchat-with-azure-bot-services-authentication/) needs to be taken into consideration. This sample shows how to secure your conversation including:
|
||||
|
||||
- Use Direct Line token instead of Direct Line secret
|
||||
- Tamper-proof user: for user id, generate it inside client side and detect if the client has changed the user ID and reject the change.
|
||||
|
||||
This demo does not include any threat models and is designed for educational purposes only. When you design a production system, threat-modelling is an important task to make sure your system is secure and provide a way to quickly identify potential source of data breaches. IETF [RFC 6819](https://tools.ietf.org/html/rfc6819) and [OAuth 2.0 for Browser-Based Apps](https://tools.ietf.org/html/draft-ietf-oauth-browser-based-apps-01#section-9) is a good starting point for threat-modelling when using OAuth 2.0.
|
||||
|
||||
![react-bot-framework-secure](./assets/sp-wp-secure.gif)
|
||||
|
||||
## Used SharePoint Framework Version
|
||||
|
||||
![1.0](https://img.shields.io/badge/version-1.0-green.svg)
|
||||
|
||||
## 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)
|
||||
* [Microsoft Bot Framework](https://dev.botframework.com/)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Node.js](https://nodejs.org) version 10.19 (Node.js v9.x, v11.x, and v12.x are not currently supported with SharePoint Framework development)
|
||||
|
||||
```bash
|
||||
# determine node version
|
||||
node --version
|
||||
```
|
||||
|
||||
## Solution
|
||||
|
||||
Solution|Author(s)
|
||||
--------|---------
|
||||
webpart | STCA BF Channel and ABS (stcabfchannel@microsoft.com) <br/> Stephan Bisser (@stephanbisser, bisser.io)
|
||||
bot | STCA BF Channel and ABS (stcabfchannel@microsoft.com)
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0|Nov 10, 2020|Initial release
|
||||
|
||||
## 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.**
|
||||
|
||||
---
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
||||
### Enlist
|
||||
|
||||
- Clone the repository
|
||||
|
||||
```bash
|
||||
git clone https://github.com/pnp/sp-dev-fx-webparts.git
|
||||
```
|
||||
|
||||
- In a terminal, navigate to `sp-dev-fx-webparts`
|
||||
|
||||
```bash
|
||||
cd sp-dev-fx-webparts
|
||||
```
|
||||
|
||||
- Navigate to the folder containing this sample
|
||||
|
||||
```bash
|
||||
cd samples
|
||||
cd react-bot-framework-secure
|
||||
```
|
||||
|
||||
|
||||
### [Setup bot](./bot/README.md)
|
||||
|
||||
- Go to `./bot`
|
||||
- Install modules
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
- Start the bot. You can use emulator to verify whether the bot works
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
- Register connections. You can get it done by [deploy your bot to Azure](https://aka.ms/azuredeployment). Save your bot service endpoint like: "https://YOUR_BOT.azurewebsites.net". Save your AAD Id as `YOUR_APP_ID` and secret as `YOUR_APP_PSW` also.
|
||||
|
||||
- [Connect to direct line](https://docs.microsoft.com/en-us/azure/bot-service/bot-service-channel-connect-directline?view=azure-bot-service-4.0), copy one of the Secret Key values as YOUR_DIRECT_LINE_SECRET and store this for later. This is your ‘Direct Line Secret’.
|
||||
|
||||
- Add `DirectLineSecret` to an `.env` config file under `./bot`
|
||||
|
||||
```bash
|
||||
MicrosoftAppId=YOUR_APP_ID
|
||||
MicrosoftAppPassword=YOUR_APP_PSW
|
||||
DirectLineSecret=YOUR_DIRECT_LINE_SECRET
|
||||
```
|
||||
|
||||
- Restart your bot in local or redeploy your bot with new config.
|
||||
|
||||
### [Setup web part](./webpart/README.md)
|
||||
|
||||
- Go to `./webpart`
|
||||
- Install modules
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
- Start web part
|
||||
|
||||
```bash
|
||||
gulp serve
|
||||
```
|
||||
|
||||
Now web part is running locally in https://localhost:4321.
|
||||
|
||||
- (Opt.) Publish the bot: follow the steps outlined in the [Deploy your bot](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-deploy-az-cli?view=azure-bot-service-4.0&tabs=csharp) article.
|
||||
|
||||
- (Opt.) Config CORS \
|
||||
[CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) must be set on bot app service to enable SharePoint client to get resource from bot service. Follow these steps to add your workbench to bot app service CORS configuration:
|
||||
1. Go to your azure portal
|
||||
1. Navigate to your bot app service, search for CORS settings
|
||||
1. Add https://localhost:4321 and https://<YOUR_SITE>.sharepoint.com to CORS origins
|
||||
|
||||
- Config bot endpoint \
|
||||
Add the web part, set bot endpoint to https://localhost:4321 (local) or https://YOUR_BOT.azurewebsites.net (remote), refresh this page, then you can successfully connect bot with SharePoint.
|
||||
|
||||
## Features
|
||||
|
||||
**Web Chat integration with security consideration**
|
||||
|
||||
The SharePoint component will integrate bot with react Web Chat component.
|
||||
|
||||
```tsx
|
||||
public render(): React.ReactElement<IBotFrameworkChatv4Props> {
|
||||
return (
|
||||
<div className={styles.botFrameworkChatv4} style={{ height: 700 }}>
|
||||
<ReactWebChat directLine={directLine} styleOptions={styleSetOptions} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Inside Web Chat, direct line will be used to connect to Bot Service. On Bot Service side, one more endpoint `directline/token` will be added besides `api/messages`, which will accept userId passed from client side and return back direct line token.
|
||||
|
||||
For production, this endpoint should also verify if the incoming request is authorized.
|
||||
|
||||
```tsx
|
||||
server.post('/directline/token', (req, res) => {
|
||||
const secret = settings.parsed.DirectLineSecret;
|
||||
const authorization = `Bearer ${secret}`;
|
||||
|
||||
const userId = 'dl_' + GetUserId((req.body || {}).user);
|
||||
const options = {
|
||||
method: 'POST',
|
||||
uri: 'https://directline.botframework.com/v3/directline/tokens/generate',
|
||||
body: JSON.stringify({ user: { id: userId} }),
|
||||
headers: { 'Authorization': authorization, 'Content-Type': 'application/json'}
|
||||
};+
|
||||
|
||||
request.post(options, (error, response, body) => {
|
||||
if (!error && response.statusCode < 300) {
|
||||
res.status(response.statusCode);
|
||||
if (body) { res.send(JSON.parse(body)) }
|
||||
} else {
|
||||
res.status(500);
|
||||
res.send('Call to retrieve token from DirectLine failed');
|
||||
}
|
||||
res.end();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
On web part side, it will fetch direct line token from bot service side with SharePoint `userId` then build up the web chat component. The `UserId` should be encrypted so it won't be easy to get other user's token by bot endpoint.
|
||||
|
||||
```tsx
|
||||
useEffect(() => {
|
||||
const userId = props.context.pageContext.user.loginName;
|
||||
generateToken(props.botEndpoint, md5(userId)).then((token: string) => {
|
||||
if (token) {
|
||||
setDirectLine(createDirectLine({ token }));
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
```
|
||||
|
||||
And enable "Enhanced authentication options" can help detect client user Id change then reject the change:
|
||||
![bot framework client web part](./assets/EnhancedAuth.png)\
|
||||
For how to find this option, please refer [connect to direct line](https://docs.microsoft.com/en-us/azure/bot-service/bot-service-channel-connect-directline?view=azure-bot-service-4.0).
|
||||
|
||||
## Further reading
|
||||
|
||||
- [SharePoint Web Parts Development Basics](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/overview-client-side-web-parts)
|
||||
- [Bot Framework Documentation](https://docs.botframework.com)
|
||||
- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0)
|
||||
- [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0)
|
||||
- [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0)
|
||||
- [Channels and Bot Connector Service](https://docs.microsoft.com/en-us/azure/bot-service/bot-concepts?view=azure-bot-service-4.0)
|
||||
- [Restify](https://www.npmjs.com/package/restify)
|
||||
- [Using WebChat with Azure Bot Service’s Authentication](https://blog.botframework.com/2018/09/01/using-webchat-with-azure-bot-services-authentication/)
|
||||
|
||||
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-bot-framework-secure" />
|
After Width: | Height: | Size: 105 KiB |
After Width: | Height: | Size: 5.1 MiB |
|
@ -0,0 +1,35 @@
|
|||
# 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
|
||||
.vscode
|
||||
bin
|
||||
obj
|
||||
|
||||
# Resx Generated Code
|
||||
*.resx.ts
|
||||
|
||||
# Styles Generated Code
|
||||
*.scss.ts
|
||||
|
||||
packages/
|
|
@ -0,0 +1,108 @@
|
|||
# Echo bot
|
||||
|
||||
## Summary
|
||||
|
||||
This bot has been created using [Bot Framework](https://dev.botframework.com). It shows how to create a simple bot that accepts input from the user and echoes it back.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Node.js](https://nodejs.org) version 10.14.1 or higher
|
||||
|
||||
```bash
|
||||
# determine node version
|
||||
node --version
|
||||
```
|
||||
|
||||
## To try this sample locally
|
||||
|
||||
- Clone the repository
|
||||
|
||||
```bash
|
||||
git clone [Placeholder]
|
||||
```
|
||||
|
||||
- In a console, navigate to [Placeholder]
|
||||
|
||||
```bash
|
||||
cd [Placeholder]
|
||||
```
|
||||
|
||||
- Install modules
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
- Start the bot
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
## Testing the bot using Bot Framework Emulator
|
||||
|
||||
[Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel.
|
||||
|
||||
- Install the Bot Framework Emulator version 4.3.0 or greater from [here](https://github.com/Microsoft/BotFramework-Emulator/releases)
|
||||
|
||||
### Connect to the bot using Bot Framework Emulator
|
||||
|
||||
- Launch Bot Framework Emulator
|
||||
- File -> Open Bot
|
||||
- Enter a Bot URL of `http://localhost:3978/api/messages`
|
||||
|
||||
## (Opt.) Deploy the bot to Azure
|
||||
|
||||
To learn more about deploying a bot to Azure, see [Deploy your bot to Azure](https://aka.ms/azuredeployment) for a complete list of deployment instructions.
|
||||
|
||||
## (Opt.) Testing Direct Line token generation
|
||||
|
||||
- [Connect to Direct Line](https://docs.microsoft.com/en-us/azure/bot-service/bot-service-channel-connect-directline?view=azure-bot-service-4.0)
|
||||
|
||||
- Add `DirectLineSecret` to `.env`
|
||||
|
||||
```bash
|
||||
DirectLineSecret=YOUR_DIRECT_LINE_SECRET
|
||||
```
|
||||
|
||||
- Start the bot
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
- Open [PostMan](https://www.postman.com/) and setup a post request to http://localhost:3978/directline/token
|
||||
with the following json request body:
|
||||
|
||||
```json
|
||||
{
|
||||
"user": "USER_ID"
|
||||
}
|
||||
```
|
||||
|
||||
Then you can see the Direct Line token generated with `YOUR_DIRECT_LINE_SECRET` and `USER_ID`:
|
||||
|
||||
```json
|
||||
{
|
||||
"conversationId": "XXXXX",
|
||||
"token": "XXXXX",
|
||||
"expires_in": 3600
|
||||
}
|
||||
```
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Bot Framework Documentation](https://docs.botframework.com)
|
||||
- [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0)
|
||||
- [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0)
|
||||
- [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0)
|
||||
- [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0)
|
||||
- [Azure CLI](https://docs.microsoft.com/cli/azure/?view=azure-cli-latest)
|
||||
- [Azure Portal](https://portal.azure.com)
|
||||
- [Language Understanding using LUIS](https://docs.microsoft.com/en-us/azure/cognitive-services/luis/)
|
||||
- [Channels and Bot Connector Service](https://docs.microsoft.com/en-us/azure/bot-service/bot-concepts?view=azure-bot-service-4.0)
|
||||
- [TypeScript](https://www.typescriptlang.org)
|
||||
- [Restify](https://www.npmjs.com/package/restify)
|
||||
- [dotenv](https://www.npmjs.com/package/dotenv)
|
||||
|
||||
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-bot-framework-secure/bot" />
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
// DO NOT MODIFY THIS CODE
|
||||
// This script is run as part of the Post Deploy step when
|
||||
// deploying the bot to Azure. It ensures the Azure Web App
|
||||
// is configured correctly to host a TypeScript authored bot.
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const replace = require('replace');
|
||||
const WEB_CONFIG_FILE = './web.config';
|
||||
|
||||
if (fs.existsSync(path.resolve(WEB_CONFIG_FILE))) {
|
||||
replace({
|
||||
regex: "url=\"index.js\"",
|
||||
replacement: "url=\"lib/index.js\"",
|
||||
paths: ['./web.config'],
|
||||
recursive: false,
|
||||
silent: true,
|
||||
})
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"groupLocation": {
|
||||
"value": ""
|
||||
},
|
||||
"groupName": {
|
||||
"value": ""
|
||||
},
|
||||
"appId": {
|
||||
"value": ""
|
||||
},
|
||||
"appSecret": {
|
||||
"value": ""
|
||||
},
|
||||
"botId": {
|
||||
"value": ""
|
||||
},
|
||||
"botSku": {
|
||||
"value": ""
|
||||
},
|
||||
"newAppServicePlanName": {
|
||||
"value": ""
|
||||
},
|
||||
"newAppServicePlanSku": {
|
||||
"value": {
|
||||
"name": "S1",
|
||||
"tier": "Standard",
|
||||
"size": "S1",
|
||||
"family": "S",
|
||||
"capacity": 1
|
||||
}
|
||||
},
|
||||
"newAppServicePlanLocation": {
|
||||
"value": ""
|
||||
},
|
||||
"newWebAppName": {
|
||||
"value": ""
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"appId": {
|
||||
"value": ""
|
||||
},
|
||||
"appSecret": {
|
||||
"value": ""
|
||||
},
|
||||
"botId": {
|
||||
"value": ""
|
||||
},
|
||||
"botSku": {
|
||||
"value": ""
|
||||
},
|
||||
"newAppServicePlanName": {
|
||||
"value": ""
|
||||
},
|
||||
"newAppServicePlanSku": {
|
||||
"value": {
|
||||
"name": "S1",
|
||||
"tier": "Standard",
|
||||
"size": "S1",
|
||||
"family": "S",
|
||||
"capacity": 1
|
||||
}
|
||||
},
|
||||
"appServicePlanLocation": {
|
||||
"value": ""
|
||||
},
|
||||
"existingAppServicePlan": {
|
||||
"value": ""
|
||||
},
|
||||
"newWebAppName": {
|
||||
"value": ""
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"groupLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Specifies the location of the Resource Group."
|
||||
}
|
||||
},
|
||||
"groupName": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Specifies the name of the Resource Group."
|
||||
}
|
||||
},
|
||||
"appId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"appSecret": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"botId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
|
||||
}
|
||||
},
|
||||
"botSku": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanName": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The name of the App Service Plan."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanSku": {
|
||||
"type": "object",
|
||||
"defaultValue": {
|
||||
"name": "S1",
|
||||
"tier": "Standard",
|
||||
"size": "S1",
|
||||
"family": "S",
|
||||
"capacity": 1
|
||||
},
|
||||
"metadata": {
|
||||
"description": "The SKU of the App Service Plan. Defaults to Standard values."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The location of the App Service Plan. Defaults to \"westus\"."
|
||||
}
|
||||
},
|
||||
"newWebAppName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"appServicePlanName": "[parameters('newAppServicePlanName')]",
|
||||
"resourcesLocation": "[parameters('newAppServicePlanLocation')]",
|
||||
"webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
|
||||
"siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
|
||||
"botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]",
|
||||
"resourceGroupId": "[concat(subscription().id, '/resourceGroups/', parameters('groupName'))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"name": "[parameters('groupName')]",
|
||||
"type": "Microsoft.Resources/resourceGroups",
|
||||
"apiVersion": "2018-05-01",
|
||||
"location": "[parameters('groupLocation')]",
|
||||
"properties": {}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Resources/deployments",
|
||||
"apiVersion": "2018-05-01",
|
||||
"name": "storageDeployment",
|
||||
"resourceGroup": "[parameters('groupName')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Resources/resourceGroups/', parameters('groupName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"mode": "Incremental",
|
||||
"template": {
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {},
|
||||
"variables": {},
|
||||
"resources": [
|
||||
{
|
||||
"comments": "Create a new App Service Plan",
|
||||
"type": "Microsoft.Web/serverfarms",
|
||||
"name": "[variables('appServicePlanName')]",
|
||||
"apiVersion": "2018-02-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"sku": "[parameters('newAppServicePlanSku')]",
|
||||
"properties": {
|
||||
"name": "[variables('appServicePlanName')]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"comments": "Create a Web App using the new App Service Plan",
|
||||
"type": "Microsoft.Web/sites",
|
||||
"apiVersion": "2015-08-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"kind": "app",
|
||||
"dependsOn": [
|
||||
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/serverfarms/', variables('appServicePlanName'))]"
|
||||
],
|
||||
"name": "[variables('webAppName')]",
|
||||
"properties": {
|
||||
"name": "[variables('webAppName')]",
|
||||
"serverFarmId": "[variables('appServicePlanName')]",
|
||||
"siteConfig": {
|
||||
"appSettings": [
|
||||
{
|
||||
"name": "WEBSITE_NODE_DEFAULT_VERSION",
|
||||
"value": "10.14.1"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppId",
|
||||
"value": "[parameters('appId')]"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppPassword",
|
||||
"value": "[parameters('appSecret')]"
|
||||
}
|
||||
],
|
||||
"cors": {
|
||||
"allowedOrigins": [
|
||||
"https://botservice.hosting.portal.azure.net",
|
||||
"https://hosting.onecloud.azure-test.net/"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2017-12-01",
|
||||
"type": "Microsoft.BotService/botServices",
|
||||
"name": "[parameters('botId')]",
|
||||
"location": "global",
|
||||
"kind": "bot",
|
||||
"sku": {
|
||||
"name": "[parameters('botSku')]"
|
||||
},
|
||||
"properties": {
|
||||
"name": "[parameters('botId')]",
|
||||
"displayName": "[parameters('botId')]",
|
||||
"endpoint": "[variables('botEndpoint')]",
|
||||
"msaAppId": "[parameters('appId')]",
|
||||
"developerAppInsightsApplicationId": null,
|
||||
"developerAppInsightKey": null,
|
||||
"publishingCredentials": null,
|
||||
"storageResourceId": null
|
||||
},
|
||||
"dependsOn": [
|
||||
"[concat(variables('resourceGroupId'), '/providers/Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"appId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings."
|
||||
}
|
||||
},
|
||||
"appSecret": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings. Defaults to \"\"."
|
||||
}
|
||||
},
|
||||
"botId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable."
|
||||
}
|
||||
},
|
||||
"botSku": {
|
||||
"defaultValue": "F0",
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The name of the new App Service Plan."
|
||||
}
|
||||
},
|
||||
"newAppServicePlanSku": {
|
||||
"type": "object",
|
||||
"defaultValue": {
|
||||
"name": "S1",
|
||||
"tier": "Standard",
|
||||
"size": "S1",
|
||||
"family": "S",
|
||||
"capacity": 1
|
||||
},
|
||||
"metadata": {
|
||||
"description": "The SKU of the App Service Plan. Defaults to Standard values."
|
||||
}
|
||||
},
|
||||
"appServicePlanLocation": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "The location of the App Service Plan."
|
||||
}
|
||||
},
|
||||
"existingAppServicePlan": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "Name of the existing App Service Plan used to create the Web App for the bot."
|
||||
}
|
||||
},
|
||||
"newWebAppName": {
|
||||
"type": "string",
|
||||
"defaultValue": "",
|
||||
"metadata": {
|
||||
"description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"."
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]",
|
||||
"useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]",
|
||||
"servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]",
|
||||
"resourcesLocation": "[parameters('appServicePlanLocation')]",
|
||||
"webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]",
|
||||
"siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]",
|
||||
"botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"comments": "Create a new App Service Plan if no existing App Service Plan name was passed in.",
|
||||
"type": "Microsoft.Web/serverfarms",
|
||||
"condition": "[not(variables('useExistingAppServicePlan'))]",
|
||||
"name": "[variables('servicePlanName')]",
|
||||
"apiVersion": "2018-02-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"sku": "[parameters('newAppServicePlanSku')]",
|
||||
"properties": {
|
||||
"name": "[variables('servicePlanName')]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"comments": "Create a Web App using an App Service Plan",
|
||||
"type": "Microsoft.Web/sites",
|
||||
"apiVersion": "2015-08-01",
|
||||
"location": "[variables('resourcesLocation')]",
|
||||
"kind": "app",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]"
|
||||
],
|
||||
"name": "[variables('webAppName')]",
|
||||
"properties": {
|
||||
"name": "[variables('webAppName')]",
|
||||
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]",
|
||||
"siteConfig": {
|
||||
"appSettings": [
|
||||
{
|
||||
"name": "WEBSITE_NODE_DEFAULT_VERSION",
|
||||
"value": "10.14.1"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppId",
|
||||
"value": "[parameters('appId')]"
|
||||
},
|
||||
{
|
||||
"name": "MicrosoftAppPassword",
|
||||
"value": "[parameters('appSecret')]"
|
||||
}
|
||||
],
|
||||
"cors": {
|
||||
"allowedOrigins": [
|
||||
"https://botservice.hosting.portal.azure.net",
|
||||
"https://hosting.onecloud.azure-test.net/"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"apiVersion": "2017-12-01",
|
||||
"type": "Microsoft.BotService/botServices",
|
||||
"name": "[parameters('botId')]",
|
||||
"location": "global",
|
||||
"kind": "bot",
|
||||
"sku": {
|
||||
"name": "[parameters('botSku')]"
|
||||
},
|
||||
"properties": {
|
||||
"name": "[parameters('botId')]",
|
||||
"displayName": "[parameters('botId')]",
|
||||
"endpoint": "[variables('botEndpoint')]",
|
||||
"msaAppId": "[parameters('appId')]",
|
||||
"developerAppInsightsApplicationId": null,
|
||||
"developerAppInsightKey": null,
|
||||
"publishingCredentials": null,
|
||||
"storageResourceId": null
|
||||
},
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/sites/', variables('webAppName'))]"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"name": "echobot",
|
||||
"version": "1.0.0",
|
||||
"description": "Bot Builder v4 echo bot sample",
|
||||
"author": "Microsoft",
|
||||
"license": "MIT",
|
||||
"main": "./lib/index.js",
|
||||
"scripts": {
|
||||
"build": "tsc --build",
|
||||
"lint": "tslint -c tslint.json 'src/**/*.ts'",
|
||||
"lintfix": "tslint -c tslint.json 'src/**/*.ts' --fix",
|
||||
"postinstall": "npm run build && node ./deploymentScripts/webConfigPrep.js",
|
||||
"start": "tsc --build && node ./lib/index.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"watch": "nodemon --watch ./src -e ts --exec \"npm run start\""
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com"
|
||||
},
|
||||
"dependencies": {
|
||||
"botbuilder": "~4.9.1",
|
||||
"dotenv": "^8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"replace": "~1.1.1",
|
||||
"restify": "~8.5.1",
|
||||
"restify-cors-middleware": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/dotenv": "6.1.1",
|
||||
"@types/restify": "8.4.2",
|
||||
"@types/restify-cors-middleware": "^1.0.1",
|
||||
"nodemon": "~2.0.4",
|
||||
"tslint": "~6.1.2",
|
||||
"typescript": "~3.9.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import { ActivityHandler, MessageFactory } from 'botbuilder';
|
||||
|
||||
export class EchoBot extends ActivityHandler {
|
||||
constructor() {
|
||||
super();
|
||||
// See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types.
|
||||
this.onMessage(async (context, next) => {
|
||||
const replyText = `Echo: ${ context.activity.text }`;
|
||||
await context.sendActivity(MessageFactory.text(replyText, replyText));
|
||||
// By calling next() you ensure that the next BotHandler is run.
|
||||
await next();
|
||||
});
|
||||
|
||||
this.onMembersAdded(async (context, next) => {
|
||||
const membersAdded = context.activity.membersAdded;
|
||||
const welcomeText = 'Hello and welcome!';
|
||||
for (const member of membersAdded) {
|
||||
if (member.id !== context.activity.recipient.id) {
|
||||
await context.sendActivity(MessageFactory.text(welcomeText, welcomeText));
|
||||
}
|
||||
}
|
||||
// By calling next() you ensure that the next BotHandler is run.
|
||||
await next();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import * as path from 'path';
|
||||
|
||||
import { config } from 'dotenv';
|
||||
const ENV_FILE = path.join(__dirname, '..', '.env');
|
||||
const settings = config({ path: ENV_FILE });
|
||||
|
||||
import * as restify from 'restify';
|
||||
|
||||
import * as request from 'request';
|
||||
|
||||
import * as express from 'express';
|
||||
|
||||
import * as corsMiddleware from 'restify-cors-middleware';
|
||||
|
||||
// Import required bot services.
|
||||
// See https://aka.ms/bot-services to learn more about the different parts of a bot.
|
||||
import { BotFrameworkAdapter } from 'botbuilder';
|
||||
|
||||
// This bot's main dialog.
|
||||
import { EchoBot } from './bot';
|
||||
|
||||
import { Guid } from './util';
|
||||
|
||||
// Add '*' origins for test only. You should update with your own origins in production code.
|
||||
const cors = corsMiddleware({
|
||||
origins: ['*'],
|
||||
allowHeaders: ['*'],
|
||||
exposeHeaders: ['*']
|
||||
});
|
||||
|
||||
// Create HTTP server.
|
||||
const server = restify.createServer();
|
||||
server.pre(cors.preflight);
|
||||
server.use(cors.actual);
|
||||
server.use(express.json());
|
||||
server.listen(process.env.port || process.env.PORT || 3978, () => {
|
||||
console.log(`\n${server.name} listening to ${server.url}`);
|
||||
console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator');
|
||||
console.log('\nTo talk to your bot, open the emulator select "Open Bot"');
|
||||
});
|
||||
|
||||
// Create adapter.
|
||||
// See https://aka.ms/about-bot-adapter to learn more about adapters.
|
||||
const adapter = new BotFrameworkAdapter({
|
||||
appId: process.env.MicrosoftAppId,
|
||||
appPassword: process.env.MicrosoftAppPassword
|
||||
});
|
||||
|
||||
// Catch-all for errors.
|
||||
const onTurnErrorHandler = async (context, error) => {
|
||||
// This check writes out errors to console log .vs. app insights.
|
||||
// NOTE: In production environment, you should consider logging this to Azure
|
||||
// application insights.
|
||||
console.error(`\n [onTurnError] unhandled error: ${ error }`);
|
||||
|
||||
// Send a trace activity, which will be displayed in Bot Framework Emulator
|
||||
await context.sendTraceActivity(
|
||||
'OnTurnError Trace',
|
||||
`${ error }`,
|
||||
'https://www.botframework.com/schemas/error',
|
||||
'TurnError'
|
||||
);
|
||||
|
||||
// Send a message to the user
|
||||
await context.sendActivity('The bot encountered an error or bug.');
|
||||
await context.sendActivity('To continue to run this bot, please fix the bot source code.');
|
||||
};
|
||||
|
||||
// Set the onTurnError for the singleton BotFrameworkAdapter.
|
||||
adapter.onTurnError = onTurnErrorHandler;
|
||||
|
||||
// Create the main dialog.
|
||||
const myBot = new EchoBot();
|
||||
|
||||
// Listen for incoming requests.
|
||||
server.post('/api/messages', (req, res) => {
|
||||
adapter.processActivity(req, res, async (context) => {
|
||||
// Route to main dialog.
|
||||
await myBot.run(context);
|
||||
});
|
||||
});
|
||||
|
||||
const GetUserId = (userName : string): string => {
|
||||
const userId = userName? userName : Guid.newGuid();
|
||||
return userId;
|
||||
};
|
||||
|
||||
// Listen for incoming token request.
|
||||
server.post('/directline/token', (req, res) => {
|
||||
const secret = settings.parsed.DirectLineSecret;
|
||||
const authorization = `Bearer ${secret}`;
|
||||
|
||||
const userId = 'dl_' + GetUserId((req.body || {}).user);
|
||||
const options = {
|
||||
method: 'POST',
|
||||
uri: 'https://directline.botframework.com/v3/directline/tokens/generate',
|
||||
body: JSON.stringify({ user: { id: userId} }),
|
||||
headers: { 'Authorization': authorization, 'Content-Type': 'application/json'}
|
||||
};
|
||||
|
||||
request.post(options, (error, response, body) => {
|
||||
if (!error && response.statusCode < 300) {
|
||||
res.status(response.statusCode);
|
||||
if (body) { res.send(JSON.parse(body)) }
|
||||
} else {
|
||||
res.status(500);
|
||||
res.send('Call to retrieve token from DirectLine failed');
|
||||
}
|
||||
res.end();
|
||||
});
|
||||
});
|
||||
|
||||
// Listen for Upgrade requests for Streaming.
|
||||
server.on('upgrade', (req, socket, head) => {
|
||||
// Create an adapter scoped to this WebSocket connection to allow storing session data.
|
||||
const streamingAdapter = new BotFrameworkAdapter({
|
||||
appId: process.env.MicrosoftAppId,
|
||||
appPassword: process.env.MicrosoftAppPassword
|
||||
});
|
||||
// Set onTurnError for the BotFrameworkAdapter created for each connection.
|
||||
streamingAdapter.onTurnError = onTurnErrorHandler;
|
||||
|
||||
streamingAdapter.useWebSocket(req, socket, head, async (context) => {
|
||||
// After connecting via WebSocket, run this logic for every request sent over
|
||||
// the WebSocket connection.
|
||||
await myBot.run(context);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
export class Guid {
|
||||
static newGuid() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||
const r = Math.random() * 16 | 0,
|
||||
v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"target": "es2016",
|
||||
"module": "commonjs",
|
||||
"outDir": "./lib",
|
||||
"rootDir": "./src",
|
||||
"sourceMap": true,
|
||||
"incremental": true,
|
||||
"tsBuildInfoFile": "./lib/.tsbuildinfo"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"defaultSeverity": "error",
|
||||
"extends": [
|
||||
"tslint:recommended"
|
||||
],
|
||||
"jsRules": {},
|
||||
"rules": {
|
||||
"interface-name" : [true, "never-prefix"],
|
||||
"max-line-length": [false],
|
||||
"no-console": [false, "log", "error"],
|
||||
"no-var-requires": false,
|
||||
"quotemark": [true, "single"],
|
||||
"one-variable-per-declaration": false,
|
||||
"curly": [true, "ignore-same-line"],
|
||||
"trailing-comma": [true, {"multiline": "never", "singleline": "never"}],
|
||||
"no-bitwise": [false]
|
||||
},
|
||||
"rulesDirectory": []
|
||||
}
|
|
@ -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 @@
|
|||
* text=auto
|
|
@ -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,14 @@
|
|||
# Folders
|
||||
.vscode
|
||||
coverage
|
||||
node_modules
|
||||
sharepoint
|
||||
src
|
||||
temp
|
||||
|
||||
# Files
|
||||
*.csproj
|
||||
.git*
|
||||
.yo-rc.json
|
||||
gulpfile.js
|
||||
tsconfig.json
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"@microsoft/generator-sharepoint": {
|
||||
"isCreatingSolution": false,
|
||||
"environment": "spo",
|
||||
"version": "1.10.0",
|
||||
"libraryName": "react-bot-framework",
|
||||
"libraryId": "1e3d14a8-d863-4a3d-b83a-dbd188d52015",
|
||||
"packageManager": "npm",
|
||||
"isDomainIsolated": false,
|
||||
"componentType": "webpart"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
# Microsoft Bot Framework Web Chat
|
||||
|
||||
## Summary
|
||||
|
||||
A web part sample that uses the [botframework-webchat module](https://www.npmjs.com/package/botframework-webchat) to create a React component to render the Bot Framework v4 webchat component. This web part is able to render Text and rich attachments (Images, Cards, Adaptive Cards, ...) and has settings for branding purposes.
|
||||
|
||||
## Used SharePoint Framework Version
|
||||
|
||||
![SPFx 1.10.0](https://img.shields.io/badge/drop-1.10.0-green.svg)
|
||||
|
||||
## Applies to
|
||||
|
||||
* [SharePoint Framework Web Parts](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/overview-client-side-web-parts)
|
||||
* [Office 365 developer tenant](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-developer-tenant)
|
||||
* [Microsoft Bot Framework](http://dev.botframework.com)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
> You need to have this [bot](../bot/) created and registered using the Microsoft Bot Framework and registered to use the **Direct Line Channel**, which will give you the token generation endpoint needed when adding this web part to the page. For more information on creating a bot and registering the channel you can see the official web site at [dev.botframework.com](http://dev.botframework.com).
|
||||
|
||||
## 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.**
|
||||
|
||||
---
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
||||
- Clone this repository
|
||||
- in the command line run:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
gulp serve
|
||||
```
|
||||
|
||||
- Set the bot service URI as Bot EndPoint.
|
||||
|
||||
- Config bot endpoint \
|
||||
Add the web part, set the bot endpoint to `https://YOUR_BOT.azurewebsites.net`, refresh the page, then you can successfully connect the bot with SharePoint.
|
||||
|
||||
## Deploy
|
||||
|
||||
If you want to deploy the bot follow the steps in the [Host your client-side web part from Microsoft 365 CDN](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/hosting-webpart-from-office-365-cdn) article
|
||||
|
||||
## Features
|
||||
|
||||
This Web Part illustrates the following concepts on top of the SharePoint Framework:
|
||||
|
||||
- Connecting and communicating with a bot built on the Microsoft Bot Framework using the Direct Line Channel
|
||||
- Validating Property Pane Settings
|
||||
- Office UI Fabric
|
||||
- React
|
||||
|
||||
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-bot-framework-secure/webpart" />
|
After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 81 KiB |
After Width: | Height: | Size: 105 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 90 KiB |
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||
"version": "2.0",
|
||||
"bundles": {
|
||||
"bot-framework-chatv-4-web-part": {
|
||||
"components": [
|
||||
{
|
||||
"entrypoint": "./lib/webparts/botFrameworkChatv4/BotFrameworkChatv4WebPart.js",
|
||||
"manifest": "./src/webparts/botFrameworkChatv4/BotFrameworkChatv4WebPart.manifest.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"externals": {},
|
||||
"localizedResources": {
|
||||
"BotFrameworkChatv4WebPartStrings": "lib/webparts/botFrameworkChatv4/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": "react-bot-framework",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||
"solution": {
|
||||
"name": "react-bot-framework-client-side-solution",
|
||||
"id": "1e3d14a8-d863-4a3d-b83a-dbd188d52015",
|
||||
"version": "1.0.0.0",
|
||||
"includeClientSideAssets": true,
|
||||
"isDomainIsolated": false
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/react-bot-framework.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 gulp = require('gulp');
|
||||
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(gulp);
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"name": "sp-bot-chat-webpart",
|
||||
"version": "0.0.2",
|
||||
"private": true,
|
||||
"main": "lib/index.js",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp bundle",
|
||||
"clean": "gulp clean",
|
||||
"test": "gulp test",
|
||||
"lint": "tslint -c tslint.json 'src/**/*.{ts,tsx}'",
|
||||
"lintfix": "tslint -c tslint.json 'src/**/*.{ts,tsx}' --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/sp-core-library": "1.10.0",
|
||||
"@microsoft/sp-lodash-subset": "1.10.0",
|
||||
"@microsoft/sp-office-ui-fabric-core": "1.10.0",
|
||||
"@microsoft/sp-property-pane": "1.10.0",
|
||||
"@microsoft/sp-webpart-base": "1.10.0",
|
||||
"@types/es6-promise": "0.0.33",
|
||||
"@types/react": "16.8.8",
|
||||
"@types/react-dom": "16.8.3",
|
||||
"@types/webpack-env": "1.13.1",
|
||||
"blueimp-md5": "^2.16.0",
|
||||
"botframework-directlinejs": "^0.11.4",
|
||||
"botframework-webchat": "^4.5.2",
|
||||
"office-ui-fabric-react": "6.189.2",
|
||||
"react": "16.8.5",
|
||||
"react-dom": "16.8.5",
|
||||
"request": "^2.88.0",
|
||||
"swagger-client": "^2.1.23"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "16.8.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/rush-stack-compiler-3.3": "0.3.5",
|
||||
"@microsoft/sp-build-web": "1.10.0",
|
||||
"@microsoft/sp-module-interfaces": "1.10.0",
|
||||
"@microsoft/sp-webpart-workbench": "1.10.0",
|
||||
"@types/chai": "3.4.34",
|
||||
"@types/mocha": "2.2.38",
|
||||
"ajv": "~5.2.2",
|
||||
"gulp": "~3.9.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
// A file is required to be in the root of the /src directory by the TypeScript compiler
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||
"id": "7aa6d70f-2a9f-4e92-ad01-a45c582e82be",
|
||||
"alias": "BotFrameworkChatv4WebPart",
|
||||
"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": "BotFrameworkChatv4" },
|
||||
"description": { "default": "Use the botframework-webchat v4 React component to render the webchat with the new v4 UI" },
|
||||
"officeFabricIconFontName": "Page",
|
||||
"properties": {
|
||||
"description": "BotFrameworkChatv4"
|
||||
}
|
||||
}]
|
||||
}
|