Showing posts with label SharePoint Online. Show all posts
Showing posts with label SharePoint Online. Show all posts

Thursday, July 15, 2021

Format view using json in SharePoint Online Modern Asset Library

 You can use view formatting to customize how items in SharePoint lists and libraries are displayed. To do this, you construct a JSON object that describes the elements that are displayed when an item is loaded in a view and any styles to be applied to those elements. View formatting does not change the data in list items; it only changes how they're displayed to users who browse the list. Anyone who can create and manage views in a list can use view formatting to configure how views are displayed

To open the view formatting pane, open the view dropdown and choose Format current view.


The pane will look like the following depending on the current layout:


Sample schema for asset library is below

{
"$schema": "https://developer.microsoft.com/json-schemas/sp/view-formatting.schema.json",
"tileProps": {
"hideSelection": true,
"height": "300",
"width": "300",
"formatter": {
"elmType": "div",
"style": {
"align-items": "stretch",
"margin": "2px 2px",
"background-color": "#fbfbfb",
"height": "380px",
"overflow": "inherit",
"border-radius": "2px",
"box-shadow": "0px 1.6px 3.6px 0 #00000024, 0px 0.3px 0.9px 0 #00000024"
},
"children": [
{
"elmType": "div",
"style": {
"display": "flex",
"flex-wrap": "wrap",
"position": "relative",
"padding-bottom": "5px",
"width": "100%"
},
"children": [
{
"elmType": "div",
"style": {
"margin-top": "0px",
"height": "200px",
"display": "block",
"align-items": "center",
"justify-content": "center",
"position": "relative",
"border-bottom": "1px solid #EEE",
"overflow": "hidden",
"border-radius": "2px 2px 0 0"
},
"children": [
{
"elmType": "button",
"style": {
"position": "absolute",
"height": "100%",
"width": "100%",
"opacity": "0",
"cursor": "pointer"
},
"customRowAction": {
"action": "defaultClick"
}
},
{
"elmType": "div",
"style": {
"width": "=if([$File_x0020_Type] == '', '100px', '100%')",
"height": "=if([$File_x0020_Type] == '', '100px', '100%')",
"oveflow": "=if([$File_x0020_Type] == '', 'auto', 'hidden')",
"text-align": "center",
"overflow": "hidden"
},
"children": [
{
"elmType": "img",
"style": {
"height": "=if([$File_x0020_Type] == '', '100%', '0'"
},
"attributes": {
"src": "=if([$File_x0020_Type] == '', 'https://spoprod-a.akamaihd.net/files/fabric/office-ui-fabric-react-assets/foldericons-fluent/folder-large_frontplate_nopreview.svg', '')"
}
},
{
"elmType": "img",
"style": {
"display": "=if([$File_x0020_Type] == '', 'none', '')"
},
"attributes": {
"src": "@thumbnail.383x383"
}
}
]
}
]
},
{
"elmType": "div",
"style": {
"margin": "25px 0 0 0",
"position": "absolute",
"top": "153px",
"width": "100%",
"color": "#333333"
},
"attributes": {
"class": "ms-fontSize-14 ms-fontWeight-semibold"
},
"children": [
{
"elmType": "img",
"attributes": {
"src": "=if([$File_x0020_Type] == 'docx', 'https://spoprod-a.akamaihd.net/files/fabric/assets/item-types-fluent/20/docx.svg?v6', if([$File_x0020_Type] == 'xlsx', 'https://spoprod-a.akamaihd.net/files/fabric/assets/item-types-fluent/20/xlsx.svg?v6', if([$File_x0020_Type] == 'pptx', 'https://spoprod-a.akamaihd.net/files/fabric/assets/item-types-fluent/20/pptx.svg?v6', if([$File_x0020_Type] == 'pdf', 'https://spoprod-a.akamaihd.net/files/fabric/assets/item-types-fluent/20/pdf.svg?v6', if([$File_x0020_Type] == 'jpg' || [$File_x0020_Type] == 'png' || [$File_x0020_Type] == 'gif','https://spoprod-a.akamaihd.net/files/fabric/assets/item-types-fluent/20/photo.svg?v6', if([$File_x0020_Type] == 'mp4' || [$File_x0020_Type] == 'avi' || [$File_x0020_Type] == 'mov', 'https://spoprod-a.akamaihd.net/files/fabric/assets/item-types-fluent/20/video.svg?v6', if([$File_x0020_Type] == 'zip', 'https://spoprod-a.akamaihd.net/files/fabric/assets/item-types-fluent/20/zip.svg?v6','Unknown')))))))"
},
"style": {
"flex": "none",
"line-height": "100%",
"font-weight": "normal",
"font-size": "2rem",
"margin": "5px 5px 5px 10px",
"height": "25px"
}
},
{
"elmType": "div",
"txtContent": "[$VideoTitle]",
"style": {
"padding": "0 0 0 16px",
"font-weight": "bold",
"font-size": "1rem"
}
},
{
"elmType": "div",
"txtContent": "[$VideoSummary]",
"style": {
"padding": "0 0 0 16px"
}
}
]
}
]
}
]
}
}
}

