Sharepoint, MOSS un WSS – 2007. versija

Šķiet, esmu viens no retajiem LV, kuram šīs lietas vispār interesē, tomēr pastāstīšu – lai jau saglabājas nākotnei.

Kopš šī gada sākuma tīmeklī it bieži tiek pieminēts “jaunais Sharepoint” – visvairāk kā apjūsmojoši ieraksti dažādos blogos, cik ērts gan tas būs. Runa ir par Sharepoint 2007, precīzāk, diviem Sharepoint saimes produktiem – Windows Sharepoint Services (WSS) 3.0 un Microsoft Office Sharepoint Server (MOSS) 2007. Ar ko tie atšķiras? Apmēram ar to pašu, ar ko Notepad atšķiras no Wordpad – ar iespēju bagātību. Ja WSS iedomājamies kā skudrupūzni, tad MOSS ir skudrupūznis ar centrālapkuri, četriem skursteņiem un peldbaseinu pagrabā. Vārdu sakot, MOSS ir daudz papildus lietu – unificēta meklēšanas sistēma, lietotāju individuālie saiti, papildus saitu templeiti, papildus darba plūsmu realizācijas, biznesam svarīgo indikatoru (KPI, key performance indicators) monitorēšana, Excel web servisi utt utt. Vēl viena atšķirība – WSS ir “par velti”, tas ir, kopā ar Windows Server 2003, bet MOSS ir maksas produkts… un dārgs maksas produkts.

Atpakaļ pie pamatstāsta. Solīja jau kādu laiku, šī gada sākumā iznāca pirmā beta versija, kas bija patiešām ar īstu “betas” garšu, šī gada maijā – otrā Beta, augustā – Beta2TR versija. Pagājušajā nedēļā Microsoft paziņoja par galaversijas iznākšanu, pašlaik jebkuram ir pieejama 180 dienu trial versija MOSS un, protams, jebkuram Windows Server 2003 īpašniekam – WSS 3.0 RTW Instalēšanai būs noderīgs .NET Framework 3.0, kas arī tika izziņots pagājušajā nedēļā.

Par jaunumiem šajā versijā. Vēl joprojām Sharepoint kā datu glabātuvi izmanto Microsoft SQL Server, tagad varam lietot gan 2000., gan 2005. versiju. Joprojām tas darbojas kā IIS webservera process (bet, atšķirībā no iepriekšējās versijas, tagad kā normāla ASP.Net aplikācija, nevis ISAPI filtrs). Ir krietni uzlabota Active Directory integrācija, tagad varam precīzāk definēt attiecību “domēna lietotājs:portāla lietotājs”, piemēram, nosakot, kurus portāla lietotāja atribūtus nolasīt no kuriem domēna lietotāja konta atribūtiem (šī atkal laikam tikai mani interesējoša nianse).

Piekļuves tiesību granularitātes līmenis (ak, svešvārdi…). 2003. versijā nebija daudz iespēju – piekļuvi varēja definēt vai nu visam saitam kopā, vai katrai dokumentu bibliotēkai vai sarakstam atsevišķi. Un viss. Jaunajā variantā piekļuves tiesības varam definēt līdz pat ieraksta līmenim, kas ir daudz loģiskāk, ja nepieciešams izveidot koplietošanas dokumentu glabātvi. Šī iemesla dēļ gan sanācis tā, ka tiesību administrēšana ir kļuvusi ķēpīgāka. Ja līdz šim šķita, ka normālā prakse būtu – 1) Sharepoint saita līmenī sadefinēt loģiskas lietotāju grupas 2) Šīm grupām piešķirt tiesības skatīt to vai citu dokumentu bibliotēku, sarakstu, darīt to vai citu darbību 3) Šajās grupās ievietot cilvēkus vai grupas no domēna … tad tagad rodas sajūta, ka “ai, vai tad nu es šī viena dokumenta dēļ taisīšu speciālu grupu?”, tātad tiesības tiek izmētātas pa visu saitu.

Audiences (šķiet, šī ir MOSS, nevis WSS iespēja). Katram ierakstam varam norādīt mērķauditoriju (target audiences), t.i., grupas, kam tas varētu būt interesants. Tāpat – lapā ievietotam webpart-am var norādīt auditoriju. Ja pareizi saprotu, piekļuve šādā veidā netiek aizliegta, bet attiecīgais ieraksts vai webparts tiek aizvākts no to lietotāju acīm, kam tas nebūs vajadzīgs. Un tas ir ērti.

