InfoPath form cleanup for faster load times

My team has been working on an InfoPath 2007 form (using Visual Studio Tools for Applications) for a while. The form is bound to an XML structure having approximately 100 fields/properties. Since its initial build, the form has been modified, recompiled and re-packaged for quite a lot of times.

Users complained that form load times are quite long. It seemed that by every version they are getting even worse. The initial thought was that the delay was due to loading of SharePoint data (through secondary data sources). However, profiling showed that it takes less than a second to load them.

Recently, we found out that by each build the manifest.xsf file becomes larger and larger. The initial version was less than 100K, while the current one is 500K. The reason for such growth was – automatically generated “InfoPath variables” in the form. Something like this:

 <xsf:xmlToEdit name="SomeFieldName_3721" item="/my:SomeContainer/my:SomeSubNode/my:SubSubNode">
    <xsf:editWith autoComplete="no" component="xField"></xsf:editWith>
 </xsf:xmlToEdit>

The variables can be later referenced in InfoPath’s own XSLT views as well as in context menu definitions in the manifest.xsf. However, these variables are never cleaned up. The list is growing each time you save your form using form designer.

It seems that Infopath initializes each variable upon form load. The more variables you have, the longer it takes to load the form.

When there are thousands of such variables, cleanup is not very easy. Hence, I wrote a PowerShell script to do the job. It enumerates all the variables and looks for references to this variable in manifest.xsf and in all .xsd views of your InfoPath form.

Download the InfoPath manifest.xsf clean-up script here.

Document title in SharePoint’s “ECB” dropdown menu

Have you ever wondered why the SharePoint team decided to show only filename in dropdown menus, but not the title of the document? So when you save your file with an unreadable filename, it is displayed in your list and looks something like this (Historical note: it was not the case in SharePoint 2001. They changed it in SPPS2003 and in 2007 versions. 2010. has it fixed):

You can, of course, modify the “All Items” view and add the “Title” column, but it never has a link to the document, nor has it the fancy drop-down for editing (see the column “Title” in the picture).

So, I decided to dig into CAML schemas and to create a field which would display the title of the document and have the dropdown menu. To achieve that, one can use “computed fields” – fields which do not actually represent data editable by the user, but are only used for presentational purposes. Hence, one only has to define the DisplayPattern of that field.
There are two fields already built into SharePoint – the _EditMenuTableStart and _EditMenuTableEnd, which build the dropdown menu contents. What I had to add, was generation of the readable content. The most important part of the display pattern is this:

<IfEqual>
  <Expr1><LookupColumn Name="Title" /></Expr1>
 <Expr2></Expr2>
<Then>
     <Field Name="FileLeafRef" />
 </Then>
 <Else>
     <Column HTMLEncode="FALSE" Name="Title" Default="(no title)" />
  </Else>
</IfEqual>

It compares the value of the Title column to an empty string. If this is the case (you have uploaded a document with no title or created a folder using Explorer view), it just shows FileLeafRef – the filename. Otherwise, it displays the value of the title field.

In my case I had an existing document library which I wanted to “fix”. Hence, I created a PowerShell script, which adds my field to the library. I used “AddFieldAsXML()” method of SPList object to inject my CAML code into sharepoint.

Now it looks like this:

You can dowload the whole script here: http://tips.naivist.net/wp-content/CreateTitleField1033.ps1_.txt Rename it to .ps1 and run it .\CreateTitleField1033.ps1 -url http://yourserver/sites/somesite/someweb -ListName "Your list". Then modify the view settings to display the field.

If you are developing your own list defintion, you can use the particular fragment in schema.xml and have your field defined in a regular fashion.

param(
  [string] $url      =  "",
  [string] $ListName = ""
)

