Technical Fun

Archive for the ‘SharePoint’ Category

As usual I have a very unimportant need of getting the list of every site collections that a user has permissions on my company’s MOSS 2007 environment. The list is then to be passed to another application, for other not so important cause.

My company’s site collection structure looks like:
http://rootsite
http://rootsite/sites/site01
http://rootsite/sites/site02
– etc ….

So a the list needs to list those site where a user got permission to access. (user A –> site01, site02, … user B –> site01, etc)

So, assuming that each site collection defines user permissions directly (not inside a group, which happens to be the case in my environment), I wrote this cheap thing…

public List<string> GetValidSites(string sitecollectionname, string userloginname) { SPSite sitecol = new SPSite(sitecollectionname); List<string> siteList = new List<string>(); foreach (SPSite s in sitecol.WebApplication.Sites) { if (s.Url.Contains("/sites/")) { if (IsAllowedFor(userloginname, s.Url)) { siteList.Add(s.Url); } } } return siteList; }

You get the dumbest idea just by looking at it, right? Next, we dig further;

private bool IsAllowedFor(string userloginname, string siteurl) { bool result = false; SPSite site = new SPSite(siteurl); SPWeb web = site.OpenWeb(); try { SPUser usr = web.AllUsers[userloginname]; result = true; } catch { result = false; } return result; }

Host it on web service or DLL, or whatever.
Then, you can call something like this on your client app:

GetValidSites("http://rootsite","domainname\\username");

Happy? I am .. πŸ™‚

Advertisements

Well, not exactly removing, just crippling it so it won’t function. Hahah…

It all began with a guy in another department who got this persistent demand of getting rid of the single file upload button from a SharePoint document library upload menu. “We can upload single file from the multiple one, no need to have a single one.” He claimed. Hahah..

Given that I am a world-class-lazy-bum, I googled, but found that some working solutions applied globally towards the entire site collection, and not strictly to one doclib inside one site. Some claimed they have managed to do so by doing various tricks. But again, due to my acute laziness, I decided not to play around with Features, or even those object models, instead, I resorted to JavaScript.

What I did was described in Lazy 101; detect the Upload Menu by it’s many possible identifier (in this case i use its’ “text” attribute), and brutally disabled it. I cunningly inserted a content editor webpart in the page where the doclib tool bar was visible, and chuckled in an insane glee. Mwhuahahahhahuahahahah …

The script is as below:

<script type="text/javascript"> function GetElementByText(tagName, title) { var a = document.getElementsByTagName(tagName); for (var i=0; i < a.length; i++) { if (a[i].text) { if (a[i].text === title) { return a[i]; } } } return null; } if (window.onload) { var oLoad = window.onload; window.onload = function bodyLoad() { oLoad(); var o = GetElementByText("ie:menuitem","Upload Document"); if (o) { o.disabled = true; } } } </script>

[Update] Hey, guess what, a very kind guy has created a better tool for this (he’s even “featurize” it), check it here

Suddenly my team mate got this strange need to prevent folder creation inside MOSS document libraries. So I said “Aah .. “, and contemplated.

The logic should be simple, intercept the creation process, detect if the item being added to the doclib is really a folder, and cancel the event.
But the reality, the ItemAdding event wouldn’t let me do the detection process.

So, a brutal method was then picked πŸ™‚
I resorted to the ItemAdded thing, and unleashed this evil;

public override void ItemAdded(SPItemEventProperties properties) { if (properties.ListItem.ContentType.Name == "Folder") { properties.ErrorMessage = "You can't create folders"; properties.ListItem.Delete(); } }

It worked. so stupidly enough, this will wipe the folder that you just created after detecting that the item you just added was a folder.

My two cents guys, who knows this might bail you out of jail too! πŸ™‚

This one’s a sequel to my last post about LINQ-ize a SPListCollection.
I got another not-so-important urge to get a maximum number from one of my custom list’s field.
Let’s just say the the field containing the number is “Document Number”, and I want to grab the current maximum value of that field to do various purposes.

Now, as I am a little bit obsessed with Linq, so let’s leave CAML behind and grab Linq instead.
Let’s begin by creating a wrapper around SPListItemCollection object to make it Linq-able (just like I did with the SPListCollection).