You can modify this based on your needs.







Friday, June 4, 2021

Redirect azure website to SharePoint Online or any other url

Below is the url rewrite rule added in azure webapp config to redirect the site to any new location, let it be a SPO site or any other web url. The sample below redirects to the microsoft password reset page.

<?xml version="1.0" encoding="UTF-8"?>

<configuration>

  <system.webServer>

    <rewrite>

      <rules>

         <rule name="SimpleURL" patternSyntax="ECMAScript" stopProcessing="true">

                                                          <match url="(.*)"/>

                                                          <action type="Redirect" url="https://aka.ms/sspr/?whr=domain" redirectType="Permanent" appendQueryString="true" />

                                           </rule>

                                           </rules>

    </rewrite>

  </system.webServer>

</configuration>

Powershell script to get SharePoint online site access report

 Check SharePoint site access permissions.

 
.PARAMETER Credential
Standard PSCredential object.
 
.PARAMETER Identity
Identity (user@domain.com) of user to check.
 
.PARAMETER PermissionToCheck
Full list of permissions to check per site. Default is ViewPages. Additional
parameters include "All" and "AllViewPermissions."
 
.PARAMETER Tenant
Tenant name as either 'tenant.onmicrosoft.com' or 'tenant.'


param (

    # Credential object

    [Parameter(Mandatory = $true)]

    [System.Management.Automation.PSCredential]$Credential,

    

    # Target user to report on

    $Identity,

    [ValidateSet('EmptyMask','ViewListItems','AddListItems','EditListItems',

              'DeleteListItems', 'ApproveItems', 'OpenItems', 'ViewVersions', 'DeleteVersions',

              'CancelCheckout', 'ManagePersonalViews', 'ManageLists', 'ViewFormPages', 'AnonymousSearchAccessList',

              'Open', 'ViewPages', 'AddAndCustomizePages', 'ApplyThemeAndBorder', 'ApplyStyleSheets', 'ViewUsageData',

              'CreateSSCSite', 'ManageSubwebs', 'CreateGroups', 'ManagePermissions', 'BrowseDirectories', 'BrowseUserInfo',

              'AddDelPrivateWebParts', 'UpdatePersonalWebParts', 'ManageWeb', 'AnonymousSearchAccessWebLists', 'UseClientIntegration',

              'UseRemoteAPIs', 'ManageAlerts', 'CreateAlerts', 'EditMyUserInfo', 'EnumeratePermissions', 'FullMask','All','AllViewPermissions')]

              [array]$PermissionToCheck = "ViewPages",

    $LogFile = (Get-Date -Format yyyy-MM-dd) + "_SiteAccessReport.txt",

    [Parameter(mandatory = $true)]

    [String]$Tenant

)


function Write-Log([string[]]$Message, [string]$LogFile = $Script:LogFile, [switch]$ConsoleOutput)

{

    $Message = $Message + $Input

    If ($Message -ne $null -and $Message.Length -gt 0)

    {

        if ($LogFile -ne $null -and $LogFile -ne [System.String]::Empty)

        {

            Out-File -Append -FilePath $LogFile -InputObject "$Message"

        }

        if ($ConsoleOutput -eq $true)

        {

            Write-Host "$Message"

        }

    }

}


function LoadSharePointLibraries

