Setting ForceCheckout on an SPList - sharepoint

I'm trying to set the ForceCheckout property on an SPList item and it's just not taking. I'm calling the Update() command as required. All it should take, in essence, is the following two lines.
$myList.ForceCheckout = $false
$myList.Update()
Any ideas why this isn't working? It's remains $true no matter what.

Are you really using $myList, or are you doing something like:
$web.lists["foo"].forcecheckout = $false
$web.lists["foo"].update()
...because the above won't work. Each time you use the Lists collection with an indexer like this, you're getting a new instance of the list. The second line doesn't know about the first line's changes. Ensure you do:
$myList = $web.Lists["foo"]
$myList.forcecheckout = $false
$myList.update()
This will work because you're using the same instance.
-Oisin

Related

How can a Powershell function specify a ComObject parameter type?

Let's say that I'm trying to write a Powershell function that prints a result set to an Excel worksheet, like this:
function Write-ToWorksheet {
param (
[Parameter( Position = 0, Mandatory = $true )]
[MyLibrary.MyCustomResultType[]]
$ResultSet,
[Parameter( Position = 1, Mandatory = $true )]
[Excel.Worksheet]
$Worksheet
)
# ... Implementation goes here ...
}
And let's say that I'm calling it in a way something like this:
$excel = New-Object -ComObject Excel.Application
$wb = $excel.Workbooks.Add()
$results = Get-MyResults # Never mind what this does.
Write-ToWorksheet -ResultSet $results -Worksheet $wb.Sheets[ 1 ]
And this code will almost work, except that it chokes on my type specification of [Excel.Worksheet].
I realize that it is not necessary to specify the parameter type, and that the code will work just fine without it, as this answer points out.
But to please my inner pedant, is there any way to constrain the parameter type using a reference to a COM object type like Excel.Worksheet?
The reason that PowerShell is complaining about your Excel.Worksheet type is because it's not the name of the true .NET class/interface.
The parameter type you'd need to specify is Microsoft.Office.Interop.Excel.Worksheet instead (once the Excel interop assembly has been loaded, either directly via Add-Type or after the call to New-Object -ComObject Excel.Application as that will load the desired library too)
With that said, I don't believe this will work as intended because of the way that PowerShell handles COM objects by creating a transparent COM adapter layer between the true type of the variable exposed in PowerShell.
Interestingly there appear to be differences in the way that PowerShell handles parameter conversions when supplying them via Named arguments vs Positional arguments as can be seen with my demo code below:
function Get-WorksheetName {
param (
[Parameter( Position = 1, Mandatory = $true )]
[Microsoft.Office.Interop.Excel.Worksheet]
$Worksheet
)
return $Worksheet.Name
}
Calling the function using named arguments fails:
Whereas calling the function via positional arguments works as expected:
If positional arguments aren't something you'd like to use, then another alternative would be to drop the parameter type constraint and instead check the type using the ValidateScript attribute instead. This still ensures type safety:
function Get-WorksheetName {
param (
[Parameter(Position = 1, Mandatory = $true)]
[ValidateScript({$_ -is [Microsoft.Office.Interop.Excel.Worksheet]})]
$Worksheet
)
return $Worksheet.Name
}
Passing a different type of object would result in this:

Powershell Excel find value better performance alternative