Navigācijas sistēma. Jau noklusētajā shēmā tā ir stipri sakarīga. 1) katram saitam var veidot savu augšējo navigācijas paneli, sastāvošu no atsevišķām cilnēm (tabs). Ir izvēle, vai mantot “parent” saita tabus, vai izmantot savējos. 2) stipri labāks ‘quick launch’ menu saita kreisajā malā. Ir izvēle, vai nu automātiski ģenerēt no visām dokumentu bibliotēkām un sarakstiem, vai veidot pašam savējo 3) breadcrumbs pasākums, kas parāda vertikālo navigāciju saitu struktūrās. Visumā ir sajūta, ka apmaldīties nevarēs.

Darba plūsmu programmēšana. Kopā ar Windows Workflow Foundation ir atnākusi iespēja strādāt ar darba plūsmām arī no Sharepoint. Ir sajūta, ka te ir savāktas vienkāršākās un nepieciešamākās lietas no Biztalk Server un palaistas plašākās tautās. Līdz ar to tādi procesi kā dažādu veidu vīzēšana, atsauksmju savākšana, atbilžu sagatavošana un citi darbiņi, ko cilvēki veic nelielās komandās, ir aprakstāmi un programmējami arī šajā vidē.

Multiple lookups. Iepriekšējā Sharepoint versijā sarakstam bija iespējams izveidot lauku, kurš norāda uz cita tajā pašā saitā esoša saraksta konkrētu ierakstu. Piemēram, cilvēku tabulā varētu būt lauks, kas ir norāde uz struktūrvienību sarakstu. Līdz šim nebija iespējams apskatīt situāciju, kad cilvēks strādātu vairākās struktūrvienībās vienlaicīgi. Tagad ir.

Custom field types. Šis ir interesants. Pēc noklusējuma Sharepoint sarakstiem var pievienot laukus ar tipiem kā “text”, “multiline text”, “number”, “date”, “choice”, “lookup”… tagad varam veidot savus tipus. Nu, kaut vai tas pats iecienītais piemērs ar personas kodu – zināms teksta formāts, zināms garums. Atliek tikai izstrādāt savu klasi, kas mantota no SPField tipa, izveidot rediģēšanas kontroli savam lauka tipam (Jo Sharepoint taču automātiski ģenerē datu ievades un parādīšanas formas katram sarakstam), varbūt vēl neliela čupiņa koda… un tam ir jādarbojas. Pagaidām neesmu izmēģinājis, bet jau ticu, ka būs labi.

Wiki un Blog saitu templeiti. Nezinu, pagaidām nešķiet, ka jaunā spēļmantiņa būs tik vērta, cik viņi paši sola. Wiki sintakse ne tuvu nelīdzinās wikipedia iespējām, blogi it kā ok, pat komentēt var… bet kaut kā nav ērti, vismaz ne priekš tiem, kas paši tajos raksta.

Overall – labais! Uzrakstīšu citreiz vēl, šoreiz jau tā par garu sanāca.

Finding SPListItem by its ID

I just found out that some operations in my SharePoint application run quite slow when used on lists containing large amounts of listitems. In the particular case it was approximately 2,500 items in the list.

The result is: NEVER use this code construction to find an element by its ID:

oList = oWeb.Lists("LongList")         'gets the list object
oItem = oList.Items.GetItemById(123)   'finds the element within the list in the list

You should rather use this one:

oList = oWeb.Lists("LongList")         'gets the list object
oItem = oList.GetItemById(123)         'finds the element within the list in the list

The big mistake is that accessing the “Items” property of the SPList object oList creates an SPQuery object in background to read all the items in particular list and then GetItemById() method of the SPListItemCollection (oList.Items is a SPListItemCollection object) runs a loop, looking for an item with ID attribute set to the value 123.

However, if you use the second code example the SPList ojbect oList creates an SPQuery object in background to read only one object having ID attribute set to 123, i.e.:

<Where><Eq><FieldRef Name="ID"></FieldRef><Value Type="Integer">123<Value></Eq></Where>

and then returns the element it found.

The same applies to the DeleteItemById() method. I had to work around in a bit different manner:

'oList.Items.DeleteItemById(123) 'this was the original code
oList.GetitemById(123).ListItems.DeleteItemById(123) 'this is the improved code

As you can see, the improved code first finds the list item by its ID, then accesses the ListItems property (which is again SPListItemCollection) and executes a DeleteItemById() command on the particular collection where the size of the collection is 1.

