Monday, March 20, 2017

Users break dispform

In this particular case a user decided to throw away the main webpart of the dispform.aspx. This caused all kinds of problems since it was a calenderlist.
In this situationen it wasn’t possible to create a new list since a lot of coded things worked with listids. Theres no versioning of dispform.
I first tried to use restore to sitedefinitions, but this kind of change didn’t qualify for making restore for some reason.
So this what came instead.

1. Find a functioning “vanilla” dispform.aspx from a list of same type
2. Open Sharepoint Designer, goto website of above list, find list and copy ListID
3. Find dispform of functioning list and export to file. Save locally on computer
4. Open locally saved dispform with notepad. Find and replace listid of sourcelist with targetlist listID (in calenderar two matches)
5. Opensharepint Designer, goto website of broken list.
6. All Files>Lists><mylist>
7. Take a backup of dispform with export to file, just in case you mess up.
8. Import locally modified dispform
9. Changes take effect immediatly.

References:
https://support.microsoft.com/en-hk/help/2000858/listview-webpart-required-on-dispform.aspx-page-for-document-library

Thursday, July 21, 2016

Script for updating URLrewrite-rules

In these days urlrewrite might not be the first choice for using but I had a situationen where i needed to be applied.
This offered an oppertunity to create a script to automate the addition of custom rules sets for a specific file.
And I got to fiddle with xml-manipulation which is also fun.

This script reads an csv for inputaddress and ruleTemplate file
csv – addresses.csv
ruleName,target,destination
tomteland,www.tomteland.se,tomteland

rewriteTemplate.xml


<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>              
        <rule name="#RuleName#_RevProxyInbound" enabled="true" stopProcessing="true">
                    <match url="(.*)" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                        <add input="{HTTP_HOST}" pattern="#target#" />
                    </conditions>
                    <serverVariables>
                        <set name="HTTP_ACCEPT_ENCODING" value="" />
                    </serverVariables>
                    <action type="Rewrite" url="http://mysite.company.com/#destination#/{R:1}" />
                </rule>
            </rules>
    <outboundRules>
                <rule name="#RuleName#_Links_To_Pages" preCondition="ResponseIsHtml" enabled="true">
                    <match filterByTags="A" pattern="(.*)/#destination#/(.*).aspx" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="true" />
                    <action type="Rewrite" value="http://#target#/{R:2}.aspx" />
                </rule>
                <rule name="#RuleName#_Response_Location_querystring" preCondition="ResponseIsHtml" enabled="true">
                    <match serverVariable="RESPONSE_LOCATION" pattern="http://mysite.company.com/#destination#/(.*)" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="true">
            <add input="{URL}" negate="true" pattern="\.axd$" />
            </conditions>
                    <action type="Rewrite" value="{R:1}" />
                </rule>
    </outboundRules>
        </rewrite>
    </system.webServer>
</configuration>
Powershellscript, this script reads web.config from designated place makes a copy to $workingDir where updates are applied to file in $workingDir. This a pretty specific script, for example outboundRules are applied before <precondition>-tagg, but haven’t really experimented what happens if the preconditions aren’t there for example. So better to use as inspiration rather than copy paste. Also rewriteRules will need to be adjusted for your particuliar scenarios. Matching for rules to change are made with replace() towards #rulename#, #destination and #target# cause this was easier for this scenario.

#created:2016-07-21
#lastModified:2016-07-21
#Desc: Adds rewrite rules for washers, only adds if rules doesn't exist.

$sourceFileRewrite = "C:\Eriks\Powershell\Prod_rewriteUrlsUpdate\From\rewrite\web.config"
$scriptPath = $(split-path -Parent $myinvocation.MyCommand.Definition)
$AddressUrlFile = "$($scriptPath)\Addresses.csv"
$workingDir = "$($scriptPath)\UpdatedFiles"
$templateRuleFile = "$($scriptPath)\rewriteTemplate.xml"
#step1 copy files
try {
    $fileRewrite = "$($workingDir)\$(($sourceFileRewrite.Split('\'))[-2])\$(($sourceFileRewrite.Split('\'))[-1])"
    if ((Test-Path -Pathtype container -Path $($fileRewrite.TrimEnd("web.config"))) -eq $false)
    {
        New-Item -ItemType directory -Force -Path $($fileRewrite.TrimEnd("web.config")) |Out-Null
    }
    Copy-Item -Path $sourceFileRewrite -Destination $fileRewrite   
    write-output "Copied to $($fileRewrite) successfully"
    }
