Showing posts with label code. Show all posts
Showing posts with label code. Show all posts

Tuesday, June 1, 2010

How to update file / list item without harming the metadata & some more tips...

Nice thing I came across with the other day -

If you need to modify some SPListItem fields in lists, but you want to keep the "modified by" or "Last modified by" as it were before you can just use the method SystemUpdate ( which is exactly like Update but without changing those fields ).

 
using (SPSite site = new SPSite("http://blah-blah.com"))
{
using(SPWeb web = site.OpenWeb())
{
SPList list = web.Lists["ListName"];
foreach(SPListItem item in list.Items)
{

item["Title"] = "*changed*";
item.SystemUpdate();
}
}
}

Another cool thing is :

You have two different "last modified" for a SPListItem. What do I mean by that ? you have the SPListItem["Last Modified"] value and you have also under SPListItem.File.TimeLastModified. In other words, you can see when the file that corresponds with that item were last used and not assuming that date from the "last modified" value that you have in the list.

 
public DateTime TimeLastModified { get; }


Last cool thing, promise , for this post :

When you want to upload a file to a list ( adding it to a file collection of that list ) you can you use a really strong overload under SPFileCollection -
 

public SPFile Add (
string urlOfFile,
Stream stream,
SPUser createdBy,
SPUser modifiedBy,
DateTime timeCreated,
DateTime timeLastModified
)


With this overload you determine what will be the createdBy,modifiedBy,timeCreated,timeLast modified...

This is very useful when you need to download/upload files from/to you SharePoint sites ( dont forget of course to backup metadata before using that so you will be able to retrieve it back :) )

Sunday, December 13, 2009

SPWeb.Users and SPWeb.SiteUsers are the same ?

Well no.

Sounds very close, but there is a difference.


SPWeb.Users - Property returns collection of user objects that are explicitly assigned permissions.


SPWeb.SiteUsers - Property returns collection of user objects that belong to site collection.

Thursday, November 12, 2009

CAML Vs Linq ? What's better ?

A few days ago I had a small project over one of my clients, one of the issues I had to deal with was to iterate via his enormous lists sizes, and to change specific MetaData according to other metadata that he already has.

Then I had to choose how I want to work with those lists

Why Linq ?

1. Easy for the .net developer, no new skills required.
2. intellisense. It's a great deal sometimes.
3. Easy to read your code, no loops, no "weird" syntax.
4. Can combine multiple sources ( XML files, SharePoint lists and databases and more ... )

Why NOT Linq ?

1.Performance issues in some cases. All items are being retrieved from the database.
The filtering phase is being done only by iterating via all all items and comparing them to the where clause. So in large collections we have a major performance problem with Linq.

An example, which we will use later again :

 
//checking if the column address is not empty and the column name equals tothe filter.
//and order it by PostedOn

var resourceListItems = fromSPListItemitemin resourceList.Items
whereitem[“Name”] .ToString().ToLower().Contains(_filter)
&& item[“Adress”].ToString().Length> 0
orderbyitem[“PostedOn”] ascending


Why CAMAL ?

1. Only items that match the filter criteria will be retrieved from the database.
2. There are some wizards to make it easier for writing those queries ( such as : LinqToSharepoint, Linq4SP and some more - just google those names ).

Why NOT CAMAL ?

1. Syntax. No intellisense. Not easy to read.
2. Problematic filtering, not as wide as you can get in Linq. If you want to compare two columns it's not feasible ( I might wrong on that ).

Same example as before in CAML :

 
//checking if the column address is not empty and the column name equals tothe filter.
//and order it by PostedOn, and at last calling getting the collection.

SPQueryquery = newSPQuery();
query.Query= String.Format(“

{0}




”, _filter);

SPListItemCollectionlistItemsColl= resourceList.GetItems(query);


And yes, you can use them both combined , for example :

 

SPQuerycamlQuery= newSPQuery();
camlQuery.Query= String.Format(“

{0}


”, _filter);
var resourceItemsCollection=fromSPListItemiteminresourceList.GetItems(camlQuery)
orderbyitem[“PostedOn”] ascending


Bottom line, it depends on what you need to do :

If you work on a very large collection, you better use CAMAL ( also recommended in the MSDN ).

If you work on a small collection , and the cost of iterating all collection is ok for you, Or when you need to make a certain filtering that requires iteration ( such as column comparison ) then choose Linq.

Enjoy.

Sunday, August 23, 2009

How to edit / update / change "Read Only" list columns

We had to retrieve data from our archive storage, back to the sharepoint sites. The problem was that when we retrieved the data back to the sharepoint doclib\lists we changed the Meta Data of our items ( such as Modified, Modified By ) which most of them appear as "read only" columns.

The solution - We had to interfere in our lists, and touch those "read only" columns \ Fields. Remembering we had to avoid changing those lists\Doclibs columns and keep the "old" metadata ( before the retrieval process ).