{

    If (Test-Path 'c:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll')

    {

        Write-Host -ForegroundColor Green "Found SharePoint Server Client Components installation."

        Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"

        Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"

        Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Taxonomy.dll"

        Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.UserProfiles.dll"

    }

    ElseIf ($filename = (Get-ChildItem 'C:\Program Files' -Recurse -ea silentlycontinue | where { $_.name -eq 'Microsoft.SharePoint.Client.DocumentManagement.dll' })[0])

    {

        $Directory = ($filename.DirectoryName)[0]

        Write-Host -ForegroundColor Green "Found SharePoint Server Client Components at $Directory."

        Add-Type -Path "$Directory\Microsoft.SharePoint.Client.dll"

        Add-Type -Path "$Directory\Microsoft.SharePoint.Client.Runtime.dll"

        Add-Type -Path "$Directory\Microsoft.SharePoint.Client.Taxonomy.dll"

        Add-Type -Path "$Directory\Microsoft.SharePoint.Client.UserProfiles.dll"

    }

    

    ElseIf (!(Test-Path 'C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll'))

    {

        Write-Host -ForegroundColor Yellow "This script requires the SharePoint Server Client Components. Attempting to download and install."

        wget 'https://download.microsoft.com/download/E/1/9/E1987F6C-4D0A-4918-AEFE-12105B59FF6A/sharepointclientcomponents_15-4711-1001_x64_en-us.msi' -OutFile ./SharePointClientComponents_15.msi

        wget 'https://download.microsoft.com/download/F/A/3/FA3B7088-624A-49A6-826E-5EF2CE9095DA/sharepointclientcomponents_16-4351-1000_x64_en-us.msi' -OutFile ./SharePointClientComponents_16.msi

        msiexec /i SharePointClientComponents_15.msi /qb

        msiexec /i SharePointClientComponents_16.msi /qb

        Sleep 60

        If (Test-Path 'c:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll')

        {

            Write-Host -ForegroundColor Green "Found SharePoint Server Client Components."

            Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"

            Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"

            Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Taxonomy.dll"

            Add-Type -Path "C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.UserProfiles.dll"

        }

        Else

        {

            Write-Host -NoNewLine -ForegroundColor Red "Please download the SharePoint Server Client Components from "

            Write-Host -NoNewLine -ForegroundColor Yellow "https://download.microsoft.com/download/F/A/3/FA3B7088-624A-49A6-826E-5EF2CE9095DA/sharepointclientcomponents_16-4351-1000_x64_en-us.msi "

            Write-Host -ForegroundColor Red "and try again."

            Break

        }

    }

    

    If (!(Get-Module -ListAvailable "*online.sharepoint*"))

    {

        Write-Host -ForegroundColor Yellow "This script requires the SharePoint Online Management Shell. Attempting to download and install."

        wget 'https://download.microsoft.com/download/0/2/E/02E7E5BA-2190-44A8-B407-BC73CA0D6B87/SharePointOnlineManagementShell_6802-1200_x64_en-us.msi' -OutFile ./SharePointOnlineManagementShell.msi

        msiexec /i SharePointOnlineManagementShell.msi /qb

        Write-Host -ForegroundColor Yellow "Please close and reopen the Windows Azure PowerShell module and re-run this script."

    }

    If (!(Get-InstalledModule -MinimumVersion 3.11.1907.0 SharePointPnPPowerShellOnline -ea SilentlyContinue))

    {

        Install-Module SharePointPnPPowerShellOnline -MinimumVersion 3.0 -Force

    }

}


LoadSharePointLibraries


# Validate Permissions

switch -regex ($PermissionToCheck)

{

    '^(?i)all$' {

        $PermissionToCheck = [array]$PermissionToCheck = @('EmptyMask', 'ViewListItems', 'AddListItems', 'EditListItems',

            'DeleteListItems', 'ApproveItems', 'OpenItems', 'ViewVersions', 'DeleteVersions',

            'CancelCheckout', 'ManagePersonalViews', 'ManageLists', 'ViewFormPages', 'AnonymousSearchAccessList',

            'Open', 'ViewPages', 'AddAndCustomizePages', 'ApplyThemeAndBorder', 'ApplyStyleSheets', 'ViewUsageData',

            'CreateSSCSite', 'ManageSubwebs', 'CreateGroups', 'ManagePermissions', 'BrowseDirectories', 'BrowseUserInfo',

            'AddDelPrivateWebParts', 'UpdatePersonalWebParts', 'ManageWeb', 'AnonymousSearchAccessWebLists', 'UseClientIntegration',

            'UseRemoteAPIs', 'ManageAlerts', 'CreateAlerts', 'EditMyUserInfo', 'EnumeratePermissions', 'FullMask'); break

    }

    '^(?i)allviewpermissions$' {

        $PermissionToCheck = [array]$PermissionToCheck = @('ViewListItems', 'ViewVersions', 'ViewPages', 'ViewUsageData',

            'BrowseDirectories', 'BrowseUserInfo', 'EnumeratePermissions'); break

    }

    '^(?i)viewpages$' { $PermissionToCheck = 'ViewPages'; break}

    default

    {

        if ($PermissionToCheck) { }

        Else { $PermissionToCheck = $PSBoundParameters.Values | ? { $PSBoundParameters.Keys -eq "PermissionToCheck" } }

    }

}


# Validate identity submitted is a valid email address/upn format

Try { $Test = New-Object Net.Mail.MailAddress($Identity) -ea stop }

Catch { "ERROR: Not a valid identity address (user@domain.com)"; break }


# Validate tenant name

If ($tenant -like "*.onmicrosoft.com") { $tenant = $tenant.split(".")[0] }

$AdminURL = "https://$tenant-admin.sharepoint.com"


# Verify if log file exists; if not, create

If (!(Test-Path $LogFile))

    {

    Write-Log -Message "Identity,Url,Permissions" -LogFile $LogFile

    }


# Connect-PnpOnline only doesn't prompt for creds if you pass it to Invoke-Expression

$cmd = "Connect-PnpOnline -Url $($AdminUrl) -credentials `$Credential"

Invoke-Expression $cmd


# Establish user identity in SPO format

$user = "i:0#.f|membership|$($Identity)"



[array]$Urls = Get-PnPTenantSite | Select -ExpandProperty Url

$i = 1

foreach ($Url in $Urls)

