update to latest spfx

This commit is contained in:
Russell Gove 2024-02-14 16:28:36 -05:00
parent 68eef8b764
commit 10d7c60ebd
45 changed files with 62923 additions and 403 deletions

View File

@ -0,0 +1,5 @@
require('@rushstack/eslint-config/patch/modern-module-resolution');
module.exports = {
extends: ['@microsoft/eslint-config-spfx/lib/profiles/react'],
parserOptions: { tsconfigRootDir: __dirname }
};

View File

@ -1,3 +1,6 @@
.heft
release
# Logs
logs
*.log

View File

@ -1,7 +1,17 @@
{
"@microsoft/generator-sharepoint": {
"environment": "spo",
"componentType": "webpart",
"packageManager": "npm",
"isCreatingSolution": true,
"isDomainIsolated": false,
"sdkVersions": {
"@microsoft/teams-js": "2.9.1",
"@microsoft/microsoft-graph-client": "3.0.2"
},
"nodeVersion": "16.20.0",
"libraryName": "react-property-bag-editor",
"libraryId": "12dac38e-b255-44ce-9f06-050571b34d39",
"framework": "react"
"version": "1.17.1"
}
}

View File

@ -47,6 +47,8 @@ Version|Date|Comments
## Minimal Path to Awesome
- Clone this repository
- This project uses the JSOM to interact with the property bag. Therefore in config/config.js you need to change the paths
on the externals sp-init,microsoft-ajax,sp-runtime, and sharepoint to point to your tenant.
- in the command line run:
- `npm install`
- `gulp serve`

View File

@ -1,26 +1,6 @@
{
"entries": [
{
"entry": "./lib/webparts/propertyBagEditor/PropertyBagEditorWebPart.js",
"manifest": "./src/webparts/propertyBagEditor/PropertyBagEditorWebPart.manifest.json",
"outputPath": "./dist/property-bag-editor.bundle.js"
},
{
"entry": "./lib/webparts/propertyBagDisplay/PropertyBagDisplayWebPart.js",
"manifest": "./src/webparts/propertyBagDisplay/PropertyBagDisplayWebPart.manifest.json",
"outputPath": "./dist/property-bag-display.bundle.js"
},
{
"entry": "./lib/webparts/propertyBagFilteredSiteList/PropertyBagFilteredSiteListWebPart.js",
"manifest": "./src/webparts/propertyBagFilteredSiteList/PropertyBagFilteredSiteListWebPart.manifest.json",
"outputPath": "./dist/property-bag-filtered-site-list.bundle.js"
},
{
"entry": "./lib/webparts/propertyBagGlobalNav/PropertyBagGlobalNavWebPart.js",
"manifest": "./src/webparts/propertyBagGlobalNav/PropertyBagGlobalNavWebPart.manifest.json",
"outputPath": "./dist/property-bag-global-nav.bundle.js"
}
],
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"externals": {
"sp-pnp-js": "https://cdnjs.cloudflare.com/ajax/libs/sp-pnp-js/2.0.1/pnp.min.js",
"sp-init": {
@ -50,9 +30,43 @@
}
},
"localizedResources": {
"propertyBagEditorStrings": "webparts/propertyBagEditor/loc/{locale}.js",
"propertyBagDisplayStrings": "webparts/propertyBagDisplay/loc/{locale}.js",
"propertyBagFilteredSiteListStrings": "webparts/propertyBagFilteredSiteList/loc/{locale}.js",
"propertyBagGlobalNavStrings": "webparts/propertyBagGlobalNav/loc/{locale}.js"
"propertyBagEditorStrings": "lib/webparts/propertyBagEditor/loc/{locale}.js",
"propertyBagDisplayStrings": "lib/webparts/propertyBagDisplay/loc/{locale}.js",
"propertyBagFilteredSiteListStrings": "lib/webparts/propertyBagFilteredSiteList/loc/{locale}.js",
"propertyBagGlobalNavStrings": "lib/webparts/propertyBagGlobalNav/loc/{locale}.js"
},
"bundles": {
"property-bag-editor-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/propertyBagEditor/PropertyBagEditorWebPart.js",
"manifest": "./src/webparts/propertyBagEditor/PropertyBagEditorWebPart.manifest.json"
}
]
},
"property-bag-display-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/propertyBagDisplay/PropertyBagDisplayWebPart.js",
"manifest": "./src/webparts/propertyBagDisplay/PropertyBagDisplayWebPart.manifest.json"
}
]
},
"property-bag-filtered-site-list-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/propertyBagFilteredSiteList/PropertyBagFilteredSiteListWebPart.js",
"manifest": "./src/webparts/propertyBagFilteredSiteList/PropertyBagFilteredSiteListWebPart.manifest.json"
}
]
},
"property-bag-global-nav-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/propertyBagGlobalNav/PropertyBagGlobalNavWebPart.js",
"manifest": "./src/webparts/propertyBagGlobalNav/PropertyBagGlobalNavWebPart.manifest.json"
}
]
}
}
}
}

View File

@ -1,3 +0,0 @@
{
"deployCdnPath": "temp/deploy"
}

View File

@ -1,5 +1,6 @@
{
"workingDir": "./temp/deploy/",
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
"workingDir": "./release/assets/",
"account": "<!-- STORAGE ACCOUNT NAME -->",
"container": "react-property-bag-editor",
"accessKey": "<!-- ACCESS KEY -->"

View File

@ -1,5 +1,64 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"includeClientSideAssets": true,
"isDomainIsolated": false,
"developer": {
"name": "Russell Gove",
"privacyUrl": "",
"termsOfUseUrl": "",
"websiteUrl": "",
"mpnId": "Undefined-1.15.0"
},
"metadata": {
"shortDescription": {
"default": "react-property-bag-editor description"
},
"longDescription": {
"default": "react-property-bag-editor description"
},
"screenshotPaths": [],
"videoUrl": "",
"categories": []
},
"features": [
{
"title": "react-property-bag-editor PropertyBagDisplayWebPart Feature",
"description": "The feature that activates PropertyBagDisplayWebPart from the react-property-bag-editor solution.",
"id": "fa63037d-d7bd-4d52-894a-b40127773283",
"version": "1.0.0.0",
"componentIds": [
"fa63037d-d7bd-4d52-894a-b40127773283"
]
},
{
"title": "react-property-bag-editor PropertyBagEditorWebPart Feature",
"description": "The feature that activates PropertyBagEditorWebPart from the react-property-bag-editor solution.",
"id": "f3ac8a07-2a9b-47a1-8a7e-a093cad63f98",
"version": "1.0.0.0",
"componentIds": [
"f3ac8a07-2a9b-47a1-8a7e-a093cad63f98"
]
},
{
"title": "react-property-bag-editor PropertyBagFilteredSiteListWebPart Feature",
"description": "The feature that activates PropertyBagFilteredSiteListWebPart from the react-property-bag-editor solution.",
"id": "b81a6789-e93b-4be5-baa7-59f34004694a",
"version": "1.0.0.0",
"componentIds": [
"b81a6789-e93b-4be5-baa7-59f34004694a"
]
},
{
"title": "react-property-bag-editor PropertyBagGlobalNavWebPart Feature",
"description": "The feature that activates PropertyBagGlobalNavWebPart from the react-property-bag-editor solution.",
"id": "8634e32b-eda4-483d-8fe9-5f2075339eb8",
"version": "1.0.0.0",
"componentIds": [
"8634e32b-eda4-483d-8fe9-5f2075339eb8"
]
}
],
"name": "react-property-bag-editor-client-side-solution",
"id": "12dac38e-b255-44ce-9f06-050571b34d39",
"version": "1.0.0.0"
@ -7,4 +66,4 @@
"paths": {
"zippedPackage": "solution/react-property-bag-editor.sppkg"
}
}
}

View File

@ -0,0 +1,3 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/core-build/sass.schema.json"
}

View File

@ -1,9 +1,6 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/spfx-serve.schema.json",
"port": 4321,
"initialPage": "https://localhost:5432/workbench",
"https": true,
"api": {
"port": 5432,
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
}
}
"initialPage": "https://{tenantDomain}/_layouts/workbench.aspx",
"https": true
}

View File

@ -1,51 +0,0 @@
{
// 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-unused-variable":"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-unused-imports": 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,
"prefer-const": true
}
}
}

View File

@ -1,3 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
"cdnBasePath": "https://rgove3.sharepoint.com/sites/cdn/spfxapps/propertybageditor"
}

View File

@ -2,5 +2,13 @@
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.`);
var getTasks = build.rig.getTasks;
build.rig.getTasks = function () {
var result = getTasks.call(build.rig);
result.set('serve', result.get('serve-deprecated'));
return result;
};
build.initialize(gulp);

File diff suppressed because it is too large Load Diff

View File

@ -6,30 +6,37 @@
"node": ">=0.10.0"
},
"dependencies": {
"@microsoft/sp-client-base": "~1.0.0",
"@microsoft/sp-core-library": "~1.0.0",
"@microsoft/sp-webpart-base": "~1.0.0",
"@types/react": "0.14.46",
"@types/react-addons-shallow-compare": "0.14.17",
"@types/react-addons-test-utils": "0.14.15",
"@types/react-addons-update": "0.14.14",
"@types/react-dom": "0.14.18",
"@types/webpack-env": ">=1.12.1 <1.14.0",
"@microsoft/sp-adaptive-card-extension-base": "1.17.1",
"@microsoft/sp-core-library": "1.17.1",
"@microsoft/sp-property-pane": "1.17.1",
"@microsoft/sp-webpart-base": "1.17.1",
"lodash": "^4.17.4",
"office-ui-fabric-react": "^0.69.0",
"react": "0.14.8",
"react-dom": "0.14.8",
"office-ui-fabric-react": "7.199.1",
"react": "17.0.1",
"react-dom": "17.0.1",
"sp-pnp-js": "^2.0.1",
"typescript": "^2.1.5"
"tslib": "2.3.1"
},
"devDependencies": {
"@microsoft/sp-build-web": "~1.0.0",
"@microsoft/sp-module-interfaces": "~1.0.0",
"@microsoft/sp-webpart-workbench": "~1.0.0",
"gulp": "~3.9.1",
"@types/chai": ">=3.4.34 <3.6.0",
"@types/mocha": ">=2.2.33 <2.6.0"
"@microsoft/eslint-config-spfx": "1.17.1",
"@microsoft/eslint-plugin-spfx": "1.17.1",
"@microsoft/rush-stack-compiler-4.5": "0.4.0",
"@microsoft/sp-build-web": "1.17.1",
"@microsoft/sp-module-interfaces": "1.17.1",
"@microsoft/sp-tslint-rules": "1.14.0",
"@rushstack/eslint-config": "2.5.1",
"@types/es6-promise": "0.0.33",
"@types/microsoft-ajax": "^0.0.41",
"@types/react": "17.0.45",
"@types/react-dom": "17.0.17",
"@types/sharepoint": "^2016.1.14",
"@types/webpack-env": "1.15.2",
"ajv": "6.12.5",
"eslint": "8.7.0",
"eslint-plugin-react-hooks": "4.3.0",
"gulp": "4.0.2",
"tslint-microsoft-contrib": "5.0.0",
"typescript": "4.5.5"
},
"scripts": {
"build": "gulp bundle",

View File

@ -0,0 +1,2 @@
// A file is required to be in the root of the /src directory by the TypeScript compiler

View File

@ -1,22 +1,32 @@
{
"$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json",
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "fa63037d-d7bd-4d52-894a-b40127773283",
"alias": "PropertyBagDisplayWebPart",
"componentType": "WebPart",
"version": "0.0.1",
"version": "*",
"manifestVersion": 2,
"preconfiguredEntries": [{
"groupId": "fa63037d-d7bd-4d52-894a-b40127773283",
"group": { "default": "Property Bag Navigation" },
"title": { "default": "Property Bag Display" },
"description": { "default": "Displays all Sites and selected properties, an lets you edit those propertiies" },
"officeFabricIconFontName": "Page",
"properties": {
"description": "propertyBagDisplay",
"propertiesToDisplay":
"CUSTOMNAVAreaName|AreaName\nCUSTOMNAVBusinessUnit|BusinessUnit\nCUSTOMNAVContinent|Continent\nCUSTOMNAVLocation|Location",
"siteTemplatesToInclude":
"STS\nPUBLISHING"
}
}]
}
"supportedHosts": [
"SharePointWebPart"
],
"safeWithCustomScriptDisabled": false,
"preconfiguredEntries": [
{
"groupId": "fa63037d-d7bd-4d52-894a-b40127773283",
"group": {
"default": "Property Bag Navigation"
},
"title": {
"default": "Property Bag Display"
},
"description": {
"default": "Displays all Sites and selected properties, an lets you edit those propertiies"
},
"officeFabricIconFontName": "Page",
"properties": {
"description": "propertyBagDisplay",
"propertiesToDisplay": "CUSTOMNAVAreaName|AreaName\nCUSTOMNAVBusinessUnit|BusinessUnit\nCUSTOMNAVContinent|Continent\nCUSTOMNAVLocation|Location",
"siteTemplatesToInclude": "STS\nPUBLISHING"
}
}
]
}

View File

@ -2,17 +2,15 @@ import * as React from "react";
import pnp from "sp-pnp-js";
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 "propertyBagDisplayStrings";
import PropertyBagDisplay from "./components/PropertyBagDisplay";
import { IPropertyBagDisplayProps } from "./components/IPropertyBagDisplayProps";
import { IPropertyBagDisplayWebPartProps } from "./IPropertyBagDisplayWebPartProps";
import utils from "../shared/utils";
import { BaseClientSideWebPart } from "@microsoft/sp-webpart-base";
import { IPropertyPaneConfiguration, PropertyPaneTextField } from "@microsoft/sp-property-pane";
export default class PropertyBagDisplayWebPart extends BaseClientSideWebPart<IPropertyBagDisplayWebPartProps> {
/**
@ -30,7 +28,7 @@ export default class PropertyBagDisplayWebPart extends BaseClientSideWebPart<IPr
{
description: this.properties.description,
propertiesToDisplay: utils.parseMultilineTextToArray(this.properties.propertiesToDisplay),
siteTemplatesToInclude:utils.parseMultilineTextToArray(this.properties.siteTemplatesToInclude)
siteTemplatesToInclude: utils.parseMultilineTextToArray(this.properties.siteTemplatesToInclude)
}
);

View File

@ -1,3 +1,5 @@
@import '~@fluentui/react/dist/sass/References.scss';
.helloWorld {
.container {

View File

@ -12,7 +12,7 @@ import { CommandBar } from "office-ui-fabric-react/lib/CommandBar";
import { Label } from "office-ui-fabric-react/lib/Label";
import { TextField } from "office-ui-fabric-react/lib/TextField";
import { Toggle } from "office-ui-fabric-react/lib/Toggle";
import { Button, ButtonType } from "office-ui-fabric-react/lib/Button";
import { ButtonType, PrimaryButton, DefaultButton } from "office-ui-fabric-react/lib/Button";
import { MessageBar, MessageBarType } from "office-ui-fabric-react/lib/MessageBar";
import * as md from "../../shared/MessageDisplay";
import MessageDisplay from "../../shared/MessageDisplay";
@ -127,7 +127,7 @@ export default class PropertyBagDisplay extends React.Component<IPropertyBagDisp
hideMessage={this.removePanelMessage.bind(this)} />
<div> <Label >Site Title</Label> {this.state.workingStorage.Title}</div>
<span> <Label label="" >Site Url</Label> {this.state.workingStorage.Url}</span>
<span> <Label >Site Url</Label> {this.state.workingStorage.Url}</span>
<table>
<thead>
<tr>
@ -155,7 +155,7 @@ export default class PropertyBagDisplay extends React.Component<IPropertyBagDisp
<td>
<Toggle label=""
checked={dp.searchable}
onChanged={this.createSearcheableOnChangedHandler(dp.crawledPropertyName)}
onChange={this.createSearcheableOnChangedHandler(dp.crawledPropertyName)}
/>
</td>
</tr>);
@ -164,10 +164,10 @@ export default class PropertyBagDisplay extends React.Component<IPropertyBagDisp
</table>
<Toggle label="Force Crawl"
checked={this.state.workingStorage.forceCrawl}
onChanged={this.onForceCrawlChange.bind(this)}
onChange={this.onForceCrawlChange.bind(this)}
/>
<Button default={true} icon="Save" buttonType={ButtonType.hero} value="Save" onClick={this.onSave.bind(this)} >Save</Button>
<Button icon="Cancel" buttonType={ButtonType.normal} value="Cancel" onClick={this.onCancel.bind(this)} >Cancel</Button>
<DefaultButton default={true} iconProps={{ iconName: "Save" }} value="Save" onClick={this.onSave.bind(this)} >Save</DefaultButton>
<PrimaryButton iconProps={{ iconName: "Cancel" }} value="Cancel" onClick={this.onCancel.bind(this)} >Cancel</PrimaryButton>
</Panel>
);
@ -248,8 +248,11 @@ export default class PropertyBagDisplay extends React.Component<IPropertyBagDisp
* @memberOf PropertyBagDisplay
*/
public stopediting() {
this.state.isediting = false;
this.setState(this.state);
this.setState((current) => ({
...current,
isediting: false,
}));
}
/**
* Caled by the Details list to render a column as a URL rather than text
@ -329,18 +332,26 @@ export default class PropertyBagDisplay extends React.Component<IPropertyBagDisp
* @memberOf PropertyBagDisplay
*/
public componentWillMount() {
this.state.columns = this.setupColumns();
this.state.managedToCrawedMapping = [];
this.state.managedPropNames = [];
// this.state.columns = this.setupColumns();
// this.state.managedToCrawedMapping = [];
// this.state.managedPropNames = [];
var initState = {
columns: this.setupColumns(),
managedToCrawedMapping: [],
managedPropNames: [],
sites: [],
errorMessages: []
};
for (const prop of this.props.propertiesToDisplay) {
const names: Array<string> = prop.split('|');// crawledpropety/managed property
this.state.managedToCrawedMapping.push(new ManagedToCrawledMappingEntry(names[0], names[1]));
this.state.managedPropNames.push(names[1]);
initState.managedToCrawedMapping.push(new ManagedToCrawledMappingEntry(names[0], names[1]));
initState.managedPropNames.push(names[1]);
}
this.state.managedPropNames.unshift("Title");
this.state.managedPropNames.unshift("Url");
this.state.managedPropNames.unshift("SiteTemplate");
this.state.managedPropNames.unshift("SiteTemplateId");
initState.managedPropNames.unshift("Title");
initState.managedPropNames.unshift("Url");
initState.managedPropNames.unshift("SiteTemplate");
initState.managedPropNames.unshift("SiteTemplateId");
let querytext = "contentclass:STS_Site ";
if (this.props.siteTemplatesToInclude) {
if (this.props.siteTemplatesToInclude.length > 0) {
@ -353,8 +364,7 @@ export default class PropertyBagDisplay extends React.Component<IPropertyBagDisp
else {
querytext += "(SiteTemplate=" + siteTemplateParts[0] + " AND SiteTemplateId=" + siteTemplateParts[1] + ")";
}
if (this.props.siteTemplatesToInclude.indexOf(siteTemplate) !== this.props.siteTemplatesToInclude.length - 1)
{ querytext += " OR "; }
if (this.props.siteTemplatesToInclude.indexOf(siteTemplate) !== this.props.siteTemplatesToInclude.length - 1) { querytext += " OR "; }
}
querytext += " )";
}
@ -362,26 +372,26 @@ export default class PropertyBagDisplay extends React.Component<IPropertyBagDisp
console.log("Using Query " + querytext);
const q: SearchQuery = {
Querytext: querytext,
SelectProperties: this.state.managedPropNames,
SelectProperties: initState.managedPropNames,
RowLimit: 999,
TrimDuplicates: false
};
pnp.sp.search(q).then((results: SearchResults) => {
for (const r of results.PrimarySearchResults) {
const obj: any = {};
for (const dp of this.state.managedPropNames) {
for (const dp of initState.managedPropNames) {
obj[dp] = r[dp];
}
obj.SiteTemplate = obj.SiteTemplate + "#" + obj.SiteTemplateId;
this.state.sites.push(obj);
initState.sites.push(obj);
}
debugger;
this.state.errorMessages.push(new md.Message("Items Recieved"));
this.setState(this.state);
initState.errorMessages.push(new md.Message("Items Recieved"));
this.setState({ ...initState });
}).catch(err => {
debugger;
this.state.errorMessages.push(new md.Message(err));
this.setState(this.state);
initState.errorMessages.push(new md.Message(err));
this.setState({ ...initState });
});
}
/** Event Handlers */
@ -394,8 +404,8 @@ export default class PropertyBagDisplay extends React.Component<IPropertyBagDisp
* @memberOf PropertyBagDisplay
*/
public onActiveItemChanged(item?: any, index?: number) {
this.state.selectedIndex = index;
this.setState(this.state);
//this.state.selectedIndex = index;
this.setState((current) => ({ ...current, selectedIndex: index }));
}
/**
@ -419,8 +429,9 @@ export default class PropertyBagDisplay extends React.Component<IPropertyBagDisp
if (this.state.workingStorage.forceCrawl) {
utils.forceCrawl(this.state.workingStorage.Url);
}
this.state.workingStorage = null;
this.state.isediting = false;
// this.state.workingStorage = null;
// this.state.isediting = false;
this.setState((current) => ({ ...current, workingStorage: null, isediting: false }));
this.setState(this.state);
}).catch((err) => {
@ -438,9 +449,9 @@ export default class PropertyBagDisplay extends React.Component<IPropertyBagDisp
* @memberOf PropertyBagDisplay
*/
public onCancel(e?: MouseEvent): void {
this.state.isediting = false;
this.state.workingStorage = null;
this.setState(this.state);
// this.state.isediting = false;
// this.state.workingStorage = null;
this.setState((current) => ({ ...current, workingStorage: null, isediting: false }));
}
/**
* Set the ForceCrawl Value in working storage which can be used to force a crawl of the site
@ -450,7 +461,7 @@ export default class PropertyBagDisplay extends React.Component<IPropertyBagDisp
*
* @memberOf PropertyBagDisplay
*/
public onForceCrawlChange(newValue: boolean) {
this.state.workingStorage.forceCrawl = newValue;
this.setState(this.state);
@ -474,7 +485,7 @@ export default class PropertyBagDisplay extends React.Component<IPropertyBagDisp
dp.searchable = value;
this.setState(this.state);
}
/**
* Called when user wishes to edit an item.
* The List displayes the values from the search index.
@ -485,6 +496,7 @@ export default class PropertyBagDisplay extends React.Component<IPropertyBagDisp
* @memberOf PropertyBagDisplay
*/
public onEditItemClicked(e?: MouseEvent): void {
debugger;
console.log("in onEditItemClicked");
const selectedSite = this.state.sites[this.state.selectedIndex];
const web = new Web(selectedSite.Url);
@ -492,17 +504,20 @@ export default class PropertyBagDisplay extends React.Component<IPropertyBagDisp
const crawledProps: Array<string> = this.props.propertiesToDisplay.map(item => {
return item.split("|")[0];
});
this.state.workingStorage = _.clone(this.state.sites[this.state.selectedIndex]);
this.state.workingStorage.searchableProps = utils.decodeSearchableProps(r.AllProperties["vti_x005f_indexedpropertykeys"]);
this.state.workingStorage.DisplayProps = utils.SelectProperties(r.AllProperties, crawledProps, this.state.workingStorage.searchableProps);
this.state.workingStorage.errorMessages = new Array<md.Message>();
let temp = _.clone(this.state.sites[this.state.selectedIndex]);
temp.searchableProps = utils.decodeSearchableProps(r.AllProperties["vti_x005f_indexedpropertykeys"]);
temp.DisplayProps = utils.SelectProperties(r.AllProperties, crawledProps, this.state.workingStorage.searchableProps);
temp.errorMessages = new Array<md.Message>();
// now add in the managed Prop
for (const dp of this.state.workingStorage.DisplayProps) {
dp.managedPropertyName =
_.find(this.state.managedToCrawedMapping, mtc => { return mtc.crawledPropertyName === dp.crawledPropertyName; }).managedPropertyName;
}
this.state.isediting = true;
this.setState(this.state);
this.setState((current) => ({
...current,
workingStorage: temp, isediting: true
}));
});
console.log("out onEditItemClicked");
}
@ -526,15 +541,19 @@ export default class PropertyBagDisplay extends React.Component<IPropertyBagDisp
column.isSortedDescending = false;
}
// Sort the items.
this.state.sites = _.orderBy(this.state.sites, [(site, x, y, z) => {
let temp = _.orderBy(this.state.sites, (site) => {
if (site[column.fieldName]) {
return site[column.fieldName].toLowerCase();
}
else {
return "";
}
}], [column.isSortedDescending ? "desc" : "asc"]);
this.setState(this.state);
}, [column.isSortedDescending ? "desc" : "asc"]);
this.setState((current) => ({
...current,
sites: temp
}));
}

View File

@ -1,9 +1,9 @@
/// <reference types="mocha" />
// /// <reference types="mocha" />
import { assert } from 'chai';
// import { assert } from 'chai';
describe('PropertyBagDisplayWebPart', () => {
it('should do something', () => {
assert.ok(true);
});
});
// describe('PropertyBagDisplayWebPart', () => {
// it('should do something', () => {
// assert.ok(true);
// });
// });

View File

@ -1,22 +1,31 @@
{
"$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json",
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "f3ac8a07-2a9b-47a1-8a7e-a093cad63f98",
"alias": "PropertyBagEditorWebPart",
"componentType": "WebPart",
"version": "0.0.1",
"version": "*",
"manifestVersion": 2,
"preconfiguredEntries": [{
"groupId": "f3ac8a07-2a9b-47a1-8a7e-a093cad63f98",
"group": { "default": "Property Bag Navigation" },
"title": { "default": "Property Bag Editor" },
"description": { "default": "Lets you edit the properties of an SPSite passed in as a query parameter" },
"officeFabricIconFontName": "Page",
"properties": {
"description": "PropertyBagEditor",
"propertiesToEdit":
"CUSTOMNAVAreaName\nCUSTOMNAVBusinessUnit\nCUSTOMNAVContinent\nCUSTOMNAVLocation"
}
}]
}
"supportedHosts": [
"SharePointWebPart"
],
"safeWithCustomScriptDisabled": false,
"preconfiguredEntries": [
{
"groupId": "f3ac8a07-2a9b-47a1-8a7e-a093cad63f98",
"group": {
"default": "Property Bag Navigation"
},
"title": {
"default": "Property Bag Editor"
},
"description": {
"default": "Lets you edit the properties of an SPSite passed in as a query parameter"
},
"officeFabricIconFontName": "Page",
"properties": {
"description": "PropertyBagEditor",
"propertiesToEdit": "CUSOMNAVAreaName\nCUSOMNAVBusinessUnit\nCUSOMNAVContinent\nCUSOMNAVLocation"
}
}
]
}

View File

@ -2,12 +2,8 @@ import * as React from "react";
import * as ReactDom from "react-dom";
import { Version, UrlQueryParameterCollection } from "@microsoft/sp-core-library";
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField
} from "@microsoft/sp-webpart-base";
import { BaseClientSideWebPart } from "@microsoft/sp-webpart-base";
import { IPropertyPaneConfiguration, PropertyPaneTextField } from "@microsoft/sp-property-pane";
import * as strings from "propertyBagEditorStrings";
import PropertyBagEditor from "./components/PropertyBagEditor";
import { IPropertyBagEditorProps } from "./components/IPropertyBagEditorProps";

View File

@ -1,3 +1,5 @@
@import '~@fluentui/react/dist/sass/References.scss';
.helloWorld {
.container {

View File

@ -17,7 +17,7 @@ import { Dialog, DialogType } from "office-ui-fabric-react/lib/Dialog";
import { Label } from "office-ui-fabric-react/lib/Label";
import { TextField } from "office-ui-fabric-react/lib/TextField";
import { Toggle } from "office-ui-fabric-react/lib/Toggle";
import { Button, ButtonType } from "office-ui-fabric-react/lib/Button";
import { PrimaryButton, DefaultButton, ButtonType } from "office-ui-fabric-react/lib/Button";
import DisplayProp from "../../shared/DisplayProp";
export interface IPropertyBagEditorState {
@ -31,7 +31,7 @@ export interface IPropertyBagEditorState {
export default class PropertyBagEditor extends React.Component<IPropertyBagEditorProps, IPropertyBagEditorState> {
public refs: {
[key: string]: React.ReactInstance;
list: DetailsList
list: any //DetailsList
};
public constructor(props: IPropertyBagEditorProps) {
super(props);
@ -77,22 +77,26 @@ export default class PropertyBagEditor extends React.Component<IPropertyBagEdito
const web = new Web(this.props.siteUrl);
web.select("Title", "AllProperties").expand("AllProperties").get().then(r => {
debugger;
const sp = utils.decodeSearchableProps(r.AllProperties["vti_x005f_indexedpropertykeys"]);
const dp = utils.SelectProperties(r.AllProperties, this.props.propertiesToEdit, sp);
this.state.searchableProps = sp;
this.state.displayProps = dp;
this.setState(this.state);
// this.state.searchableProps = sp;
// this.state.displayProps = dp;
this.setState((current) => ({ ...current, searchableProps: sp, displayProps: dp }))
});
}
/** event hadlers */
public stopediting() {
this.state.isediting = false;
this.setState(this.state);
//this.state.isediting = false;
this.setState((current) => ({ ...current, isediting: false }))
}
public onActiveItemChanged(item?: any, index?: number) {
this.state.selectedIndex = index;
this.setState(this.state);
//this.state.selectedIndex = index;
this.setState((current) => ({ ...current, selectedIndex: index }))
}
/**
* Gets fired when the user changes the 'Searchable' value in the ui.
@ -127,9 +131,10 @@ export default class PropertyBagEditor extends React.Component<IPropertyBagEdito
* @memberOf PropertyBagEditor
*/
public onEditItemClicked(e?: MouseEvent): void {
this.state.isediting = true;
this.state.workingStorage = _.clone(this.state.displayProps[this.state.selectedIndex]);
this.setState(this.state);
//this.state.isediting = true;
//this.state.workingStorage = _.clone(this.state.displayProps[this.state.selectedIndex]);
//this.setState(this.state);
this.setState((current) => ({ ...current, isediting: true, workingStorage: _.clone(current.displayProps[current.selectedIndex]) }))
}
/**
* Saves the item in workingStorage back to sharepoint, then clears workingStorage and stops editing.
@ -139,14 +144,16 @@ export default class PropertyBagEditor extends React.Component<IPropertyBagEdito
* @memberOf PropertyBagEditor
*/
public onSave(e?: MouseEvent): void {
debugger;
utils.setSPProperty(this.state.workingStorage.crawledPropertyName, this.state.workingStorage.value, this.props.siteUrl)
.then(value => {
this.changeSearchable(this.state.workingStorage.crawledPropertyName, this.state.workingStorage.searchable)
.then(s => {
this.state.displayProps[this.state.selectedIndex] = this.state.workingStorage;
this.state.workingStorage = null;
this.state.isediting = false;
this.setState(this.state);
// this.state.workingStorage = null;
// this.state.isediting = false;
// this.setState(this.state);
this.setState((current) => ({ ...current, isediting: false, workingStorage: null }))
});
});
}
@ -158,9 +165,10 @@ export default class PropertyBagEditor extends React.Component<IPropertyBagEdito
* @memberOf PropertyBagEditor
*/
public onCancel(e?: MouseEvent): void {
this.state.isediting = false;
this.state.workingStorage = null;
this.setState(this.state);
// this.state.isediting = false;
// this.state.workingStorage = null;
// this.setState(this.state);
this.setState((current) => ({ ...current, isediting: false, workingStorage: null }))
}
@ -245,8 +253,8 @@ export default class PropertyBagEditor extends React.Component<IPropertyBagEdito
/>
<Button default={true} icon="Save" buttonType={ButtonType.icon} value="Save" onClick={this.onSave.bind(this)} >Save</Button>
<Button icon="Cancel" buttonType={ButtonType.icon} value="Cancel" onClick={this.onCancel.bind(this)} >Cancel</Button>
<DefaultButton default={true} iconProps={{ iconName: "Save" }} value="Save" onClick={this.onSave.bind(this)} >Save</DefaultButton>
<PrimaryButton iconProps={{ iconName: "Cancel" }} value="Cancel" onClick={this.onCancel.bind(this)} >Cancel</PrimaryButton>
</Dialog>

View File

@ -1,9 +1,9 @@
/// <reference types="mocha" />
// /// <reference types="mocha" />
import { assert } from 'chai';
// import { assert } from 'chai';
describe('PropertyBagEditorWebPart', () => {
it('should do something', () => {
assert.ok(true);
});
});
// describe('PropertyBagEditorWebPart', () => {
// it('should do something', () => {
// assert.ok(true);
// });
// });

View File

@ -1,15 +1,23 @@
{
"$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json",
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "b81a6789-e93b-4be5-baa7-59f34004694a",
"alias": "PropertyBagFilteredSiteListWebPart",
"componentType": "WebPart",
"version": "0.0.1",
"version": "*",
"manifestVersion": 2,
"supportedHosts": [
"SharePointWebPart"
],
"safeWithCustomScriptDisabled": false,
"preconfiguredEntries": [
{
"groupId": "b81a6789-e93b-4be5-baa7-59f34004694a",
"group": { "default": "Property Bag Navigation" },
"title": { "default": "Property Bag Site List" },
"group": {
"default": "Property Bag Navigation"
},
"title": {
"default": "Property Bag Site List"
},
"description": {
"default": "Displays a list of sites with the selected properties"
},

View File

@ -1,13 +1,14 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField,
PropertyPaneToggle, PropertyPaneChoiceGroup
} from '@microsoft/sp-webpart-base';
// import {
// BaseClientSideWebPart,
// IPropertyPaneConfiguration,
// PropertyPaneTextField,
// PropertyPaneToggle, PropertyPaneChoiceGroup
// } from '@microsoft/sp-webpart-base';
import { BaseClientSideWebPart } from "@microsoft/sp-webpart-base";
import { IPropertyPaneConfiguration, PropertyPaneTextField, PropertyPaneToggle, PropertyPaneChoiceGroup } from "@microsoft/sp-property-pane";
import * as strings from 'propertyBagFilteredSiteListStrings';
import PropertyBagFilteredSiteList from './components/PropertyBagFilteredSiteList';
import { IPropertyBagFilteredSiteListProps } from './components/IPropertyBagFilteredSiteListProps';

View File

@ -1,3 +1,5 @@
@import '~@fluentui/react/dist/sass/References.scss';
.helloWorld {
.container {

View File

@ -1,35 +1,14 @@
import * as React from "react";
import pnp from "sp-pnp-js";
import { SortDirection } from "sp-pnp-js";
import * as _ from "lodash";
import * as React from "react";
import pnp, { SearchQuery, SearchResults } from "sp-pnp-js";
import DisplayProp from "../../shared/DisplayProp";
import { SearchQuery, SearchResults } from "sp-pnp-js";
import { css } from "office-ui-fabric-react";
//import styles from "./PropertyBagFilteredSiteList.module.scss";
import { IPropertyBagFilteredSiteListProps } from "./IPropertyBagFilteredSiteListProps";
import { CommandBar } from "office-ui-fabric-react/lib/CommandBar";
import { Label } from "office-ui-fabric-react/lib/Label";
import { TextField } from "office-ui-fabric-react/lib/TextField";
import { Link } from "office-ui-fabric-react/lib/Link";
import { List } from "office-ui-fabric-react/lib/List";
import { Button, ButtonType } from "office-ui-fabric-react/lib/Button";
import { MessageBar, MessageBarType } from "office-ui-fabric-react/lib/MessageBar";
import * as md from "../../shared/MessageDisplay";
import MessageDisplay, * as md from "../../shared/MessageDisplay";
import utils from "../../shared/utils";
import MessageDisplay from "../../shared/MessageDisplay";
import { CommandBar, ICommandBarProps } from "office-ui-fabric-react/lib/CommandBar";
import {
DetailsList, DetailsListLayoutMode, IColumn, IGroupedList, SelectionMode, CheckboxVisibility, IGroup
} from "office-ui-fabric-react/lib/DetailsList";
import {
GroupedList
} from "office-ui-fabric-react/lib/GroupedList";
import {
IViewport
} from "office-ui-fabric-react/lib/utilities/decorators/withViewport";
import { IPropertyBagFilteredSiteListProps } from "./IPropertyBagFilteredSiteListProps";
import {
Panel, PanelType
} from "office-ui-fabric-react/lib/Panel";
import { IContextualMenuItem, } from "office-ui-fabric-react/lib/ContextualMenu";
export interface IPropertyBagFilteredSiteListState {
errorMessages: Array<md.Message>;
@ -71,8 +50,7 @@ export class AppliedUserFilter {
*/
public constructor(
public managedPropertyName: string,
public value: string)
{ }
public value: string) { }
}
export class UserFilter {
@ -91,7 +69,7 @@ export class UserFilter {
}
export default class PropertyBagFilteredSiteList extends React.Component<IPropertyBagFilteredSiteListProps, IPropertyBagFilteredSiteListState> {
public constructor(props) {
console.log(JSON.stringify("in constructor"));
console.log(JSON.stringify("in constructor"));
super(props);
this.state = { sites: [], filteredSites: [], errorMessages: [], userFilters: [], appliedUserFilters: [] };
}
@ -119,11 +97,17 @@ export default class PropertyBagFilteredSiteList extends React.Component<IProper
* @memberOf PropertyBagFilteredSiteList
*/
public setupUserFilters(userFilterNames: Array<string>) {
console.log(JSON.stringify("in extractUserFilterValues"));
this.state.userFilters = [];
console.log(JSON.stringify("in extractUserFilterValues"));
// this.state.userFilters = [];
// for (const userFilterName of userFilterNames) {
// this.state.userFilters.push(new UserFilter(userFilterName));
// }
let userFilters = [];
for (const userFilterName of userFilterNames) {
this.state.userFilters.push(new UserFilter(userFilterName));
userFilters.push(new UserFilter(userFilterName));
}
this.setState((current) => ({ ...current, userFilters: userFilters }));
}
/**
@ -134,7 +118,7 @@ export default class PropertyBagFilteredSiteList extends React.Component<IProper
* @memberOf PropertyBagFilteredSiteList
*/
public extractUserFilterValues(r) {
console.log(JSON.stringify("in extractUserFilterValues"));
console.log(JSON.stringify("in extractUserFilterValues"));
for (const userFilter of this.state.userFilters) {
const value = r[userFilter.managedPropertyName].trim();
if (_.find(userFilter.values, v => { return v === value; })) {
@ -157,14 +141,14 @@ export default class PropertyBagFilteredSiteList extends React.Component<IProper
*
* @memberOf PropertyBagFilteredSiteList
*/
public getSites(siteTemplatesToInclude: Array<string>, filters:Array<string>, showQueryText: boolean, userFilters: Array<string>, showSiteDescriptions: boolean) {
console.log(JSON.stringify("in getSites"));
public getSites(siteTemplatesToInclude: Array<string>, filters: Array<string>, showQueryText: boolean, userFilters: Array<string>, showSiteDescriptions: boolean) {
console.log(JSON.stringify("in getSites"));
const userFilterNameArray = [];
if (userFilters) {
for (const userFilter of userFilters) {
userFilterNameArray.push(userFilter);
userFilterNameArray.push(userFilter);
}
}
let querytext = "contentclass:STS_Site ";
@ -201,19 +185,34 @@ export default class PropertyBagFilteredSiteList extends React.Component<IProper
};
pnp.sp.search(q).then((results: SearchResults) => {
this.state.sites = [];
// this.state.sites = [];
// debugger;
// this.setupUserFilters(userFilterNameArray);
// for (const r of results.PrimarySearchResults) {
// const index = this.state.sites.push(new Site(r.Title, r.Description, r.SPSiteUrl));
// for (const mp of this.props.userFilters) {
// this.state.sites[index-1][mp] = r[mp];
// }
// this.extractUserFilterValues(r);
// }
// this.filterSites();
// this.setState(this.state);
let sites = [];
debugger;
this.setupUserFilters(userFilterNameArray);
for (const r of results.PrimarySearchResults) {
const index = this.state.sites.push(new Site(r.Title, r.Description, r.SPSiteUrl));
debugger;
const index = sites.push(new Site(r.Title, r.Description, r["SPSiteUrl"]));
for (const mp of this.props.userFilters) {
this.state.sites[index-1][mp] = r[mp];
sites[index - 1][mp] = r[mp];
}
this.extractUserFilterValues(r);
}
this.filterSites();
this.setState(this.state);
debugger;
let filteredSites = this.filterSites(sites);// need to pass sites iun here and return the filtered array!!!
this.setState((current) => ({ ...current, filteredSites: filteredSites, sites: sites }));
}).catch(err => {
debugger;
this.state.errorMessages.push(new md.Message(err));
@ -228,7 +227,7 @@ export default class PropertyBagFilteredSiteList extends React.Component<IProper
* @memberOf PropertyBagFilteredSiteList
*/
public componentWillMount() {
console.log(JSON.stringify("in componentWillMount"));
console.log(JSON.stringify("in componentWillMount"));
this.getSites(this.props.siteTemplatesToInclude, this.props.filters, this.props.showQueryText, this.props.userFilters, this.props.showSiteDescriptions);
}
/**
@ -241,7 +240,7 @@ export default class PropertyBagFilteredSiteList extends React.Component<IProper
* @memberOf PropertyBagFilteredSiteList
*/
public componentWillReceiveProps(nextProps: IPropertyBagFilteredSiteListProps, nextContext: any) {
console.log(JSON.stringify("in componentWillReceiveProps"));
console.log(JSON.stringify("in componentWillReceiveProps"));
this.getSites(nextProps.siteTemplatesToInclude, nextProps.filters, nextProps.showQueryText, nextProps.userFilters, nextProps.showSiteDescriptions);
}
/**
@ -255,7 +254,7 @@ export default class PropertyBagFilteredSiteList extends React.Component<IProper
* @memberOf PropertyBagFilteredSiteList
*/
public conditionallyRenderDescription(site: Site) {
console.log(JSON.stringify("in conditionallyRenderDescription"));
console.log(JSON.stringify("in conditionallyRenderDescription"));
if (this.props.showSiteDescriptions) {
return (<Label>{site.description}</Label>);
}
@ -273,19 +272,19 @@ export default class PropertyBagFilteredSiteList extends React.Component<IProper
* @memberOf PropertyBagFilteredSiteList
*/
private SetupFilters(): Array<IContextualMenuItem> {
console.log(JSON.stringify("in SetupFilters"));
console.log(JSON.stringify("in SetupFilters"));
const items = new Array<IContextualMenuItem>();
for (const uf of this.state.userFilters) {
const item: IContextualMenuItem = {
key: uf.managedPropertyName,
name: uf.managedPropertyName,
title: uf.managedPropertyName,
href:null,
href: null,
}
item.items = [];
item.subMenuProps = { items: [] };
for (const value of uf.values) {
item.items.push({
item.subMenuProps.items.push({
key: value,
data: {
managedPropertyName: uf.managedPropertyName,
@ -311,16 +310,16 @@ export default class PropertyBagFilteredSiteList extends React.Component<IProper
* @memberOf PropertyBagFilteredSiteList
*/
public AppliedFilterExists(managedPropertyName: string, value: string): boolean {
console.log(JSON.stringify("in AppliedFilterExists"));
console.log(JSON.stringify("in AppliedFilterExists"));
const selectedFilter = _.find(this.state.appliedUserFilters, af => {
return (af.managedPropertyName === managedPropertyName && af.value === value);
});
if (selectedFilter) {
if (selectedFilter) {
return true;
} else {
return false;
}
} else {
return false;
}
}
/**
@ -331,14 +330,24 @@ export default class PropertyBagFilteredSiteList extends React.Component<IProper
* @memberOf PropertyBagFilteredSiteList
*/
public ToggleAppliedUserFilter(item: IContextualMenuItem) {
console.log(JSON.stringify("in ToggleAppliedUserFilter"));
console.log(JSON.stringify("in ToggleAppliedUserFilter"));
if (this.AppliedFilterExists(item.data.managedPropertyName, item.data.value)) {
this.state.appliedUserFilters = this.state.appliedUserFilters.filter(af => {
return (af.managedPropertyName !== item.data.managedPropertyName || af.value !== item.data.value);
});
// this.state.appliedUserFilters = this.state.appliedUserFilters.filter(af => {
// return (af.managedPropertyName !== item.data.managedPropertyName || af.value !== item.data.value);
// });
this.setState((current) => ({
...current,
appliedUserFilters: current.appliedUserFilters.filter(af => {
return (af.managedPropertyName !== item.data.managedPropertyName || af.value !== item.data.value);
})
}))
}
else {
this.state.appliedUserFilters.push(new AppliedUserFilter(item.data.managedPropertyName, item.data.value));
// this.state.appliedUserFilters.push(new AppliedUserFilter(item.data.managedPropertyName, item.data.value));
let temp = this.state.appliedUserFilters;
temp.push(new AppliedUserFilter(item.data.managedPropertyName, item.data.value));
this.setState((current) => ({ ...current, appliedUserFilters: temp }))
}
}
@ -351,22 +360,24 @@ export default class PropertyBagFilteredSiteList extends React.Component<IProper
*
* @memberOf PropertyBagFilteredSiteList
*/
public filterSites() {
console.log(JSON.stringify("in filterSites"));
public filterSites(sites: Site[]): Site[] {
console.log(JSON.stringify("in filterSites"));
if (this.state.appliedUserFilters.length === 0) {
this.state.filteredSites = this.state.sites;
return sites;
}
else {
this.state.filteredSites = this.state.sites.filter(site => {
let filteredSites = sites.filter(site => {
debugger;
for (const auf of this.state.appliedUserFilters) {
if (site[auf.managedPropertyName] !== auf.value) {
return false;
if (site[auf.managedPropertyName] === auf.value) {
return true;
}
}
return true;
return false;
});
return filteredSites;
}
}
/**
@ -381,16 +392,17 @@ export default class PropertyBagFilteredSiteList extends React.Component<IProper
* @memberOf PropertyBagFilteredSiteList
*/
public filterOnMetadata(ev?: React.MouseEvent<HTMLElement>, item?: IContextualMenuItem) {
console.log(JSON.stringify("in filterOnMetadata"));
console.log(JSON.stringify("in filterOnMetadata"));
this.ToggleAppliedUserFilter(item);
this.filterSites();
this.setState(this.state);
// this.filterSites();
// this.setState(this.state);
this.setState((current) => ({ ...current, filteredSites: this.filterSites(current.sites) }));
}
public doNothing(ev?: React.MouseEvent<HTMLElement>, item?: IContextualMenuItem) {
console.log(JSON.stringify("in doNothing"));
ev.stopPropagation();
return false;
public doNothing(ev?: React.MouseEvent<HTMLElement>, item?: IContextualMenuItem) {
console.log(JSON.stringify("in doNothing"));
ev.stopPropagation();
return false;
}
@ -411,11 +423,13 @@ export default class PropertyBagFilteredSiteList extends React.Component<IProper
</li>
);
const commandItems: Array<IContextualMenuItem> = this.SetupFilters();
console.log(JSON.stringify("commandItems follow"));
console.log(JSON.stringify("commandItems follow"));
console.log(JSON.stringify(commandItems));
return (
<div >
<Label>{this.props.description}</Label>
<Label>Sites:{this.state.sites.length}</Label>
<Label>FoltererdSites:{this.state.filteredSites.length}</Label>
<CommandBar items={commandItems} />
<MessageDisplay
messages={this.state.errorMessages}

View File

@ -1,9 +1,9 @@
/// <reference types="mocha" />
// /// <reference types="mocha" />
import { assert } from 'chai';
// import { assert } from 'chai';
describe('PropertyBagFilteredSiteListWebPart', () => {
it('should do something', () => {
assert.ok(true);
});
});
// describe('PropertyBagFilteredSiteListWebPart', () => {
// it('should do something', () => {
// assert.ok(true);
// });
// });

View File

@ -1,10 +1,14 @@
{
"$schema": "../../../node_modules/@microsoft/sp-module-interfaces/lib/manifestSchemas/jsonSchemas/clientSideComponentManifestSchema.json",
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "8634e32b-eda4-483d-8fe9-5f2075339eb8",
"alias": "PropertyBagGlobalNavWebPart",
"componentType": "WebPart",
"version": "0.0.1",
"version": "*",
"manifestVersion": 2,
"supportedHosts": [
"SharePointWebPart"
],
"safeWithCustomScriptDisabled": false,
"preconfiguredEntries": [
{
"groupId": "8634e32b-eda4-483d-8fe9-5f2075339eb8",

View File

@ -1,11 +1,8 @@
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 { BaseClientSideWebPart } from "@microsoft/sp-webpart-base";
import { IPropertyPaneConfiguration, PropertyPaneTextField } from "@microsoft/sp-property-pane";
import utils from "../shared/utils";
import * as strings from 'propertyBagGlobalNavStrings';
import PropertyBagGlobalNav from './components/PropertyBagGlobalNav';
@ -19,7 +16,7 @@ export default class PropertyBagGlobalNavWebPart extends BaseClientSideWebPart<I
PropertyBagGlobalNav,
{
description: this.properties.description,
managedProperties: utils.parseMultilineTextToArray( this.properties.managedProperties),
managedProperties: utils.parseMultilineTextToArray(this.properties.managedProperties),
siteTemplatesToInclude: utils.parseMultilineTextToArray(this.properties.siteTemplatesToInclude),
filters: utils.parseMultilineTextToArray(this.properties.filters),
}

View File

@ -1,6 +1,5 @@
export interface IPropertyBagGlobalNavProps {
description: string;
siteTemplatesToInclude: Array<string>; //STS#1 STS#2 leave off the #1 to get all STS
filters: Array<string>; // managedPropertyname=valiust separated by \n (new line)
managedProperties: Array<string>;// managed properties to build the menu from.

View File

@ -1,3 +1,5 @@
@import '~@fluentui/react/dist/sass/References.scss';
.helloWorld {
.container {
max-width: 700px;
@ -29,7 +31,7 @@
// Basic Button
outline: transparent;
position: relative;
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
font-family: "Segoe UI WestEuropean", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 14px;
font-weight: 400;

View File

@ -1,30 +1,12 @@
import * as React from 'react';
import { IPropertyBagGlobalNavProps } from './IPropertyBagGlobalNavProps';
import pnp from "sp-pnp-js";
import { SortDirection } from "sp-pnp-js";
import * as _ from "lodash";
import { SearchQuery, SearchResults } from "sp-pnp-js";
import { css } from "office-ui-fabric-react";
import * as React from 'react';
import pnp, { SearchQuery, SearchResults } from "sp-pnp-js";
import { IPropertyBagGlobalNavProps } from './IPropertyBagGlobalNavProps';
import { Label } from "office-ui-fabric-react/lib/Label";
import { TextField } from "office-ui-fabric-react/lib/TextField";
import { Link } from "office-ui-fabric-react/lib/Link";
import { List } from "office-ui-fabric-react/lib/List";
import { Button, ButtonType } from "office-ui-fabric-react/lib/Button";
import { MessageBar, MessageBarType } from "office-ui-fabric-react/lib/MessageBar";
import { CommandBar } from "office-ui-fabric-react/lib/CommandBar";
import { IContextualMenuItem, } from "office-ui-fabric-react/lib/ContextualMenu";
import * as md from "../../shared/MessageDisplay";
import utils from "../../shared/utils";
import { CommandBar, ICommandBarProps } from "office-ui-fabric-react/lib/CommandBar";
import {
DetailsList, DetailsListLayoutMode, IColumn, IGroupedList, SelectionMode, CheckboxVisibility, IGroup
} from "office-ui-fabric-react/lib/DetailsList";
import {
GroupedList
} from "office-ui-fabric-react/lib/GroupedList";
import {
IViewport
} from "office-ui-fabric-react/lib/utilities/decorators/withViewport";
import { IContextualMenuItem, } from "office-ui-fabric-react/lib/ContextualMenu";
export class PropertyBagGlobalNavState {
public menuitems: Array<IContextualMenuItem>; // The menuItems to display
public errorMessages: Array<md.Message>;// any error messages
@ -43,28 +25,29 @@ export default class PropertyBagGlobalNav extends React.Component<IPropertyBagGl
*
* @memberOf PropertyBagGlobalNav
*/
public addMenuItem(r: any): void {
public addMenuItem(items: Array<IContextualMenuItem>, r: any): Array<IContextualMenuItem> {
let currentItem: IContextualMenuItem;
let currentSet: Array<IContextualMenuItem> = this.state.menuitems;
debugger;
let currentSet: Array<IContextualMenuItem> = items;
for (const managedProperty of this.props.managedProperties) {
if (r[managedProperty]) {// if site does not have this property set
const value = r[managedProperty].trim();
currentItem = _.find(currentSet, i => { return i.key === value; });
if (!currentItem) {
const idx = currentSet.push({ key: value, name: value, items: [] });
const idx = currentSet.push({ key: value, name: value, subMenuProps: { items: [] } });
currentItem = currentSet[idx - 1];
}
currentSet = currentItem.items;
currentSet = currentItem.subMenuProps.items;
}
}
if (currentItem) { // should have it if site does have this property set
currentItem.items.push({
currentItem.subMenuProps.items.push({
key: r['Title'],
name: r['Title'],
href: r['SPSiteUrl']
});
}
return items;
}
/**
* Gets the list of sites to be displayed in the Menu using the filters specified in
@ -107,12 +90,20 @@ export default class PropertyBagGlobalNav extends React.Component<IPropertyBagGl
};
pnp.sp.search(q).then((results: SearchResults) => {
this.state.menuitems = [];
// this.state.menuitems = [];
// for (const r of results.PrimarySearchResults) {
// this.addMenuItem(r);
// }
// this.setState(this.state);
let menuitems = [];
for (const r of results.PrimarySearchResults) {
this.addMenuItem(r);
this.addMenuItem(menuitems, r);
}
debugger;
this.setState((current) => ({ ...current, menuitems: menuitems }));
this.setState(this.state);
}).catch(err => {
debugger;
this.state.errorMessages.push(new md.Message(err));

View File

@ -1,9 +1,9 @@
/// <reference types="mocha" />
// /// <reference types="mocha" />
import { assert } from 'chai';
// import { assert } from 'chai';
describe('PropertyBagGlobalNavWebPart', () => {
it('should do something', () => {
assert.ok(true);
});
});
// describe('PropertyBagGlobalNavWebPart', () => {
// it('should do something', () => {
// assert.ok(true);
// });
// });

View File

@ -10,8 +10,8 @@ import { MessageBar, MessageBarType } from "office-ui-fabric-react/lib/MessageBa
* @class Message
*/
export class Message {
public Id: string;
public text: string;
public Id: string;
public text: string;
public constructor(msg: string) {
this.text = msg;
this.Id = Guid.newGuid().toString();
@ -37,15 +37,16 @@ export default class MessageDisplay extends React.Component<IMessageDisplayProps
* @memberOf MessageDisplay
*/
public createDismissHandler = (messageId) => (vale) => {
this.props.hideMessage(messageId);
this.props.hideMessage(messageId);
}
public render(): React.ReactElement<IMessageDisplayProps> {
return (
return (
<div>
{this.props.messages.map((message, y, z) => {
return (
<MessageBar
messageBarType={MessageBarType.remove}
messageBarType={MessageBarType.error}
isMultiline={true}
onDismiss={this.createDismissHandler(message.Id)}>
{message.text}

View File

@ -122,7 +122,7 @@ export default class utils {
*
* @memberOf utils
*/
public static setSPProperty(name: string, value: string, siteUrl: string) {
public static setSPProperty(name: string, value: string, siteUrl: string) {
return new Promise((resolve, reject) => {
let webProps;
const clientContext = new SP.ClientContext(siteUrl);
@ -134,7 +134,7 @@ export default class utils {
clientContext.load(web);
clientContext.load(webProps);
clientContext.executeQueryAsync(
(sender, args) => { resolve(); },
(sender, args) => { resolve(webProps); },
(sender, args) => { reject(args.get_message()); }
);
@ -246,7 +246,7 @@ export default class utils {
*
* @memberOf utils
*/
public static parseMultilineTextToArray(value: string): Array<string> {
if (!value) {
return [];

View File

@ -1,13 +1,41 @@
{
"extends": "./node_modules/@microsoft/rush-stack-compiler-4.5/includes/tsconfig-web.json",
"compilerOptions": {
"skipLibCheck": true,
"inlineSources": false,
"strictNullChecks": false,
"noUnusedLocals": false,
"target": "es5",
"forceConsistentCasingInFileNames": true,
"module": "commonjs",
"module": "esnext",
"moduleResolution": "node",
"jsx": "react",
"declaration": true,
"sourceMap": true,
"noImplicitAny": false,
"experimentalDecorators": true,
"types": [
"webpack-env"
"webpack-env",
"microsoft-ajax",
"sharepoint"
],
"lib": [
"es2015.promise",
"es5",
"dom",
"es2015.collection"
],
"outDir": "lib",
"typeRoots": [
"./node_modules/@types",
"./node_modules/@microsoft"
]
}
}
},
"include": [
"src/**/*.tsx"
],
"exclude": [
"node_modules",
"lib"
],
}

View File

@ -1,3 +0,0 @@
{
"rulesDirectory": "./config"
}

View File

@ -1,5 +0,0 @@
// Type definitions for Microsoft ODSP projects
// Project: ODSP
/* Global definition for UNIT_TEST builds */
declare const UNIT_TEST: boolean;

View File

@ -1,4 +0,0 @@
/// <reference path="@ms/odsp.d.ts" />
/// <reference path="assertion-error/assertion-error.d.ts" />
/// <reference path="knockout/knockout.d.ts" />
/// <referehce path="SP/SP.d.ts />

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,197 @@
SPFX PROJECT UPGRADE
====================
Upgrades SharePoint Framework project to the specified version
USAGE
m365 spfx project upgrade [options]
OPTIONS
-v, --toVersion [toVersion]
The version of SharePoint Framework to which upgrade the project
--packageManager [packageManager]
The package manager you use. Supported managers npm,pnpm,yarn. Default npm
--shell [shell]
The shell you use. Supported shells bash,powershell,cmd. Default bash
--preview
Upgrade project to the latest SPFx preview version
-f, --outputFile [outputFile]
Path to the file where the upgrade report should be stored in. Ignored when output is tour
-h, --help [help]
Output usage information. Optionally, specify which section of command's help you want to see. Allowed values are options, examples, remarks, response, full. Default is full.
--query [query]
JMESPath query string. See http://jmespath.org/ for more information and examples
-o, --output [output]
Output type. json,text,csv,md. Default json
--verbose
Runs command with verbose logging
--debug
Runs command with debug logging
REMARKS
The spfx project upgrade command helps you upgrade your SharePoint Framework project to the specified version. If no version is specified, the command will upgrade to the latest version of the SharePoint Framework it supports (v1.17.2).
This command doesn't change your project files. Instead, it gives you a report with all steps necessary to upgrade your project to the specified version of the SharePoint Framework. Changing project files is error-prone, especially when it comes to updating your solution's code. This is why at this moment, this command produces a report that you can use yourself to perform the necessary updates and verify that everything is working as expected.
!!! important
Run this command in the folder where the project that you want to upgrade is located. This command doesn't change your project files.
EXAMPLES
Get instructions to upgrade the current SharePoint Framework project to SharePoint Framework version 1.5.0 and save the findings in a Markdown file
m365 spfx project upgrade --toVersion 1.5.0 --output md > "upgrade-report.md"
Get instructions to upgrade the current SharePoint Framework project to SharePoint Framework version 1.5.0 and show the summary of the findings in the shell
m365 spfx project upgrade --toVersion 1.5.0 --output text
Get instructions to upgrade the current SharePoint Framework project to the latest preview version
m365 spfx project upgrade --preview --output text
Get instructions to upgrade the current SharePoint Framework project to the specified preview version
m365 spfx project upgrade --toVersion 1.12.1-rc.0 --output text
Get instructions to upgrade the current SharePoint Framework project to the latest SharePoint Framework version supported by the CLI for Microsoft 365 using pnpm
m365 spfx project upgrade --packageManager pnpm --output text
Get instructions to upgrade the current SharePoint Framework project to the latest SharePoint Framework version supported by the CLI for Microsoft 365
m365 spfx project upgrade --output text
Get instructions to upgrade the current SharePoint Framework project to the latest SharePoint Framework version supported by the CLI for Microsoft 365 using PowerShell
m365 spfx project upgrade --shell powershell --output text
Get instructions to upgrade the current SharePoint Framework project to the latest version of SharePoint Framework and save the findings in a CodeTour (https://aka.ms/codetour) file
m365 spfx project upgrade --output tour
RESPONSE
When upgrading an SPFx project built using version 1.15.0 to SPFx version 1.15.2, you'll get output similar to following (output is truncated):
JSON
[
{
"description": "Upgrade SharePoint Framework dependency package @microsoft/sp-core-library",
"id": "FN001001",
"file": "./package.json",
"position": {
"line": 15,
"character": 5
},
"resolution": "npm i -SE @microsoft/sp-core-library@1.15.2",
"resolutionType": "cmd",
"severity": "Required",
"title": "@microsoft/sp-core-library"
},
{
"description": "Update version in .yo-rc.json",
"id": "FN010001",
"file": "./.yo-rc.json",
"position": {
"line": 5,
"character": 5
},
"resolution": "{\\\n \"@microsoft/generator-sharepoint\": {\\\n \"version\": \"1.15.2\"\\\n }\\\n}",
"resolutionType": "json",
"severity": "Recommended",
"title": ".yo-rc.json version"
}
]
Text
Execute in bash
-----------------------
npm i -SE @microsoft/sp-core-library@1.15.2
./.yo-rc.json
-------------
Update version in .yo-rc.json:
{
"@microsoft/generator-sharepoint": {
"version": "1.15.2"
}
}
Markdown
# Upgrade project HelloWorld to v1.15.2
Date: 20/11/2022
## Findings
Following is the list of steps required to upgrade your project to SharePoint Framework version 1.15.2. Summary of the modifications is included at the end of the report.
### FN001001 @microsoft/sp-core-library | Required
Upgrade SharePoint Framework dependency package @microsoft/sp-core-library
Execute the following command:
`sh
npm i -SE @microsoft/sp-core-library@1.15.2
File: ./package.json:17:5
## Summary
### Execute script
`sh
npm i -SE @microsoft/sp-core-library@1.15.2
`
### Modify files
#### ./.yo-rc.json
Update version in .yo-rc.json:
`json
{
"@microsoft/generator-sharepoint": {
"version": "1.15.2"
}
}
`