public class SPListItemCollectionAdapter : List<SPListItem>
{
private SPListItemCollection _listItemCollection;

public SPListItemCollectionAdapter(SPListItemCollection listItemCollection)
{
_listItemCollection = listItemCollection;

Refresh();
}

private void Refresh()
{
this.Clear();

foreach (SPListItem item in _listItemCollection)
{
this.Add(item);
}
}

}

then voila!, you can use Linq’s aggregation function to grab the max value currently on a field called “Document Number” inside a list called “Document”. Write a method that might look like this:

private static double GetMaxFrom(string listname, string fieldname)
{
SPListItemCollection itemcol = mossWeb.Lists[listname].Items;
SPListItemCollectionAdapter itemsAdapter = new SPListItemCollectionAdapter(itemcol);
var result = itemsAdapter.Max(x => x[fieldname]);
return (double)result;
}

mossWeb is just a singleton for the SPWeb object, as I was using it numerous times on other methods as well.
I also took the liberty of using the lambda expression (x => x[fieldname]) just to evaluate the specific field.
Using it is as easy as calling GetMaxFrom("Document","Document Number");

After trying this LINQToSharePoint, I must admit I was a bit carried away in forcing Linq to my SharePoint programming needs.
I had this one very “not so important” ambition of iterating through SPList object within the SPListCollection on a SPWeb object.
Problem popped out, SPListCollection was not implementing IEnumerable, so I could not walk easily using Linq to this collection.
So.. to fulfill my ambition, I created a pseudo-adapter class which looked like this:

public class SPListCollectionAdapter : List<SPList>
{
private SPListCollection _listCol;

public SPListCollectionAdapter(SPListCollection listCol)
{
_listCol = listCol;

Refresh();
}

private void Refresh()
{
this.Clear();

foreach (SPList item in _listCol)
{
this.Add(item);
}
}
}

and shortly after that, I could then satisfy my quaint hunger with this method:

private static void DoMOSSLinq()
{
SPSite site = new SPSite(“http://titanctp2:9000&#8221;);
SPWeb web = site.AllWebs[0];

SPListCollectionAdapter listAdapter = new SPListCollectionAdapter(web.Lists);

var result = from l in listAdapter
select l;

foreach (var i in result)
Console.WriteLine(i.Title);

Console.ReadLine();
}

woohooo!!! I am no longer hungry.

As many have had, I applied KB934525 to my company’s MOSS environment.
We had two web front ends, and one clustered database serving everything.
Obviously, we had an error completing the configuration wizard after the upgrade binaries were installed.
It is solved by the famous command line mentioned in Bill Baer‘s blog:

psconfig -cmd upgrade -inplace b2b –force

This command solved our upgrade error on both web front ends.

BUT!!!
A few days afterwards, we noticed something funny was happening inside our very MOSS system.
The usage analysis job WAS NOT RUNNING ON the main web front end!!
AND… Our Windows Sharepoint Web Services (a required service that should run on a FARM) was not running on the main web front end (it said Upgrading), AND that service is in the state of Stopping on the other web front end.
Whoaaa .. how could this be??

Panic was in the air, and after a deep dive into the realm of modern contemplation,
this line of command was executed:

stsadm -o provisionservice -action start -servicetype SPWebService

to actually force the service to start on the main web front end. We also force stop the service on the other front end.
It brought back the services to its desired state. (Running on my main one, and Stopped on the other web front end).

It screwed the home directory of my MOSS web site inside IIS(as it defaulted back to C:\Inetpub\blablabla), but then everything was back to normal once I supplied my original MOSS web site path.

After all the chaos, my usage analysis job was back to normal again.

Let’s say I got a custom content type called “Boring Items” on my SharePoint list. It got some specific fields bound to it.
Now, I want to add an item to that list which will use the “Boring Items” content type. How?

First, perform the usual ritual of calling the SPSite and SPWeb thing, then find the list of your target, and grab its’ items [can be like this, but not necessarily has to be]:

SPSite site = new SPSite("http://YOUR_SITE_URL");
SPWeb web = site.OpenWeb();
SPList list = web.Lists["YOUR_LIST_NAME"];
SPListItemCollection items = list.Items;

Then, bind it to the desired content type

SPContentType itemtype = list.ContentTypes["Boring Items"];
SPListItem newitem = items.Add();
newitem["ContentTypeId"] = itemtype.Id; //VOILA!
newitem.Update();

Continue adding the necessary field, and don’t forget to call the Update() method once you’re done.