{

    Write-Progress -Activity "SharePoint Site Permissions Report" -Percent (($i/$Urls.Count)*100) -CurrentOperation "Checking site $($Url)"

    Connect-PnPOnline -Url $Url -credentials $Credential

    $web = Get-PnPWeb

    $UserEffectivePermission = $web.GetUserEffectivePermissions($user)

    try { Invoke-PnPQuery -ea stop }

    catch { Write-Log -LogFile ErrorLog.txt -Message "Error running Invoke-PnP against $($Url)."}

    

    $EffectivePermissions = @()

    foreach ($Perm in $PermissionToCheck)

    {

        try { $HasAccess = $UserEffectivePermission.Value.Has($Perm) }

        catch { Write-Log -LogFile ErrorLog.txt -Message "Error evaluating permission $($perm) against $($Url)."}

        If ($HasAccess -eq $true) { $EffectivePermissions += $perm }

    }

    

    if ($EffectivePermissions)

    {

        $PermissionArray = $EffectivePermissions -join ";"

        Write-Log -Message "$($Identity),$($Url),$($PermissionArray)" -LogFile $LogFile

    }

    $i++

}

Updating SharePoint UserProfile Properties

In case the SharePoint user profile property for a specific user or set of users then you can use the below powershell script to update the user profile property directly from sharepoint. 

$web=get-spweb -identity http://<weburl>


$user=$web.EnsureUser("<staffid with domain- user claims>")


$user | fl     -- this will show the user id and pass that id to below script in GetitembyID method


$web=get-spweb -identity  http://<weburl>


$list=$web.lists["user information list"]


$item=$list.GetItemById(6007) 


$item["JobTitle"]="<Title to update>"

$item["Department"]="<New Dept>"


$item.Update()


$list.Update()


$web.Update() 

Tuesday, September 24, 2019

Email Enabled lists in Office 365 & SharePoint Online

For many years, users of SharePoint have enjoyed a handy feature known as “Incoming Email”.  This feature allows for assigning email addresses to SharePoint lists.  Emails sent to the assigned email address will process to the list associated to that email address.  However, as organizations begin to migrate to Office 365 they are left with the dilemma of whether to seek alternatives or abandon this functionality all together.  Why you ask?  This is because the incoming email feature is no longer available in SharePoint Online out of the box.


Why is the feature not available in SharePoint Online?

To answer this question, one must first learn how the on-premises “Incoming Email” feature works.  Figure 1-1 below shows the typical flow of an incoming email into SharePoint

  1. The User sends the email to an address like testlist@sp.demo.com
  2. The mail server forwards that email on to an SMTP server that is set up for the SharePoint Farm.
  3. Once this SMTP server receives the email, it places it in a drop folder.
  4. The incoming email timer job runs on a schedule to read in email messages in this folder and process them to the list that matches the email address the message was sent to.
Figure 1-2 below shows one of the reasons why this feature does not work in SharePoint Online


  1. SharePoint Online restricts users to Site collection or below. No more farm level access exists for your SharePoint site.  This means the farm level timer jobs and configuration settings that used to take care of this processing no longer exist.
  2. Since these settings do not exist on the back-end, they have also been removed from the front-end list settings as well.

What are my Options?

Now that we covered reasons why this feature is not available out of the box, we need to address what can be done about it.  In this void of having a solution to for this feature, many players have showed up on the scene.  Below I have listed some of the options, and the advantages and disadvantages to each.

Option 1: Site Mailboxes (This is no more an option as SPO deprecated Site Mailbox feature as of July 2017)

This option allows for setting up an Exchange mailbox used to collaborate between team members.  Emails can be sent to the mailbox to be shared among team members.
Advantages
  • Allows end users to create these mailboxes from the SharePoint online ‘Add an app’ page.
  • Permissions set on the SharePoint site are also applied to the mailbox.
Disadvantages
  • Only one mailbox can be created per site.
  • Mail and documents do not live in SharePoint. Data is accessed through Outlook Web Access.
  • Site Mailbox is chosen for you based on the SharePoint site name.
  • Cannot set up event receivers or workflows on the data.
  • Documents and emails are opened in outlook web access, and not SharePoint.

 Option 2: Office 365 Groups

Uses the Office 365 group functionality to set up a shared work space for collaboration.
Advantages
  • A collaboration space is created to share documents, calendars, conversations.
  • Feature is integrated with your OneDrive for Business site.
Disadvantages
  • Every group creates a new hidden site collection.
  • No workflow options.

Option 3: Azure Logic Apps

Azure Logic Apps can be used to set up connectors between an Office 365 mailbox and a SharePoint list.
Advantages
  • Processes email within a mailbox to the associated SharePoint list.
Disadvantages
  • Must have an Azure subscription.
  • Must pay licensing fee for Logic Apps.
  • Requires setting up a new user and mailbox for each list (requires user to be licensed for Exchange Online and SharePoint Online).

Option 4: Microsoft Flow

Microsoft Flow is a feature offered in Office 365 to allow processing email messages in Office 365 to SharePoint libraries.
Advantages
  • Can process email attachments from an Office 365 mailbox into SharePoint.
  • Can configure to use one mailbox to process to many different lists.
Disadvantages
  • Requires setting up a user to go with a mailbox in Office 365 (requires user to be licensed for Exchange Online and SharePoint Online).
  • Only has template for email attachment. No current offering to save the email itself.
  • Additional cost per user for Microsoft Flow Licensing.