A really difficult workaround was needed to fix the AddNewItem function where I had code like this:

Dim oNewItem as SPListItem
oNewItem = oList.Items.Add()

Again, accessing the Items attribute leads to reading all the items in the list. To avoid that, I created a SPQuery object that never returned any elements, then called Add() method on the collection returned by the query.

'reads all items having id=0. Should return no rows
oEmptyQuery.Query = "<Where><Eq><FieldRef Name='ID' />" & _
        "<Value Type='Counter'>0</Value&gt;</Eq></Where>"
oItemColl = oList.GetItems(oEmptyQuery)
oNewSPItem = oItemColl.Add()    'adds an item to the empty list
'saves the new item and re-reads it from SharePoint
oNewSPItem.Update()
oNewSPItem = oList.GetItemById(oNewSPItem.ID)

The two last lines were needed because it seems that the field collection is not geting populated when a query returns no rows. However, my code didn’t seem to run nice without the last two rows.

SPUser? SPException!

Something on SharePoint(TM) again. Either you love it or hate it, you have to work around all the pitfalls anyway.

So we all know that in SharePoint you can allow people and assign them SharePoint roles in two ways:

  • creating a “Sharepoint User” within the portal and assigning roles to the user,
  • adding a domain group to users and assigning roles to a particular group.

The first case is the easy one. The second is not. Continue reading SPUser? SPException!

Calculated fields in CAML

So you have a SharePoint list with a calculated field. You want to select items based on the calculated field beginning with some specific substring. You write a CAML query:

<Where>
  <BeginsWith>
     <FieldRef Name="SortDate" />
     <Value Type="Calculated">200509</Value>
  </BeginsWith>
</Where>

This won’t work. (At least on my server) you’d always get informed that ” The SQL Server might not be started” + get a useless COM exception number 0x81020024. You can work around the problem by changing the value type to “Text”

<Where>
  <BeginsWith>
     <FieldRef Name="SortDate" />
     <Value Type="Text">200509</Value>
  </BeginsWith>
</Where>

This also works on the “Contains” operator. Other operators such as LessThan, GreaterThan, Equals, IsNull allow you to set the real value type – Calculated. Don’t know why. Probably a bug in Sharepoint.

Changing the title of an SPFile object.

Ok, from now on, I’ll write SharePoint related posts here in English. However, English is not my native language, so I beg your pardon in advance for all those grammatical mistakes, style errors etc. I believe the use of commas is the most horrible part of it.

In Sharepoint it’s very easy to believe that a document library is actually a custom list with some “extras”. You can add property fields to a document library the same way you add them to a usual list, the AllItems.aspx form looks quite similar for both usual lists and document libraries etc. Even in the API code model you can work with a SPList and leave SPDocumentLibrary for use only in special cases.
This impression is a bit misleading, however. When it comes to work with an SPFile class instance, you discover that every file is associated with an SPListItem instance.

To read custom properties defined by a document library, we don’t need to use the SPListItem object, we can use the Properties property of SPFile class. For instance, if there is a field in document library named “FileOwnerDepartment”, we can code like this:

        Dim oWeb As SPWeb
        Dim oFile As SPFile
        Dim sDept As String
        oWeb = SPControl.GetContextWeb(context)
        oFile = oWeb.GetFile("fileurl")
        sDept = CStr(oFile.Properties("FileOwnerDepartment"))

To set a value of a custom property we have to use the SPFile.Item property, set the property of the SPListItem object and then update the SPListItem:

        oFile.Item.Item("FileOwnerDepartment") = "someOtherValue"
        oFile.Item.Update()

Due to the fact that Title property is mandatory in every list (generic list definition from onet.xml), there is a property in SPFile class named Title (as described in MSDN – The Title property of the SPFile class gets the display name of the file.) If we try to access the title through Properties property of a file, we get a Nothing value. If we use the Title property, we get the actual title. That is, this script outputs two lines of “True”:

    Console.WriteLine(oFile.Properties("Title") Is Nothing)
    Console.WriteLine(Not oFile.Title Is Nothing)

Even though Title property is read-only (which can make you think there’s no legal way to change the title of a document), you can update it the same way you update any other field:

        oFile.Item("Title") = "newTitle"
        oFile.Item.Update()

The interesting part is that you can also update the title of a file using the localized name (there are language-packs for SharePoint, too) of title field. For instance, in Latvian version of SharePoint we have “Nosaukums” instead of “Title”. That is, both oFile.Item("Title") and oFile.Item("Nosaukums") actually change the same property – title.