I have to go through a loop in excel using the COM Object (no additional modules allow in environment aside from what comes installed with POSH 5).
In each loop I have to look through a worksheet (from a list of variables) for a particular set of values and pull and append data according to it.
My problem isnt so much accomplishing it, but rather the performance hit i get every time I do a Find Value2 in each worksheet.
With future expected massive increase of list of worksheets, and old ones with just more and more columns to parse through and work on in the future, how can I make this smoother and faster.
What I currently do is the following:
$Exl = New-Object -ComObject "Excel.Application"
$Exl.Visible = $false
$Exl.DisplayAlerts = $false
$WB = $Exl.Workbooks.Open($excel)
Foreach ($name in $names) {
$ws = $WB.worksheets | where {$_.name -like "*$name*"}
$range = $ws.Range("C:C")
$findstuff = $range.find($item)
$stuffrow = $findstuff.row
$stuffcolumn = $findstuff.column
}
This last part is what takes A LOT of time, and with each additional sheet and more columns I only see it growing, where it might take 10-20 mins
what can be done to optimize this?
On a side note: while I only need the one row and columnar results, there is also a slight issue with when finding value, it only shows the first result. If in the future there might be a need for the multiple rows and columns where value2 = $variable what should I do? (thats less important though, I asked in case if its related)
Anytime the pipeline is used, there's a performance hit. Instead of using the where object, try something like this (using an if statement):
foreach ($name in $names) {
$ws = if ($WB.worksheets.name -like "*$name*")
$range = Range("C:C")
$findstuff = $range.find($item)
$stuffrow = $findstuff.row
$stuffcolumn = $findstuff.column
}
Note that maybe your line has a typo for the part *where {$_.name -like "*$names*"}*. Maybe it should read *where {$_.name -like "*$name*"}*?
I found my basis from the following bookmark I had: http://community.idera.com/powershell/powershell_com_featured_blogs/b/tobias/posts/speeding-up-your-scripts
So I found a very simple answer.... which is somehow simultaneously EXTREMELY obvious and EXTREMELY unintuitive.
When defining the $range variable Add a pipe to select ONLY the stuff you need.
Instead of:
$range = $ws.Range("C:C")
do:
$range = $ws.Range("C:C") | Select Row, text, value2, column
Why is this unintuitive?
1) Normally Piping would make things slower especially if your pushing many to filter a few
2) One would expect that, especially since its going through the COM object, since it ACTUALLY runs the action when setting a variable rather than just defining. But that is not what happens here. When you set the Variable, it runs AFTER the variable has been defined and gathers the data THE MOMENT the variable is called [I tested this, and saw resource usage at that particular period only], and saves the data after that first variable call. (WHICH IS VERY WEIRD)

Pass a Microsoft.SharePoint.SPList to a function

I'm pretty new to powershell & sharepoint and i'm having hard time trying to create a function.
I'm trying to make a generic function to add new items into a SPList but i can't pass the SPList to the function.
Here is my function prototype:
function Add-IntoList([Microsoft.SharePoint.SPList] $List,[hashtable] $Columns)
{
... some code
}
And here is my call to the function:
$web = Get-Web("http://some_url/sandbox1") #It returns the Get-SPWeb
$test = #{"Title" = "Olympia"; "Body" = "Salem"}
Add-IntoList($web.Lists["Announcements"], $test)
And it doesn't work, i can't see why.
Here is the error powershell tells me:
Add-IntoList : Cannot process argument transformation on parameter 'List'. Cannnot convert the "System.Object[]" value of type "System.Object[]" to type "Microsoft.Sharepoint.SPList".
What am i doing wrong ?
Thanks in advance,
Nicolas
When you call the function, instead of calling it like:
Add-IntoList($web.Lists["Announcements"], $test)
call it like:
Add-IntoList $web.Lists["Announcements"] $test

powershell code for comparing two xls files