Option 5: 3rd Party Paid Apps

There are many 3rd party paid apps out there that offer similar or replace this out of the box functionality.
Advantages
  • Usually moderately priced.
  • Offers functionality similar in nature to the out of the box solution.
Disadvantages
  • Some of the major players charge based on the number of emails processed.
  • Most also require a mailbox per list to be processed (would require licensing each mailbox)

Option 6: Custom Code

By writing custom code to replace this functionality, you are provided with a customized solution for processing these emails.
Advantages
  • Custom solution.
  • Can use existing infrastructure.
  • Ability to configure the polling schedule.
  • No need for extra licensing.
  • Users allowed to configure the list settings.
Disadvantages
  • Requires an existing SMTP server and a asp.net server for the process to function.

Wednesday, October 25, 2017

Adding Custom Action to SharePoint Online site

In this post, I'm providing the SPO powershell script which you can run from SPO management shell to add a new custom action to specific site.


You can use this script to render scripts on specific pages inside a site and  there by add any new options to users/ hide any option from end users.


Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Publishing.dll"
# Authenticate with the SharePoint site.
#
$actionName = "EM_SPO_JOSPODELWEB_JS_Injection"
$actionType = "ScriptLink"
$actionSourceFile ="https://yourtenant.sharepoint.com/sites/JoSPO/SiteAssets/HideSiteDeletion.js"
$siteUrl = Read-Host -Prompt "Enter web url:"
$username = Read-Host -Prompt "Enter Username:"
$password = Read-Host -Prompt "Enter password" -AsSecureString
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)
# SharePoint Online
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $password)
$ctx.Credentials = $credentials
$rootWeb = $ctx.Web
$ctx.Load($rootWeb)
$actions = $rootWeb.get_userCustomActions()
$ctx.Load($actions)
$ctx.ExecuteQuery()
if($actions)
{
$actionsToDelete = @()
foreach($action in $actions)
{
if($action.get_description() -eq $actionName -and $action.get_location() -eq $actionType) {
Write-Host "Action found:" $action.get_description() -foregroundcolor white -backgroundcolor Green
$actionsToDelete += $action
}
}
foreach($actionToDelete in $actionsToDelete) {
    $actionToDelete.deleteObject()
Write-Host "Action deleted" -foregroundcolor white -backgroundcolor Green
}
$ctx.ExecuteQuery()
}
Write-Host "Installing action"  -foregroundcolor white -backgroundcolor Green
$newAction = $actions.add();
$newAction.set_description($actionName);
$newAction.set_location('ScriptLink');
$scriptBlock = 'var headIDDetails = document.getElementsByTagName("head")[0];var newScriptDetails = document.createElement("script");newScriptDetails.type = "text/javascript";newScriptDetails.src="';
$scriptBlock += $actionSourceFile + '?ver=' + (Get-Date) ;
$scriptBlock += '";headIDDetails.appendChild(newScriptDetails);';
$newAction.set_scriptBlock($scriptBlock);
$newAction.update();
$ctx.ExecuteQuery();
Write-Host "Action" $newAction.Description "installed" -foregroundcolor white -backgroundcolor Green


Wednesday, June 21, 2017

Avoid getting throttled or blocked in SharePoint Online

What is throttling?


SharePoint Online uses throttling to maintain optimal performance and reliability of the SharePoint Online service. Throttling limits the number of user actions or concurrent calls (by script or code) to prevent overuse of resources.
That said, it is extremely rare for a user to get throttled in SharePoint Online. The service is robust, and it is designed to handle very high volume. If you do get throttled, 99% of the time it is because of custom code. That doesn't mean that there aren't other ways to get throttled, just that they are less common. For example you spin up 10 machines and have a sync client going on all 10. On each sync 1TB of content. This would likely get you throttled.


What happens when you get throttled in SharePoint Online?

When a user exceeds usage limits, SharePoint Online throttles any further requests from that user account for a short period. All user actions are throttled while the throttle is in effect.
  • For requests that a user performs directly in the browser, SharePoint Online redirects you to the throttling information page, and the requests fail.
  • For all other requests, including CSOM or REST calls, SharePoint Online returns HTTP status code 429 ("Too many requests"), and the requests fail.
If the offending process continues to exceed usage limits, SharePoint Online might completely block the process; in this case, you may see HTTP status code 503 ("Service unavailable"), and we'll notify you of the block in the Office 365 Message Center. The error message is shown below:



503 Server unavailable message.

Common throttling scenarios in SharePoint Online