catch {}
#step2 read files
[xml]$xmlRewriteInput = (Get-Content $fileRewrite -ReadCount -1)
$AddressCollection = import-csv -Delimiter "," -Path $AddressUrlFile -Encoding UTF8
$templateRuleStatic = (Get-Content $templateRuleFile -raw)
$rewriteNodes = $xmlRewriteInput.configuration.'system.webServer'
foreach ($address in $AddressCollection ) {
    write-output "Processing $($address.rulename)..."
    #step3 update files   
    $tempTemplate = $templateRuleStatic.Replace("#RuleName#",$address.ruleName).Replace("#destination#",$address.destination).replace("#target#",$address.target)
    [xml]$tempXMLTemplate = $tempTemplate   
    foreach ($node in $tempXMLTemplate.configuration.'system.webServer'.rewrite.rules.rule) {
        #check if exists
        if ($rewriteNodes.rewrite.rules.rule.name -notcontains $node.name) {
            $extractedInbound = $xmlRewriteInput.ImportNode(($node),$true)     
            $xmlRewriteInput.configuration.'system.webserver'.rewrite.rules.AppendChild($extractedInbound)  |Out-Null
            }
        else { write-warning "Rule $($node.name) already exists in file! Skipping..." }
        }
    foreach ($secondNode in $tempXMLTemplate.configuration.'system.webServer'.rewrite.outboundRules.rule) {
        if ($rewritenodes.rewrite.outboundRules.rule.name -notcontains $secondNode.name) {
            $extractedInbound = $xmlRewriteInput.ImportNode(($secondNode),$true)                     
            $before = $xmlrewriteInput.configuration.'system.webserver'.rewrite.outboundrules.preconditions
            $xmlRewriteInput.configuration.'system.webserver'.rewrite.outboundRules.InsertBefore($extractedInbound,$before) |out-null          
            }
        else { write-warning "Rule $($secondNode.name) already exists in file! Skipping..." }
        }
    try {
        $xmlRewriteInput.save($fileRewrite)
        write-output "Updated file saved to $fileRewrite"
        }
    catch {}
}

Monday, July 18, 2016

Finding duplicate content types

Got to a chance to build a script for finding content type duplicates in a Site Collection

#Description:Find duplicate content types in sitecollection

Add-PSSnapin microsoft.sharepoint.powershell

$siteURL = "https://test-mysite.mycompany.com"
$site = get-spsite $siteURL
$object = @()

foreach ($newWeb in $site.AllWebs) {
    foreach ($cType in $newWeb.contentTypes) {
        $tempObj = new-object PSObject -Property @{webURL=$newWeb.Url; CTName=$cType.Name; CTid=$cType.Id}
        $object += $tempObj
        }
    }
#sort out duplicates
$duplicates = $object |Group-Object ctid |Where-Object {$_.count -gt 1} |select -ExpandProperty group

write-output "Found $($object.count) content types in $siteURL"
write-output "Total duplicates: $($duplicates.count)"
$duplicates

Monday, March 14, 2016

Urlrewrite and sharepoint 2013

I some trouble recently getting urlrewrite to play along with SP2013 and IIS 8.
HostNamedSiteCollection wasn’t doable for a lot of other reasons.
Scenario: Using urlrewrites to handle vanityurls for site collections in SP2007. When migrating to SP2013 these rules don’t work at all.

Symptoms: image

  • With outboundrules activated nothing works for webapplication. (Website cannot display the page) 
  • Always lands on top site collection, rewrite dont work.