i'am stuck coding with below requirement.
I have two excel(xls) files(old and new users list). In each file there are 4 fields "Userid", "UserName", "Costcenter", Approving Manager" . Now, i need to check whether each Userid from New user list exists in Old user list. If so, i have to copy/replace the values of "Costcenter" and Approving Manager" in the New User list with the values from the same columns from Old user list. If this condition fails then hightlight the entire row for the "userid" in the New User List for which there is no corresponding matching record in the Old User list and finally not last but least we have to save the New user list. There are about 2000+ userid's
below, i started of coding to get the Userid list from the New user list into an Array. will be doing the same for Old user list. From there on how do i go by modifying the new user list like i explained above?
$objExcel = new-object -comobject excel.application
$UserWorkBook = $objExcel.Workbooks.Open("O:\UserCert\New_Users.xls")
$UserWorksheet = $UserWorkBook.Worksheets.Item(1)
$OldUsers = #()
$intRow = 2 #starting from 2 since i have to exclude the header
do {
$OldUsers = $UserWorksheet.Cells.Item($intRow, 1).Value()
$intRow++
} while ($UserWorksheet.Cells.Item($intRow,1).Value() -ne $null)
Any help Greatly appreciated...
If the userIDs in each list in some type of regular order, then it should be easy to open both workbooks up at the same time, maintain 2 pointers (Row_for_old_userlist and Row_for_new_userlist) and compare the contents of the New User ID with the old one.
If they aren't in some semblance of order, then for each item in the new userlist, you'll have to scan the entire old userlist to find them and then take your action.
I'm not sure that saving in CSV is a valid approach from your requirements - you may not be able to get the data that way.
However - I think you're really asking how to set the value of an Excel spreadsheet cell.
If so, here's some code I use to insert a list of defects into a spreadsheet...and then set the last cell's column A to a different color. Strangely enough, that "value2" part...it's non-negotiable. Don't ask me why it's "value2", but it is.
for ($x=0; $x -lt $defects.count; $x++)
{
$row++
[string] $ENGIssue = $defects[$x]
$sheet.Cells.Item($row,1).value2 = $ENGIssue
}
$range = $sheet.range(("A{0}" -f $row), ("B{0}" -f $row))
$range.select() | Out-Null
$range.Interior.ColorIndex=6

Powershell: Loading all items/properties into a new object

Take this code:
$logged_on_user = get-wmiobject win32_computersystem | select username
If I want to output the value into a new string I'd do something like:
$A = $logged_on_user.username
However, if I do the following:
$logged_on_user = get-wmiobject win32_computersystem | select *
..to try to assign all the values to a new "object", do I?:
$logged_on_user.items
$logged_on_user.value
$logged_on_user.text
$logged_on_user.propertry
I've tried them all and they don't work.
Anybody got any ideas?
Thanks
P.S. I think I may have got the title of this question wrong.
In your example:
$logged_on_user = get-wmiobject win32_computersystem | select username
creates a new PSCustomObject with a single property - username. When you do the following:
$A = $logged_on_user.username
you are assigning the return value of the PSCustomObject's username property to a variable $A. Because the return type of the username property is a string, $A will also be a string.
When executing the following:
$cs = get-wmiobject win32_computersystem
If you assign $cs to a new variable like in the following:
$newVariable = $cs
Then $newVariable will reference the same object $cs does, so all properties and methods that are accessible on $cs will also be accessible on $newVariable.
If you don't specify any properties or call any methods on an object when assigning a return value to another variable, then the return value is the object itself, not the return value of one of the object's properties or methods.
Additional info, but not directly related to the question:
When you pipe the output of get-wmiobject to select-object, like in the following:
$cs = get-wmiobject win32_computersystem | select-object *
The variable $cs is of type: PSCustomObject as opposed to ManagementObject (as it is when you do not pipe to Select-Object) which has all of the same properties and their values that the ManagementObject that was piped in did.
So, if you only want the property values contained by the ManagementObject, there is no need to pipe the output to Select-Object as this just creates a new object (of type PSCustomObject) with the values from the MangementObject. Select-Object is useful when you either want to select a subset of the properties of the object that is being piped in, or if you want to create a new PSCustomObject with different properties that are calculated through expressions.
I'm not sure if you're asking about copying the results of Get-WmiObject or PowerShell objects in general. In the former case, Get-WmiObject returns instances of the ManagementObject class, which implements the ICloneable interface that provides a Clone method. You can use it like this...
$computerSystem = Get-WmiObject -Class 'Win32_ComputerSystem';
$computerSystemCopy = $computerSystem.Clone();
After the above code executes, $computerSystem and $computerSystemCopy will be identical but completely separate ManagementObject instances. You can confirm this by running...
$areSameValue = $computerSystem -eq $computerSystemCopy;
$areSameInstance = [Object]::ReferenceEquals($computerSystem, $computerSystemCopy);
...and noting that $areSameValue is $true and $areSameInstance is $false.

Resources