The most common causes of per-user throttling in SharePoint Online are client-side object model (CSOM) or Representational State Transfer (REST) code that performs too many actions too frequently.
  • Sporadic traffic
    Not a lot of traffic at any one time, but enough over time that you run in and out of throttling in an episodic way.
    • For example, after migrating files to SharePoint Online, you run a custom CSOM or REST script to update metadata on the files. The CSOM/REST script is updating a large number of files at a very high frequency, which triggers throttling. Similarly, an autocomplete UI widget using REST services, making too many calls to lists during each end user operation, may also cause throttling, depending on what other operations are consuming resources at the same time.
  • Overwhelming traffic
    A single process dramatically exceeds throttling limits, continually, over a long time period.
    • You used web services to build a tool to synchronize user profile properties. The tool updates user profile properties based on information from your line-of-business (LOB) human resources (HR) system. The tool makes calls at too high a frequency.
    • You're running a load-testing script on SharePoint Online and you get throttled. Load testing is not allowed on SharePoint Online.
    • You customized your team site on SharePoint Online, for example, by adding a status indicator on the Home page. This status indicator updates frequently, which causes the page to make too many calls to the SharePoint Online service - this triggered throttling.

Why can't you just tell me the exact throttling limits?


Setting and publishing exact throttling limits sounds very straightforward, but in fact, it's not the best way to go. We continually monitor resource usage on SharePoint Online. Depending on usage, we fine-tune thresholds so users can consume the maximum number of resources without degrading the reliability and performance of SharePoint Online. That's why it's so important for your CSOM or REST code to include incremental back off to handle throttling; this lets your code run as fast as possible on any given day, and it lets your code back off "just enough" if it hits throttling limits. The code samples later in this article show you how to use incremental back off.

Best practices to handle throttling


  • Reduce the number of operations per request
  • Reduce the frequency of calls
  • Use incremental back off to reduce the number and frequency of calls until no more throttling occurs
Incremental back off uses progressively longer waits between retries before trying again to run the code that was throttled. You can use the GitHub code samples, later in this article, written as extension methods, to add incremental back off to your code.
Backing off is the fastest way to handle being throttled because SharePoint Online continues to log resource usage while a user is being throttled. In other words, aggressive retries work against you because even though the calls fail, they still accrue against your usage limits. The faster you back off, the faster you'll stop exceeding usage limits. 

GitHub CSOM code samples: SharePoint Online Throttling

Before you run this code sample:
  • Open Program.cs and enter the following information in the Main method:
    • Your Office 365 Developer account credentials.
    • The URL of your Office 365 Developer Site.
    • The name of a test document library on your Office 365 Developer Site.
  • If you receive an error stating that the App.Config file is invalid, go to Solution Explorer, right click App.config, and choose Exclude From Project.
Core.Throttling runs as a console application using a user-only authorization policy, which means this code sample uses the permissions of the current user. In the Main method in Program.cs, a while loop repeatedly creates new folders in the test document library. A call is then made to ctx.ExecuteQueryWithExponentialRetry, which uses CSOM to perform the ExecuteQuery method. ExecuteQueryWithExponentialRetry is an extension method on the ClientContext object, and is defined in ClientContextExtension.cs.
If SharePoint Online throttles the ExecuteQuery statement, ExecuteQueryWithIncrementalRetry starts the incremental back off technique by:
  • Catching a WebException and checking the HttpWebResponse.StatusCode. If SharePoint Online throttled the ExecuteQuery statement, the HttpWebResponse.StatusCode is 429.
  • The current thread is suspended for the period specified in backoffInterval.
  • When the current thread resumes, the backoffInterval is doubled and the number of retries performed ( retryAttempts ) is incremented. By doubling backoffInterval your code suspends activity for a longer period of time before retrying the code that was throttled by SharePoint Online.
  • The process is repeated until either the ExecuteQuery statement is successful, or the number of allowed retries ( retryCount ) is exceeded.

CSOM Code sample: Incremental back off and retry (calls ExecuteQueryWithIncrementalRetry method, later in this article)

using (var ctx = new ClientContext(serverUrl))
{
//Provide account and pwd for connecting to the source
var passWord = new SecureString();
foreach (char c in password.ToCharArray()) passWord.AppendChar(c);
ctx.Credentials = new SharePointOnlineCredentials(login, passWord);
try
{
int number = 0;
// This loop will be executed 1000 times, which will cause throttling to occur
while (number < 1000)
{
// Try to create new folder based on Ticks to the given list as an example process
var folder = ctx.Site.RootWeb.GetFolderByServerRelativeUrl(listUrlName);
ctx.Load(folder);
folder = folder.Folders.Add(DateTime.Now.Ticks.ToString());
// Extension method for executing query with throttling checks
ctx.ExecuteQueryWithIncrementalRetry(5, 30000); //5 retries, with a base delay of 30 secs.
// Status indication for execution.
Console.WriteLine("CSOM request successful.");
// For loop handling.
number = number + 1;
}
}
catch (MaximumRetryAttemptedException mex)
{
// Exception handling for the Maximum Retry Attempted
Console.WriteLine(mex.Message);
}
}

CSOM Code sample: ExecuteQueryWithIncrementalRetry method