Code Snippet for how changing the data in the "read only" column such as "Modified" -

 

SPFieldCollection colFields = list.Fields;
for (int i = 0; i < colFields.Count; i++)
{
SPField field = colFields[i];
try
{

if (list.Fields[i].Title == "Modified")
{
if (field.ReadOnlyField)
{
list.Fields[i].ReadOnlyField = false;
for (int j = 0; j < list.Items.Count; j++)
{
SPListItem oListItem = list.Items[j];
DateTime date = (DateTime)oListItem[list.Fields[i].Title];

// updating the relvant stuff on that item.

oListItem[list.Fields[i].Title] = date;
oListItem.Update();

}
}
}
}
catch(Exception e)
{
Console.WriteLine(e.ToString());
}
}


You can play with it more, and adjust it. Hope you got the idea.

Good luck.

Monday, August 17, 2009

How to change Site theme programatically

Very simple -

 

using (SPSite oSite = new SPSite("http://Sharepoint_Site"))
{

// Open website
using (SPWeb oWeb = oSite.OpenWeb())
{
oWeb.ApplyTheme("Theme_name");
oWeb.Update();
}
}

Saturday, August 8, 2009

A memory problem using the SPListItemCollection - How to avoid it ?

In the last few weeks my main core is tracking down memory leaks in client Sharepoint applications. This issue might not be happening to all of you who using this Object/Class. but it does might have something to do with you if you work on large lists or "future to be" large lists.

Ok, SPListItemCollection is a very strong class. Why ? Only with him you can get in flat way all the files you have in the doclib/list, regardless how deep they are located in the doclib ( in whatsoever folder in it ). I saw people that in order to get all the files which located in the sub folders in their doblibs, they were doing some serious digging ( including inefficient recursive iteration to get all those files ). But I'm sure you say - but if I need to to know in which subfolder this file was in ? Easy, you can track it down with the URL property.

So where the problem begins ? When you need just to do something very simple and very common - Automatically/pragmatically adding files\data into a list.
Let's say I want to add an item for my list, in order to do so I need to get the collection , right ? But what if my collection is huge ? above 2000 items in the list ( Above the recommendation in the whitepapers ) ?
Yes, now you start getting the problem. By calling this huge collection repeatedly Im using a lot of memory, till I might even get "Out of memory" exception.

Some code snippet to make sure you got the scenario -
 

SPWeb web = site.OpenWeb();
SPList list = web.Lists["Shared Documents"];
//Getting all Items in the collection ,even though we dont really need it
SPListItemCollection itemCol = list.Items;
SPListItem listItem = itemCol.Add();
//Working on the listItem we just added
listItem["Field1"] = //adding metadata and so on
listItem.Update();



So what we do just to workaround this scenario ? we get the collection from a certain view, which will be "thinner" version or in other words not as big as the default set of the collection. In fact, we made a special view only for that.

Short example to make sure you got the idea again -

 

SPWeb web = site.OpenWeb();
SPList list = web.Lists["Shared Documents"];
//Getting only the view items - could be empty
SPListItemCollection itemCol = list.GetItems(FakeView);
SPListItem listItem = itemCol.Add();
//Working on the listItem we just added
listItem["Field1"] = //adding metadata again or whatsoever
listItem.Update();



Apparently , This issue is very well known, it's much greater than I thought when I first saw this issue over one of my clients. Even in the MSDN they made a clearafaction about it.

Another disclaimer, one of my colleagues asked me right away, does it mean we added those items to that certain view ? Well, No. Since we still added the data to the list, the view might present those items if they fall in his view criteria. I personally made a "dummy/fake" view so it will always present nothing.

Enjoy.

Tuesday, August 4, 2009

Sharepoint Designer - what did you do ? ACCESS DENIED !

Hey all,
A few weeks ago we created with the SPD ( Sharepoint Designer ) a workflow.
After deploying it in one of our Moss websites, we saw that every time when end users created a list - there was a problem to edit an item.

The weird thing was , that every attempt to edit an item, even with the system account or with our strong privileges users, we got an access denied page.

After doing some research, we narrowed down the issue to occur only in a certain version of Moss. Googling for a while led me to the February hotfix rollup kb961756 & kb961755 - that solve this issue.
But what can I do regarding the lists that already "damaged" ? I got to this code snippet that was published by Microsoft support -







 
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using System.Xml;

