IchicraftWidgets.psm1
function validateConnection([SharePointPnP.PowerShell.Commands.Base.SPOnlineConnection]$connection, [SharePointPnP.PowerShell.Commands.Enums.ConnectionType]$type) { if ($null -eq $connection) { throw "No SPOnlineConnection is available, connect to the target site with the Connect-PnPOnline Cmdlet" return $false } if ($connection.ConnectionType -ne $type) { if ($type -eq [SharePointPnP.PowerShell.Commands.Enums.ConnectionType]::TenantAdmin) { Write-Error "Current SPOnlineConnection is not a valid connection (expected: $type, actual: $($connection.ConnectionType)), connect to the tenant admin site with the Connect-PnPOnline Cmdlet" } elseif ($type -eq [SharePointPnP.PowerShell.Commands.Enums.ConnectionType]::O365) { Write-Error "Current SPOnlineConnection is not a valid connection (expected: $type, actual: $($connection.ConnectionType)), connect to the site-collection with the Connect-PnPOnline Cmdlet" } return $false } return $true } function Add-IchicraftWidgetsApp { [CmdletBinding()] param( [parameter( Mandatory = $false, HelpMessage = "Connection with permissions to add the app to the tenant Application Catalog")] [SharePointPnP.PowerShell.Commands.Base.SPOnlineConnection] $connection ) begin { if ($null -eq $connection) { $connection = Get-PnPConnection } if ($null -eq $connection) { $connection = Get-PnPConnection } [bool]$isValidConnection = validateConnection -connection $connection -type TenantAdmin if (!$isValidConnection) { break } } process { # Download the Widgets SPPKG package Write-Host "Start package download" Start-BitsTransfer -Source https://ichicraft.blob.core.windows.net/widgetboard/public/0.2.3/ichicraft-widgetboard.sppkg -Destination "$($env:userprofile)\downloads" # Add the package to the sites app catalog Write-Host "Start adding the Widgets app" Retry-Command -ScriptBlock { Add-PnPApp -Path "$($env:userprofile)\downloads\ichicraft-widgetboard.sppkg" -Scope Tenant -Publish -Overwrite -ErrorAction Stop } -Verbose # remove the download Remove-Item -LiteralPath "$($env:userprofile)\downloads\ichicraft-widgetboard.sppkg" } } function Approve-IchicraftWidgetsTenantServicePrincipalPermissionRequests { [CmdletBinding()] param( [parameter( Mandatory = $false, HelpMessage = "Connection with permissions to approve the TenantServicePrincipalPermissionRequest (Approve-PnPTenantServicePrincipalPermissionRequest)")] [SharePointPnP.PowerShell.Commands.Base.SPOnlineConnection] $connection ) begin { if ($null -eq $connection) { $connection = Get-PnPConnection } [bool]$isValidConnection = validateConnection -connection $connection -type TenantAdmin if (!$isValidConnection) { break } } process { # Approve the API permission requests Write-Host "Start approving the API permission requests" Retry-Command -ScriptBlock { Get-PnPTenantServicePrincipalPermissionRequests | Where-Object { $_.PackageName -eq "Ichicraft-WidgetBoardWebpart" } | ForEach-Object { Approve-PnPTenantServicePrincipalPermissionRequest -RequestId $_.Id -Force -ErrorAction SilentlyContinue } } -Verbose } } function Add-IchicraftWidgetsClientSideWebPart { [CmdletBinding()] param( [parameter( Mandatory = $false, HelpMessage = "Connection to the site where the webpart should be added")] [SharePointPnP.PowerShell.Commands.Base.SPOnlineConnection] $connection ) begin { if ($null -eq $connection) { $connection = Get-PnPConnection } [bool]$isValidConnection = validateConnection -connection $connection -type O365 if (!$isValidConnection) { break } } process { $siteUrl = $connection.Url $widgetsApp = Get-PnPApp -Identity "Ichicraft-WidgetBoardWebpart" -Scope Tenant -ErrorAction SilentlyContinue if ($null -eq $widgetsApp) { throw "The Ichicraft-WidgetBoardWebpart app is not found in the tenant Application Catalog. Please run Add-IchicraftWidgetsApp first." } # Install the package to the site Write-Host "Start installing the Widgets app to $siteUrl" Retry-Command -ScriptBlock { Install-PnPApp -Identity "Ichicraft-WidgetBoardWebpart" -Scope Tenant -ErrorAction Stop } -Verbose # Add a full-with section to the page Write-Host "Start adding a full-width section to the homepage of $siteUrl" Retry-Command -ScriptBlock { Add-PnPClientSidePageSection -Page "home" -SectionTemplate OneColumnFullWidth -Order 0 } -Verbose # Add the Widgets webpart to the page Write-Host "Start adding the Widgets webpart" Retry-Command -ScriptBlock { Add-PnPClientSideWebPart -Page "home" -Component "Widget Board" -Section 1 -Column 1 -ErrorAction Stop } -Verbose write-host "Finished, now try and open $siteUrl to configure your widgetboard" } } function Export-IchicraftWidgetsConfiguration { [CmdletBinding()] # This script ensures the lists needed by the Ichicraft Widgets webpart param( [parameter( Mandatory = $false, HelpMessage = "Connection to the site from which to export the configuration")] [SharePointPnP.PowerShell.Commands.Base.SPOnlineConnection] $connection, $outputFilePath = "./adminConfig.json" ) begin { if ($null -eq $connection) { $connection = Get-PnPConnection } [bool]$isValidConnection = validateConnection -connection $connection -type O365 if (!$isValidConnection) { break } } process { $siteUrl = $connection.Url $adminConfigItem = Get-PnPListItem -List "WidgetBoard_AdminConfig" -ErrorAction SilentlyContinue | Select-Object -First 1 if (-not $adminConfigItem) { throw "No configuration found in site $siteUrl" } $config = $adminConfigItem["Config"]; $config | Out-File -Encoding utf8 -Force -FilePath $outputFilePath -Append:$false } } function CreateConfigList($name, $connection) { $list = Get-PnPList -Identity $name -ErrorAction:SilentlyContinue -Connection $connection if (-not $list) { New-PnPList -Title $name -Template GenericList -Hidden -EnableContentTypes:$false -EnableVersioning:$true -OnQuickLaunch:$false -Connection $connection } Set-PnPList -Identity $name -Description 'Used by ichicraft widgets' -EnableAttachments:$false -Connection $connection # Set NoCrawl $list = Get-PnPList -Identity $name -Connection $connection $list.NoCrawl = $true $list.Update() $list.Context.ExecuteQuery() if (-not (Get-PnPField -List "WidgetBoard_UserConfig" -Identity "Config" -ErrorAction SilentlyContinue -Connection $connection)) { # Add the config field Add-PnPField -List $name -DisplayName "Config" -InternalName "Config" -Type Note -AddToDefaultView -Connection $connection } } function Initialize-IchicraftWidgetsWebpart { [CmdletBinding()] # This script ensures the lists needed by the Ichicraft Widgets webpart param( [parameter( Mandatory = $false, HelpMessage = "Connection to the site with the webpart")] [SharePointPnP.PowerShell.Commands.Base.SPOnlineConnection] $connection, $configFilePath = "./adminConfig.json" ) begin { if ($null -eq $connection) { $connection = Get-PnPConnection } [bool]$isValidConnection = validateConnection -connection $connection -type O365 if (!$isValidConnection) { break } } process { $siteUrl = $connection.Url write-host $siteUrl # create adminconfig list CreateConfigList -name "WidgetBoard_AdminConfig" $adminConfig = Get-Content -Path $configFilePath -Raw -Encoding UTF8 $item = Get-PnPListItem -List "WidgetBoard_AdminConfig" | Select-Object -First 1 if ($item) { write-host "Updating admin config list item" $item = Set-PnPListItem -Identity $item.Id -List "WidgetBoard_AdminConfig" -Values @{"Title" = "Config"; "Config" = $adminConfig } } else { write-host "Adding admin config list item" $item = Add-PnPListItem -List "WidgetBoard_AdminConfig" -Values @{"Title" = "Config"; "Config" = $adminConfig } } # create userconfig list CreateConfigList -name "WidgetBoard_UserConfig" $list = Get-PnPList -Identity "WidgetBoard_UserConfig" $list.ReadSecurity = 2 $list.WriteSecurity = 2 $list.Context.ExecuteQuery() # create assets list $list = Get-PnPList -Identity "WidgetBoard_Assets" -ErrorAction:SilentlyContinue if (-not $list) { New-PnPList -Title "WidgetBoard_Assets" -Template DocumentLibrary -Hidden -EnableContentTypes:$false -EnableVersioning:$false -OnQuickLaunch:$false -ErrorAction SilentlyContinue } # Tell the service the board has been installed [int]$rand = Get-Random -Maximum 10000 -Minimum 1000 Invoke-RestMethod -Method Post -Uri "https://ichicraft-widgetboard.azurewebsites.net/config/update/etag/$($rand)?referrer=$($siteUrl)" } } function Retry-Command { [CmdletBinding()] param ( [parameter(Mandatory, ValueFromPipeline)] [ValidateNotNullOrEmpty()] [scriptblock] $ScriptBlock, [int] $RetryCount = 100, [int] $TimeoutInSecs = 5, [string] $description = "" ) process { $Attempt = 1 $Flag = $true Write-Host "[START] $description" do { try { $PreviousPreference = $ErrorActionPreference $ErrorActionPreference = 'Stop' Invoke-Command -ScriptBlock $ScriptBlock -OutVariable Result $ErrorActionPreference = $PreviousPreference # flow control will execute the next line only if the command in the scriptblock executed without any errors # if an error is thrown, flow control will go to the 'catch' block Write-Host "[FINISHED] $description `n" $Flag = $false } catch { if ($Attempt -gt $RetryCount) { Write-Host "[FAILED] $description! Total retry attempts: $RetryCount" $Flag = $false } else { Write-Verbose "[Error Message] $($_.exception.message) `n" Write-Verbose "[$Attempt/$RetryCount] $description. Retrying in $TimeoutInSecs seconds..." Start-Sleep -Seconds $TimeoutInSecs $Attempt = $Attempt + 1 } } } While ($Flag) } } Export-ModuleMember -Function Add-IchicraftWidgetsApp, Add-IchicraftWidgetsClientSideWebPart, Approve-IchicraftWidgetsTenantServicePrincipalPermissionRequests, Initialize-IchicraftWidgetsWebpart, Export-IchicraftWidgetsConfiguration; |