public static void ExecuteQueryWithIncrementalRetry(this ClientContext context, 
int retryCount, int delay)
        {
            int retryAttempts = 0;
            int backoffInterval = delay;
            if (retryCount <= 0)
                throw new ArgumentException("Provide a retry count greater than zero.");
           if (delay <= 0)
                throw new ArgumentException("Provide a delay greater than zero.");
           while (retryAttempts < retryCount)
            {
                try
                {
                    context.ExecuteQuery();
                    return;
                }
                catch (WebException wex)
                {
                    var response = wex.Response as HttpWebResponse;
                    if (response != null &amp;&amp; response.StatusCode == (HttpStatusCode)429)
                    {
                        Console.WriteLine(string.Format("CSOM request exceeded usage limits. 
Sleeping for {0}
 seconds before retrying.", backoffInterval));
                        //Add delay.
                        System.Threading.Thread.Sleep(backoffInterval);
                        //Add to retry count and increase delay.
                        retryAttempts++;
                        backoffInterval = backoffInterval * 2;
                    }
                    else
                    {
                        throw;
                    }
                }
            }
            throw new MaximumRetryAttemptedException(string.Format("Maximum retry attempts {0}, 
have been attempted.", retryCount));
       }

What should you do if you get blocked in SharePoint Online?

Blocking is the most extreme form of throttling. We rarely ever block a tenant,
unless we detect long-term, extremely excessive traffic that may threaten the overall 
health of the SharePoint Online service. We apply blocks to prevent excessive traffic from
 degrading the performance and reliability of SharePoint Online. A block - which is usually
 placed at the tenancy level - prevents the offending process from running until you fix the problem. 

If we block your subscription, you must take action to modify the offending processes before
 the block can be removed.

If we block your subscription, you'll see HTTP status code 503, and we'll notify you of the
 block in the Office 365 Message Center. The message describes what caused the block, provides
 guidance on how to resolve the offending issue, and tells you who to contact to get the block removed.

Monday, May 22, 2017

Hide Ribbon in SharePoint Online using JS Link

Create a new js file and name it as DetailsView.js. Add the below script to it


window.onload = function () {

if (window.location.href.indexOf("user.aspx?obj") > -1) {

document.getElementById('Ribbon.Permission.Manage.Inherit-Large').style.display = "none";

}
};

upload it to a cdn location.

Here I'm hiding the Delete Unique Permissions Ribbon element from lists/libraries permission page. You can change code based on your ribbon id.


Below is the powershell to add the above js file as a link to SharePoint online site.

Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Publishing.dll"

# Authenticate with the SharePoint site.
#
$actionName = "EM_SPO_ZIN_JS_Injection"
$actionType = "ScriptLink"
$actionSourceFile ="https://cdnpath/Detailsview.js"


$siteUrl = Read-Host -Prompt "Enter web url:"
$username = Read-Host -Prompt "Enter Username:"
$password = Read-Host -Prompt "Enter password" -AsSecureString
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)

# SharePoint Online
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $password)
$ctx.Credentials = $credentials

$rootWeb = $ctx.Web
$ctx.Load($rootWeb)
$actions = $rootWeb.get_userCustomActions()
$ctx.Load($actions)
$ctx.ExecuteQuery()
if($actions)
{
$actionsToDelete = @()

foreach($action in $actions)
{
if($action.get_description() -eq $actionName -and $action.get_location() -eq $actionType) {

Write-Host "Action found:" $action.get_description() -foregroundcolor white -backgroundcolor Green
$actionsToDelete += $action

}
}

foreach($actionToDelete in $actionsToDelete) {
    $actionToDelete.deleteObject()
Write-Host "Action deleted" -foregroundcolor white -backgroundcolor Green
}
$ctx.ExecuteQuery()
}


Write-Host "Installing action"  -foregroundcolor white -backgroundcolor Green
$newAction = $actions.add();
$newAction.set_description($actionName);
$newAction.set_location('ScriptLink');
$scriptBlock = 'var headID = document.getElementsByTagName("head")[0];var newScript = document.createElement("script");newScript.type = "text/javascript";newScript.src="';
$scriptBlock += $actionSourceFile + '?ver=' + (Get-Date);
$scriptBlock += '";headID.appendChild(newScript);';
$newAction.set_scriptBlock($scriptBlock);
$newAction.update();
$ctx.ExecuteQuery();

Write-Host "Action" $newAction.Description "installed" -foregroundcolor white -backgroundcolor Green

Powershell to add JS Link in SharePoint Online

Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll"
Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll"
Add-Type -Path "c:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Publishing.dll"

# Authenticate with the SharePoint site.
#
$actionName = "EM_SPO_ZIN_JS_Injection"
$actionType = "ScriptLink"
$actionSourceFile ="https://cdnpath/Detailsview.js"


$siteUrl = Read-Host -Prompt "Enter web url:"
$username = Read-Host -Prompt "Enter Username:"
$password = Read-Host -Prompt "Enter password" -AsSecureString
$ctx = New-Object Microsoft.SharePoint.Client.ClientContext($siteUrl)

# SharePoint Online
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $password)
$ctx.Credentials = $credentials

$rootWeb = $ctx.Web
$ctx.Load($rootWeb)
$actions = $rootWeb.get_userCustomActions()
$ctx.Load($actions)
$ctx.ExecuteQuery()
if($actions)
{
$actionsToDelete = @()

foreach($action in $actions)
{
if($action.get_description() -eq $actionName -and $action.get_location() -eq $actionType) {

Write-Host "Action found:" $action.get_description() -foregroundcolor white -backgroundcolor Green
$actionsToDelete += $action

}
}

foreach($actionToDelete in $actionsToDelete) {
    $actionToDelete.deleteObject()
Write-Host "Action deleted" -foregroundcolor white -backgroundcolor Green
}
$ctx.ExecuteQuery()
}


Write-Host "Installing action"  -foregroundcolor white -backgroundcolor Green
$newAction = $actions.add();
$newAction.set_description($actionName);
$newAction.set_location('ScriptLink');
$scriptBlock = 'var headID = document.getElementsByTagName("head")[0];var newScript = document.createElement("script");newScript.type = "text/javascript";newScript.src="';
$scriptBlock += $actionSourceFile + '?ver=' + (Get-Date);
$scriptBlock += '";headID.appendChild(newScript);';
$newAction.set_scriptBlock($scriptBlock);
$newAction.update();
$ctx.ExecuteQuery();

Write-Host "Action" $newAction.Description "installed" -foregroundcolor white -backgroundcolor Green

Sunday, May 21, 2017

List of sites user has access in SharePoint Online


Getting a list of sites that a user has access to in SharePoint Online

My customer needed a way to show all the sites and site collection where the user had access. This is for an the sharepoint online site which they have. Since we cannot have any server side code and also looping through all sites and checking whether user has got access to the site is difficult.
So I came up with the option of using search api which is already security trimmed and based on contentclass, we can get the list. Below is the sample code for the same.
You can build your html and apply.
<button id='btnGetSites'>Get Sites</button>

<div id='list'>

</div>
<script src="https://tenant.sharepoint.com/sites/spdev/SiteAssets/jquery.min.js" type="text/javascript"></script>
<script type='text/javascript'>


(function(){
    var siteUrl = _spPageContextInfo.siteAbsoluteUrl;
    var url = siteUrl + "/_api/search/query?querytext='(contentclass:STS_Site)'";
    $('#btnGetSites').click(function(){
        console.log('start..');

        $.ajax({
            method: 'GET',
            url: url,
            dataType: 'json',
            success: success,
            error: error
        });

        function success(data){
            var html = '';

            $.each(data.PrimaryQueryResult.RelevantResults.Table.Rows, function(index, row){              
                var title = '';
                var url = '';
                $.each(row.Cells, function(i, obj){            
                    if(obj.Key === 'Title'){
                        html += obj.Value;
                    }
                    else if(obj.Key === 'Path'){
                        html += ": " + obj.Value + "<br/>";
                    }              
                });        
            });

            $('#list').append(html);
        }
        function error(err){
            console.log(err);
        }      
        return false;
    });

}());

</script>

Sunday, November 6, 2016

Save site as template option is not available in SharePoint Online in Office 365

Problem

This issue most frequently occurs because the Community Sites Feature site feature, the SharePoint Server Publishing site feature, or the SharePoint Server Publishing Infrastructure site collection feature is currently enabled or was previously enabled for the affected site. 
SharePoint doesn’t support creating a template from a site where publishing or community features were enabled

More Information
Well, since the Save site as a template option is hidden from the publishing sites settings page we all used the following URL to go there directly:
/_layouts/savetmpl.aspx
Or
/_layouts/15/savetmpl.aspx
Well, that is not going to work anymore in SharePoint online (SPO) if your site is a publishing site or have the publishing features activated.
Resolution

In order to get around this issue you need to update a single property page value in your SPWeb object for the site you are trying to save as a template and you are good to go. The property is called SaveSiteAsTemplateEnabled. We need to set that property to true that’s all

You can use the below sample script for the same in SPO
#Load SharePoint CSOM Assemblies
[System.Reflection.Assembly]::LoadFile("C:\***\Microsoft.SharePoint.Client.dll") | Out-Null
[System.Reflection.Assembly]::LoadFile("C:\***\Microsoft.SharePoint.Client.Runtime.dll") | Out-Null
#Variables for Processing
$SiteUrl = "https://sharepointcollabs.sharepoint.com/sites/pdkmtest"
# Get a reference to the target site
$password = Read-Host -Prompt "Enter password" -AsSecureString
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials("****@domain.com", $password)
#Set up the context
$Context = New-Object Microsoft.SharePoint.Client.ClientContext($SiteUrl)
$Context.Credentials = $credentials
# Update the property bag value and set it  to the string value “true”
$Context.Web.AllProperties["SaveSiteAsTemplateEnabled"] = "true"
# Commit the property change to server
$Context.Web.Update()
$Context.ExecuteQuery()
Now if you navigate to the Save site as a template page using the URL directly it will show fine and it will allow you to save the site as a template which stores a copy in the Solution gallery of the site collection