New sample SPFx webhooks real time (#351)
* first commit * Implemented GetChanges API, Readme updated, New property "socket server url" * added some scripts in order to automate the provisioning and deployment, readme updated, minor bug fix
This commit is contained in:
parent
99ea9eefde
commit
cbcc2041d4
|
@ -0,0 +1,25 @@
|
|||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
[*]
|
||||
|
||||
# change these settings to your own preference
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# we recommend you to keep these unchanged
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[{package,bower}.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
|
@ -0,0 +1,32 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Dependency directories
|
||||
node_modules
|
||||
|
||||
# Build generated files
|
||||
dist
|
||||
lib
|
||||
solution
|
||||
temp
|
||||
*.sppkg
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# OSX
|
||||
.DS_Store
|
||||
|
||||
# Visual Studio files
|
||||
.ntvs_analysis.dat
|
||||
.vs
|
||||
bin
|
||||
obj
|
||||
|
||||
# Resx Generated Code
|
||||
*.resx.ts
|
||||
|
||||
# Styles Generated Code
|
||||
*.scss.ts
|
|
@ -0,0 +1,75 @@
|
|||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
// Configure glob patterns for excluding files and folders in the file explorer.
|
||||
"files.exclude": {
|
||||
"**/.git": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/bower_components": true,
|
||||
"**/coverage": true,
|
||||
"**/lib-amd": true,
|
||||
"src/**/*.scss.ts": true
|
||||
},
|
||||
"typescript.tsdk": ".\\node_modules\\typescript\\lib",
|
||||
"json.schemas": [
|
||||
{
|
||||
"fileMatch": [
|
||||
"/config/config.json"
|
||||
],
|
||||
"url": "./node_modules/@microsoft/sp-build-core-tasks/lib/configJson/schemas/config-v1.schema.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"/config/copy-assets.json"
|
||||
],
|
||||
"url": "./node_modules/@microsoft/sp-build-core-tasks/lib/copyAssets/copy-assets.schema.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"/config/deploy-azure-storage.json"
|
||||
],
|
||||
"url": "./node_modules/@microsoft/sp-build-core-tasks/lib/deployAzureStorage/deploy-azure-storage.schema.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"/config/package-solution.json"
|
||||
],
|
||||
"url": "./node_modules/@microsoft/sp-build-core-tasks/lib/packageSolution/package-solution.schema.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"/config/serve.json"
|
||||
],
|
||||
"url": "./node_modules/@microsoft/gulp-core-build-serve/lib/serve.schema.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"/config/tslint.json"
|
||||
],
|
||||
"url": "./node_modules/@microsoft/gulp-core-build-typescript/lib/schemas/tslint.schema.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"/config/write-manifests.json"
|
||||
],
|
||||
"url": "./node_modules/@microsoft/sp-build-core-tasks/lib/writeManifests/write-manifests.schema.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"/config/configure-webpack.json"
|
||||
],
|
||||
"url": "./node_modules/@microsoft/sp-build-core-tasks/lib/configureWebpack/configure-webpack.schema.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"/config/configure-external-bundling-webpack.json"
|
||||
],
|
||||
"url": "./node_modules/@microsoft/sp-build-core-tasks/lib/configureWebpack/configure-webpack-external-bundling.schema.json"
|
||||
},
|
||||
{
|
||||
"fileMatch": [
|
||||
"/copy-static-assets.json"
|
||||
],
|
||||
"url": "./node_modules/@microsoft/sp-build-core-tasks/lib/copyStaticAssets/copy-static-assets.schema.json"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"@microsoft/generator-sharepoint": {
|
||||
"version": "1.3.2",
|
||||
"libraryName": "react-webhooks-realtime",
|
||||
"libraryId": "d4eee588-a8e6-45ce-b9e1-c40f0b92ef2d",
|
||||
"environment": "spo"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
#################
|
||||
# Configuration #
|
||||
#################
|
||||
$catalogSite = "https://giuleon.sharepoint.com/sites/apps" # => App Catalog site
|
||||
$catalogRelativePath = "sites/apps/AppCatalog" # => App Catalog relative url
|
||||
#######
|
||||
# End #
|
||||
#######
|
||||
|
||||
# Get Web Request
|
||||
function GetRequest ($apiUrl, $webSession) {
|
||||
return Invoke-WebRequest -Uri $apiUrl -Method Get -WebSession $webSession
|
||||
}
|
||||
|
||||
# Post Web Request
|
||||
function PostRequest($apiUrl, $webSession, $body) {
|
||||
return Invoke-WebRequest -Uri $apiUrl -Body $body -Method Post -WebSession $webSession
|
||||
|
||||
}
|
||||
|
||||
# Settting the right parameters value
|
||||
function setXmlMapping($xmlBody, $siteId, $webId, $listId, $fileId, $fileVersion, $skipDeployment) {
|
||||
# Replace the random token with a random guid
|
||||
$randomGuid = [guid]::NewGuid()
|
||||
if($skipDeployment -eq $True){
|
||||
$skipDeployment = "true"
|
||||
}
|
||||
else{
|
||||
$skipDeployment = "false"
|
||||
}
|
||||
$xmlBody = [regex]::replace($xmlBody, "{randomId}", $randomGuid)
|
||||
# Replace the site ID token with the actual site ID string
|
||||
$xmlBody = [regex]::replace($xmlBody, "{siteId}", $siteId)
|
||||
# Replace the web ID token with the actual web ID string
|
||||
$xmlBody = [regex]::replace($xmlBody, "{webId}", $webId)
|
||||
# Replace the list ID token with the actual list ID string
|
||||
$xmlBody = [regex]::replace($xmlBody, "{listId}", $listId)
|
||||
# Replace the item ID token with the actual item ID number
|
||||
$xmlBody = [regex]::replace($xmlBody, "{itemId}", $fileId)
|
||||
# Replace the file version token with the actual file version number
|
||||
$xmlBody = [regex]::replace($xmlBody, "{fileVersion}", $fileVersion)
|
||||
# Replace the skipFeatureDeployment token with the skipFeatureDeployment option
|
||||
$xmlBody = [regex]::replace($xmlBody, "{skipFeatureDeployment}", $skipDeployment)
|
||||
return $xmlBody;
|
||||
}
|
||||
|
||||
Write-Host ***************************************** -ForegroundColor Yellow
|
||||
Write-Host * Uploading the sppkg on the AppCatalog * -ForegroundColor Yellow
|
||||
Write-Host ***************************************** -ForegroundColor Yellow
|
||||
$packageConfig = Get-Content -Raw -Path .\config\package-solution.json | ConvertFrom-Json
|
||||
$packagePath = Join-Path "sharepoint/" $packageConfig.paths.zippedPackage -Resolve
|
||||
$skipFeatureDeployment = $packageConfig.solution.skipFeatureDeployment
|
||||
|
||||
# Connect-PnPOnline $catalogSite -Credentials (Get-Credential)
|
||||
Connect-PnPOnline $catalogSite -Credentials giuleon
|
||||
Add-PnPFile -Path $packagePath -Folder "AppCatalog"
|
||||
|
||||
Write-Host *************************************************** -ForegroundColor Yellow
|
||||
Write-Host * The SPFx solution has been succesfully uploaded to the AppCatalog * -ForegroundColor Yellow
|
||||
Write-Host *************************************************** -ForegroundColor Yellow
|
||||
|
||||
# Connect to SharePoint Online
|
||||
$targetSite = "https://giuleon.sharepoint.com/sites/apps"
|
||||
$targetSiteUri = [System.Uri]$targetSite
|
||||
|
||||
# Retrieve the client credentials and the related Authentication Cookies
|
||||
$context = (Get-PnPWeb).Context
|
||||
$credentials = $context.Credentials
|
||||
$authenticationCookies = $credentials.GetAuthenticationCookie($targetSiteUri, $true)
|
||||
|
||||
# Set the Authentication Cookies and the Accept HTTP Header
|
||||
$webSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession
|
||||
$webSession.Cookies.SetCookies($targetSiteUri, $authenticationCookies)
|
||||
$webSession.Headers.Add("Accept", "application/json;odata=verbose")
|
||||
|
||||
$apiUrl = $catalogSite + "/_api/contextinfo?$"+"select=FormDigestValue"
|
||||
$result = PostRequest -apiUrl $apiUrl -webSession $webSession
|
||||
$formDigest = $result.Content | ConvertFrom-Json
|
||||
Write-Host "FormDigestValue - " $formDigest.d.GetContextWebInformation.FormDigestValue
|
||||
$formDigest = $formDigest.d.GetContextWebInformation.FormDigestValue
|
||||
$webSession.Headers.Add("X-RequestDigest", $formDigest)
|
||||
|
||||
# Set request variables
|
||||
$apiUrl = "$targetSite" + "/_api/site?$"+"select=Id"
|
||||
|
||||
# Make the REST request
|
||||
$webRequest = GetRequest -apiUrl $apiUrl -webSession $webSession # Invoke-WebRequest -Uri $apiUrl -Method Get -WebSession $webSession
|
||||
$response = $webRequest.Content | ConvertFrom-Json
|
||||
$siteId = $response.d.Id
|
||||
Write-Host "Site Id - " $response.d.Id
|
||||
|
||||
# Retrieving webId and listId
|
||||
$apiUrl = "$targetSite" + "/_api/web/getList('$catalogRelativePath')?$"+"select=Id,ParentWeb/Id`&`$"+"expand=ParentWeb"
|
||||
$webRequest = GetRequest -apiUrl $apiUrl -webSession $webSession # Invoke-WebRequest -Uri $apiUrl -Method Get -WebSession $webSession
|
||||
$response = $webRequest.Content | ConvertFrom-Json
|
||||
$webId = $response.d.ParentWeb.Id
|
||||
$listId = $response.d.Id
|
||||
Write-Host "Web Id - " $webId " / List Id - " + $listId
|
||||
|
||||
# Get the ListItemAllFields Id and Version
|
||||
$fileName = $packageConfig.paths.zippedPackage.Substring($packageConfig.paths.zippedPackage.LastIndexOf("/")+1)
|
||||
$apiUrl = "$targetSite" + "/_api/web/GetFolderByServerRelativeUrl('AppCatalog')/Files('$fileName')?$"+"expand=ListItemAllFields`&`$" + "select=ListItemAllFields/ID,ListItemAllFields/owshiddenversion"
|
||||
$webRequest = GetRequest -apiUrl $apiUrl -webSession $webSession
|
||||
$response = $webRequest.Content -replace '"id":', '"id_":' | ConvertFrom-Json
|
||||
$fileId = $response.d.ListItemAllFields.id_
|
||||
$fileVersion = $response.d.ListItemAllFields.owshiddenversion
|
||||
Write-Host "ListItem Id - " $fileId " / Version - " $fileVersion
|
||||
|
||||
# Read the xml
|
||||
$xmlBody = Get-Content DeploySPFxToAppCatalogRequestBody.xml -Encoding UTF8
|
||||
$xmlBody = setXmlMapping -xmlBody $xmlBody -siteId $siteId -webId $webId -listId $listId -fileId $fileId -fileVersion $fileVersion -skipDeployment $skipFeatureDeployment
|
||||
Write-Host "deployment in progress....."
|
||||
|
||||
# Deploy the sspkg
|
||||
$webSession.Headers.Add("Content-type", "application/xml")
|
||||
$apiUrl = $catalogSite + "/_vti_bin/client.svc/ProcessQuery"
|
||||
$result = PostRequest -apiUrl $apiUrl -webSession $webSession -body $xmlBody
|
||||
Write-Host $result
|
|
@ -0,0 +1,148 @@
|
|||
<Request
|
||||
xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009" SchemaVersion="15.0.0.0" LibraryVersion="16.0.0.0" ApplicationName="Javascript Library">
|
||||
<Actions>
|
||||
<Method Name="SetFieldValue" Id="18" ObjectPathId="10" Version="{fileVersion}">
|
||||
<Parameters>
|
||||
<Parameter Type="String">IsClientSideSolutionDeployed</Parameter>
|
||||
<Parameter Type="Boolean">true</Parameter>
|
||||
</Parameters>
|
||||
</Method>
|
||||
<Method Name="SetFieldValue" Id="19" ObjectPathId="10" Version="{fileVersion}">
|
||||
<Parameters>
|
||||
<Parameter Type="String">IsClientSideSolutionCurrentVersionDeployed</Parameter>
|
||||
<Parameter Type="Boolean">true</Parameter>
|
||||
</Parameters>
|
||||
</Method>
|
||||
<Method Name="SetFieldValue" Id="20" ObjectPathId="10" Version="{fileVersion}">
|
||||
<Parameters>
|
||||
<Parameter Type="String">SkipFeatureDeployment</Parameter>
|
||||
<Parameter Type="Boolean">{skipFeatureDeployment}</Parameter>
|
||||
</Parameters>
|
||||
</Method>
|
||||
<Method Name="Update" Id="21" ObjectPathId="10" Version="{fileVersion}" />
|
||||
<Query Id="22" ObjectPathId="10">
|
||||
<Query SelectAllProperties="false">
|
||||
<Properties>
|
||||
<Property Name="FileSystemObjectType" ScalarProperty="true" />
|
||||
<Property Name="Id" ScalarProperty="true" />
|
||||
<Property Name="ServerRedirectedEmbedUri" ScalarProperty="true" />
|
||||
<Property Name="ServerRedirectedEmbedUrl" ScalarProperty="true" />
|
||||
<Property Name="ID" ScalarProperty="true" />
|
||||
<Property Name="ContentTypeId" ScalarProperty="true" />
|
||||
<Property Name="Created" ScalarProperty="true" />
|
||||
<Property Name="Author" ScalarProperty="true" />
|
||||
<Property Name="Modified" ScalarProperty="true" />
|
||||
<Property Name="Editor" ScalarProperty="true" />
|
||||
<Property Name="_HasCopyDestinations" ScalarProperty="true" />
|
||||
<Property Name="_CopySource" ScalarProperty="true" />
|
||||
<Property Name="_ModerationStatus" ScalarProperty="true" />
|
||||
<Property Name="_ModerationComments" ScalarProperty="true" />
|
||||
<Property Name="FileRef" ScalarProperty="true" />
|
||||
<Property Name="FileDirRef" ScalarProperty="true" />
|
||||
<Property Name="Last_x0020_Modified" ScalarProperty="true" />
|
||||
<Property Name="Created_x0020_Date" ScalarProperty="true" />
|
||||
<Property Name="File_x0020_Size" ScalarProperty="true" />
|
||||
<Property Name="FSObjType" ScalarProperty="true" />
|
||||
<Property Name="SortBehavior" ScalarProperty="true" />
|
||||
<Property Name="CheckedOutUserId" ScalarProperty="true" />
|
||||
<Property Name="IsCheckedoutToLocal" ScalarProperty="true" />
|
||||
<Property Name="CheckoutUser" ScalarProperty="true" />
|
||||
<Property Name="FileLeafRef" ScalarProperty="true" />
|
||||
<Property Name="UniqueId" ScalarProperty="true" />
|
||||
<Property Name="SyncClientId" ScalarProperty="true" />
|
||||
<Property Name="ProgId" ScalarProperty="true" />
|
||||
<Property Name="ScopeId" ScalarProperty="true" />
|
||||
<Property Name="VirusStatus" ScalarProperty="true" />
|
||||
<Property Name="CheckedOutTitle" ScalarProperty="true" />
|
||||
<Property Name="_CheckinComment" ScalarProperty="true" />
|
||||
<Property Name="Modified_x0020_By" ScalarProperty="true" />
|
||||
<Property Name="Created_x0020_By" ScalarProperty="true" />
|
||||
<Property Name="File_x0020_Type" ScalarProperty="true" />
|
||||
<Property Name="HTML_x0020_File_x0020_Type" ScalarProperty="true" />
|
||||
<Property Name="_SourceUrl" ScalarProperty="true" />
|
||||
<Property Name="_SharedFileIndex" ScalarProperty="true" />
|
||||
<Property Name="MetaInfo" ScalarProperty="true" />
|
||||
<Property Name="_Level" ScalarProperty="true" />
|
||||
<Property Name="_IsCurrentVersion" ScalarProperty="true" />
|
||||
<Property Name="ItemChildCount" ScalarProperty="true" />
|
||||
<Property Name="FolderChildCount" ScalarProperty="true" />
|
||||
<Property Name="Restricted" ScalarProperty="true" />
|
||||
<Property Name="OriginatorId" ScalarProperty="true" />
|
||||
<Property Name="NoExecute" ScalarProperty="true" />
|
||||
<Property Name="ContentVersion" ScalarProperty="true" />
|
||||
<Property Name="_ComplianceFlags" ScalarProperty="true" />
|
||||
<Property Name="_ComplianceTag" ScalarProperty="true" />
|
||||
<Property Name="_ComplianceTagWrittenTime" ScalarProperty="true" />
|
||||
<Property Name="_ComplianceTagUserId" ScalarProperty="true" />
|
||||
<Property Name="BSN" ScalarProperty="true" />
|
||||
<Property Name="_ListSchemaVersion" ScalarProperty="true" />
|
||||
<Property Name="_Dirty" ScalarProperty="true" />
|
||||
<Property Name="_Parsable" ScalarProperty="true" />
|
||||
<Property Name="AccessPolicy" ScalarProperty="true" />
|
||||
<Property Name="_VirusStatus" ScalarProperty="true" />
|
||||
<Property Name="_VirusVendorID" ScalarProperty="true" />
|
||||
<Property Name="_VirusInfo" ScalarProperty="true" />
|
||||
<Property Name="AppAuthor" ScalarProperty="true" />
|
||||
<Property Name="AppEditor" ScalarProperty="true" />
|
||||
<Property Name="SMTotalSize" ScalarProperty="true" />
|
||||
<Property Name="SMLastModifiedDate" ScalarProperty="true" />
|
||||
<Property Name="SMTotalFileStreamSize" ScalarProperty="true" />
|
||||
<Property Name="SMTotalFileCount" ScalarProperty="true" />
|
||||
<Property Name="owshiddenversion" ScalarProperty="true" />
|
||||
<Property Name="_UIVersion" ScalarProperty="true" />
|
||||
<Property Name="_UIVersionString" ScalarProperty="true" />
|
||||
<Property Name="InstanceID" ScalarProperty="true" />
|
||||
<Property Name="Order" ScalarProperty="true" />
|
||||
<Property Name="GUID" ScalarProperty="true" />
|
||||
<Property Name="WorkflowVersion" ScalarProperty="true" />
|
||||
<Property Name="WorkflowInstanceID" ScalarProperty="true" />
|
||||
<Property Name="ParentVersionString" ScalarProperty="true" />
|
||||
<Property Name="ParentLeafName" ScalarProperty="true" />
|
||||
<Property Name="DocConcurrencyNumber" ScalarProperty="true" />
|
||||
<Property Name="ParentUniqueId" ScalarProperty="true" />
|
||||
<Property Name="StreamHash" ScalarProperty="true" />
|
||||
<Property Name="ComplianceAssetId" ScalarProperty="true" />
|
||||
<Property Name="Title" ScalarProperty="true" />
|
||||
<Property Name="AppProductID" ScalarProperty="true" />
|
||||
<Property Name="AssetID" ScalarProperty="true" />
|
||||
<Property Name="AppPublisher" ScalarProperty="true" />
|
||||
<Property Name="AppThumbnailURL" ScalarProperty="true" />
|
||||
<Property Name="AppDescription" ScalarProperty="true" />
|
||||
<Property Name="AppShortDescription" ScalarProperty="true" />
|
||||
<Property Name="AppVersion" ScalarProperty="true" />
|
||||
<Property Name="AppSupportURL" ScalarProperty="true" />
|
||||
<Property Name="AppVideoURL" ScalarProperty="true" />
|
||||
<Property Name="IsAppPackageEnabled" ScalarProperty="true" />
|
||||
<Property Name="SharePointAppCategory" ScalarProperty="true" />
|
||||
<Property Name="AppPrerequisitesXML" ScalarProperty="true" />
|
||||
<Property Name="IsFeaturedApp" ScalarProperty="true" />
|
||||
<Property Name="AppMetadataLocale" ScalarProperty="true" />
|
||||
<Property Name="IsDefaultAppMetadataLocale" ScalarProperty="true" />
|
||||
<Property Name="AppImageURL1" ScalarProperty="true" />
|
||||
<Property Name="AppImageURL2" ScalarProperty="true" />
|
||||
<Property Name="AppImageURL3" ScalarProperty="true" />
|
||||
<Property Name="AppImageURL4" ScalarProperty="true" />
|
||||
<Property Name="AppImageURL5" ScalarProperty="true" />
|
||||
<Property Name="AppPackageHash" ScalarProperty="true" />
|
||||
<Property Name="IsValidAppPackage" ScalarProperty="true" />
|
||||
<Property Name="AppPackageErrorMessage" ScalarProperty="true" />
|
||||
<Property Name="AppPermissionXML" ScalarProperty="true" />
|
||||
<Property Name="AppSubtypeID" ScalarProperty="true" />
|
||||
<Property Name="IsAutoHostedApp" ScalarProperty="true" />
|
||||
<Property Name="AppTitleInfo" ScalarProperty="true" />
|
||||
<Property Name="AppSupportedLocales" ScalarProperty="true" />
|
||||
<Property Name="IsClientSideSolution" ScalarProperty="true" />
|
||||
<Property Name="IsClientSideSolutionDeployed" ScalarProperty="true" />
|
||||
<Property Name="ExternalContentDomains" ScalarProperty="true" />
|
||||
<Property Name="IsClientSideSolutionCurrentVersionDeployed" ScalarProperty="true" />
|
||||
<Property Name="SkipFeatureDeployment" ScalarProperty="true" />
|
||||
<Property Name="PackageDefaultSkipFeatureDeployment" ScalarProperty="true" />
|
||||
<Property Name="UniqueSolutionId" ScalarProperty="true" />
|
||||
</Properties>
|
||||
</Query>
|
||||
</Query>
|
||||
</Actions>
|
||||
<ObjectPaths>
|
||||
<Identity Id="10" Name="{randomId}|740c6a0b-85e2-48a0-a494-e0f1759d4aa7:site:{siteId}:web:{webId}:list:{listId}:item:{itemId},1" />
|
||||
</ObjectPaths>
|
||||
</Request>
|
|
@ -0,0 +1,24 @@
|
|||
#################
|
||||
# Configuration #
|
||||
#################
|
||||
$cdnSite = "https://giuleon.sharepoint.com/" # => CDN SharePoint site
|
||||
$cdnLib = "cdn/SPFx-react-webhooks-realtime" # => Document library and eventual folders
|
||||
#######
|
||||
# End #
|
||||
#######
|
||||
|
||||
Write-Host ************************************************************************************** -ForegroundColor Yellow
|
||||
Write-Host * Reading the cdnBasePath from write-manifests.json and collectiong the bundle files * -ForegroundColor Yellow
|
||||
Write-Host ************************************************************************************** -ForegroundColor Yellow
|
||||
$cdnConfig = Get-Content -Raw -Path .\config\copy-assets.json | ConvertFrom-Json
|
||||
$bundlePath = Convert-Path $cdnConfig.deployCdnPath
|
||||
$files = Get-ChildItem $bundlePath\*.*
|
||||
|
||||
Write-Host **************************************** -ForegroundColor Yellow
|
||||
Write-Host Uploading the bundle on Office 365 CDN * -ForegroundColor Yellow
|
||||
Write-Host **************************************** -ForegroundColor Yellow
|
||||
Connect-PnPOnline $cdnSite -Credentials giuleon
|
||||
foreach ($file in $files) {
|
||||
$fullPath = $file.DirectoryName + "\" + $file.Name
|
||||
Add-PnPFile -Path $fullPath -Folder $cdnLib
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
$spSite = "https://giuleon.sharepoint.com/sites/demo" # => SharePoint site
|
||||
$spListTitle = "Events" # => List name
|
||||
|
||||
Connect-PnPOnline $spSite -Credentials (Get-Credential)
|
||||
|
||||
New-PnPList -Title $spListTitle -Template GenericList
|
||||
|
||||
Add-PnPField -List $spListTitle -DisplayName "Description" -InternalName "SPFxDescription" -Type Text -Group "SPFx Group" -AddToDefaultView
|
||||
Add-PnPField -List $spListTitle -DisplayName "Thumbnail" -InternalName "SPFxThumbnail" -Type Url -Group "SPFx Group" -AddToDefaultView
|
|
@ -0,0 +1,58 @@
|
|||
# Webhooks Realtime
|
||||
|
||||
## Summary
|
||||
This web part demonstrates how to leverage the capabilities of SharePoint Webhooks.
|
||||
The libraries used by this web part are Socket.io, sp pnp js, moment.
|
||||
|
||||
![Preview](./assets/spfx-react-webhooks-realtime.gif)
|
||||
|
||||
### Solution architecture
|
||||
![Architecture](./assets/Architecture.png)
|
||||
|
||||
## Used SharePoint Framework Version
|
||||
![drop](https://img.shields.io/badge/version-GA-green.svg)
|
||||
|
||||
## Applies to
|
||||
|
||||
* [SharePoint Framework](https:/dev.office.com/sharepoint)
|
||||
* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
> In order to use properly this web part is necessary follow these steps:
|
||||
> * Istall a webserver that will receive the webhooks, for this PoC I created a NodeJs Application hosted on Azure take a look on my solution [https://github.com/giuleon/SharePoint-Webhooks-Broadcaster](https://github.com/giuleon/SharePoint-Webhooks-Broadcaster)
|
||||
> * run the Powershell script **ProvisioningArtifacts.ps1** in order to provision the list Events which is required for this web part
|
||||
> * Create a new webhooks subscription for the SharePoint List **Events** (that will be installed by running the script **ProvisioningArtifacts.ps1**), as you prefer, across your solution or Postman, please read the following guideline to achieve this goal [https://docs.microsoft.com/en-us/sharepoint/dev/apis/webhooks/overview-sharepoint-webhooks](https://docs.microsoft.com/en-us/sharepoint/dev/apis/webhooks/overview-sharepoint-webhooks)
|
||||
> * The web part has been developed (GetChanges API) to notify new items added in the **Events** list
|
||||
|
||||
## Solution
|
||||
|
||||
Solution|Author(s)
|
||||
--------|---------
|
||||
react-webhooks-realtime|Giuliano De Luca ([@giuleon](https://twitter.com/giuleon) , [www.delucagiuliano.com](delucagiuliano.com))
|
||||
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0|October 29, 2017|Initial release
|
||||
|
||||
## Disclaimer
|
||||
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
||||
|
||||
---
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
||||
- Clone this repository
|
||||
- in the command line run:
|
||||
- `npm install`
|
||||
- `gulp serve`
|
||||
|
||||
## Features
|
||||
This Web Part illustrates the following concepts on top of the SharePoint Framework:
|
||||
|
||||
- How to leverage the capabilities of SharePoint webhooks.
|
||||
|
||||
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-webhooks-realtime" />
|
Binary file not shown.
After Width: | Height: | Size: 88 KiB |
Binary file not shown.
After Width: | Height: | Size: 687 KiB |
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||
"version": "2.0",
|
||||
"bundles": {
|
||||
"real-time-list-web-part": {
|
||||
"components": [
|
||||
{
|
||||
"entrypoint": "./lib/webparts/realTimeList/RealTimeListWebPart.js",
|
||||
"manifest": "./src/webparts/realTimeList/RealTimeListWebPart.manifest.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"externals": {
|
||||
"sp-pnp-js": "https://cdnjs.cloudflare.com/ajax/libs/sp-pnp-js/3.0.1/pnp.min.js"
|
||||
},
|
||||
"localizedResources": {
|
||||
"RealTimeListWebPartStrings": "lib/webparts/realTimeList/loc/{locale}.js"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||
"deployCdnPath": "temp/deploy"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||
"workingDir": "./temp/deploy/",
|
||||
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||
"container": "react-webhooks-realtime",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||
"solution": {
|
||||
"name": "react-webhooks-realtime-client-side-solution",
|
||||
"id": "d4eee588-a8e6-45ce-b9e1-c40f0b92ef2d",
|
||||
"version": "1.0.0.0",
|
||||
"skipFeatureDeployment": true
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/react-webhooks-realtime.sppkg"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/core-build/serve.schema.json",
|
||||
"port": 4321,
|
||||
"https": true,
|
||||
"initialPage": "https://localhost:5432/workbench",
|
||||
"api": {
|
||||
"port": 5432,
|
||||
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/core-build/tslint.schema.json",
|
||||
// Display errors as warnings
|
||||
"displayAsWarning": true,
|
||||
// The TSLint task may have been configured with several custom lint rules
|
||||
// before this config file is read (for example lint rules from the tslint-microsoft-contrib
|
||||
// project). If true, this flag will deactivate any of these rules.
|
||||
"removeExistingRules": true,
|
||||
// When true, the TSLint task is configured with some default TSLint "rules.":
|
||||
"useDefaultConfigAsBase": false,
|
||||
// Since removeExistingRules=true and useDefaultConfigAsBase=false, there will be no lint rules
|
||||
// which are active, other than the list of rules below.
|
||||
"lintConfig": {
|
||||
// Opt-in to Lint rules which help to eliminate bugs in JavaScript
|
||||
"rules": {
|
||||
"class-name": false,
|
||||
"export-name": false,
|
||||
"forin": false,
|
||||
"label-position": false,
|
||||
"member-access": true,
|
||||
"no-arg": false,
|
||||
"no-console": false,
|
||||
"no-construct": false,
|
||||
"no-duplicate-case": true,
|
||||
"no-duplicate-variable": true,
|
||||
"no-eval": false,
|
||||
"no-function-expression": true,
|
||||
"no-internal-module": true,
|
||||
"no-shadowed-variable": true,
|
||||
"no-switch-case-fall-through": true,
|
||||
"no-unnecessary-semicolons": true,
|
||||
"no-unused-expression": true,
|
||||
"no-use-before-declare": true,
|
||||
"no-with-statement": true,
|
||||
"semicolon": true,
|
||||
"trailing-comma": false,
|
||||
"typedef": false,
|
||||
"typedef-whitespace": false,
|
||||
"use-named-parameter": true,
|
||||
"valid-typeof": true,
|
||||
"variable-name": false,
|
||||
"whitespace": false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||
"cdnBasePath": "https://publiccdn.sharepointonline.com/giuleon.sharepoint.com/cdn/SPFx-react-webhooks-realtime"
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
const gulp = require('gulp');
|
||||
const build = require('@microsoft/sp-build-web');
|
||||
|
||||
build.initialize(gulp);
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"name": "react-webhooks-realtime",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp bundle",
|
||||
"clean": "gulp clean",
|
||||
"test": "gulp test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/sp-core-library": "~1.3.0",
|
||||
"@microsoft/sp-lodash-subset": "~1.3.0",
|
||||
"@microsoft/sp-webpart-base": "~1.3.0",
|
||||
"@types/react": "15.0.38",
|
||||
"@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",
|
||||
"@uifabric/example-app-base": "^5.1.2",
|
||||
"moment": "^2.19.1",
|
||||
"react": "15.4.2",
|
||||
"react-dom": "15.4.2",
|
||||
"socket.io-client": "^2.0.4",
|
||||
"sp-pnp-js": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/sp-build-web": "~1.3.0",
|
||||
"@microsoft/sp-module-interfaces": "~1.3.0",
|
||||
"@microsoft/sp-webpart-workbench": "~1.3.0",
|
||||
"gulp": "~3.9.1",
|
||||
"@types/chai": ">=3.4.34 <3.6.0",
|
||||
"@types/mocha": ">=2.2.33 <2.6.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||
"id": "874bb168-abe1-4176-92c2-24251077c23e",
|
||||
"alias": "RealTimeListWebPart",
|
||||
"componentType": "WebPart",
|
||||
|
||||
// The "*" signifies that the version should be taken from the package.json
|
||||
"version": "*",
|
||||
"manifestVersion": 2,
|
||||
|
||||
// If true, the component can only be installed on sites where Custom Script is allowed.
|
||||
// Components that allow authors to embed arbitrary script code should set this to true.
|
||||
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
|
||||
"requiresCustomScript": false,
|
||||
|
||||
"preconfiguredEntries": [{
|
||||
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
|
||||
"group": { "default": "Other" },
|
||||
"title": { "default": "Real Time List" },
|
||||
"description": { "default": "get the last changes in real time" },
|
||||
"officeFabricIconFontName": "News",
|
||||
"properties": {
|
||||
"description": "RealTimeList"
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import { Version } from '@microsoft/sp-core-library';
|
||||
import {
|
||||
BaseClientSideWebPart,
|
||||
IPropertyPaneConfiguration,
|
||||
PropertyPaneTextField
|
||||
} from '@microsoft/sp-webpart-base';
|
||||
|
||||
import * as strings from 'RealTimeListWebPartStrings';
|
||||
import RealTimeList from './components/RealTimeList';
|
||||
import { IRealTimeListProps } from './components/IRealTimeListProps';
|
||||
import pnp from "sp-pnp-js";
|
||||
|
||||
export interface IRealTimeListWebPartProps {
|
||||
socketserverurl: string;
|
||||
siteUrl: string;
|
||||
}
|
||||
|
||||
export default class RealTimeListWebPart extends BaseClientSideWebPart<IRealTimeListWebPartProps> {
|
||||
|
||||
// Ovverriding onInit in order to set up the sp pnp js with the web part context
|
||||
public onInit(): Promise<void> {
|
||||
return super.onInit().then(_ => {
|
||||
// establish SPFx context
|
||||
pnp.setup({
|
||||
spfxContext: this.context
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public render(): void {
|
||||
const element: React.ReactElement<IRealTimeListProps > = React.createElement(
|
||||
RealTimeList,
|
||||
{
|
||||
socketserverurl: this.properties.socketserverurl,
|
||||
siteUrl: this.context.pageContext.web.absoluteUrl
|
||||
}
|
||||
);
|
||||
|
||||
ReactDom.render(element, this.domElement);
|
||||
}
|
||||
|
||||
protected get dataVersion(): Version {
|
||||
return Version.parse('1.0');
|
||||
}
|
||||
|
||||
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||
return {
|
||||
pages: [
|
||||
{
|
||||
header: {
|
||||
description: strings.PropertyPaneDescription
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
groupName: strings.BasicGroupName,
|
||||
groupFields: [
|
||||
PropertyPaneTextField('socketserverurl', {
|
||||
label: strings.SocketserverurlFieldLabel
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
protected get disableReactivePropertyChanges(): boolean {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export interface IRealTimeListProps {
|
||||
socketserverurl: string;
|
||||
siteUrl: string;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import {
|
||||
IColumn
|
||||
} from 'office-ui-fabric-react/lib/DetailsList';
|
||||
|
||||
export interface IRealTimeListState {
|
||||
sortedItems?: any[];
|
||||
columns?: IColumn[];
|
||||
loading?: boolean;
|
||||
newsFeed?: string;
|
||||
newsFeedVisible?: boolean;
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
.realTimeList {
|
||||
.container {
|
||||
max-width: 700px;
|
||||
margin: 0px auto;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.row {
|
||||
padding: 0;//20px;
|
||||
}
|
||||
|
||||
.listItem {
|
||||
max-width: 715px;
|
||||
margin: 5px auto 5px auto;
|
||||
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.button {
|
||||
// Our button
|
||||
text-decoration: none;
|
||||
height: 32px;
|
||||
|
||||
// Primary Button
|
||||
min-width: 80px;
|
||||
background-color: "[theme:themePrimary, default:#0078d7]";
|
||||
border-color: "[theme:themePrimary, default:#0078d7]";
|
||||
color: #ffffff;
|
||||
|
||||
// Basic Button
|
||||
outline: transparent;
|
||||
position: relative;
|
||||
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
border-width: 0;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
padding: 0 16px;
|
||||
|
||||
.label {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
margin: 0 4px;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,270 @@
|
|||
import * as React from 'react';
|
||||
import styles from './RealTimeList.module.scss';
|
||||
import { IRealTimeListProps } from './IRealTimeListProps';
|
||||
import { IRealTimeListState } from './IRealTimeListState';
|
||||
import { escape } from '@microsoft/sp-lodash-subset';
|
||||
import pnp from "sp-pnp-js";
|
||||
import { Web } from "sp-pnp-js";
|
||||
import * as io from 'socket.io-client';
|
||||
import { createListItems } from '@uifabric/example-app-base';
|
||||
import { autobind } from 'office-ui-fabric-react/lib/Utilities';
|
||||
import { Link } from 'office-ui-fabric-react/lib/Link';
|
||||
import { Image, ImageFit } from 'office-ui-fabric-react/lib/Image';
|
||||
import { DefaultButton, IButtonProps } from 'office-ui-fabric-react/lib/Button';
|
||||
import { Label } from 'office-ui-fabric-react/lib/Label';
|
||||
import {
|
||||
DetailsList,
|
||||
buildColumns,
|
||||
IColumn
|
||||
} from 'office-ui-fabric-react/lib/DetailsList';
|
||||
import {
|
||||
Spinner,
|
||||
SpinnerSize
|
||||
} from 'office-ui-fabric-react/lib/Spinner';
|
||||
import * as moment from 'moment';
|
||||
|
||||
let _items: any[];
|
||||
let _lastQueryDate: moment.Moment;
|
||||
|
||||
export interface IList {
|
||||
Id: number;
|
||||
Title: string;
|
||||
SPFxDescription: string;
|
||||
SPFxThumbnail: ITumbnailUrl;
|
||||
}
|
||||
|
||||
export interface ITumbnailUrl {
|
||||
Url: string;
|
||||
}
|
||||
|
||||
export default class RealTimeList extends React.Component<IRealTimeListProps, IRealTimeListState> {
|
||||
public componentDidMount(): void {
|
||||
if (this.props.socketserverurl != null && this.props.socketserverurl != "" && this.props.socketserverurl !== undefined) {
|
||||
this._connectSocket(this.props.socketserverurl);
|
||||
}
|
||||
}
|
||||
public componentWillReceiveProps(nextProps: IRealTimeListProps): void {
|
||||
if (nextProps.socketserverurl != null && nextProps.socketserverurl != "" && nextProps.socketserverurl !== undefined) {
|
||||
this._connectSocket(nextProps.socketserverurl);
|
||||
}
|
||||
}
|
||||
constructor(props: IRealTimeListProps, state: IRealTimeListState) {
|
||||
super(props);
|
||||
|
||||
_items = [];
|
||||
|
||||
this.state = {
|
||||
sortedItems: _items,
|
||||
columns: _buildColumns(),
|
||||
loading: true
|
||||
};
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<IRealTimeListProps> {
|
||||
if (this.props.siteUrl.toLowerCase().indexOf("wwww.contoso.com") >= 0
|
||||
|| this.props.socketserverurl === undefined || this.props.socketserverurl === "") {
|
||||
return (
|
||||
<div className={styles.realTimeList}>
|
||||
<div className={styles.container}>
|
||||
<div className={`ms-Grid-row ms-bgColor-themeDark ms-fontColor-white ${styles.row}`}>
|
||||
<div className="ms-Grid-col ms-u-lg10 ms-u-xl8 ms-u-xlPush2 ms-u-lgPush1">
|
||||
<span className="ms-font-xl ms-fontColor-white">Connect the web part with SharePoint and configuring it before to begin</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
let { sortedItems, columns } = this.state;
|
||||
const loading: JSX.Element = (this.state.loading == true) ? <Spinner size={SpinnerSize.large} /> : null;
|
||||
const list: JSX.Element = (this.state.loading == false) ?
|
||||
<DetailsList
|
||||
items={sortedItems as any[]}
|
||||
setKey='set'
|
||||
columns={columns}
|
||||
onRenderItemColumn={_renderItemColumn}
|
||||
onColumnHeaderClick={this._onColumnClick}
|
||||
onItemInvoked={this._onItemInvoked}
|
||||
onColumnHeaderContextMenu={this._onColumnHeaderContextMenu}
|
||||
/>
|
||||
: null;
|
||||
const newsFeed: JSX.Element =
|
||||
this.state.newsFeedVisible == true ?
|
||||
<DefaultButton
|
||||
text={this.state.newsFeed}
|
||||
onClick={() => this._loadList()}
|
||||
/>
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className={styles.realTimeList}>
|
||||
<div className={styles.container}>
|
||||
<div className={`ms-Grid-row ms-bgColor-white ms-fontColor-white ${styles.row}`}>
|
||||
<div className="ms-Grid-col ms-lg6 ms-xl6 ms-xlPush5 ms-lgPush5">
|
||||
{newsFeed}
|
||||
</div>
|
||||
</div>
|
||||
<div className={`ms-Grid-row ms-bgColor-white ms-fontColor-white ${styles.row}`}>
|
||||
<div className="ms-Grid-col ms-lg12 ms-xl12">
|
||||
{loading}
|
||||
</div>
|
||||
</div>
|
||||
<div className={`ms-Grid-row ms-bgColor-white ms-fontColor-white ${styles.row}`}>
|
||||
<div className="ms-Grid-col ms-lg12 ms-xl12">
|
||||
{list}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
private toTicks(date: moment.Moment): number {
|
||||
return (date.valueOf() * 10000) + 621355968000000000;
|
||||
}
|
||||
private async _connectSocket(socketServerUrl: string) {
|
||||
// Connect to the server
|
||||
const socket = io(socketServerUrl);
|
||||
// Add the socket io listeners
|
||||
socket.on('list:changes', (data) => {
|
||||
this._getListChanges(data);
|
||||
console.log(JSON.stringify(data));
|
||||
});
|
||||
await this._loadList();
|
||||
}
|
||||
private async _getListChanges(dataWebhooks: any): Promise<void> {
|
||||
let dataParsed = JSON.parse(dataWebhooks);
|
||||
let resource = dataParsed[0].resource;
|
||||
let changeToken = `1;3;${resource};${this.toTicks(_lastQueryDate)};-1`;
|
||||
let changes = await pnp.sp.web.lists.getByTitle("Events").getChanges(
|
||||
{
|
||||
Add: true,
|
||||
Item: true,
|
||||
ChangeTokenStart: { StringValue: changeToken }
|
||||
});
|
||||
console.log(changes);
|
||||
console.log(_lastQueryDate);
|
||||
console.log(_items.length);
|
||||
if (changes.length > 0) {
|
||||
let newsFeedText = (changes.length == 1) ? changes.length + " new item" : changes.length + " new items";
|
||||
this.setState({
|
||||
newsFeedVisible: true,
|
||||
newsFeed: newsFeedText
|
||||
});
|
||||
}
|
||||
}
|
||||
private async _loadList(): Promise<void> {
|
||||
this.setState({
|
||||
loading: true
|
||||
});
|
||||
let items = await pnp.sp.web.lists.getByTitle("Events").items.select("Id", "Title", "SPFxDescription", "SPFxThumbnail")
|
||||
.orderBy("Modified", false).get();
|
||||
_items = items.map((item: IList, index: number) => {
|
||||
return {
|
||||
thumbnail: item.SPFxThumbnail != null ? item.SPFxThumbnail.Url : "",
|
||||
key: item.Id,
|
||||
name: item.Title,
|
||||
description: item.SPFxDescription
|
||||
}
|
||||
});
|
||||
this.setState({
|
||||
sortedItems: _items,
|
||||
columns: _buildColumns(),
|
||||
loading: false,
|
||||
newsFeedVisible: false
|
||||
});
|
||||
_lastQueryDate = moment();
|
||||
}
|
||||
private _getListItems(count: number, startIndex: number = 0): any {
|
||||
// get all the items from a list
|
||||
pnp.sp.web.lists.getByTitle("Events").items.get().then((items: any[]) => {
|
||||
console.log(items);
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
private _onColumnClick(event: React.MouseEvent<HTMLElement>, column: IColumn) {
|
||||
let { sortedItems, columns } = this.state;
|
||||
let isSortedDescending = column.isSortedDescending;
|
||||
|
||||
// If we've sorted this column, flip it.
|
||||
if (column.isSorted) {
|
||||
isSortedDescending = !isSortedDescending;
|
||||
}
|
||||
|
||||
// Sort the items.
|
||||
sortedItems = sortedItems!.concat([]).sort((a, b) => {
|
||||
let firstValue = a[column.fieldName];
|
||||
let secondValue = b[column.fieldName];
|
||||
|
||||
if (isSortedDescending) {
|
||||
return firstValue > secondValue ? -1 : 1;
|
||||
} else {
|
||||
return firstValue > secondValue ? 1 : -1;
|
||||
}
|
||||
});
|
||||
|
||||
// Reset the items and columns to match the state.
|
||||
this.setState({
|
||||
sortedItems: sortedItems,
|
||||
columns: columns!.map(col => {
|
||||
col.isSorted = (col.key === column.key);
|
||||
|
||||
if (col.isSorted) {
|
||||
col.isSortedDescending = isSortedDescending;
|
||||
}
|
||||
|
||||
return col;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
private _onColumnHeaderContextMenu(column: IColumn | undefined, ev: React.MouseEvent<HTMLElement> | undefined): void {
|
||||
console.log(`column ${column!.key} contextmenu opened.`);
|
||||
}
|
||||
|
||||
private _onItemInvoked(item: any, index: number | undefined): void {
|
||||
alert(`Item ${item.name} at index ${index} has been invoked.`);
|
||||
}
|
||||
}
|
||||
|
||||
function _buildColumns() {
|
||||
if (_items.length == 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let columns = buildColumns(_items);
|
||||
|
||||
let thumbnailColumn = columns.filter(column => column.name === 'thumbnail')[0];
|
||||
// Special case one column's definition.
|
||||
thumbnailColumn.name = '';
|
||||
thumbnailColumn.maxWidth = 100;
|
||||
|
||||
let keyColumn = columns.filter(column => column.name === 'key')[0];
|
||||
keyColumn.maxWidth = 100;
|
||||
|
||||
let nameColumn = columns.filter(column => column.name === 'name')[0];
|
||||
nameColumn.maxWidth = 200;
|
||||
|
||||
let descriptionColumn = columns.filter(column => column.name === 'description')[0];
|
||||
descriptionColumn.maxWidth = 300;
|
||||
|
||||
return columns;
|
||||
}
|
||||
|
||||
function _renderItemColumn(item: any, index: number, column: IColumn) {
|
||||
let fieldContent = item[column.fieldName];
|
||||
|
||||
switch (column.key) {
|
||||
case 'thumbnail':
|
||||
return <Image src={fieldContent} width={50} height={50} imageFit={ImageFit.cover} />;
|
||||
|
||||
case 'name':
|
||||
return <Link href='#'>{fieldContent}</Link>;
|
||||
|
||||
case 'color':
|
||||
return <span data-selection-disabled={true} style={{ color: fieldContent }}>{fieldContent}</span>;
|
||||
|
||||
default:
|
||||
return <span>{fieldContent}</span>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
define([], function() {
|
||||
return {
|
||||
"PropertyPaneDescription": "Description",
|
||||
"BasicGroupName": "Configuration Panel",
|
||||
"SocketserverurlFieldLabel": "Insert the Socket.IO server URL"
|
||||
}
|
||||
});
|
10
samples/react-webhooks-realtime/src/webparts/realTimeList/loc/mystrings.d.ts
vendored
Normal file
10
samples/react-webhooks-realtime/src/webparts/realTimeList/loc/mystrings.d.ts
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
declare interface IRealTimeListWebPartStrings {
|
||||
PropertyPaneDescription: string;
|
||||
BasicGroupName: string;
|
||||
SocketserverurlFieldLabel: string;
|
||||
}
|
||||
|
||||
declare module 'RealTimeListWebPartStrings' {
|
||||
const strings: IRealTimeListWebPartStrings;
|
||||
export = strings;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
/// <reference types="mocha" />
|
||||
|
||||
import { assert } from 'chai';
|
||||
|
||||
describe('RealTimeListWebPart', () => {
|
||||
it('should do something', () => {
|
||||
assert.ok(true);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "commonjs",
|
||||
"jsx": "react",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"types": [
|
||||
"es6-promise",
|
||||
"es6-collections",
|
||||
"webpack-env"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// Type definitions for Microsoft ODSP projects
|
||||
// Project: ODSP
|
||||
|
||||
/* Global definition for UNIT_TEST builds
|
||||
Code that is wrapped inside an if(UNIT_TEST) {...}
|
||||
block will not be included in the final bundle when the
|
||||
--ship flag is specified */
|
||||
declare const UNIT_TEST: boolean;
|
||||
|
||||
/* Global defintion for SPO builds */
|
||||
declare const DATACENTER: boolean;
|
|
@ -0,0 +1 @@
|
|||
/// <reference path="@ms/odsp.d.ts" />
|
Loading…
Reference in New Issue