Solution:
A combination of a few fixes

  • Install patch kb2749660 to fix outboundrules
  • Add registry key ("HKLM:\Software\Microsoft\InetStp\Rewrite\" (Dword) LogRewrittenUrlEnabled=0)
  • Deactivate Static Compression on IIS-site
  • Add rewrite rule like {HTTP_HOST} matches pattern ^www.labb13.nu(.*)

image

References:
https://support.microsoft.com/en-us/kb/2749660 - Response is corrupted when you configure an outgoing rule in URL Rewrite Module 2.0 for IIS 7.0 or IIS 7.5
http://ruslany.net/2010/05/storing-url-rewrite-mappings-in-a-separate-file/
https://blogs.msdn.microsoft.com/danielvl/2010/01/07/registry-values-for-iis-url-rewrite/
http://stackoverflow.com/questions/14607390/iis-urlrewrite-is-not-working-for-iis-8
http://social.technet.microsoft.com/wiki/contents/articles/23074.sharepoint-2013-url-rewrite.aspx
http://blogs.technet.com/b/mspfe/archive/2013/11/27/how-to-create-a-url-alias-using-iis-url-rewrite.aspx – example (without sharepoint)
http://johnliu.net/blog/2010/7/23/sharepoint-2010-with-iis-url-rewrite-20.html
https://www.iis.net/learn/extensions/url-rewrite-module/creating-outbound-rules-for-url-rewrite-module
https://social.technet.microsoft.com/Forums/en-US/8d076df4-d93c-47be-80cc-f22dddc1c4c8/how-to-implement-url-rewriting-for-sharepoint?forum=sharepointdevelopmentprevious – similar problem. http://blog.mastykarz.nl/friendly-urls-sharepoint-site-4-steps-iis7-url-rewrite-module/ - rewrite with sharepoint 2013
http://blogs.msdn.com/b/chiranth/archive/2014/06/13/url-rewrite-part-3-outbound-rules-amp-rewrite-maps.aspx - why use outbound rules?
https://support.microsoft.com/sv-se/kb/2818415 - supported assymentrical solutions

Tuesday, January 26, 2016

A fun script for creating meetings in Outlook.
For the very lazy admin

function createMeeting(
    $Subject = "Test Meeting",
    $body = "Just Testing",
    $location = "Here",
    $start = "1/26/2016 12:00 PM",
    $duration = 60,
    $reminderSet = $true,
    $reminderMinutesBeforeStart = 15
    )
{
$olAppointmentItem = 1
$o = New-Object -comobject outlook.application
$a = $o.CreateItem($olAppointmentItem)
$a.MeetingStatus.olMeeting
$a.Start = $start
$a.Duration = $duration
$a.Subject = $Subject
$a.body = $body
$a.Location = $location
$a.ReminderMinutesBeforeStart = $reminderMinutesBeforeStart
$a.reminderSet = $true
$result = $a.Save()
}


$Duration = 60
$Subject = "Test1"
$Body = "testar2"
$Location="Office"
$ReminderMinutesBeforeStart = 30
$reminderSet = $true

#This scenario is set of dates with same meeting time
$arrayOfDates =  "2016-01-26", "2016-01-27","2016-01-28"
$hour = 10
$minute = 00
for ($i=0;$i -le ($ArrayOfDates.count-1);$i++) {
$dateTime = get-date $ArrayOfDates[$i] -Hour $hour -Minute $minute
$dateTime
createMeeting -Subject $Subject -body $Body -location $Location -reminderSet $reminderSet -reminderMinutesBeforeStart $ReminderMinutesBeforeStart -duration $Duration -start $dateTime
}

References:
https://social.technet.microsoft.com/Forums/windowsserver/en-US/077733c5-6e39-489e-908a-d3098c512a71/need-to-create-meeting-request-using-powershell-with-outlook-2010-2013?forum=winserverpowershell

Thursday, November 05, 2015

Script to check databases upgrade status

Ever wonder how the upgrade is doing?
This script checks the status of all Sharepoint-databases.

 

Add-PSSnapin microsoft.sharepoint.powershell

 

$alldatabases  = get-spdatabase

write-output "Status for all databases"

$alldatabases |Sort-Object needsupgrade |select name,webapplication, canupgrade,needsupgrade |format-table -autosize

write-output "Databases needs upgrade: $(($alldatabases |where-object {$_.needsupgrade -like "true"}).count)"

write-output "Timestamp: $(get-date -format "yyyy-MM-dd.HH:mm")"

Tuesday, September 29, 2015

Speedier scripts

Some notes on how to handle output from scripts.
I did some comparing on how much extra time is added on executiontime just by using write-output to display information to users.
While running script below these are the results:

With write-output uncommented : 11,8s
With write-progress uncommented: 3,9s
With no output: 0,9s

$global:varNumb = 1

function foo {

 #write-host "entering function 'foo'"

 write-verbose "entering function 'foo'"

 $global:varNumb++

 #write-host "completed function 'foo' $varnumb"

 write-verbose "completed function 'foo' $global:varnumb"

}

 

$startTime = get-date

write-host "script started at $startime"

$cycles = 1000

$i = 1

1..$cycles | foreach {

 #write-progress -Activity "Processing..." -CurrentOperation "Working on $i"

 #write-output "Testar $i"

 $i++

foo

}

 

$endtime = get-date

write-host "Done in $(($endtime-$starttime).totalseconds) seconds"

 

$global:varNumb

 

Its amazing how much time is added just by adding write-output.
But as seen in this example write-progress is prefererred, if anything.
I suggest using write-verbose instead of write-output and changing $verbosepreference = Continue when needed. This is by default SilentlyContinue

 

References:
http://blogs.technet.com/b/kpalmvig/archive/2013/10/29/easy-progress-bar-in-your-powershell-scripts.aspx