namespace CA_TestingHotfix
{
class Program
{
static void Main(string[] args)
{
FixField(args);
}

static void FixField(string[] args)
{
string RenderXMLPattenAttribute = "RenderXMLUsingPattern";

//Console.WriteLine("Please enter the URL of the site: (Press enter after typing):");
string weburl = args[0];

//Console.WriteLine("Please enter the Document Library Name: (Press enter after typing):");
string listName = args[1];
SPSite site = new SPSite(weburl);
SPWeb web = site.OpenWeb();
SPList list = web.Lists[listName];
SPField f = list.Fields.GetFieldByInternalName("PermMask");
string s = f.SchemaXml;
Console.WriteLine("schemaXml before: " + s);
XmlDocument xd = new XmlDocument();
xd.LoadXml(s);
XmlElement xe = xd.DocumentElement;
if (xe.Attributes[RenderXMLPattenAttribute] == null)
{
XmlAttribute attr = xd.CreateAttribute(RenderXMLPattenAttribute);
attr.Value = "TRUE";
xe.Attributes.Append(attr);
}
string strXml = xe.OuterXml;
Console.WriteLine("schemaXml after: " + strXml);
f.SchemaXml = strXml;
}

}
}



This console application just adds to your schema the attribute that is missing. We did some more research in order to understand what really happened. The thing is that somehow we made the list schema be unghosted on that Site Collection, which means that instead of taking the schema.xml for list from the WFE itself, it made a "new" copy of the schema.xml in the DB - literally a simple case of Unghosting.

We wanted to see if we can fix it without deploying SP2 or that hotfix, So in our virtual machine we decided to track that record down.

 

SELECT SiteId, Class,Scope,ContentTypeID,Version,NextChildByte,Definition, Size, ResourceDir,IsFromFeature
FROM ContentTypes
WHERE (ContentTypeId = XXXXXXXXXXXXXXXX)




To sum things up, You can deploy service pack 2 or just run the query above ( NOT SUPPORTED ), and in order to fix the lists that already affected, run the console application.

Hope it helps.

Sunday, June 21, 2009

How to Delete specific user from your sharepoint sites

Hey.

Small task I had today, to delete a certain user who left the company. They have a list in which they document all the workers who left, so I had to just check the list every day, see if there is anything new and if so, just delete that user from all SiteCollection - be aware, when you delete a user from the siteCollection it's enough, there is no need to drill down to each and each web site.

 


foreach (SPWebApplication webApp in service.WebApplications)
{
SPSiteCollection colSites = webApp.Sites;
for (int i = 0; i < colSites.Count; i++)
{
using (SPWeb web = colSites[i].OpenWeb())
{
SPUser user = web.SiteUsers[UserName_ToDelete];

try
{
if (!user.IsDomainGroup && !user.IsSiteAdmin)
{
web.SiteUsers.Remove(user.LoginName);
web.Update();
}
}
catch (Exception ex)
{
// user not found
}
}
colSites[i].Dispose();
}

}

Sunday, May 17, 2009

SPUserCollection is not including groups ? Or just a Bug in Wss 2.0 Object Model ?

Hey.

I wrote today a small application that need to track down all the administrators of all the websites in my client sharepoint farm ( and write the data into a list with the website url and etc ).

Anyway , just to remind you I was dealing with wss 2.0 ( 2003 ), I was doing very simple stuff as iterating via all web applications->SiteCollections->WebSites.
and then just wanted to get all the users ( including Active directory groups )

 
SPUserCollection colUser = web.AllUsers;
foreach(SPUser user in colUser)
{
// check if admin, update list and blah blah
}


Unfortunately, it gives you back only the regular users, and not the AD groups too. I know you have under the SPWeb object the groups/sitegroups property but both of them are for Sharepoint groups and not for AD group.
Moreover, Under the SPUser object you have the property IsDomainGroup ( like I cant ask for more ) - BUT sorry, it just gives you back regular users all the time, and just "ignore" the AD gorups.

What I did at the end ? Just made a query to the SQL table ( Not so good, cause It's not supported ) - I will try now with WebService, let's hope it work via that.

If you think i'm wrong or you have another idea. Please comment.

Tuesday, February 24, 2009

What is the diffrence between AllUsers, SiteUsers, Users ?

In the SPWeb object you have those three properties that sounds a like, but there is a slight diffrence between them -

AllUsers - Returns "almost" all users in the site collection ( including "guests" ).

SiteUsers - Returns all users that are really exist in the site collection, including groups such as "authenticated users" and system accounts and etc.

Users - Returns only the users that appear in that specific web site.

The explanation in the SDK wasn't clear enough ( as for now , before the service packs ).

If you want to check it yourself just run this short code and see the difference.

 

//Open a SPWeb object ( a site collection )

using (SPWeb web = colSites[i].OpenWeb())
{
SPUserCollection colUsers = web.Users;
foreach (SPUser user in colUsers)
{
Console.WriteLine(user.Name);
}
Console.WriteLine("=======================");
SPUserCollection colUsers1 = web.SiteUsers;

foreach (SPUser user in colUsers1)
{
Console.WriteLine(user.Name);
}
Console.WriteLine("=======================");
SPUserCollection colUsers2 = web.AllUsers;

foreach (SPUser user in colUsers2)
{
Console.WriteLine(user.Name);
}


}