Forum Discussion
RESOLVED: How to change Modern page Author from the "Created By" field to something else
- Oct 16, 2018
Dear all,
I finally found how to do that change based on a PowerShell script.
This script will directly check the page list and for each item will check if the "Modified By" value is different than the "Created By", and check also if the "created by" account was not an admin (or system).
If yes, the item fields (Author, Creator) will be updated with the values "Modified By" and the Modified Date will be applied into all the fields using the same type.
You can easily adapt that script code if you need another logic, but the solution is working really well and I applied it in many Modern Page List.
[string]$SitePagesURL ="https://[yourtenant].sharepoint.com/sites/YourSiteColl/YourSubsite" [string]$PageLibPublicName = "Site Pages" [DateTime]$modifiedDate = Get-Date [string]$DefaultEmailAddress = "PublisherEmail@yourdomain.com" [string]$MyTempEmailAddress = "" # --------------------------------------------------------------------------------------------------------------- function Load-DLLandAssemblies { [string]$defaultDLLPath = "" # Load assemblies to PowerShell session $defaultDLLPath = "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.dll" [System.Reflection.Assembly]::LoadFile($defaultDLLPath) $defaultDLLPath = "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.Runtime.dll" [System.Reflection.Assembly]::LoadFile($defaultDLLPath) $defaultDLLPath = "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.Online.SharePoint.Client.Tenant.dll" [System.Reflection.Assembly]::LoadFile($defaultDLLPath) } # --------------------------------------------------------------------------------------------------------------- Function Get-All-PageList-UpdateMetadata($MyctxTemp, $MyspoRootwebTemp) { $Alllists = $MyspoRootwebTemp.Lists $MyPagelist = $Alllists.GetByTitle($PageLibPublicName) $MyQuery = New-Object Microsoft.SharePoint.Client.CamlQuery; $MyQuery.ViewXml = "<View><Query><OrderBy><FieldRef Name='Modified' Ascending='FALSE'></FieldRef></OrderBy></Query><RowLimit>9999</RowLimit></View>" $MyPagelistItems = $MyPagelist.GetItems($MyQuery); $MyctxTemp.load($MyPagelistItems) $MyctxTemp.executeQuery() foreach($PageItem in $MyPagelistItems) { Write-Host "" Write-Host "" Write-Host " --------------------------------------------------------- " <# foreach($MyFieldval in $PageItem.FieldValues) { Write-Host " >>> FieldName:", $MyFieldval } #> $modifiedDate = $PageItem["Modified"] $ModifiedByuser = [Microsoft.SharePoint.Client.FieldUserValue]$PageItem["Editor"] $CreatedByuser = [Microsoft.SharePoint.Client.FieldUserValue]$PageItem["Author"] Write-Host "ID:", $PageItem["ID"], "- Title:", $PageItem["Title"], "- Original Publication Date:", $modifiedDate.ToString("dd-MM-yyyy") Write-Host " ==>>> PromotedState:", $PageItem["PromotedState"], "- PageLayoutType:", $PageItem["PageLayoutType"] -ForegroundColor red Write-Host " >> Description:", $PageItem["Description"] Write-Host " >> BannerImageUrl:", $PageItem["BannerImageUrl"].URL, "- URLDesc:", $PageItem["BannerImageUrl"].Description Write-Host " >> ContentTypeId:", $PageItem["ContentTypeId"] if ($ModifiedByuser.LookupId -ne $CreatedByuser.LookupId) { Write-Host " ===> Modified by:", $ModifiedByuser.LookupValue, " - ", $ModifiedByuser.Email ,"[", $ModifiedByuser.LookupId, "]" -ForegroundColor green Write-Host " ===> Created by:", $CreatedByuser.LookupValue, " - ", $CreatedByuser.Email ,"[", $CreatedByuser.LookupId, "]" -ForegroundColor green if($ModifiedByuser.Email -ne "") { $MyTempEmailAddress = $ModifiedByuser.Email } else { $MyTempEmailAddress = $DefaultEmailAddress #Admin Account to reset with the default one } $MyEditoruserAccount = $MyspoRootwebTemp.EnsureUser("i:0#.f|membership|$($MyTempEmailAddress)"); $MyctxTemp.load($MyEditoruserAccount) $MyctxTemp.executeQuery() Write-Host " ===> Modified Account Login:", $MyEditoruserAccount.LoginName -ForegroundColor Magenta $PageItem["Modified"] = $modifiedDate; $PageItem["Created"] = $modifiedDate; $PageItem["FirstPublishedDate"] = $modifiedDate; $PageItem["Created_x0020_By"] = $MyEditoruserAccount.LoginName $PageItem["Modified_x0020_By"] = $MyEditoruserAccount.LoginName $PageItem["Editor"] = $MyEditoruserAccount.Id; $PageItem["Author"] = $MyEditoruserAccount.Id $PageItem.Update() $MyctxTemp.ExecuteQuery() } else { Write-Host " ===> Modified by:", $ModifiedByuser.LookupValue, " - ", $ModifiedByuser.Email ,"[", $ModifiedByuser.LookupId, "]" -ForegroundColor red Write-Host " ===> Created by:", $CreatedByuser.LookupValue, " - ", $CreatedByuser.Email ,"[", $CreatedByuser.LookupId, "]" -ForegroundColor red if($ModifiedByuser.Email -eq "") #Admin Account to reset with the default one { $MyTempEmailAddress = $DefaultEmailAddress $MyEditoruserAccount = $MyspoRootwebTemp.EnsureUser("i:0#.f|membership|$($MyTempEmailAddress)"); $MyctxTemp.load($MyEditoruserAccount) $MyctxTemp.executeQuery() Write-Host " ===> Modified Account Login:", $MyEditoruserAccount.LoginName -ForegroundColor Magenta $PageItem["Modified"] = $modifiedDate; $PageItem["Created"] = $modifiedDate; $PageItem["FirstPublishedDate"] = $modifiedDate; $PageItem["Created_x0020_By"] = $MyEditoruserAccount.LoginName $PageItem["Modified_x0020_By"] = $MyEditoruserAccount.LoginName $PageItem["Editor"] = $MyEditoruserAccount.Id; $PageItem["Author"] = $MyEditoruserAccount.Id $PageItem.Update() $MyctxTemp.ExecuteQuery() } } Write-Host " --------------------------------------------------------- " } } # --------------------------------------------------------------------------------------------------------------- Load-DLLandAssemblies #get and save your O365 credentials [string]$username = "adminAccount@[yourtenant].onmicrosoft.com" [string]$PwdTXTPath = "C:\SECUREDPWD\ExportedPWD-$($username).txt" $secureStringPwd = ConvertTo-SecureString -string (Get-Content $PwdTXTPath) $cred = New-Object System.Management.Automation.PSCredential -ArgumentList $username, $secureStringPwd #connect to the web site using the stored credentials Write-host " " Write-host " -------------------------------------------------------------------------------------------- " -ForegroundColor green Write-host " ---- CONNECT THE SITE --- " -ForegroundColor green Write-host " CONNECTED SITE:", $SitePagesURL -ForegroundColor Yellow $Myctx = New-Object Microsoft.SharePoint.Client.ClientContext($SitePagesURL) $Myctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($cred.UserName,$cred.Password) $Myctx.RequestTimeout = 1000000 # milliseconds $MyspoRootweb = $Myctx.Web $Myctx.Load($MyspoRootweb) $Myctx.ExecuteQuery() Write-Host " " Write-Host " ---------------------------------------------------------" Write-Host " >>>> # Server Version:" $Myctx.ServerVersion " # <<<<<<" -ForegroundColor Green Write-Host " ---------------------------------------------------------" Write-Host " " Write-host " -------------------------------------------------------- " Write-host " -->> RootSite:", $MyspoRootweb.URL -ForegroundColor green Write-host " " Get-All-PageList-UpdateMetadata $Myctx $MyspoRootweb
Thanks all for your help.
French version of the article:
Fabrice Romelard
If you are looking into updating the Created By / Modified By fields, that is actually possible with PowerShell / CSOM with the sufficient permissions. See the following blog post for additional details.
Would that solve your scenario?
Dear Vesa,
I tried to follow your recommendation via the script I wrote:
[string]$SitePagesURL ="https://tenant.sharepoint.com/sites/SiteCollectionurl/subsite/" [string]$PageLibPublicName = "Site Pages" [int]$TempUserID = 0 function Load-DLLandAssemblies { [string]$defaultDLLPath = "" # Load assemblies to PowerShell session $defaultDLLPath = "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.dll" [System.Reflection.Assembly]::LoadFile($defaultDLLPath) $defaultDLLPath = "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.Runtime.dll" [System.Reflection.Assembly]::LoadFile($defaultDLLPath) $defaultDLLPath = "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.Online.SharePoint.Client.Tenant.dll" [System.Reflection.Assembly]::LoadFile($defaultDLLPath) } Load-DLLandAssemblies #get and save your O365 credentials [string]$username = "AdminLogin@tenant.onmicrosoft.com" [string]$PwdTXTPath = "C:\SECUREDPWD\ExportedPWD-$($username).txt" $secureStringPwd = ConvertTo-SecureString -string (Get-Content $PwdTXTPath) $cred = New-Object System.Management.Automation.PSCredential -ArgumentList $username, $secureStringPwd # --------------------------------------------------------------------------------------------------------------- #connect to the web site using the stored credentials Write-host " " Write-host " -------------------------------------------------------------------------------------------- " -ForegroundColor green Write-host " ---- CONNECT THE SITE --- " -ForegroundColor green Write-host " CONNECTED SITE:", $SitePagesURL -ForegroundColor Yellow $Myctx = New-Object Microsoft.SharePoint.Client.ClientContext($SitePagesURL) $Myctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($cred.UserName,$cred.Password) $Myctx.RequestTimeout = 1000000 # milliseconds $MyspoRootweb = $Myctx.Web $Myctx.Load($MyspoRootweb) $Myctx.ExecuteQuery() Write-Host " " Write-Host " ---------------------------------------------------------" Write-Host " >>>> # Server Version:" $Myctx.ServerVersion " # <<<<<<" -ForegroundColor Green Write-Host " ---------------------------------------------------------" Write-Host " " Write-host " -------------------------------------------------------- " Write-host " -->> RootSite:", $MyspoRootweb.URL -ForegroundColor green Write-host " " $Alllists = $MyspoRootweb.Lists $MyPagelist = $Alllists.GetByTitle($PageLibPublicName) $MyPagelistItems = $MyPagelist.GetItems([Microsoft.SharePoint.Client.CamlQuery]::CreateAllItemsQuery()) $Myctx.load($MyPagelistItems) $Myctx.executeQuery() foreach($PageItem in $MyPagelistItems) { Write-Host "ID:", $PageItem["ID"], " - Title:", $PageItem["Title"] $ModifiedByuser = $PageItem["Editor"] $CreatedByuser = $PageItem["Author"] if ($ModifiedByuser.LookupId -eq $CreatedByuser.LookupId) { Write-Host " ===> Modified by: ", $ModifiedByuser.LookupValue, "[", $ModifiedByuser.LookupId, "] - Created by:", $CreatedByuser.LookupValue, "[", $CreatedByuser.LookupId, "]" -ForegroundColor Yellow } else { $TempUserID = $ModifiedByuser.LookupId Write-Host " ===> Modified by: ", $ModifiedByuser.LookupValue, "[", $ModifiedByuser.LookupId, "] - Created by:", $CreatedByuser.LookupValue, "[", $CreatedByuser.LookupId, "]" -ForegroundColor Red $PageItem["Author"] = $TempUserID $PageItem.Update(); $Myctx.ExecuteQuery(); } }
But nothing change at the "Created by" level, the change is apply for the "modified by" with my admin account used for that script execution.
So my issue is not solved and i'm showing only the script execution account as news publisher.
Thanks for your help if you have another way.
Fab
- VesaJuvonenAug 16, 2018
Microsoft
Seems that your user resolution is not properly done as following did work without any issues. I just tested it in a normal E3 tenant in production. Just used the basic code provided from the blog post for proof of concept.
// Get pwd from environment variable, so that we do not need to show that. string pwd = "pwdfortheaccount"; // Get access to source site using (var ctx = new ClientContext("https://contoso.sharepoint.com/sites/OverrideTest")) { //Provide count and pwd for connecting to the source var passWord = new SecureString(); foreach (char c in pwd.ToCharArray()) passWord.AppendChar(c); ctx.Credentials = new SharePointOnlineCredentials("whatever@contoso.onmicrosoft.com", passWord); var list = ctx.Web.GetList("/sites/OverrideTest/SitePages"); ListItem item = list.GetItemById(1); ctx.Load(item); ctx.ExecuteQuery(); User user = ctx.Web.EnsureUser("i:0#.f|membership|admin@contoso.onmicrosoft.com"); ctx.Load(user); ctx.ExecuteQuery(); item["Modified"] = DateTime.Now.AddYears(-5); item["Created"] = DateTime.Now.AddYears(-5); item["Editor"] = user.Id; item["Author"] = user.Id; item.Update(); ctx.ExecuteQuery(); }
This resulted updated values as seen in this picture below... with updated values for Modified By and Created By.... I was using my personal account (Vesa) and set the account for the item as "James Admin". I would simply double check the identity values in your tenant for the person using, for example, user profile admin UIs... and then test things in simplistic code like above... before making the script too complex. First POC - then polishing.
- FromelardAug 16, 2018Iron Contributor
Dear Vesa,
Thanks for your message, I tried to use it adapting to my case, but that is driving me mad.
Now I can confirm the xxx.update() is changing the title, but nothing related to the Author
The adapted code is the following one:
[string]$SitePagesURL ="https://tenant.sharepoint.com/sites/SiteCollection/Subsite/" [string]$PageLibPublicName = "Site Pages" [int]$TempUserID = 0 function Load-DLLandAssemblies { [string]$defaultDLLPath = "" # Load assemblies to PowerShell session $defaultDLLPath = "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.dll" [System.Reflection.Assembly]::LoadFile($defaultDLLPath) $defaultDLLPath = "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.SharePoint.Client.Runtime.dll" [System.Reflection.Assembly]::LoadFile($defaultDLLPath) $defaultDLLPath = "C:\Program Files\SharePoint Online Management Shell\Microsoft.Online.SharePoint.PowerShell\Microsoft.Online.SharePoint.Client.Tenant.dll" [System.Reflection.Assembly]::LoadFile($defaultDLLPath) } Load-DLLandAssemblies #get and save your O365 credentials [string]$username = "login@tenant.onmicrosoft.com" [string]$PwdTXTPath = "C:\SECUREDPWD\ExportedPWD-$($username).txt" $secureStringPwd = ConvertTo-SecureString -string (Get-Content $PwdTXTPath) $cred = New-Object System.Management.Automation.PSCredential -ArgumentList $username, $secureStringPwd # --------------------------------------------------------------------------------------------------------------- #connect to the web site using the stored credentials Write-host " " Write-host " -------------------------------------------------------------------------------------------- " -ForegroundColor green Write-host " ---- CONNECT THE SITE --- " -ForegroundColor green Write-host " CONNECTED SITE:", $SitePagesURL -ForegroundColor Yellow $Myctx = New-Object Microsoft.SharePoint.Client.ClientContext($SitePagesURL) $Myctx.Credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($cred.UserName,$cred.Password) $Myctx.RequestTimeout = 1000000 # milliseconds $MyspoRootweb = $Myctx.Web $Myctx.Load($MyspoRootweb) $Myctx.ExecuteQuery() Write-Host " " Write-Host " ---------------------------------------------------------" Write-Host " >>>> # Server Version:" $Myctx.ServerVersion " # <<<<<<" -ForegroundColor Green Write-Host " ---------------------------------------------------------" Write-Host " " Write-host " -------------------------------------------------------- " Write-host " -->> RootSite:", $MyspoRootweb.URL -ForegroundColor green Write-host " " $Alllists = $MyspoRootweb.Lists $MyPagelist = $Alllists.GetByTitle($PageLibPublicName) $MyPagelistItems = $MyPagelist.GetItems([Microsoft.SharePoint.Client.CamlQuery]::CreateAllItemsQuery()) $Myctx.load($MyPagelistItems) $Myctx.executeQuery() foreach($PageItem in $MyPagelistItems) { $ModifiedByuser = $PageItem["Editor"] $CreatedByuser = $PageItem["Author"] $MyEditoruserAccount = $MyspoRootweb.EnsureUser($ModifiedByuser.LookupValue); $Myctx.load($MyEditoruserAccount) $Myctx.executeQuery() if ($ModifiedByuser.LookupId -eq $CreatedByuser.LookupId) { #Write-Host " ===> Modified by:", $ModifiedByuser.LookupValue, "[", $ModifiedByuser.LookupId, "] - Created by:", $CreatedByuser.LookupValue, "[", $CreatedByuser.LookupId, "]" -ForegroundColor Yellow #Write-Host " ===> Modified Account Login:", $MyEditoruserAccount.LoginName -ForegroundColor Magenta } else { Write-Host "ID:", $PageItem["ID"], " - Title:", $PageItem["Title"] Write-Host " ===> Modified by:", $ModifiedByuser.LookupValue, "[", $ModifiedByuser.LookupId, "] - Created by:", $CreatedByuser.LookupValue, "[", $CreatedByuser.LookupId, "]" -ForegroundColor Red Write-Host " ===> Modified Account Login:", $MyEditoruserAccount.LoginName -ForegroundColor Magenta $PageItem["Title"] = $PageItem["Title"] + " XXXXX" # < CHANGE IS OK $PageItem["Author"] = $MyEditoruserAccount.Id # < CHANGE IS NOT OK !!! $PageItem.Update() $Myctx.ExecuteQuery() } }
But are you sure that option is sustainable for the future, because I will have to execute that script each time someone modify one of the help page, and I have no idea when they will do that.
Is there any plan to evaluate the showing of the Modified by instead ?
Fab
- Joe McGowanAug 21, 2018Iron Contributor
I'm also trying to find a way to modify the "Author" of a News article. Came across this post on the Uservoice site and it's flagged as "in the plans". Promising, but I guess we can't expect much until it hits the Office 365 Roadmap. https://sharepoint.uservoice.com/forums/329214-sites-and-collaboration/suggestions/31875550-change-page-or-news-page-authors