$sharepoint = [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
$site=[Microsoft.Sharepoint.SPSite]($url);
$web=$site.openweb();
$list= $web.lists[$ListName];

$schema =@"
    <Field ID="{E2ABF8D3-6435-4773-A3D6-67508FEB7CF5}" ReadOnly="TRUE" 
             Type="Computed" 
             Name="KRItemLinkTitleDropdown" 
             DisplayName="Title" 
             DisplayNameSrcField="Title"
             AuthoringInfo="(dropdown displaying the title field)"
             EnableLookup="TRUE" 
             SourceID="http://schemas.microsoft.com/sharepoint/v3"
             StaticName="KRItemLinkTitleDropdown" FromBaseType="TRUE" ClassInfo="Menu">
         <FieldRefs>
          <FieldRef Name="Title" />
          <FieldRef Name="LinkTitleNoMenu" />
          <FieldRef Name="FSObjType" />
        </FieldRefs>
        <DisplayPattern>
           <Field Name="_EditMenuTableStart" />
           <HTML>
           <![CDATA[<A onfocus="OnLink(this)" HREF="]]></HTML>
       <IfEqual>
            <Expr1>
              <LookupColumn Name="FSObjType" />
            </Expr1>
            <Expr2>1</Expr2>
            <Then>
                <FieldSwitch>
                <Expr>
                  <GetVar Name="RecursiveView" />
                </Expr>
                <Case Value="1">
                  <LookupColumn Name="FileLeafRef" HTMLEncode="TRUE" />
                </Case>
                <Default>
                  <SetVar Name="UnencodedFilterLink">
                    <SetVar Name="RootFolder"><HTML>/</HTML>
                      <LookupColumn Name="FileRef" />
                    </SetVar>
                    <SetVar Name="FolderCTID">
                      <FieldSwitch>
                        <Expr>
                          <ListProperty Select="EnableContentTypes" />
                        </Expr>
                        <Case Value="1">
                          <Column Name="ContentTypeId" />
                        </Case>
                      </FieldSwitch>
                    </SetVar>
                    <FilterLink Default="" Paged="FALSE" />
                  </SetVar>
                  <GetVar Name="UnencodedFilterLink" HTMLEncode="TRUE" />
                </Default>
              </FieldSwitch>     
             </Then>
            <Else>
                <Field Name="ServerUrl" URLEncodeAsURL="TRUE" />
            </Else>
            </IfEqual>


       <HTML><![CDATA[" onclick="return DispEx(this,event,']]></HTML>
                  <ScriptQuote NotAddingQuote="TRUE">
                    <ServerProperty Select="HtmlTransform" />
                  </ScriptQuote><HTML><![CDATA[',']]></HTML>
                  <ScriptQuote NotAddingQuote="TRUE">
                    <ServerProperty Select="HtmlTrAcceptType">
                      <Column Name="File_x0020_Type" />
                    </ServerProperty>
                  </ScriptQuote><HTML><![CDATA[',']]></HTML>
                  <ScriptQuote NotAddingQuote="TRUE">
                    <ServerProperty Select="HtmlTrHandleUrl">
                      <Column Name="File_x0020_Type" />
                    </ServerProperty>
                  </ScriptQuote><HTML><![CDATA[',']]></HTML>
                  <ScriptQuote NotAddingQuote="TRUE">
                    <ServerProperty Select="HtmlTrProgId">
                      <Column Name="File_x0020_Type" />
                    </ServerProperty>
                  </ScriptQuote><HTML><![CDATA[',']]></HTML>
                  <ScriptQuote NotAddingQuote="TRUE">
                    <ListProperty Select="DefaultItemOpen" />
                  </ScriptQuote><HTML><![CDATA[',']]></HTML>
                  <ScriptQuote NotAddingQuote="TRUE">
                    <MapToControl>
                      <Column Name="HTML_x0020_File_x0020_Type" /><HTML>|</HTML>
                      <Column Name="File_x0020_Type" />
                    </MapToControl>
                  </ScriptQuote><HTML><![CDATA[',']]></HTML>
                  <ScriptQuote NotAddingQuote="TRUE">
                    <Column Name="HTML_x0020_File_x0020_Type" />
                  </ScriptQuote><HTML><![CDATA[',']]></HTML>
                  <ScriptQuote NotAddingQuote="TRUE">
                    <ServerProperty Select="GetServerFileRedirect">
                      <Field Name="ServerUrl" /><HTML>|</HTML>
                      <Column Name="HTML_x0020_File_x0020_Type" />
                    </ServerProperty>
                  </ScriptQuote><HTML><![CDATA[',']]></HTML>
                  <ScriptQuote NotAddingQuote="TRUE">
                    <Column Name="CheckoutUser" />
                  </ScriptQuote><HTML><![CDATA[',']]></HTML>
                  <ScriptQuote NotAddingQuote="TRUE">
                    <UserID AllowAnonymous="TRUE" />
                  </ScriptQuote><HTML><![CDATA[',']]></HTML>
                  <ScriptQuote NotAddingQuote="TRUE">
                    <ListProperty Select="ForceCheckout" />
                  </ScriptQuote><HTML><![CDATA[',']]></HTML>
                  <ScriptQuote NotAddingQuote="TRUE">
                    <Field Name="IsCheckedoutToLocal" />
                  </ScriptQuote><HTML><![CDATA[',']]></HTML>
                  <ScriptQuote NotAddingQuote="TRUE">
                    <Field Name="PermMask" />
                  </ScriptQuote><HTML><![CDATA[')">]]></HTML>
                  <UrlBaseName HTMLEncode="TRUE">
                  </UrlBaseName>
                    <IfEqual>
                    <Expr1>
                      <LookupColumn Name="Title" />
                    </Expr1>
                    <Expr2></Expr2>
                    <Then>
                        <Field Name="FileLeafRef" />
                     </Then>
                    <Else>
                        <Column HTMLEncode="FALSE" Name="Title" Default="(no title)" />
                    </Else>
                    </IfEqual>
                  <IfEqual>
                    <Expr1>
                      <GetVar Name="ShowAccessibleIcon" />
                    </Expr1>
                    <Expr2>1</Expr2>
                    <Then><HTML><![CDATA[<img src="/_layouts/images/blank.gif" class="ms-hidden" border=0 width=1 height=1>]]></HTML>
                    </Then>
                  </IfEqual><HTML><![CDATA[</A>]]></HTML>
                  <IfNew Name="Created_x0020_Date"><HTML><![CDATA[<IMG SRC="/_layouts/1033/images/new.gif" alt="New!">]]></HTML>
                  </IfNew>
                  <Field Name="_EditMenuTableEnd" />
        </DisplayPattern>
      </Field>
"@
$fieldname = $list.fields.AddFieldAsXml($schema);
$list.update();

Restoring the top navigation bar of a SharePoint site

It’s a known issue that if you delete the “Top Navigation” menu through the API in SharePoint 2007, you cannot restore it easily. I came across this problem in one of my development sites. When looking for a solution, I found this article, saying you have to do it through the database.

The code below solves the problem programmatically (and yes, it changes the database directly). Of course, it’s an unsupported solution – if you use it, you may get in trouble with Microsoft official support.

string sSiteUrl = "http://moss/sites/yoursite/";
//after the code gets executed, we have to dispose the 
//spweb object, since the Navigation object of the current SPWeb instance will contain wrong information
using (SPSite oSite = new SPSite(sSiteUrl))
{
    using (SPWeb oWeb = oSite.RootWeb)
    {
        //check if the top navigation bar is already there
        SPNavigationNode oTopnav = null;
        try
        {
            oTopnav = oWeb.Navigation.GetNodeById(1002);
        }
        catch (Exception)
        {
            oTopnav = null;
        }

    if (oTopnav == null)
    {
        //we've found out there is no top navigation bar. Let's create one;

        //we create a temporary navigation node pointing back to web site root
        Microsoft.SharePoint.Navigation.SPNavigationNode oTempNode = new Microsoft.SharePoint.Navigation.SPNavigationNode("Navigation", oWeb.ServerRelativeUrl);
        //we add this node to the "global" collection (where quick launch and top nav bar normally live)
        oWeb.Navigation.GlobalNodes.AddAsLast(oTempNode);

        //now the dirty work - go to the database and change the ID of the navigation node
        System.Data.SqlClient.SqlConnection oConn = new System.Data.SqlClient.SqlConnection(oSite.ContentDatabase.DatabaseConnectionString);
        System.Data.SqlClient.SqlCommand oCmd = new System.Data.SqlClient.SqlCommand();
        oCmd.Connection = oConn;
        oCmd.CommandType = System.Data.CommandType.Text;
        //we only update the node which has the same ID as the one we just created, but, to be completely sure that 
        //nothing else gets changed, we add a requirement to have the same siteid and webid
        oCmd.CommandText = @"UPDATE NavNodes SET Eid=1002 WHERE (Eid=" + oTempNode.Id.ToString() + 
             @") AND (SiteId='" + oSite.ID.ToString("D") + "') AND (WebId='" + oWeb.ID.ToString("D") + "')"; ;
        oConn.Open();
        oCmd.ExecuteNonQuery();
        //closing the connection
        oConn.Dispose();
    }
}

} //the oSite object is disposed here

“_vti_bin” folder defined

Ever wondered why SharePoint “lists web service” (i.e., lists.asmx) lives in an interesting address of http://mossserver/sites/somesite/_vti_bin/lists.asmx ?

This is actually not the only place where you can see the magic _vti_something. I remember myself deleting such directories from “wwwroot” quite often back in the days “everyone” used MS FrontPage. FrontPage used to create the folders “_vti_bin”, “_vti_cnf” on your computer locally as you developed a website .. and also on the server, if you used “Frontpage Extensions” for publishing. The _bin folder would normally contain executables required for FP server components (as far as I understand, something like cgi_bin).

So, even though FP is announced dead, guys from the SharePoint team apparently like the idea of puting executables in the _vti_bin folder which was used by FrontPage Extensions.

Anyway, the question is, what does the acronym “VTI” mean. Apparently, Microsoft did not develop MS FrontPage from scratch, they acquiredVermeer Technologies Inc”. Thus the name of “bin” folder in FrontPage. Thus the URL for lists.asmx in SharePoint.

MOSS & custom master pages

I just found an interesting “feature” in MOSS publishing infrastructure. We’re designing a custom master page for our MOSS portal solution. If I recall correctly, we started with Heather Solomon’s minimal master page, but I’m not really sure.

We swiped out all of the built-in styles and layouts to achieve maximum flexibility and stylability of MOSS infrastructure. I believe this is the best choice when you have to adapt your portal to custom tailored design layout.

Well, the result was, the portal worked fine with our master page, BUT the content query web part failed. It failed every time we enabled the RSS feed feature. Instead of seeing the actual content, it showed only an error message (in Latvian, since we’re using the latvian language pack) “Šo Web daļu nevar parādīt. Lai novērstu problēmu, atveriet šo Web lapu ar Windows SharePoint Services saderīgā HTML redaktorā, piemēram, Microsoft Office SharePoint Designer. Ja problēma netiek novērsta, sazinieties ar Web servera administratoru.”

No traces could be found in the ULS log nor in the event log of the server. After setting the diagnostic logging of all categories to verbose, finally an exception was written to the logfile:

 Error while executing web part: System.Xml.Xsl.XslTransformException: An error occurred during a
 call to extension function 'RegisterFeedUrl'. See InnerException for a complete description of the error. ---> 
 System.Web.HttpException: The control collection cannot be modified during DataBind, 
 Init, Load, PreRender or Unload phases.     at System.Web.UI.ControlCollection.Add(Control 
 child)     at Microsoft.SharePoint.Publishing.WebControls.WebPartRuntime.RegisterFeedUrl(String url, String type)    
 --- End of inner exception stack trace ---     at ....

When looking at the RegisterFeedUrl in .Net Reflector, we found out that the code actually accesses the Header property of the Page object that is being rendered and adds HtmlLink control to the collection. It gave us a hint that probably there is something wrong with the page and header objects. And – yes, we had left out the runat="server" part of the HTML and HEAD tags. So, this is the absolute minimum you must have:

<HTML runat="server">
    <HEAD runat="server">
....

.. and, of course, the name space references and other stuff you usually put in the HTML element tag.

Sharepoint – LOC?

This is rather a question or an idea for a discussion than a regular blog post.

I’m just wondering how does one count LOC in Sharepoint? Or, to be more general, how do you measure the amount of work that has been invested in creating a solution in Sharepoint.

A solution can consist of

  • content type definitions (XML files),
  • list definitions (XML files again)
  • feature definitions (XML),
  • Solution manifests (XML),
  • CAB file descriptors (“code”),
  • ASPX pages (ASP.net code),
  • ASPX code-behind files (finally, VB.net, countable!),
  • Event handlers code (VB.net, cool),
  • Workflow diagrams (generated VB.net code),
  • Workflow code-behind files (VB.Net files),
  • Workflow rules (XML),
  • … and many more things which are not crurrently the case for me;-)

So how do you put it all together? In my opinion, a line in feature.xml file has much more value than a table-tr-td line in ASPX page (the former can break “everything” in your site while the latter cannot). Some lines in list definitions are valuable, some are not. Should one assign values to LOCs of each file type? Seems like LOC is no answer at all. For instance, workflow designer generated code has kind of “no value” as a VB.Net code and LOC measurement wouldn’t tell you much about the effort made to create the particular workflow.

Sharepoint Workflow modificēšanas formas izveide ar InfoPath

Iesākumam

Workflow modificēšanas formas jāveido kā InfoPath XSN formas, nevis kā ASPX formas. InfoPath ir dabūjams pilnajā MSOffice paketē, oficiāli tas piederas pie “Microsoft Office Suites and Applications 2007” pakas

Pašas formas izveide

Jāveido jauna forma, to veidojot uzreiz vēlams uzstādīt ķeksi “hide features that do not work in InfoPath Forms Services” (saucas kaut kā citādi, bet ideja ir tā).

Svarīgi ir uzlikt sekojošus atribūtus iekš “Tools->Form options”:

Browser
  • Izvācam ķeķšus pie “Show toolbar at top of form; show toolbar at bottom of form.
    Modifikācijas formā šie toolbari nav būtiski, jo formas submitošanai visticamāk izmantosim pogu. Savukārt “Save”, “Update” utt darbības vispār šeit ir liekas.
Security and trust
  • Security level: Domain (neļaujam automātiski noteikt)
  • Security and trust -> Sign this form template. Ja ir administratoru piešķirts sertifikāts, var parakstīt ar to, bet šīm vajadzībām pietiek arī ar “Create certificate” (pats priekš sevis izveidos sertifikātu)
Compatibility
  • Design a form template that can be opened in a browser or InfoPath

Datu submitēšanai jāizveido datu konekcija, kas nodos atpakaļ informāciju uz “hosting environment” (mūsu gadījumā – sharepoint workflow-am). To dara zem Tools->Data Connections. Jāspiež “Add”, jāizvēlas “Create new connection to: Submit data”. Nākamajā logā – “To the hosting environment…” un jāiedod konekcijai vārds.

Tai pogai, kas būs kā “submit” attiecīgi jāiekonfigurē, ka vai nu:

  • tā ir submit poga – tad prasīs, pa kuru datu konekciju sazināties ar formu. Tad jāuzstāda, ka pie submitēšanas lietotājam nerāda paziņojumu, ka submits ir veiksmīgs un pēc submitēšanas aizver formu
  • tā ir parasta poga, kas darbojas uz likumu (rules) pamata. Tad jāizveido likums, kas uzstāda formas vērtības un beigās nosubmitē formu un to aizver

Formas datu shēmai būtu vēlams iedot sakarīgu nosaukumu, pēc noklusējuma tas ir “myFields” – ja negribam ar tādu turpmāk strādāt, jāpārsauc un jāizveido normāli.

Kad viss gatavs, dodamies File->Publish. * Izvēlamies “To a network location”. Norādām vietu uz c:, kur vēlamies formu “publicēt”, norādām formas nosaukumu vai, ja apmierina piedāvātais, atstājam to pašu. * Pēc tam wizardā ir jautājums “If all form users can access the location that you entered in the previous step, Click next”. Šeit ir svarīgi atstāt to teksta lauku TUKŠU, citādi forma nestrādās worklfowā! * Izmetīs warningu, ka tas var būt slikti – spiežam OK.

Paņemam File->Properties un apskatāmies/nokopējam automātiski noģenerēto “ID” lauka vērtību, kas izskatās apmēram kā “urn:schemas-microsoft-com:office:infopath:ReviewModForm:-myXSD-2007-08-22T06-32-52”

Formas lietošana worklfowā

Lai formu piesaistītu workflovam, kas aprakstīts kā Sharepoint Feature.

Iekopējam publicēto XSN failu blakus feature.xml failam. Instalējot featuri, šim XSN jānokļūst zem c:\program files….\12\template\features\ManaFeature

Feature.XML:
  • Feature tagā:
    svarīgas ir šādas rindas (<Feature> taga iekšpusē, To rezultātā, pie feature aktivizēšanas, izpildīsies kods, kas aktivizē arī workflow-am piesaistītās formas):
    ReceiverAssembly=”Microsoft.Office.Workflow.Feature, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c” ReceiverClass=”Microsoft.Office.Workflow.Feature.WorkflowFeatureReceiver”

  • ElementManifests tagā:
    Visi XSN faili jāpiemin arī ElementManifests tagā. Manā gadījumā bija:
    <ElementFile Location=”ReviewModForm.xsn”/>

  • Properties tagā:
    Lai tiktu aktivizētas šīs formas, papildus vēl arī <Properties> tagā jāieliek šāda rinda:
    <Property Key=”RegisterForms” Value=”*.xsn” />

Workflow.xml (vai kā nu saucas XMLs, kas apraksta workflowu:
  • Workflow tagā:
    Uztādām noklusēto WorkflowModifications:
    ModificationUrl=”_layouts/ModWrkflIP.aspx”

  • MetaData tagā:
    Jāveido jauns modifikācijas apraksts, kas atsaucas uz formu. Manā gadījumā bija:
    <Modification_d03867f5-99f7-469e-94af-7fcefe476a22_FormURN>
    urn:schemas-microsoft-com:office:infopath:ReviewModForm:-myXSD-2007-08-22T06-32-52
    </Modification_d03867f5-99f7-469e-94af-7fcefe476a22_FormURN>
    <Modification_d03867f5-99f7-469e-94af-7fcefe476a22_Name>
    Atsaukt saskaņošanu
    </Modification_d03867f5-99f7-469e-94af-7fcefe476a22_Name>

Ar slīprakstu atzīmēju tās vietas, kas katram gadījumam ir citādi. Kā redzams, šeit FormURN tagā iekšpusē esošais sakrīt ar to formas ID, kas tika nokopēts InfoPath-ā. Jaunu GUID ģenerējam ar GuidGen, UZMANĪGI – tam guidgen uzģenerēs ar lielajiem burtiem. VisualStudio izmantojot, tas automātiski pārtaisa uz mazajiem burtiem. Tāpēc ērtāk būs arī šeit izmantot ar mazajiem, jo citādi tas netiks atpazīts.

Pašā workflowā, vietā, kur šāda modifikācija ir pieļaujama, jāievieto EnableModification aktivitāte, kurai ModificationID uzstāda uz iepriekšējā solī ģenerēto GUIDu (ar mazajiem burtiem). Pēc tam vēlams ieveitot “EventHandlingScope”, kur normālajā zarā veic normālo darbu, bet eventu ķērājā ieliek EventDriven aktivitāti, kas gaida OnWorkflowModified notikumu (šeit atkal sasaiste notiek pēc ModificationID).

Šajā brīdī vēl nekas nestrādās, jo nav izveidota datu apmaiņa starp InfoPath formu un Workflow. No WF puses tā tiek nodrošināta, izmantojot ContextData atribūtu – tur tiek sagaidīts XMLs kas atbilst InfoPath formas aprakstītajai shēmai. Tieši tādu pašu ContextData piedāvā arī OnWorkflowModified aktivitāte, tikai šeit saņemam no formas atpakaļ pienākušos datus.

XMLu, protams, var sacerēt pats, bet var arī iegūt automātiski.

XSD shēmas ģenerēšana

InfoPath formu atveram dizainēšanas režīmā, izvēlamies tai “Save as Source files” un izvēlas, kur saglabās. Izvēlētajā folderī uzradīsies vairāki faili, no kuriem svarīgākais: myschema.xsd – tas satur mūsu InfoPath formas shēmu.

Pēc tam no šīs shēmas var uzģenerēt VB vai C# klasi, kas “apkalpo” šādu shēmu. To darām ar komandrindas rīku “xsd.exe”:

  cd c:\manataka
  xsd.exe myschema.xsd /C /o:.\ /L:VB

Uzģenerējas fails myschema.vb, kurā iekšā klase ar nosaukumu tādu, kā iedevām savam datasetam 1. solī. Manā gadījumā – ReviewModDataSet

Šo klases failu iekļaujam savā projektā. Lai sagatavotu aktivitātei nepieciešamo XML stringu, var lietot šādu koda fragmentu:

        Dim oFormData As New ReviewModDataSet
        Using stream As New IO.MemoryStream
            Dim serializer As New Xml.Serialization.XmlSerializer(GetType(ReviewModDataSet))
            serializer.Serialize(stream, oFormData)
            Me.enableRecall_ContextData = Encoding.UTF8.GetString(stream.GetBuffer())
        End Using

Ar slīprakstu iezīmēju atribūtu, kurā nepieciešams uzstādīt iegūto XML vērtību.

Sharepoint un yyyy.MM.dd

Kad uzinstalēsiet Sharepoint valodu paku, par kuru rakstīju iepriekšējā ierakstā un gribēsiet, lai “viss būtu pavisam latviski”, var gadīties vilties tāpat kā man.

SPPS ļauj glīti nomainīt gan “Sort order”, gan “Locale” katram atsevišķam Web vai Site objektam. To dara apmēram šādi. Varam uzstādīt latviešu lokāli un latviešu kārtošanas secību. Rezultātā iegūstam, ka konkrētā vietne tiešām izmanto 1062 lokāli (&H426 vai lv-LV, var saukt dažādi).

Pēc vērtības nomainīšanas, varam, skatot kādu ierakstu, pamanīt: “Izveidots: 2007.07.31 , 11:30. Autors: TādsUnTāds”.
Rodas jautājums, kas gan tas par interesantu datuma formātu, kurā sākumā raksta gadu, tad mēnesi un tad datumu (yyyy.MM.dd.) Skolā taču mums ir mācīts izmantot formātu dd.MM.yyyy, tātad šodien es gribētu rakstīt 31.07.2007.

Pēc nelādzīgi plašas izpētes esmu noskaidrojis, ka tā tas ir paredzēts! Microsoft darinātais (bet acīmredzot taču kādas latviešu komisijas ieteiktais) formāts ir tieši tāds.

Neliels VB.Net koda fragments parāda, ka tā ir:

    Dim oLoc As New Globalization.CultureInfo( _ 
                    culture:=1062, _ 
                    useUserOverride:=False)
    Debug.WriteLine(oLoc.Name & ": " & _
                    oLoc.DisplayName & ":" & _
                    oLoc.DateTimeFormat.ShortDatePattern)

Izejā tiek izdrukāts

   lv-LV: Latvian (Latvia):yyyy.MM.dd.

Sharepoint to ņem vērā un tieši tā arī parāda datumus. Kāpēc mēs to ikdienā neredzam? Visticamāk, tāpēc, ka vai nu paši vai sistēmu administratori pēc operētājsistēmas instalēšanas esam atvēruši Control Panel un nomainījuši datuma formātu sadaļā “Regional Settings”. Koda piemērā bija redzams, ka parametrs “useUserOverride” tiek nodots kā “False”, tātad – ignorējot lietotāja veiktos pielāgojumus. Protams, savā datorā veicot pārbaudi un šo parametru norādot kā “True”, ieraudzīju jau sagaidāmo datuma formātu.

Sharepoint, kas ir servera produkts, nedrīkstētu izmantot atsevišķa lietotāja veiktus pielāgojumus, lai rādītu saturu citiem lietotājiem – un tā tas arī dara.

Pašlaik diemžēl neredzu labu apkārtceļu. Vienkāršākais ir uzstādīt, ka izmantosim vācu lokāli (1032), bet tad vietās, kur uz ekrāna parādās garais datums, parādīsies “Diensdag, 31. Juli”.

Sharepoint runā latviski

Šodien (31.07.2007) ir nopublicēta latviešu valodas paka, kas paredzēta Microsoft Office Sharepoint Server. Tā pieejama Microsoft lejupielāžu centrā “par velti”, tas ir, licencēšanas noteikumi tie paši vecie. Ļoti ceru, ka tulkojums būs pilnīgs un bez īpaši pamanāmām kļūdām.
Lejupielāde: MOSS2007 valodu paka.

Ja nepieciešama valodu paka “plikajam” Windows Sharepoint Services, tā pieejama jau ilgāku laiku.Te gan uzreiz gribu brīdināt, ka šis latviskojums ir nepilnīgs. Publiskajā daļā redzamie teksti ir nolatviskoti, administrācijas daļā vietām redzami arī angliski teksti, vietām vispār parādās resursu identifikatori (izskatās apmēram kā $Resources:UberAdminLink$).
Lejupielāde: WSS 3.0 valodu paka