added new react-graph-evalurl sample (#492)
This commit is contained in:
parent
934d1d92d1
commit
81e5367231
|
@ -0,0 +1,25 @@
|
|||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
[*]
|
||||
|
||||
# change these settings to your own preference
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# we recommend you to keep these unchanged
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[{package,bower}.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
|
@ -0,0 +1,32 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Dependency directories
|
||||
node_modules
|
||||
|
||||
# Build generated files
|
||||
dist
|
||||
lib
|
||||
solution
|
||||
temp
|
||||
*.sppkg
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# OSX
|
||||
.DS_Store
|
||||
|
||||
# Visual Studio files
|
||||
.ntvs_analysis.dat
|
||||
.vs
|
||||
bin
|
||||
obj
|
||||
|
||||
# Resx Generated Code
|
||||
*.resx.ts
|
||||
|
||||
# Styles Generated Code
|
||||
*.scss.ts
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"@microsoft/generator-sharepoint": {
|
||||
"version": "1.4.1",
|
||||
"libraryName": "react-graph-evalurl",
|
||||
"libraryId": "46731e63-80f2-4711-92ce-48173b10f0c8",
|
||||
"environment": "spo"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
## Webpart showing Url validation for SharePoint using Office Graph
|
||||
|
||||
### Summary
|
||||
This sample contains a class that evaluates the url input of a text field against the Microsoft Graph. It is possible to evalute the existance of the following three SharePoint Elements:
|
||||
|
||||
* Site Collection
|
||||
* Web Site
|
||||
* Lists and Document libraries
|
||||
|
||||
The web part shows all those three example and the reuslt returned by the Microsoft Graph.
|
||||
|
||||
![Evaluation Client searching for suitable site collection][figure1]
|
||||
|
||||
### Usage
|
||||
|
||||
To evaluate the web part the input for site collection, web site or list and document library simply place a URL from your tennant in one of the text fields shown above. After the text field loses `onblur` its focus the evaluation happens automatically in the background and showing the debug information in the debugging information.
|
||||
|
||||
![Evaluation of web site with current debug informations][figure2]
|
||||
|
||||
### Project Setup and important files
|
||||
|
||||
```txt
|
||||
src
|
||||
└── webparts
|
||||
└── graphEvalUrl
|
||||
├── GraphEvalUrlWebPart.manifest.json
|
||||
├── GraphEvalUrlWebPart.ts
|
||||
├── components
|
||||
│ ├── GraphEvalClient.ts **<-- Evalution Class**
|
||||
│ ├── GraphEvalDebug.tsx **<-- Debug Panel Component**
|
||||
│ ├── GraphEvalUrl.module.scss
|
||||
│ ├── GraphEvalUrl.module.scss.ts
|
||||
│ ├── GraphEvalUrl.tsx **<-- Demo Control for web part**
|
||||
│ └── IGraphEvalUrlProps.ts
|
||||
└── loc
|
||||
├── en-us.js
|
||||
└── mystrings.d.ts
|
||||
```
|
||||
|
||||
## Used SharePoint Framework Version
|
||||
![drop](https://img.shields.io/badge/drop-1.4.1-green.svg)
|
||||
|
||||
## Demo
|
||||
![Evaluation of web site with current debug informations][figure3]
|
||||
|
||||
## Applies to
|
||||
|
||||
* [SharePoint Framework Developer Preview](http://dev.office.com/sharepoint/docs/spfx/sharepoint-framework-overview)
|
||||
* [Office 365 developer tenant](http://dev.office.com/sharepoint/docs/spfx/set-up-your-developer-tenant)
|
||||
|
||||
|
||||
## Solution
|
||||
|
||||
Solution|Author(s)
|
||||
--------|---------
|
||||
react-graph-evalurl|Stefan Bauer (MVP, Stefan Bauer - N8D, [@stfbauer](https://twitter.com/stfbauer) )
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0.0|April 15, 2018|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.**
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- SharePoint Online tenant with Office Graph content
|
||||
- Site Collection created under the **/sites/** or **/**
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
||||
- clone this repo
|
||||
- `$ npm i`
|
||||
- `$ gulp serve --nobrowser`
|
||||
- Open workbench on your tennant, ie. https://contoso.sharepoint.com/sites/salestesm/_layouts/15/workbench.aspx
|
||||
- Search and add web part "Graph - Eval Url"
|
||||
|
||||
## Features
|
||||
|
||||
This sample web part shows how URLs in SharePoint can be checked and evaluated agains the Microsoft Graph. This scenario and the introduced class can be use to evluate not only user input but can also be used in the configuration panel of web parts. The benefit is that the URL of the target location remains visible and can be use for debugging reasons to.
|
||||
The class returns all the Graph objects identified during the evaluation process. It can also be used in backend code.
|
||||
|
||||
- using React for building SharePoint Framework Client-side Web Parts
|
||||
- using Office UI Fabric React components
|
||||
- create additional custom react components
|
||||
- access information from the Microsoft Graph using SharePoint Framework version 1.4.1 and above
|
||||
- using ES6 Promises with vanilla-JavaScript web requests
|
||||
|
||||
## Limitations
|
||||
To identify a list or document library the following two token will be use:
|
||||
|
||||
* 'Forms' - View or SharePoint List Form for document libraries
|
||||
* 'Lists' - SharePoint stores all lists in the sub folder lists so this will be use to identify if a list is present in the URL
|
||||
|
||||
|
||||
|
||||
[figure1]: ./assets/evaluation-client-searching-for-site-collection.png
|
||||
[figure2]: ./assets/eval-web-after-site-collection.png
|
||||
[figure3]: ./assets/url-graph-eval.gif
|
Binary file not shown.
After Width: | Height: | Size: 94 KiB |
Binary file not shown.
After Width: | Height: | Size: 91 KiB |
Binary file not shown.
After Width: | Height: | Size: 11 MiB |
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||
"version": "2.0",
|
||||
"bundles": {
|
||||
"graph-eval-url-web-part": {
|
||||
"components": [
|
||||
{
|
||||
"entrypoint": "./lib/webparts/graphEvalUrl/GraphEvalUrlWebPart.js",
|
||||
"manifest": "./src/webparts/graphEvalUrl/GraphEvalUrlWebPart.manifest.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"externals": {},
|
||||
"localizedResources": {
|
||||
"GraphEvalUrlWebPartStrings": "lib/webparts/graphEvalUrl/loc/{locale}.js"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||
"deployCdnPath": "temp/deploy"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||
"workingDir": "./temp/deploy/",
|
||||
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||
"container": "react-graph-evalurl",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||
"solution": {
|
||||
"name": "react-graph-evalurl-client-side-solution",
|
||||
"id": "46731e63-80f2-4711-92ce-48173b10f0c8",
|
||||
"version": "1.0.0.0",
|
||||
"includeClientSideAssets": true
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/react-graph-evalurl.sppkg"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"$schema": "https://dev.office.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,45 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/core-build/tslint.schema.json",
|
||||
// Display errors as warnings
|
||||
"displayAsWarning": true,
|
||||
// The TSLint task may have been configured with several custom lint rules
|
||||
// before this config file is read (for example lint rules from the tslint-microsoft-contrib
|
||||
// project). If true, this flag will deactivate any of these rules.
|
||||
"removeExistingRules": true,
|
||||
// When true, the TSLint task is configured with some default TSLint "rules.":
|
||||
"useDefaultConfigAsBase": false,
|
||||
// Since removeExistingRules=true and useDefaultConfigAsBase=false, there will be no lint rules
|
||||
// which are active, other than the list of rules below.
|
||||
"lintConfig": {
|
||||
// Opt-in to Lint rules which help to eliminate bugs in JavaScript
|
||||
"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-case": true,
|
||||
"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,
|
||||
"valid-typeof": true,
|
||||
"variable-name": false,
|
||||
"whitespace": false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://dev.office.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);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "react-graph-evalurl",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp bundle",
|
||||
"clean": "gulp clean",
|
||||
"test": "gulp test"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "15.6.2",
|
||||
"react-dom": "15.6.2",
|
||||
"@types/react": "15.6.6",
|
||||
"@types/react-dom": "15.5.6",
|
||||
"@microsoft/sp-core-library": "~1.4.1",
|
||||
"@microsoft/sp-webpart-base": "~1.4.1",
|
||||
"@microsoft/sp-lodash-subset": "~1.4.1",
|
||||
"@microsoft/sp-office-ui-fabric-core": "~1.4.1",
|
||||
"@types/webpack-env": ">=1.12.1 <1.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/sp-build-web": "~1.4.1",
|
||||
"@microsoft/sp-module-interfaces": "~1.4.1",
|
||||
"@microsoft/sp-webpart-workbench": "~1.4.1",
|
||||
"gulp": "~3.9.1",
|
||||
"@types/chai": ">=3.4.34 <3.6.0",
|
||||
"@types/mocha": ">=2.2.33 <2.6.0",
|
||||
"ajv": "~5.2.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||
"id": "92bdb2d9-57de-40a5-93d5-6941b90038b2",
|
||||
"alias": "GraphEvalUrlWebPart",
|
||||
"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,
|
||||
|
||||
"preconfiguredEntries": [{
|
||||
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
|
||||
"group": { "default": "Other" },
|
||||
"title": { "default": "Graph - Eval Url" },
|
||||
"description": { "default": "Evaluates Site Collection, Web, list or document library using Microsoft Graph" },
|
||||
"officeFabricIconFontName": "Page",
|
||||
"properties": {
|
||||
"description": "Shows how to evaluate URL using Microsoft Graph"
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import { Version } from '@microsoft/sp-core-library';
|
||||
import {
|
||||
BaseClientSideWebPart,
|
||||
IPropertyPaneConfiguration,
|
||||
PropertyPaneTextField
|
||||
} from '@microsoft/sp-webpart-base';
|
||||
|
||||
import * as strings from 'GraphEvalUrlWebPartStrings';
|
||||
import GraphEvalUrl from './components/GraphEvalUrl';
|
||||
import { IGraphEvalUrlProps } from './components/IGraphEvalUrlProps';
|
||||
|
||||
export interface IGraphEvalUrlWebPartProps {
|
||||
description: string;
|
||||
}
|
||||
|
||||
export default class GraphEvalUrlWebPart extends BaseClientSideWebPart<IGraphEvalUrlWebPartProps> {
|
||||
|
||||
public render(): void {
|
||||
const element: React.ReactElement<IGraphEvalUrlProps > = React.createElement(
|
||||
GraphEvalUrl,
|
||||
{
|
||||
description: this.properties.description,
|
||||
graphClient: this.context.graphHttpClient
|
||||
}
|
||||
);
|
||||
|
||||
ReactDom.render(element, 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
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,450 @@
|
|||
import {
|
||||
GraphHttpClient, HttpClientResponse,
|
||||
} from '@microsoft/sp-http';
|
||||
|
||||
export default class GraphEvalClient {
|
||||
|
||||
_context: any;
|
||||
_graphClient: GraphHttpClient;
|
||||
_urlToEvaluate: URL;
|
||||
_isList = false;
|
||||
_isLibrary = false;
|
||||
|
||||
constructor(graphClient: any) {
|
||||
|
||||
// use locally stored graphHttpClient
|
||||
this._graphClient = graphClient;
|
||||
|
||||
}
|
||||
|
||||
private _getUrlJunks(urlPath: string): string[] {
|
||||
return urlPath
|
||||
.toLowerCase() // convert urlPath to lowerstring
|
||||
.split('\/') // split out all slashes
|
||||
// Filter all empty values
|
||||
.filter((junk) => {
|
||||
if (junk !== '') {
|
||||
return junk;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Assume the site collectio on an URL
|
||||
* @param urlPath URL that should be evaluated
|
||||
*/
|
||||
private _getSiteCollectionUrl(urlPath: string): string {
|
||||
|
||||
let urlPathElements = this._getUrlJunks(urlPath);
|
||||
|
||||
// Use the root site collection
|
||||
if (urlPathElements[0] !== 'sites') {
|
||||
|
||||
return ''
|
||||
|
||||
}
|
||||
|
||||
// found sub site collection and use only /sites/<your subweb>
|
||||
if (urlPathElements.length >= 2) {
|
||||
|
||||
// assumes we have a /sites/something
|
||||
return `/${urlPathElements[0]}/${urlPathElements[1]}/`
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Assume the url on an URL
|
||||
* @param urlPath URL that should be evaluated
|
||||
*/
|
||||
private _getAssumedWeburl(urlPath: string) {
|
||||
|
||||
let siteColleciton = this._getSiteCollectionUrl(urlPath),
|
||||
urlPathElements = this._getUrlJunks(urlPath);
|
||||
|
||||
|
||||
/* Get location if there is for form or lists to identify
|
||||
docuemnt library or list
|
||||
*/
|
||||
let formLocation = urlPathElements.indexOf('forms'),
|
||||
listLocation = urlPathElements.indexOf('lists'),
|
||||
sitePages = urlPathElements.indexOf('sitepages');
|
||||
|
||||
/* Check if it is a full list url
|
||||
* otherwise check if it is a list
|
||||
* otherwise truncate the path for ine level
|
||||
*/
|
||||
if (formLocation !== -1) {
|
||||
|
||||
this._isLibrary = true;
|
||||
return `/${urlPathElements.slice(0, formLocation - 1).join('/')}`;
|
||||
|
||||
} else if (listLocation !== -1) {
|
||||
|
||||
this._isList = true;
|
||||
return `/${urlPathElements.slice(0, listLocation).join('/')}`;
|
||||
|
||||
} else if (sitePages !== -1) {
|
||||
|
||||
console.log(urlPathElements.slice(0, sitePages));
|
||||
|
||||
return `/${urlPathElements.slice(0, sitePages).join('/')}`;
|
||||
|
||||
} else {
|
||||
|
||||
// just return path one higher then the final web or list in case no one have entered no view
|
||||
return `/${urlPathElements.slice(0, urlPathElements.length).join('/')}`;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Assume the list or library url based on a path
|
||||
* @param urlPath URL that should be evaluated
|
||||
*/
|
||||
private _getAssumedList(urlPath: string) {
|
||||
|
||||
let webUrl = this._getAssumedWeburl(urlPath),
|
||||
urlPathElements = this._getUrlJunks(urlPath);
|
||||
|
||||
// Check if a list should be found otherwise docuemnt library or unspecified
|
||||
if (this._isList) {
|
||||
|
||||
let listIndex = urlPathElements.indexOf('lists');
|
||||
|
||||
// +2 for adding /lists/<listname>
|
||||
return urlPathElements.slice(0, listIndex + 2).join('/');
|
||||
|
||||
} else if (this._isLibrary) {
|
||||
|
||||
let formIndex = urlPathElements.indexOf('forms');
|
||||
|
||||
/*
|
||||
No additional index needs to be added bcause document libraries already exist
|
||||
in root folder of Site Collection
|
||||
*/
|
||||
return `/${urlPathElements.slice(0, formIndex).join('/')}`;
|
||||
|
||||
} if (urlPathElements[urlPathElements.length - 1].indexOf('.apsx')) {
|
||||
|
||||
return `/${urlPathElements.slice(0, urlPathElements.length - 1).join('/')}`;
|
||||
|
||||
} else {
|
||||
|
||||
throw 'Neither list nor document library could be identified';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to parse the url to a JavaScript Url Object
|
||||
* @param url
|
||||
*/
|
||||
private _evaluateUrl(url: string) {
|
||||
|
||||
console.log(url);
|
||||
try {
|
||||
|
||||
return new URL(url)
|
||||
|
||||
} catch (Exception) {
|
||||
|
||||
throw Exception;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Graph query to resturn a site collection or web
|
||||
* @param hostname
|
||||
* @param scUrl
|
||||
*/
|
||||
private _evaluateWeb(hostname: string, scUrl: string): Promise<any> {
|
||||
|
||||
let graphQuery;
|
||||
|
||||
if (scUrl !== '' && scUrl.indexOf('sites') !== -1) {
|
||||
// query just a regular sub site collection
|
||||
graphQuery = `beta/sites/${hostname}:${scUrl}`;
|
||||
|
||||
} else if (scUrl !== '' && scUrl.indexOf('sites') === -1) {
|
||||
// query just a regular sub site collection
|
||||
graphQuery = `beta/sites/${hostname}/sites/`;
|
||||
|
||||
} else {
|
||||
|
||||
// query only the root
|
||||
graphQuery = `beta/sites/${hostname}`;
|
||||
|
||||
}
|
||||
|
||||
console.log(graphQuery, scUrl);
|
||||
|
||||
return this._graphClient.get(
|
||||
graphQuery,
|
||||
GraphHttpClient.configurations.v1
|
||||
).then(
|
||||
(response: HttpClientResponse) => {
|
||||
|
||||
return response.json();
|
||||
|
||||
}
|
||||
).catch(
|
||||
(error) => {
|
||||
|
||||
throw error;
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Graph query that returns all lists in a specific web
|
||||
* @param siteId
|
||||
* @param url
|
||||
*/
|
||||
private _evaluateLists(siteId: string, url: string): Promise<any> {
|
||||
|
||||
// Transfer result values to the group variable
|
||||
// TODO: Check until you find the final subsite
|
||||
|
||||
const NO_RESULT = 'No document library could be found under the give path';
|
||||
|
||||
let pathToFileQuery = `beta/sites/${siteId}/Lists`;
|
||||
|
||||
return this._graphClient.get(
|
||||
pathToFileQuery,
|
||||
GraphHttpClient.configurations.v1
|
||||
).then(
|
||||
(response) => {
|
||||
|
||||
return response.json();
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate if site collection exists and return it if found
|
||||
* @param url
|
||||
*/
|
||||
public EvaluateSiteCollection(url: string): Promise<any> {
|
||||
|
||||
let scUrl;
|
||||
|
||||
// General url evaluation
|
||||
try {
|
||||
|
||||
this._urlToEvaluate = this._evaluateUrl(url);
|
||||
|
||||
} catch (Exception) {
|
||||
throw Exception;
|
||||
}
|
||||
|
||||
// Try to make site collection url
|
||||
try {
|
||||
|
||||
scUrl = this._getSiteCollectionUrl(this._urlToEvaluate.pathname);
|
||||
|
||||
} catch (Exception) {
|
||||
|
||||
throw Exception
|
||||
|
||||
}
|
||||
|
||||
// if building a site collection url worked well
|
||||
if (scUrl !== null && scUrl !== undefined) {
|
||||
|
||||
|
||||
return this._evaluateWeb(this._urlToEvaluate.hostname, scUrl)
|
||||
.then(
|
||||
(response) => {
|
||||
return response;
|
||||
}
|
||||
)
|
||||
.catch(
|
||||
(error) => {
|
||||
|
||||
throw `Error retrieving Site Collection at ${scUrl} - ${error}`;
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evaluate a web by URL and returns the web object
|
||||
* @param url
|
||||
*/
|
||||
public EvaluateWeb(url: string): Promise<any> {
|
||||
|
||||
let scUrl;
|
||||
|
||||
// General url evaluation
|
||||
try {
|
||||
|
||||
this._urlToEvaluate = this._evaluateUrl(url);
|
||||
|
||||
} catch (Exception) {
|
||||
|
||||
throw Exception;
|
||||
|
||||
}
|
||||
|
||||
// Try to make web url
|
||||
try {
|
||||
|
||||
scUrl = this._getAssumedWeburl(this._urlToEvaluate.pathname);
|
||||
|
||||
} catch (Exception) {
|
||||
console.log('Error: Identify Site Collection URL');
|
||||
throw Exception
|
||||
|
||||
}
|
||||
|
||||
if (scUrl !== null && scUrl !== undefined) {
|
||||
|
||||
return this._evaluateWeb(this._urlToEvaluate.hostname, scUrl)
|
||||
.then(
|
||||
(response) => {
|
||||
return response;
|
||||
}
|
||||
)
|
||||
.catch(
|
||||
(error) => {
|
||||
|
||||
throw `Error retrieving Site Collection at ${scUrl} - ${error}`;
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate if web and a specific list exists in the web
|
||||
* @param url
|
||||
* @param webid
|
||||
*/
|
||||
public EvaluateList(url: string): Promise<any> {
|
||||
|
||||
let webUrl,
|
||||
listUrl;
|
||||
|
||||
// General url evaluation
|
||||
try {
|
||||
|
||||
this._urlToEvaluate = this._evaluateUrl(url);
|
||||
|
||||
} catch (Exception) {
|
||||
|
||||
throw Exception;
|
||||
|
||||
}
|
||||
|
||||
// Try to make web url url
|
||||
try {
|
||||
|
||||
webUrl = this._getAssumedWeburl(this._urlToEvaluate.pathname);
|
||||
|
||||
} catch (Exception) {
|
||||
|
||||
throw Exception
|
||||
|
||||
}
|
||||
|
||||
// try to find a list or document library url
|
||||
try {
|
||||
|
||||
listUrl = this._getAssumedList(this._urlToEvaluate.pathname);
|
||||
|
||||
} catch (Exception) {
|
||||
|
||||
throw Exception
|
||||
|
||||
}
|
||||
|
||||
// If list and web are ok proceed
|
||||
if (webUrl !== null && webUrl !== undefined &&
|
||||
listUrl !== null && listUrl !== undefined) {
|
||||
|
||||
/**
|
||||
* Evaluate web because the graph ID is needed to evaluate a list
|
||||
*/
|
||||
return this._evaluateWeb(this._urlToEvaluate.hostname, webUrl)
|
||||
.then(
|
||||
(response) => {
|
||||
|
||||
let webIdResults,
|
||||
webId;
|
||||
|
||||
// just in case multiple items got returned by the graph
|
||||
if (response.value !== undefined) {
|
||||
|
||||
// filter the best match
|
||||
webIdResults = response.value.filter((obj) => {
|
||||
|
||||
return obj.webUrl.toLowerCase().indexOf(webUrl) !== -1;
|
||||
|
||||
});
|
||||
|
||||
// return first found id
|
||||
webId = webIdResults[0].id;
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
// if only single item was returned
|
||||
webId = response.id;
|
||||
|
||||
}
|
||||
|
||||
return this._evaluateLists(webId, listUrl)
|
||||
.then(
|
||||
(response) => {
|
||||
|
||||
/**
|
||||
* Graph always returns all lists and docuemnt libraries from site collection
|
||||
* This filters out to return only the searched List or library instead of all
|
||||
* In case none could be found only a empty array will get returned
|
||||
*/
|
||||
const suiteableLists = response.value.filter((obj) => {
|
||||
|
||||
return obj.webUrl.toLowerCase().indexOf(listUrl) !== -1;
|
||||
|
||||
});
|
||||
|
||||
response.value = suiteableLists;
|
||||
|
||||
return response;
|
||||
|
||||
}
|
||||
)
|
||||
.catch(
|
||||
(error) => {
|
||||
|
||||
throw `List cannot be found or evaluated - ${error}`;
|
||||
|
||||
}
|
||||
|
||||
)
|
||||
|
||||
}
|
||||
)
|
||||
.catch(
|
||||
(error) => {
|
||||
|
||||
throw `Error retrieving Site Collection at ${webUrl} - ${error}`;
|
||||
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import * as React from 'react';
|
||||
|
||||
export interface IGraphEvalDebugState {
|
||||
panelText: string;
|
||||
}
|
||||
|
||||
export interface IGraphEvalDebugProps {
|
||||
panelText: string;
|
||||
}
|
||||
|
||||
export default class GraphEvalDebug extends React.Component<IGraphEvalDebugProps, IGraphEvalDebugState> {
|
||||
|
||||
constructor(props) {
|
||||
|
||||
super(props);
|
||||
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<void> {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label>
|
||||
<strong>Debugging Information</strong>
|
||||
</label>
|
||||
<pre className="dbgInfo">{this.props.panelText}</pre>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
|
||||
.graphEvalUrl {
|
||||
.container {
|
||||
max-width: 700px;
|
||||
margin: 0px auto;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
:global {
|
||||
.dbgInfo {
|
||||
background-color: whitesmoke;
|
||||
display: block;
|
||||
padding: 0.5em 0.25em;
|
||||
overflow: auto;
|
||||
box-sizing: border-box;
|
||||
min-height: 0px;
|
||||
transition: height 1s ease-in-out;
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,237 @@
|
|||
import * as React from 'react';
|
||||
import styles from './GraphEvalUrl.module.scss';
|
||||
import { IGraphEvalUrlProps } from './IGraphEvalUrlProps';
|
||||
import { escape } from '@microsoft/sp-lodash-subset';
|
||||
import { GraphHttpClient } from '@microsoft/sp-http';
|
||||
import GraphEvalClient from './GraphEvalClient';
|
||||
|
||||
// Office UI Fabric Imports
|
||||
import {
|
||||
TextField
|
||||
} from 'office-ui-fabric-react/lib/TextField';
|
||||
import GraphEvalDebug from './GraphEvalDebug'
|
||||
|
||||
export interface IGraphEvalUrlState {
|
||||
debugMessage: string
|
||||
};
|
||||
|
||||
|
||||
export default class GraphEvalUrl extends React.Component<IGraphEvalUrlProps, {}> {
|
||||
|
||||
|
||||
_graphClient: GraphHttpClient
|
||||
state: IGraphEvalUrlState;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._graphClient = props.graphClient;
|
||||
|
||||
this.state = {
|
||||
debugMessage: ''
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Catches all Graph Client errors and print it in debug console
|
||||
* @param errors
|
||||
*/
|
||||
private _catchErrors(errors: string) {
|
||||
|
||||
this.setState(
|
||||
{
|
||||
debugMessage: errors
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all debug messages
|
||||
*/
|
||||
private _cleanDebug() {
|
||||
|
||||
this.setState(
|
||||
{
|
||||
debugMessage: ''
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evaluate a site collection on blur
|
||||
* @param event
|
||||
*/
|
||||
private _evaluateSiteCollection(event) {
|
||||
|
||||
// get value of current input
|
||||
let currentUrlValue = event.target.value;
|
||||
|
||||
if(currentUrlValue === ""){
|
||||
this._catchErrors('No URL specified')
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// create new graph evaluation client aka a validator
|
||||
let graphEval = new GraphEvalClient(this._graphClient);
|
||||
|
||||
// request site collection from the Microsoft Graph
|
||||
graphEval.EvaluateSiteCollection(currentUrlValue)
|
||||
.then(
|
||||
(response) => {
|
||||
|
||||
this.setState({
|
||||
debugMessage: 'Found Site Collection:\n\n' + JSON.stringify(response, null, 2)
|
||||
})
|
||||
|
||||
}
|
||||
)
|
||||
.catch(this._catchErrors);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate a site collection and web
|
||||
* @param event
|
||||
*/
|
||||
private _evaluateWeb(event) {
|
||||
|
||||
// get value of current input
|
||||
let currentUrlValue = event.target.value;
|
||||
// create new graph evaluation client aka a validator
|
||||
let graphEval = new GraphEvalClient(this._graphClient);
|
||||
|
||||
if(currentUrlValue === ""){
|
||||
this._catchErrors('No URL specified')
|
||||
return;
|
||||
}
|
||||
|
||||
// request site collection from the Microsoft Graph
|
||||
graphEval.EvaluateWeb(currentUrlValue)
|
||||
.then(
|
||||
(response) => {
|
||||
|
||||
this.setState({
|
||||
debugMessage: 'Site Collection found:\n'
|
||||
+ JSON.stringify(response, null, 2)
|
||||
+ '\nTry to evaluate web next:\n\n'
|
||||
})
|
||||
|
||||
// then try to the get web
|
||||
graphEval.EvaluateWeb(currentUrlValue)
|
||||
.then(
|
||||
(response) => {
|
||||
|
||||
this.setState({
|
||||
debugMessage: 'Web found:\n'
|
||||
+ JSON.stringify(response, null, 2)
|
||||
});
|
||||
|
||||
}
|
||||
).catch(this._catchErrors);
|
||||
|
||||
}
|
||||
)
|
||||
.catch(this._catchErrors);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evaluate if a list exists in a particulare web site
|
||||
* @param event
|
||||
*/
|
||||
private _evaluateList(event) {
|
||||
|
||||
// get value of current input
|
||||
let currentUrlValue = event.target.value;
|
||||
|
||||
if(currentUrlValue === ""){
|
||||
this._catchErrors('No URL specified')
|
||||
return;
|
||||
}
|
||||
// create new graph evaluation client aka a validator
|
||||
let graphEval = new GraphEvalClient(this._graphClient);
|
||||
|
||||
// then try to the get web
|
||||
|
||||
graphEval.EvaluateList(currentUrlValue)
|
||||
.then(
|
||||
(response) => {
|
||||
|
||||
if (response.value !== null && response.value.length !== 0) {
|
||||
|
||||
this.setState({
|
||||
debugMessage: '\nList found:\n'
|
||||
+ JSON.stringify(response, null, 2)
|
||||
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
this.setState({
|
||||
debugMessage: '\nNo valid list could be found:\n'
|
||||
+ JSON.stringify(response, null, 2)
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
).catch((error) => {
|
||||
this.setState({
|
||||
debugMessage: error
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders demo web part to evaluate three different variants using Microsoft Graph
|
||||
*/
|
||||
public render(): React.ReactElement<IGraphEvalUrlProps> {
|
||||
return (
|
||||
<div>
|
||||
<h2>SharePoint URL Input validation using Microsoft Graph</h2>
|
||||
<p>Enter a url to site collection, web or list below. Press <kbd>Tab</kbd> to start evaluation.
|
||||
Once the evaluation of any of the specific text field is completed the debugging information
|
||||
the information returned by the graph or displays the error message.
|
||||
</p>
|
||||
{/* Evaluates the inpurt on tab leave and try to find a site collection base in url */}
|
||||
<TextField
|
||||
name='scInput'
|
||||
id='scInput'
|
||||
label='Site Collection Evaluation'
|
||||
onBlur={this._evaluateSiteCollection.bind(this)}
|
||||
/>
|
||||
{/* Evaluates the inpurt on tab leave and try to find
|
||||
a site collection and corresponding web based on url base in url */}
|
||||
<TextField
|
||||
name='webInput'
|
||||
id='webInput'
|
||||
label='Web Evaluation'
|
||||
onBlur={this._evaluateWeb.bind(this)}
|
||||
/>
|
||||
{/* Evaluates the inpurt on tab leave and try to find
|
||||
corresponding web and list based on url base in url */}
|
||||
<TextField
|
||||
name='listInput'
|
||||
id='listInput'
|
||||
prefix='https://'
|
||||
label='List Evaluation'
|
||||
onBlur={this._evaluateList.bind(this)}
|
||||
/>
|
||||
<br />
|
||||
{/* Displays Graph Debugging information */}
|
||||
<GraphEvalDebug panelText={this.state.debugMessage} />
|
||||
{/* Dummy container to make sure CSS got rendered */}
|
||||
<div className={styles.container} />
|
||||
</div >
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import { GraphHttpClient } from "@microsoft/sp-http";
|
||||
|
||||
export interface IGraphEvalUrlProps {
|
||||
description: string;
|
||||
graphClient: GraphHttpClient
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
define([], function() {
|
||||
return {
|
||||
"PropertyPaneDescription": "Description",
|
||||
"BasicGroupName": "Group Name",
|
||||
"DescriptionFieldLabel": "Description Field"
|
||||
}
|
||||
});
|
|
@ -0,0 +1,10 @@
|
|||
declare interface IGraphEvalUrlWebPartStrings {
|
||||
PropertyPaneDescription: string;
|
||||
BasicGroupName: string;
|
||||
DescriptionFieldLabel: string;
|
||||
}
|
||||
|
||||
declare module 'GraphEvalUrlWebPartStrings' {
|
||||
const strings: IGraphEvalUrlWebPartStrings;
|
||||
export = strings;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "commonjs",
|
||||
"jsx": "react",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"skipLibCheck": true,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"./node_modules/@microsoft"
|
||||
],
|
||||
"types": [
|
||||
"es6-promise",
|
||||
"webpack-env"
|
||||
],
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom",
|
||||
"es2015.collection"
|
||||
]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue