diff --git a/samples/react-list-form/.vscode/launch.json b/samples/react-list-form/.vscode/launch.json index 40095289b..d28437457 100644 --- a/samples/react-list-form/.vscode/launch.json +++ b/samples/react-list-form/.vscode/launch.json @@ -1,36 +1,37 @@ { - "version": "0.2.0", - "configurations": [{ - "name": "Local workbench", - "type": "chrome", - "request": "launch", - "url": "https://localhost:4321/temp/workbench.html", - "webRoot": "${workspaceRoot}", - "sourceMaps": true, - "sourceMapPathOverrides": { - "webpack:///../../../src/*": "${webRoot}/src/*", - "webpack:///../../../../src/*": "${webRoot}/src/*", - "webpack:///../../../../../src/*": "${webRoot}/src/*" - }, - "runtimeArgs": [ - "--remote-debugging-port=9222" - ] + "version": "0.2.0", + "configurations": [ + { + "name": "Local workbench", + "type": "chrome", + "request": "launch", + "url": "https://localhost:4321/temp/workbench.html", + "webRoot": "${workspaceRoot}", + "sourceMaps": true, + "sourceMapPathOverrides": { + "webpack:///../../../src/*": "${webRoot}/src/*", + "webpack:///../../../../src/*": "${webRoot}/src/*", + "webpack:///../../../../../src/*": "${webRoot}/src/*" }, - { - "name": "Hosted workbench", - "type": "chrome", - "request": "launch", - "url": "https://skybow.sharepoint.com/sites/test_dwy/_layouts/15/workbench.aspx", - "webRoot": "${workspaceRoot}", - "sourceMaps": true, - "sourceMapPathOverrides": { - "webpack:///../../../src/*": "${webRoot}/src/*", - "webpack:///../../../../src/*": "${webRoot}/src/*", - "webpack:///../../../../../src/*": "${webRoot}/src/*" - }, - "runtimeArgs": [ - "--remote-debugging-port=9222" - ] - } - ] - } \ No newline at end of file + "runtimeArgs": [ + "--remote-debugging-port=9222" + ] + }, + { + "name": "Hosted workbench", + "type": "chrome", + "request": "launch", + "url": "https://sharepointknight.sharepoint.com/_layouts/15/workbench.aspx", + "webRoot": "${workspaceRoot}", + "sourceMaps": true, + "sourceMapPathOverrides": { + "webpack:///../../../src/*": "${webRoot}/src/*", + "webpack:///../../../../src/*": "${webRoot}/src/*", + "webpack:///../../../../../src/*": "${webRoot}/src/*" + }, + "runtimeArgs": [ + "--remote-debugging-port=9222" + ] + } + ] +} \ No newline at end of file diff --git a/samples/react-list-form/README.md b/samples/react-list-form/README.md index 42c8e53f6..79f49bf8c 100755 --- a/samples/react-list-form/README.md +++ b/samples/react-list-form/README.md @@ -39,6 +39,7 @@ The web part allows configuring which list to use and if a form for adding a new | --------------- | ----------------------------------------------------------------- | | react-list-form | Dany Wyss | | react-list-form | Harsha Vardhini ([@harshagracy](https://twitter.com/harshagracy)) | +| react-list-form | Ryan Schouten ([@shrpntknight](https://twitter.com/shrpntknight)) | ## Version history @@ -48,6 +49,7 @@ The web part allows configuring which list to use and if a form for adding a new | 1.0.1 | February 22, 2019 | Updated to SPFx 1.7.1 and dependencies, Added Turkish translation, Added RichText Mode and Tinymce Editor | | 1.0.2 | October 14, 2019 | Updated to SPFx 1.9.1 and dependencies | | 1.0.3 | July 7, 2020 | Updated to SPFx 1.10.0 and dependencies. Fixed required field validation (Harsha Vardhini) | +| 1.0,4 | September 12, 2020| Added support for User, UserMulti, Taxonomy, and TaxonomyMulti field types (Ryan Schouten) | ## Disclaimer diff --git a/samples/react-list-form/config/config.json b/samples/react-list-form/config/config.json index b7191d6de..0faf20aa0 100755 --- a/samples/react-list-form/config/config.json +++ b/samples/react-list-form/config/config.json @@ -1,23 +1,24 @@ -{ - "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json", - "version": "2.0", - "bundles": { - "list-form-web-part": { - "components": [ - { - "entrypoint": "./lib/webparts/listForm/ListFormWebPart.js", - "manifest": "./src/webparts/listForm/ListFormWebPart.manifest.json" - } - ] - } - }, - "externals": { - "moment": "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment-with-locales.min.js" - }, - "localizedResources": { - "ListFormWebPartStrings": "lib/webparts/listForm/loc/{locale}.js", - "ListFormStrings": "lib/webparts/listForm/components/loc/{locale}.js", - "FormFieldStrings": "lib/webparts/listForm/components/formFields/loc/{locale}.js", - "servicesStrings": "lib/common/services/loc/{locale}.js" - } -} +{ + "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json", + "version": "2.0", + "bundles": { + "list-form-web-part": { + "components": [ + { + "entrypoint": "./lib/webparts/listForm/ListFormWebPart.js", + "manifest": "./src/webparts/listForm/ListFormWebPart.manifest.json" + } + ] + } + }, + "externals": { + "moment": "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment-with-locales.min.js" + }, + "localizedResources": { + "ListFormWebPartStrings": "lib/webparts/listForm/loc/{locale}.js", + "ListFormStrings": "lib/webparts/listForm/components/loc/{locale}.js", + "FormFieldStrings": "lib/webparts/listForm/components/formFields/loc/{locale}.js", + "servicesStrings": "lib/common/services/loc/{locale}.js", + "ControlStrings": "node_modules/@pnp/spfx-controls-react/lib/loc/{locale}.js" + } +} \ No newline at end of file diff --git a/samples/react-list-form/npm-shrinkwrap.json b/samples/react-list-form/npm-shrinkwrap.json index 6cc243447..c40d4dc58 100644 --- a/samples/react-list-form/npm-shrinkwrap.json +++ b/samples/react-list-form/npm-shrinkwrap.json @@ -4196,6 +4196,130 @@ "integrity": "sha512-518yewjSga1jLdiLrcmpMFlaba5P+50b0TWNFUpC+SL9Yzf0kMi57qw+bMl+rQ08cGqH1vLx4eg9YFUbZXgZ0Q==", "dev": true }, + "@pnp/common": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pnp/common/-/common-1.0.1.tgz", + "integrity": "sha1-T+cuONHexjlQSvxxQclSEh5YqOk=", + "requires": { + "tslib": "1.8.1" + } + }, + "@pnp/logging": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pnp/logging/-/logging-1.0.1.tgz", + "integrity": "sha1-Nl1/dmiW943xIMgd9D3dlrCgojY=", + "requires": { + "tslib": "1.8.1" + } + }, + "@pnp/odata": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pnp/odata/-/odata-1.0.1.tgz", + "integrity": "sha1-yE5s/MV2VdZj2IEFlgGT8yiOwAI=", + "requires": { + "tslib": "1.8.1" + } + }, + "@pnp/sp": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pnp/sp/-/sp-1.0.1.tgz", + "integrity": "sha1-5XXJVqZWk9KRkI4yEdzWbc5KFWM=", + "requires": { + "tslib": "1.8.1" + } + }, + "@pnp/spfx-controls-react": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@pnp/spfx-controls-react/-/spfx-controls-react-1.19.0.tgz", + "integrity": "sha512-W3PS6I8NsdbOZjE9I9djloYutQW17QHd4nT7jFwPyJFoxnt1MDfWyN6nrPhaeGnnPde3t3TlUbWP4HKLXChFiw==", + "requires": { + "@pnp/common": "1.0.1", + "@pnp/logging": "1.0.1", + "@pnp/odata": "1.0.1", + "@pnp/sp": "1.0.1", + "@pnp/telemetry-js": "2.0.0", + "@types/chart.js": "2.7.40", + "chart.js": "2.7.3", + "color": "^3.1.2", + "lodash": "4.17.13", + "office-ui-fabric-react": "5.131.0", + "react-quill": "1.3.3" + }, + "dependencies": { + "@uifabric/icons": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@uifabric/icons/-/icons-5.8.0.tgz", + "integrity": "sha512-EUhKxYlIPJshg4fQvCNTYSk0p7RhzEWeEAJBV4sao1SKmN0/pZBnkLbDqWjU5VUfdwZZYiIdaLRpM+pyzhniZw==", + "requires": { + "@uifabric/styling": ">=5.30.1 <6.0.0", + "tslib": "^1.7.1" + } + }, + "@uifabric/merge-styles": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@uifabric/merge-styles/-/merge-styles-5.17.1.tgz", + "integrity": "sha512-4/EtO6Ns7kNtKxC+6InShwVQeNQEDT5H8Ex7m/i4OrT9i7csje4YwBQPkkpm31qJwEZEyD7bbAwyLezI63sLhg==", + "requires": { + "tslib": "^1.7.1" + } + }, + "@uifabric/styling": { + "version": "5.37.0", + "resolved": "https://registry.npmjs.org/@uifabric/styling/-/styling-5.37.0.tgz", + "integrity": "sha512-3hC0itW/hWSD5J4uANzUKk8XVGWUNkU+VLjEjWsQ6i5lvwFGaanR6Qy0bTkZdFGqFWMXe91CkBHV7HnvEx7tCA==", + "requires": { + "@microsoft/load-themed-styles": "^1.7.13", + "@uifabric/merge-styles": ">=5.17.1 <6.0.0", + "@uifabric/utilities": ">=5.34.2 <6.0.0", + "tslib": "^1.7.1" + } + }, + "@uifabric/utilities": { + "version": "5.34.3", + "resolved": "https://registry.npmjs.org/@uifabric/utilities/-/utilities-5.34.3.tgz", + "integrity": "sha512-6dERFkNNCUrPUuNG1nxlDDvt7DN5hxb41zp9AmKhK5cXZTYCblmlLBvb/qyielCnicfoagoA+lqH9NgnSE8u/A==", + "requires": { + "@uifabric/merge-styles": ">=5.17.1 <6.0.0", + "prop-types": "^15.5.10", + "tslib": "^1.7.1" + } + }, + "lodash": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.13.tgz", + "integrity": "sha512-vm3/XWXfWtRua0FkUyEHBZy8kCPjErNBT9fJx8Zvs+U6zjqPbTUOpkaoum3O5uiA8sm+yNMHXfYkTUHFoMxFNA==" + }, + "office-ui-fabric-react": { + "version": "5.131.0", + "resolved": "https://registry.npmjs.org/office-ui-fabric-react/-/office-ui-fabric-react-5.131.0.tgz", + "integrity": "sha512-QOYu1uf92qhTTIlBAj8teKvRpCmpliRZjynYtgeeUbDm4C4GtXdb/O1rPNFsfT0PNtPC8dCNeQ7/CXjQenUkyw==", + "requires": { + "@microsoft/load-themed-styles": "^1.7.13", + "@uifabric/icons": ">=5.8.0 <6.0.0", + "@uifabric/merge-styles": ">=5.17.1 <6.0.0", + "@uifabric/styling": ">=5.36.0 <6.0.0", + "@uifabric/utilities": ">=5.34.2 <6.0.0", + "prop-types": "^15.5.10", + "tslib": "^1.7.1" + } + } + } + }, + "@pnp/telemetry-js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@pnp/telemetry-js/-/telemetry-js-2.0.0.tgz", + "integrity": "sha512-qFNm3mTerTnxgTR6c/4iMMt8EUKrQn5z0XG/IQtpNlp6m7KXRDFR87mQKeBVtSv2LhxGO0VNFndKJIibBw52zQ==", + "requires": { + "whatwg-fetch": "2.0.4" + }, + "dependencies": { + "whatwg-fetch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", + "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" + } + } + }, "@pnpm/link-bins": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@pnpm/link-bins/-/link-bins-1.0.4.tgz", @@ -4300,6 +4424,11 @@ "integrity": "sha1-ox10JBprHtu5c8822XooloNKUfk=", "dev": true }, + "@types/chart.js": { + "version": "2.7.40", + "resolved": "https://registry.npmjs.org/@types/chart.js/-/chart.js-2.7.40.tgz", + "integrity": "sha512-yC8Ff5vsHFTClGCWXoAmNCh33cNYfP2/yFANBLjLiso4jTKsLfQ0KQuBEuKxOWTRoOSLyT6v+ZYcvz0uonvvsA==" + }, "@types/es6-promise": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/@types/es6-promise/-/es6-promise-0.0.33.tgz", @@ -4461,6 +4590,14 @@ "integrity": "sha512-7s9EQWupR1fTc2pSMtXRQ9w9gLOcrJn+h7HOXw4evxyvVqMi4f+q7d2tnFe3ng3SNHjtK+0EzGMGFUQX4/AQRA==", "dev": true }, + "@types/quill": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.10.tgz", + "integrity": "sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==", + "requires": { + "parchment": "^1.1.2" + } + }, "@types/ramda": { "version": "0.25.51", "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.25.51.tgz", @@ -6746,6 +6883,32 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, + "chart.js": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-2.7.3.tgz", + "integrity": "sha512-3+7k/DbR92m6BsMUYP6M0dMsMVZpMnwkUyNSAbqolHKsbIzH2Q4LWVEHHYq7v0fmEV8whXE0DrjANulw9j2K5g==", + "requires": { + "chartjs-color": "^2.1.0", + "moment": "^2.10.2" + } + }, + "chartjs-color": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz", + "integrity": "sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==", + "requires": { + "chartjs-color-string": "^0.6.0", + "color-convert": "^1.9.3" + } + }, + "chartjs-color-string": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz", + "integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==", + "requires": { + "color-name": "^1.0.0" + } + }, "chokidar": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", @@ -7039,7 +7202,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/color/-/color-3.1.2.tgz", "integrity": "sha512-vXTJhHebByxZn3lDvDJYw4lR5+uB3vuoHsuYA5AKuxRVn5wzzIfQKGLBmgdVRHKTJYeK5rvJcHnrd0Li49CFpg==", - "dev": true, "requires": { "color-convert": "^1.9.1", "color-string": "^1.5.2" @@ -7049,7 +7211,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -7057,14 +7218,12 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "color-string": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", - "dev": true, "requires": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" @@ -7361,6 +7520,16 @@ "sha.js": "^2.4.8" } }, + "create-react-class": { + "version": "15.6.3", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz", + "integrity": "sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==", + "requires": { + "fbjs": "^0.8.9", + "loose-envify": "^1.3.1", + "object-assign": "^4.1.1" + } + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -7943,6 +8112,19 @@ "esprima": "4.0.1" } }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -8005,7 +8187,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, "requires": { "object-keys": "^1.0.12" } @@ -8433,7 +8614,6 @@ "version": "1.17.6", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", - "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", @@ -8452,7 +8632,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -8691,6 +8870,11 @@ "through": "^2.3.8" } }, + "eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha1-teEHm1n7XhuidxwKmTvgYKWMmbo=" + }, "events": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", @@ -8960,8 +9144,7 @@ "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "extend-shallow": { "version": "3.0.2", @@ -9084,6 +9267,11 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-diff": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", + "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==" + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -9130,7 +9318,6 @@ "version": "0.8.17", "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", - "dev": true, "requires": { "core-js": "^1.0.0", "isomorphic-fetch": "^2.1.1", @@ -9144,8 +9331,7 @@ "core-js": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", - "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=", - "dev": true + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" } } }, @@ -9615,8 +9801,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "gauge": { "version": "2.7.4", @@ -10289,7 +10474,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -10321,8 +10505,7 @@ "has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, "has-unicode": { "version": "2.0.1", @@ -11048,6 +11231,11 @@ } } }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -11072,8 +11260,7 @@ "is-callable": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", - "dev": true + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" }, "is-ci": { "version": "1.2.1", @@ -11121,8 +11308,7 @@ "is-date-object": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" }, "is-descriptor": { "version": "0.1.6", @@ -11287,7 +11473,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", - "dev": true, "requires": { "has-symbols": "^1.0.1" } @@ -11325,7 +11510,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, "requires": { "has-symbols": "^1.0.1" } @@ -14924,14 +15108,21 @@ "object-inspect": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", - "dev": true + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" + }, + "object-is": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz", + "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "object-visit": { "version": "1.0.1", @@ -14946,7 +15137,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, "requires": { "define-properties": "^1.1.2", "function-bind": "^1.1.1", @@ -15044,29 +15234,29 @@ "integrity": "sha512-gcBs5HHr7tjkvk/+Ls10ttb3jEllRn7SvJitX/kx/gQq8BiFMSMKr1w+oNqXlh4EgkBHWUlJVPrYUu1KW/jVaQ==" }, "office-ui-fabric-react": { - "version": "6.189.2", - "resolved": "https://registry.npmjs.org/office-ui-fabric-react/-/office-ui-fabric-react-6.189.2.tgz", - "integrity": "sha512-1Y111Ip78u7aCbmyRTucRldY4lYwRPkxhFL+y1rgQC4TivB2FmoHN6eSA1nAA59Ix4k2etM0UCzh7MdC1SbP5Q==", + "version": "6.214.1", + "resolved": "https://registry.npmjs.org/office-ui-fabric-react/-/office-ui-fabric-react-6.214.1.tgz", + "integrity": "sha512-aFTV9pAzx3yWoRkqhOn2J6HoI18fzZ0qXCUfjeJd/hkQ3wL/XwDW+AM/wasvhKAHjW+icNoDuhgC2YZ48S0W1g==", "requires": { "@microsoft/load-themed-styles": "^1.7.13", - "@uifabric/foundation": "^0.7.6", - "@uifabric/icons": "^6.5.2", - "@uifabric/merge-styles": "^6.18.0", + "@uifabric/foundation": "^0.8.3", + "@uifabric/icons": "^6.5.5", + "@uifabric/merge-styles": "^6.19.4", "@uifabric/set-version": "^1.1.3", - "@uifabric/styling": "^6.48.0", - "@uifabric/utilities": "^6.40.1", + "@uifabric/styling": "^6.50.7", + "@uifabric/utilities": "^6.45.2", "prop-types": "^15.5.10", "tslib": "^1.7.1" }, "dependencies": { "@uifabric/foundation": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/@uifabric/foundation/-/foundation-0.7.7.tgz", - "integrity": "sha512-ZsX6vuPX6OpvUb26GL7ribJELYt2SOKFhuM4W+YcowPSBTRLl2DSC+ZkaLI4VngT//D89tl0SqmipukcSn4hDA==", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@uifabric/foundation/-/foundation-0.8.3.tgz", + "integrity": "sha512-r3WhRj7Out8QAOe50BGA36R8zgRPy0D0GNPuwFK9BB9oIidf91ycj7/miY8U0TZRZi5fB3Kg+dy/uxSDfgC6Og==", "requires": { "@uifabric/set-version": "^1.1.3", - "@uifabric/styling": "^6.48.1", - "@uifabric/utilities": "^6.41.0", + "@uifabric/styling": "^6.50.7", + "@uifabric/utilities": "^6.41.7", "tslib": "^1.7.1" } }, @@ -15369,6 +15559,11 @@ "no-case": "^2.2.0" } }, + "parchment": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", + "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==" + }, "parse-asn1": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", @@ -16526,7 +16721,6 @@ "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "dev": true, "requires": { "asap": "~2.0.3" } @@ -16658,6 +16852,36 @@ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", "dev": true }, + "quill": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz", + "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==", + "requires": { + "clone": "^2.1.1", + "deep-equal": "^1.0.1", + "eventemitter3": "^2.0.3", + "extend": "^3.0.2", + "parchment": "^1.1.4", + "quill-delta": "^3.6.2" + }, + "dependencies": { + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" + } + } + }, + "quill-delta": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz", + "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==", + "requires": { + "deep-equal": "^1.0.1", + "extend": "^3.0.2", + "fast-diff": "1.1.2" + } + }, "ramda": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.25.0.tgz", @@ -16779,6 +17003,11 @@ "scheduler": "^0.13.5" } }, + "react-dom-factories": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/react-dom-factories/-/react-dom-factories-1.0.2.tgz", + "integrity": "sha1-63cFxNs2+1AbOqOP91lhaqD/luA=" + }, "react-html-parser": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/react-html-parser/-/react-html-parser-2.0.2.tgz", @@ -16792,6 +17021,20 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.3.tgz", "integrity": "sha512-Y4rC1ZJmsxxkkPuMLwvKvlL1Zfpbcu+Bf4ZigkHup3v9EfdYhAlWAaVyA19olXq2o2mGn0w+dFKvk3pVVlYcIA==" }, + "react-quill": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/react-quill/-/react-quill-1.3.3.tgz", + "integrity": "sha512-T9RubLaWJ8gCfp7sOqmFupjiTiEp/EdGqhCG+PWGKc5UHiK6xIWNKWYsOHHEhQ+sZCKs8u/DPx47gc1VfFmcLg==", + "requires": { + "@types/quill": "1.3.10", + "@types/react": "*", + "create-react-class": "^15.6.0", + "lodash": "^4.17.4", + "prop-types": "^15.5.10", + "quill": "^1.2.6", + "react-dom-factories": "^1.0.0" + } + }, "read": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", @@ -17014,6 +17257,15 @@ "safe-regex": "^1.1.0" } }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, "regexpu-core": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", @@ -17619,8 +17871,7 @@ "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" }, "setprototypeof": { "version": "1.1.0", @@ -17675,7 +17926,6 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", - "dev": true, "requires": { "is-arrayish": "^0.3.1" }, @@ -17683,8 +17933,7 @@ "is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "dev": true + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" } } }, @@ -18263,7 +18512,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" @@ -18273,7 +18521,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "dev": true, "requires": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" @@ -19140,8 +19387,7 @@ "ua-parser-js": { "version": "0.7.21", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz", - "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==", - "dev": true + "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==" }, "uglify-js": { "version": "3.10.0", diff --git a/samples/react-list-form/package.json b/samples/react-list-form/package.json index ef3241652..493c86b2b 100755 --- a/samples/react-list-form/package.json +++ b/samples/react-list-form/package.json @@ -1,6 +1,6 @@ { "name": "react-form-webpart", - "version": "1.0.3", + "version": "1.0.4", "private": true, "engines": { "node": ">=0.10.0" @@ -18,6 +18,7 @@ "@microsoft/sp-office-ui-fabric-core": "1.10.0", "@microsoft/sp-property-pane": "1.10.0", "@microsoft/sp-webpart-base": "1.10.0", + "@pnp/spfx-controls-react": "^1.19.0", "@tinymce/tinymce-react": "^3.0.1", "@types/es6-promise": "0.0.33", "@types/react-dnd": "~2.0.34", @@ -44,4 +45,4 @@ "gulp": "~3.9.1", "tslint-microsoft-contrib": "5.0.0" } -} +} \ No newline at end of file diff --git a/samples/react-list-form/src/common/services/GroupService.ts b/samples/react-list-form/src/common/services/GroupService.ts new file mode 100644 index 000000000..e0ca07b93 --- /dev/null +++ b/samples/react-list-form/src/common/services/GroupService.ts @@ -0,0 +1,18 @@ +import { Text } from '@microsoft/sp-core-library'; +import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http'; + +export class GroupService { + + private spHttpClient: SPHttpClient; + constructor(spHttpClient: SPHttpClient) { + this.spHttpClient = spHttpClient; + } + + public async getGroupFromWeb(webUrl: string, groupId: number) { + const endpoint = `${webUrl}/_api/Web/SiteGroups/GetById(${groupId})`; + let res = await this.spHttpClient.get(endpoint, SPHttpClient.configurations.v1); + let group = await res.json(); + + return group; + } +} diff --git a/samples/react-list-form/src/common/services/IListFormService.ts b/samples/react-list-form/src/common/services/IListFormService.ts index 8065613ed..785ff1e62 100644 --- a/samples/react-list-form/src/common/services/IListFormService.ts +++ b/samples/react-list-form/src/common/services/IListFormService.ts @@ -1,9 +1,11 @@ import { ControlMode } from '../datatypes/ControlMode'; import { IFieldSchema } from './datatypes/RenderListData'; +import { IWebPartContext } from '@microsoft/sp-webpart-base'; export interface IListFormService { getFieldSchemasForForm: (webUrl: string, listUrl: string, formType: ControlMode) => Promise; getDataForForm: (webUrl: string, listUrl: string, itemId: number, formType: ControlMode) => Promise; + getExtraFieldData(data: any, fieldSchema: any, ctx: IWebPartContext, siteUrl: string); updateItem: (webUrl: string, listUrl: string, itemId: number, fieldsSchema: IFieldSchema[], data: any, originalData: any) => Promise; diff --git a/samples/react-list-form/src/common/services/ISPPeopleSearchService.ts b/samples/react-list-form/src/common/services/ISPPeopleSearchService.ts new file mode 100644 index 000000000..f28f9bbf1 --- /dev/null +++ b/samples/react-list-form/src/common/services/ISPPeopleSearchService.ts @@ -0,0 +1,16 @@ +//import { PrincipalType } from '../PropertyFieldPeoplePicker'; +//import { IPropertyFieldGroupOrPerson } from './../propertyFields/peoplePicker/IPropertyFieldPeoplePicker'; +import { IWebPartContext } from '@microsoft/sp-webpart-base'; + +/** + * Service interface definition + */ + +export interface ISPPeopleSearchService { + + /** + * Search People from a query + */ + searchPeople(ctx: IWebPartContext, query: string, principleType: any[], siteUrl?: string): Promise>; + resolvePeople(ctx: IWebPartContext, query: string, siteUrl?: string): Promise; +} \ No newline at end of file diff --git a/samples/react-list-form/src/common/services/ListFormService.ts b/samples/react-list-form/src/common/services/ListFormService.ts index 7ad5d4f98..81ffd21e2 100644 --- a/samples/react-list-form/src/common/services/ListFormService.ts +++ b/samples/react-list-form/src/common/services/ListFormService.ts @@ -5,6 +5,8 @@ import { ControlMode } from '../datatypes/ControlMode'; import { IFieldSchema, RenderListDataOptions } from './datatypes/RenderListData'; import { IListFormService } from './IListFormService'; import { IAttachment } from '../../types/IAttachment'; +import { IWebPartContext } from '@microsoft/sp-webpart-base'; +import { SPPeopleSearchService } from './SPPeopleSearchService'; export class ListFormService implements IListFormService { @@ -108,6 +110,28 @@ export class ListFormService implements IListFormService { }); }); } + public async getExtraFieldData(data: any, fieldSchema: any, ctx: IWebPartContext, siteUrl: string) { + const userFields = fieldSchema.filter((x) => x.FieldType === "User" || x.FieldType === "UserMulti"); + + let searchSvc = null; + if (userFields.length > 0) { + searchSvc = new SPPeopleSearchService(); + } + for (let i = 0; i < userFields.length; i++) { + let x = userFields[i]; + let val = data[x.InternalName]; + //Need group lookups + for (let j = 0; j < val.length; j++) { + let y = val[j]; + if (y.Key.indexOf("c:0") == 0) { + let res = await searchSvc.resolvePeople(ctx, y.Key, siteUrl); + y.Key = res.Description; + } + } + } + + return data; + } /** * Saves the given data to the specified SharePoint list item. @@ -291,12 +315,22 @@ export class ListFormService implements IListFormService { && (field.InternalName != "Attachments") )) .map((field) => { - return { - ErrorMessage: null, - FieldName: field.InternalName, - FieldValue: data[field.InternalName], - HasException: false, - }; + if (field.FieldType === "User" || field.FieldType === "UserMulti") { + return { + ErrorMessage: null, + FieldName: field.InternalName, + FieldValue: JSON.stringify(data[field.InternalName]), + HasException: false, + }; + } + else { + return { + ErrorMessage: null, + FieldName: field.InternalName, + FieldValue: data[field.InternalName], + HasException: false, + }; + } }); } private GetAttachmentsCreate(data: any) diff --git a/samples/react-list-form/src/common/services/SPPeopleSearchService.ts b/samples/react-list-form/src/common/services/SPPeopleSearchService.ts new file mode 100644 index 000000000..c10548232 --- /dev/null +++ b/samples/react-list-form/src/common/services/SPPeopleSearchService.ts @@ -0,0 +1,166 @@ +import { ISPHttpClientOptions, SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http'; +import { Environment, EnvironmentType } from '@microsoft/sp-core-library'; +//import { PrincipalType, IPropertyFieldGroupOrPerson } from './../propertyFields/peoplePicker/IPropertyFieldPeoplePicker'; +import { ISPPeopleSearchService } from './ISPPeopleSearchService'; +//import SPPeoplePickerMockHttpClient from './SPPeopleSearchMockService'; +import { IWebPartContext } from '@microsoft/sp-webpart-base'; + +/** + * Service implementation to search people in SharePoint + */ +export class SPPeopleSearchService implements ISPPeopleSearchService { + /** + * Search people from the SharePoint People database + */ + public searchPeople(ctx: IWebPartContext, query: string, principalType: any[], siteUrl: string = null): Promise { + if (Environment.type === EnvironmentType.Local) { + // If the running environment is local, load the data from the mock + return null; //this.searchPeopleFromMock(ctx, query); + } else { + // If the running env is SharePoint, loads from the peoplepicker web service + const userRequestUrl: string = `${siteUrl ? siteUrl : ctx.pageContext.web.absoluteUrl}/_api/SP.UI.ApplicationPages.ClientPeoplePickerWebServiceInterface.clientPeoplePickerSearchUser`; + const data = { + 'queryParams': { + 'AllowEmailAddresses': true, + 'AllowMultipleEntities': false, + 'AllUrlZones': false, + 'MaximumEntitySuggestions': 20, + 'PrincipalSource': 15, + // PrincipalType controls the type of entities that are returned in the results. + // Choices are All - 15, Distribution List - 2 , Security Groups - 4, SharePoint Groups - 8, User - 1. + // These values can be combined (example: 13 is security + SP groups + users) + 'PrincipalType': 15, + 'QueryString': query + } + }; + + let httpPostOptions: ISPHttpClientOptions = { + headers: { + 'accept': 'application/json', + 'content-type': 'application/json' + }, + body: JSON.stringify(data) + }; + + // Do the call against the People REST API endpoint + return ctx.spHttpClient.post(userRequestUrl, SPHttpClient.configurations.v1, httpPostOptions).then((searchResponse: SPHttpClientResponse) => { + return searchResponse.json().then((usersResponse: any) => { + let res: any[] = []; + let values: any = JSON.parse(usersResponse.value); + + // Filter out "UNVALIDATED_EMAIL_ADDRESS" + values = values.filter(v => !(v.EntityData && v.EntityData.PrincipalType && v.EntityData.PrincipalType === "UNVALIDATED_EMAIL_ADDRESS")); + // Filter out NULL keys + values = values.filter(v => v.Key !== null); + res = values.map(element => { + switch (element.EntityType) { + case 'User': + let email: string = element.EntityData.Email !== null ? element.EntityData.Email : element.Description; + const groupOrPerson: any = { fullName: element.DisplayText, login: element.Description }; + groupOrPerson.id = element.Key; + groupOrPerson.email = email; + groupOrPerson.jobTitle = element.EntityData.Title; + groupOrPerson.initials = this.getFullNameInitials(groupOrPerson.fullName); + groupOrPerson.imageUrl = this.getUserPhotoUrl(groupOrPerson.email, siteUrl ? siteUrl : ctx.pageContext.web.absoluteUrl); + return element; + case 'SecGroup': + const group: any = { + fullName: element.DisplayText, + login: element.ProviderName, + id: element.Key, + description: element.Description, + }; + return element; + case 'FormsRole': + const formsRole: any = { + fullName: element.DisplayText, + login: element.ProviderName, + id: element.Key, + description: element.Description + }; + return element; + default: + const persona: any = { + fullName: element.DisplayText, + login: element.EntityData.AccountName, + id: element.EntityData.SPGroupID, + description: element.Description + }; + return element; + } + }); + return res; + }); + }); + } + } + public resolvePeople(ctx: IWebPartContext, query: string, siteUrl: string = null): Promise { + if (Environment.type === EnvironmentType.Local) { + // If the running environment is local, load the data from the mock + return null; + } else { + // If the running env is SharePoint, loads from the peoplepicker web service + const userRequestUrl: string = `${siteUrl ? siteUrl : ctx.pageContext.web.absoluteUrl}/_api/SP.UI.ApplicationPages.ClientPeoplePickerWebServiceInterface.clientPeoplePickerResolveUser`; + const data = { + 'queryParams': { + 'AllowEmailAddresses': true, + 'AllowMultipleEntities': false, + 'AllUrlZones': false, + 'MaximumEntitySuggestions': 20, + 'PrincipalSource': 15, + // PrincipalType controls the type of entities that are returned in the results. + // Choices are All - 15, Distribution List - 2 , Security Groups - 4, SharePoint Groups - 8, User - 1. + // These values can be combined (example: 13 is security + SP groups + users) + 'PrincipalType': 13, + 'QueryString': query + } + }; + + let httpPostOptions: ISPHttpClientOptions = { + headers: { + 'accept': 'application/json', + 'content-type': 'application/json' + }, + body: JSON.stringify(data) + }; + + // Do the call against the People REST API endpoint + return ctx.spHttpClient.post(userRequestUrl, SPHttpClient.configurations.v1, httpPostOptions).then((searchResponse: SPHttpClientResponse) => { + return searchResponse.json().then((usersResponse: any) => { + let res: any[] = []; + let values: any = JSON.parse(usersResponse.value); + + return values; + }); + }); + } + } + + /** + * Generates Initials from a full name + */ + private getFullNameInitials(fullName: string): string { + if (fullName === null) { + return fullName; + } + + const words: string[] = fullName.split(' '); + if (words.length === 0) { + return ''; + } else if (words.length === 1) { + return words[0].charAt(0); + } else { + return (words[0].charAt(0) + words[1].charAt(0)); + } + } + + /** + * Gets the user photo url + */ + private getUserPhotoUrl(userEmail: string, siteUrl: string): string { + if (userEmail) { + return `${siteUrl}/_layouts/15/userphoto.aspx?size=S&accountname=${encodeURIComponent(userEmail)}`; + } + return null; + } +} \ No newline at end of file diff --git a/samples/react-list-form/src/webparts/listForm/IListFormWebPartProps.ts b/samples/react-list-form/src/webparts/listForm/IListFormWebPartProps.ts index f23ebe9ad..acadfa18f 100644 --- a/samples/react-list-form/src/webparts/listForm/IListFormWebPartProps.ts +++ b/samples/react-list-form/src/webparts/listForm/IListFormWebPartProps.ts @@ -1,7 +1,6 @@ import { ControlMode } from '../../common/datatypes/ControlMode'; import { IFieldConfiguration } from './components/IFieldConfiguration'; - export interface IListFormWebPartProps { title: string; description: string; diff --git a/samples/react-list-form/src/webparts/listForm/ListFormWebPart.ts b/samples/react-list-form/src/webparts/listForm/ListFormWebPart.ts index 5a6693b2d..c8e02e820 100644 --- a/samples/react-list-form/src/webparts/listForm/ListFormWebPart.ts +++ b/samples/react-list-form/src/webparts/listForm/ListFormWebPart.ts @@ -74,6 +74,7 @@ export default class ListFormWebPart extends BaseClientSideWebPart this.formSubmitted(id), onUpdateFields: (fields: IFieldConfiguration[]) => this.updateField(fields), + context: this.context, } ); } else { diff --git a/samples/react-list-form/src/webparts/listForm/components/IListFormProps.ts b/samples/react-list-form/src/webparts/listForm/components/IListFormProps.ts index 6eaecb97d..2d2cf539c 100644 --- a/samples/react-list-form/src/webparts/listForm/components/IListFormProps.ts +++ b/samples/react-list-form/src/webparts/listForm/components/IListFormProps.ts @@ -1,6 +1,7 @@ import { SPHttpClient } from '@microsoft/sp-http'; import { ControlMode } from '../../../common/datatypes/ControlMode'; import { IFieldConfiguration } from './IFieldConfiguration'; +import { WebPartContext } from '@microsoft/sp-webpart-base'; export interface IListFormProps { title: string; @@ -16,4 +17,5 @@ export interface IListFormProps { onSubmitSucceeded?(id: number): void; onSubmitFailed?(fieldErrors: any): void; onUpdateFields?(newFields: IFieldConfiguration[]): void; + context: WebPartContext; } diff --git a/samples/react-list-form/src/webparts/listForm/components/ListForm.tsx b/samples/react-list-form/src/webparts/listForm/components/ListForm.tsx index 9b8490823..ff7047db1 100644 --- a/samples/react-list-form/src/webparts/listForm/components/ListForm.tsx +++ b/samples/react-list-form/src/webparts/listForm/components/ListForm.tsx @@ -7,6 +7,9 @@ import { ControlMode } from '../../../common/datatypes/ControlMode'; import { IListFormService } from '../../../common/services/IListFormService'; import { ListFormService } from '../../../common/services/ListFormService'; +import { ISPPeopleSearchService } from '../../../common/services/ISPPeopleSearchService'; +import { SPPeopleSearchService } from '../../../common/services/SPPeopleSearchService'; +import { GroupService } from '../../../common/services/GroupService'; import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/Spinner'; import { DefaultButton, PrimaryButton } from 'office-ui-fabric-react/lib/Button'; @@ -35,6 +38,9 @@ import { Validate } from '@microsoft/sp-core-library'; class ListForm extends React.Component { private listFormService: IListFormService; + private spPeopleService: ISPPeopleSearchService; + private groupService: GroupService; + constructor(props: IListFormProps) { super(props); @@ -52,6 +58,8 @@ class ListForm extends React.Component { errorInfo: '' }; this.listFormService = new ListFormService(props.spHttpClient); + this.spPeopleService = new SPPeopleSearchService(); + this.groupService = new GroupService(props.spHttpClient); } public render() { @@ -156,6 +164,7 @@ class ListForm extends React.Component { private renderFields() { const { fieldsSchema, data, fieldErrors } = this.state; const fields = this.getFields(); + return (fields && (fields.length > 0)) ?
@@ -181,7 +190,8 @@ class ListForm extends React.Component { extraData: extraData, errorMessage: errorMessage, hideIfFieldUnsupported: !this.props.showUnsupportedFields, - valueChanged: (val) => this.valueChanged(field.fieldName, val) + valueChanged: (val) => this.valueChanged(field.fieldName, val), + context: this.props.context, }); if (fieldComponent && this.props.inDesignMode) { return ( @@ -234,6 +244,18 @@ class ListForm extends React.Component { listUrl, formType, ); + let userfields = fieldsSchema.filter((x) => { + return x.FieldType === "User" || x.FieldType === "UserMulti"; + }); + + for (let i = 0; i < userfields.length; i++) { + if (userfields[i].SharePointGroupID > 0) { + //Get the groupname from sharepoint for the group id + let group = await this.groupService.getGroupFromWeb(this.props.webUrl, userfields[i].SharePointGroupID); + userfields[i]['SharePointGroupName'] = group.Title; + } + } + this.setState({ ...this.state, isLoadingSchema: false, fieldsSchema }); } catch (error) { const errorText = `${strings.ErrorLoadingSchema}${listUrl}: ${error}`; @@ -256,7 +278,9 @@ class ListForm extends React.Component { return; } this.setState({ ...this.state, data: {}, originalData: {}, fieldErrors: {}, isLoadingData: true }); - const dataObj = await this.listFormService.getDataForForm(this.props.webUrl, listUrl, id, formType); + let dataObj = await this.listFormService.getDataForForm(this.props.webUrl, listUrl, id, formType); + const schema = this.state.fieldsSchema; + dataObj = await this.listFormService.getExtraFieldData(dataObj, schema, this.props.context, this.props.webUrl); // We shallow clone here, so that changing values on dataObj object fields won't be changing in originalData too const dataObjOriginal = { ...dataObj }; this.setState({ ...this.state, data: dataObj, originalData: dataObjOriginal, isLoadingData: false }); @@ -267,21 +291,54 @@ class ListForm extends React.Component { } @autobind - private valueChanged(fieldName: string, newValue: any) { - this.setState((prevState, props) => { - return { - ...prevState, - data: { ...prevState.data, [fieldName]: newValue }, - fieldErrors: { - ...prevState.fieldErrors, - [fieldName]: - (prevState.fieldsSchema.filter((item) => item.InternalName === fieldName)[0].Required) && !newValue - ? strings.RequiredValueMessage - : '' + private async valueChanged(fieldName: string, newValue: any) { + let schema = this.state.fieldsSchema.filter((item) => item.InternalName === fieldName)[0]; + if (schema.Type == "User" || schema.Type === "UserMulti") { + for (let i = 0; i < newValue.length; i++) { + // Security Group and Office 365 group need special handling + if (newValue[i].Key.indexOf("c:0") === 0) { + let newVal = await this.spPeopleService.resolvePeople(this.props.context, newValue[i].Key, this.props.webUrl); + + if (newVal.EntityData != null && newVal.EntityData.Email != null) { + newValue[i].Key = newVal.EntityData.Email; + } + else { + newValue[i].Key = newVal.Description; + } } - }; - }, - ); + } + + this.setState((prevState, props) => { + return { + ...prevState, + data: { ...prevState.data, [fieldName]: newValue }, + fieldErrors: { + ...prevState.fieldErrors, + [fieldName]: + (prevState.fieldsSchema.filter((item) => item.InternalName === fieldName)[0].Required) && !newValue + ? strings.RequiredValueMessage + : '' + } + }; + }, + ); + } + else { + this.setState((prevState, props) => { + return { + ...prevState, + data: { ...prevState.data, [fieldName]: newValue }, + fieldErrors: { + ...prevState.fieldErrors, + [fieldName]: + (prevState.fieldsSchema.filter((item) => item.InternalName === fieldName)[0].Required) && !newValue + ? strings.RequiredValueMessage + : '' + } + }; + }, + ); + } } private validator = () => { diff --git a/samples/react-list-form/src/webparts/listForm/components/formFields/DateFormField.tsx b/samples/react-list-form/src/webparts/listForm/components/formFields/DateFormField.tsx index eeff6314d..6de4b234a 100644 --- a/samples/react-list-form/src/webparts/listForm/components/formFields/DateFormField.tsx +++ b/samples/react-list-form/src/webparts/listForm/components/formFields/DateFormField.tsx @@ -35,7 +35,6 @@ export default class DateFormField extends React.Component = (props) => { + let context: WebPartContext = null; + let termsetId = ""; + let allowMultipleSelections = false; + let terms = null; + + if (props != null) { + context = props.context; + termsetId = props.fieldSchema.TermSetId; + allowMultipleSelections = props.fieldSchema.AllowMultipleValues; + if (props.value != null) { + terms = []; + let multiparts = props.value.split(";"); + multiparts.forEach((x) => { + let parts = x.split("|"); + terms.push({ + key: parts[1], + name: parts[0], + }); + }); + } + } + + return props.valueChanged(getUpdatedValue(items))} + isTermSetSelectable={false} /> +}; + +function getUpdatedValue(terms) { + let value = ""; + for (let i = 0; i < terms.length; i++) { + if (i > 0) { + value += ";"; + } + value += terms[i].name + "|" + terms[i].key; + } + return value; +} +export default SPFieldTaxonomyEdit; diff --git a/samples/react-list-form/src/webparts/listForm/components/formFields/SPFieldUserEdit.tsx b/samples/react-list-form/src/webparts/listForm/components/formFields/SPFieldUserEdit.tsx new file mode 100644 index 000000000..4e7bc71b3 --- /dev/null +++ b/samples/react-list-form/src/webparts/listForm/components/formFields/SPFieldUserEdit.tsx @@ -0,0 +1,75 @@ +import * as React from 'react'; +import { ISPFormFieldProps } from './SPFormField'; + + + +import { PeoplePicker, PrincipalType } from "@pnp/spfx-controls-react/lib/PeoplePicker"; +import { WebPartContext } from '@microsoft/sp-webpart-base'; + +const SPFieldUserEdit: React.SFC = (props) => { + let principalType = []; + let principals = []; + let context: WebPartContext = null; + let selectionCount = 1; + let required = false; + let readOnly = false; + let groupName = ""; + if (props != null) { + if (props.fieldSchema.PrincipalAccountType.indexOf("User") >= 0) { + principalType.push(PrincipalType.User); + } + if (props.fieldSchema.PrincipalAccountType.indexOf("SecGroup") >= 0) { + principalType.push(PrincipalType.SecurityGroup); + } + if (props.fieldSchema.PrincipalAccountType.indexOf("SPGroup") >= 0) { + principalType.push(PrincipalType.SharePointGroup); + } + if (props.fieldSchema.AllowMultipleValues) { + selectionCount = 100; + } + context = props.context; + + if (props.value != null && props.value != "") { + principals = props.value.map((x) => { + if (x.EntityData != null) { + if (x.EntityData.Email != null) { + return x.EntityData.Email; + } + else { + return x.Key; + } + } + else { + let parts = x.Key.split("|"); + return parts[parts.length - 1]; + } + }); + } + required = props.fieldSchema.Required; + readOnly = props.fieldSchema.ReadOnlyField; + if (props.fieldSchema['SharePointGroupName'] != null) { + groupName = props.fieldSchema['SharePointGroupName']; + } + } + + return props.valueChanged(getUpdatedValue(items))} + showHiddenInUI={false} + principalTypes={principalType} + resolveDelay={500} />; +}; + +function getUpdatedValue(items): [] { + let values = items.map((x) => { + return { 'Key': x.loginName }; + }); + return values; +} +export default SPFieldUserEdit; diff --git a/samples/react-list-form/src/webparts/listForm/components/formFields/SPFormField.tsx b/samples/react-list-form/src/webparts/listForm/components/formFields/SPFormField.tsx index 3d5d8e6ea..5913c045f 100755 --- a/samples/react-list-form/src/webparts/listForm/components/formFields/SPFormField.tsx +++ b/samples/react-list-form/src/webparts/listForm/components/formFields/SPFormField.tsx @@ -2,6 +2,8 @@ import * as React from 'react'; import { ControlMode } from '../../../../common/datatypes/ControlMode'; import { IFieldSchema } from '../../../../common/services/datatypes/RenderListData'; +import { WebPartContext } from '@microsoft/sp-webpart-base'; + import FormField from './FormField'; import { IFormFieldProps } from './FormField'; import { IDatePickerStrings } from 'office-ui-fabric-react/lib/DatePicker'; @@ -20,6 +22,8 @@ import SPFieldRichTextDisplay from './SPFieldRichTextDisplay'; import SPFieldLookupDisplay from './SPFieldLookupDisplay'; import SPFieldUserDisplay from './SPFieldUserDisplay'; import SPFieldUrlDisplay from './SPFieldUrlDisplay'; +import SPFieldUserEdit from './SPFieldUserEdit'; +import SPFieldTaxonomyEdit from './SPFieldTaxonomyEdit'; import SPAttachmentFormFieldEdit from './SPAttachmentFormFieldEdit'; @@ -39,14 +43,14 @@ const EditFieldTypeMappings: { [fieldType: string]: React.StatelessComponent = (props) => { diff --git a/samples/react-list-form/src/webparts/listForm/components/formFields/loc/en-us.js b/samples/react-list-form/src/webparts/listForm/components/formFields/loc/en-us.js index 6bbf0f02d..46bfde3ee 100644 --- a/samples/react-list-form/src/webparts/listForm/components/formFields/loc/en-us.js +++ b/samples/react-list-form/src/webparts/listForm/components/formFields/loc/en-us.js @@ -42,5 +42,7 @@ define([], function () { 'RemoveButtonLabel': "Remove", 'ReplaceButtonLabel': "Replace", + //Taxonomy + 'SelectTerm': "Select Term", } }); diff --git a/samples/react-list-form/src/webparts/listForm/components/formFields/loc/formfieldstrings.d.ts b/samples/react-list-form/src/webparts/listForm/components/formFields/loc/formfieldstrings.d.ts index fc7dbbd27..94b738d19 100644 --- a/samples/react-list-form/src/webparts/listForm/components/formFields/loc/formfieldstrings.d.ts +++ b/samples/react-list-form/src/webparts/listForm/components/formFields/loc/formfieldstrings.d.ts @@ -28,6 +28,9 @@ declare interface IFormFieldStrings { ReplaceButtonLabel: string; AttachmentTermsConditionTitleText: string; AttachmentTermsConditionText: string; + + //Taxonomy + SelectTerm: string; } declare module 'FormFieldStrings' { diff --git a/samples/react-list-form/src/webparts/listForm/components/loc/en-us.js b/samples/react-list-form/src/webparts/listForm/components/loc/en-us.js index 00b11536f..61874e8f9 100644 --- a/samples/react-list-form/src/webparts/listForm/components/loc/en-us.js +++ b/samples/react-list-form/src/webparts/listForm/components/loc/en-us.js @@ -12,6 +12,6 @@ define([], function () { 'FieldsErrorOnSaving': 'The item could not be saved. Please check detailed error messages on the fields below.', 'ErrorOnSavingListItem': 'Error on loading lists: ', 'MoveField': "Move field", - 'RemoveField': "Remove field" + 'RemoveField': "Remove field", } });