Kārtošana latviešu valodā ar Microsoft SQL Server

Jau kopš senām Microsoft SQL Server versijām ir bijusi problēma, ka latviešu valodas teksti tiek kārtoti nepareizi. Proti, pat tad, ja datubāzei iestatīta “Latviešu” valoda, netiek šķiroti īsie un garie burti (šeit ar “latviešu” saprotu tādas kolācijas kā, piemēram, ‘Latvian_CI_AS’ vai ‘SQL_Latvian_Cp1257_CI_AS_KI_WI’). Līdz ar to sarakstu bērziņš, avotiņš, āboliņš SQL serveris kārtoja kā āboliņš, avotiņš, bērziņš. Kā zinām no ābeces mācīšanās, latviešu alfabēts ir A, Ā, B, C, Č..., tas ir, diakritiskās zīmes ir atsevišķas un tās seko aiz tā paša burta bez diakritiskās zīmes, tāpēc pareizi būtu bijis avotiņš, āboliņš, bērziņš.

Kopš SQL server 2008 ir pieejama vēl viena kolāciju saime – tādas, kas sākas ar Latvian_100, piemēram, Latvian_100_CI_AS. Šajās kolācijās gan viss ir salabots un kārtošana tiešām strādā pareizi:

select VARDS from (
        select 'Bērziņš' as VARDS union
        select 'Āboliņš' as VARDS union
        select 'Avotiņš' as VARDS) as SAMPLES
order by VARDS collate Latvian_100_CI_AS

rezultāts ir

VARDS
-------
Avotiņš
Āboliņš
Bērziņš

Papildus informāciju var pasmelties MSDN dokumentācijā.

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();

Tulkošanas makrosi

Divi Word makrosi, kas varētu noderēt tiem, kam nav instalēta Tildes vārdnīca, bet ir pieejams internets.

Lai lietotu, makrosu projektiem referencēs jāpieliek Microsoft XML (es liku V 6.0, bet derēs jebkurš). Pēc tam atliek uzbindēt klaviatūras saīsni. Piemēram, es ieliku Shift+Ctrl+Alt+E tulkojumam uz angļu valodu un Shift+Ctrl+Alt+L tulkojumam uz latviešu valodu. Pēc tam, kad vajag tulkojumu, iezīmējam tulkojamo vārdu un izpildām klaviatūras maģiju.

Uzskatu, ka SIA “Tilde” tiesības ar šo neesmu pārkāpis, jo esmu tikai vienkāršojis piekļuvi datiem, kas ir publiski pieejami.

Rezultāts izskatās šādi:

translator.png

Continue reading Tulkošanas makrosi

WTF

RTU, maģistri, sieviešu komanda

            if (Edit1.Text = 'ozols') OR (Edit1.Text = 'logs') OR (Edit1.Text = 'zaicevs')
               OR (Edit1.Text = 'tukins') OR (Edit1.Text = 'petrovs') OR (Edit1.Text = 'sidorovs')
               OR (Edit1.Text = 'ivanovs') OR (Edit1.Text = 'celms') OR (Edit1.Text = 'koks')
               OR (Edit1.Text = 'liepins') OR (Edit1.Text = 'krastins') OR (Edit1.Text = 'pavlovs')
               OR (Edit1.Text = 'rukins') OR (Edit1.Text = 'papovs') OR (Edit1.Text = 'mamovs')
               OR (Edit1.Text = 'durovs')
               then
                    begin
                         lietot := Edit1.Text;
                         if forma = 'Form5'
                            then
                                Form5.Visible := True
                         else if forma = 'Form11'
                            then
                                Form11.Visible := True;
                         Form7.Visible := False;
                    end
            else MessageDlg ('Parole nav pareiza!',mtError,[mbOK],0);

Word to Markdown converter

Some smart people who didn’t want to teach users how to code in HTML invented Textile markup language

Someone more lazy invented MarkDown syntax which is much easier to learn. MarkDown is really nice for small content management systems, blogging engines etc.

For instance, to mark text as bold, you write the “bold text” like “__bold text__”. Other rules can be found in the syntax page.

However, when you have a MS Word document with several pages of bolds, italics and lists, you don’t really want to re-code all the markup.

This is why I wrote a simple macro for converting the most trivial things automatically. This macro does:

  • Replace bold and italics
  • Replace headings (marked heading1-6)
  • Replace numbered and bulleted lists

It’s very buggy, I believe it hangs on larger documents, however I’m NOT stating it’s a stable release anyway! :-) Experimental use only, recode and reuse it as you like, post a comment if you’ve found a better solution.

Click here to download the code.

To use the code:

  • open WinWord,
  • press Alt+F11 to open the VBA editor,
  • right click the first project in the project browser
  • choose insert->module
  • paste the code from the file
  • close macro editor
  • go tools>macro>macros; run the macro named MarkDown

Does this user belong to that domain group?

UPD: This works only on LDAP groups and LDAP:// provider, not WinNT groups!

If we know two things – a login name of a user for instance northwind\JohnDoe and a domain group northwind\board. We want to check if John Doe belongs to the “board” group or not.

This is how you find it: IsUserMember( "northwind\JohnDoe", "northwind\board")

Continue reading Does this user belong to that domain group?

Asm?

Kā jums šķiet, vai “objektorientētais asamblers” ir oksimorons? Zinu tikai to, ka “objektorientācija” un “asemblers” vienmēr ir rādīti kā pretstati. Bet varbūt tak tomēr ir kāds gudrinieks, kurš pamanījies to apvienot?

Modulus

Rakstot par nesmukajiem datumiem, biju izmantojis šādu izteiksmi: $sundayEveningSpan = ((($now["wday"]-1)%7)*24*60*60);. Rezultātā mainīgais $sundayEveningSpan tiek uzstādīts (tam vajadzētu tikt uzstādītam) uz sekunžu skaitu starp šodienas plkst. 00:00 un šīs pirmdienas 00:00. Vārdu sakot, tiek aprēķināts, cik sekundes pagājušas šajā nedēļā.

Kā redzams, izteiksmē parādās operators %, kas atgriež izteiksmes $now["wday"]-1 vērtību pēc moduļa 7. Tas darīts tāpēc, ka PHP funkcijas GetDate() atgrieztajā masīvā wday lauks atgriež “Numeric representation of the day of the week: 0 (for Sunday) through 6 (for Saturday)”. Tātad svētdiena ir 0-tā diena, sestiena ir 6. diena. Tā kā man (un arī daudziem citiem latviešiem) nedēļa sākas ar pirmdienu, no dienas numura bija jāatņem vieninieks pēc moduļa 7, lai iegūtu pareizo piekārtojumu: pirmdienai 0, svētdienai (0-1) mod 7, kas vienāds ar 6 klasiskajā moduļu aritmētikā.

Tomēr izrādījās, ka šādi mana funkcija darbojas nekorekti. Izpētot tuvāk, secināju, ka PHP valodai arī ir savas īpatnības – modulis no negatīva skaitļa ir negatīvs. Tas arī oficiāli ir dokumentēts modulus operatora aprakstā. Tādēļ -1 % 7==-6, nevis 6, kā bija sagaidāms.

Lai novērstu šīs PHP īpatnības sekas, aizstājam -1 ar +6 (kas ir kongruenti pēc moduļa 7) un turpmāk nedēļas sākumu aprēķinām šādi: $sundayEveningSpan = ((($now["wday"]+6)%7)*24*60*60);.

Kā rāda testi, šāda pat problēma novērojama arī Visual Basic.Net un, ļoti iespējams, arī citās .Net valodās. Kā sacīts MSDN, “The result of x Mod y is the value produced by x – (x \ y) * y”. Tas nozīmē, ka gadījumā, kad pats x ir negatīvs, arī atgrieztais rezultāts būs negatīvs.

Tā, lūk. Atliek vien secināt, ka programmēšanas valodu veidotājiem ir vienalga, vai tiek saglabāta multiplikatīvās grupas pēc moduļa N un gredzena īpašība, kas tik svarīga ir matemātiķiem. Un tas, protams, ir tikai normāli – ja vajadzēs, vienmēr varēsim pieskaitīt 6 tā vietā, lai atņemtu 1.

Daudzrindiņu links

Ja jūs zinātu, kāaaaa man ir apnicis cīnīties ar to, ka e-pasta klientprogramma neatpazīst garas www adreses. Piemēram, vēstules tekstā ir adrese, kuras pirmo rindiņu MSOutlook rāda zilu un pasvītrotu (jo atpazīst kā adresi), bet otro rāda melnu un nepazīst:

http://www.google.lv/search?sourceid=navclient&ie=UTF-8&rls=GGLD,GGLD:2004-4
7,GGLD:en&q=photo+workshop

Lai apskatītos, kas tur iekšā, pārkopēju adresi uz Notepad, tur skrienu cauri rindām, velku visu kopā… garlaicīgi.

Cīnījos ar to, izveidojot bookmarkletu, kas dara šādi:

var s,w=window;
s=w.clipboardData.getData("Text").replace(/\r|\n|\s/g,"");
if(s.indexOf("http://")>-1) 
  w.location.href=s

Tas ir, paņemam no Windows starpliktuves (clipboard) datus, kas tur šobrīd ir, izdzēšam visas rindas beigu pazīmes, ja palikušais teksts satur http://, tad mēģinām navigēt uz šo lapu. Un viss.

Savelkot to visu kopā, pie favorītiem jāpieliek šāds links:
javascript:(function x(){var s,w=window;s=w.clipboardData.getData(“Text”).replace(/\r|\n|\s/g,””);if(s.indexOf(“http://”)>-1) w.location.href=s});x()
Internet Explorer gan dusmosies, ka tas neesot atpazīts protokols, bet nu ko lai dara… bookmarkleti vispār ir “hacks”.

Ja gribas šo lietiņu izmantot, var doties uz manu bookmarkletu lapu un darīt, kā tur rakstīts.

Iexplorer ContextMenu

Attiecībā uz tīmekļa sistēmu drošību ir teiciens, ka 100% droša sistēma būs 0% lietojama. Tātad, jo drošāk, jo neērtāk. Tā kā pēdējā laikā ir populāri dažādi phishing pasākumi, arī .lv internetbankas cenšas panākt arvien augstāku savu pakalpojumu drošību. Līdz ar to ne tik vien tiek izmantoti SSL pieslēgumi, nesaprotami lietotāja vārdi un pieprasītas garas un sarežģītas paroles, bet arī tiek lietotas kodu kartes (tās ir tādas kartiņas ar daudzām burtu/skaitļu virknītēm, katrai virknītei savs numuriņš. Pieslēdzoties e-bankai tiek pieprasīts ievadīt kādu no virknītēm, piemēram septīto pēc kārtas).

Ideja jau skaista, bet lietojamība… katru reizi, kad gribi apskatīties, kā tavā kontā vēl joprojām ir tikai 3 santīmi, tev jāmeklē karte, uz tās jāmeklē kodi, tie “jāpārdrukā” u.t.t.

Šeit pastāstīšu risinājumu, kā papildināt Internet Explorer contextMenu ar savu funkciju, kas atgriež vajadzīgo kodu kartes ierakstu.

“Gudrību” iešujam JavaScript koda gabalā, kurš pārbauda, vai pārlūkprogramma atrodas manas internetbankas lapā un pēc tam no kodu kartes atrod vajadzīgo kodu un to iekopē “starpliktuvē” (clipboard).

< SCRIPT LANGUAGE="JavaScript">
var parentwin = external.menuArguments;
var doc = parentwin.document;
var sel = doc.selection;
var rng = sel.createRange();
var iNum = 0;

//vai esam nokļuvuši e-bankā
if (external.menuArguments.location.host=="mana.e-banka.lv") {

    //ja jā, tad uzstāda kodu kartes vērtības
    var myCodes = new Array("*****", "*****", "*****", "*****", 
                             "*****", "*****", "*****", "*****",
                             "*****", "*****", "*****", "*****", 
                             "*****", "*****", "*****", "*****");
    //kas iezīmēts browser logā?
    if (rng.text!="")
        iNum = rng.text*1;
        //ja iezīmēts skaitlis, mēģina nolasīt kodu
        if (!isNaN(iNum)) 
            window.clipboardData.setData("Text", myCodes[iNum-1]);
        else 
            alert("Nav iezīmēts skaitlis!") 
    }
else {
   alert("Šobrīd Tu neesi bankas lapā!");
}
< /SCRIPT>

Šādu skriptu saglabājam .htm failā uz cietā diska. Piemēram iekš, c:\i\banka.htm Pēc tam veram vaļā RegEdit, un meklējam šādu zaru: HKEYCURRENTUSER\Software\Microsoft\Internet Explorer\MenuExt Iespējams, tur jau priekšā būs kādi ieraksti no Tilde vai kāda cita programmatūras izstrādātāja, kurš savus produktus integrē MSIE. Pievienojam jaunu atslēgu, tās nosaukums būs redzams konteksta dialogā. Piemēram, es pievienoju atslēgu ar nosaukumu “&KoduKarte”. Atslēgas Default vērtību uzstāda uz .htm failu, kurā saglabāts skripts, tas ir manā gadījumā, c:\i\banka.htm Papildus tam, atslēgā pievieno DWORD vērtību Contexts, kurā ierakstām “10”. Tas nozīmē, ka funkcija kontekstu dialogā būs redzama tikai tad, kad ir iezīmēts teksts. Eksportējot no reģistra, atslēga izskatās šādi:

[HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt\&KoduKarte]
@="c:\\i\\banka.htm"
"Contexts"=dword:00000010

Pēc tam atliek atvērt jaunu InternetExplorer logu un tajā jau būs pieejama jaunā funkcija. Tikai tad, kad būs atvērta internetbankas lapa, tikai tad, kad būs iezīmēts teksts un nospiesta peles labā poga.

Vēlreiz brīdinu, ka šādi darīt nav labi un pavisam noteikti IR SLIKTI, jo šādā veidā Tu samazini Tavas bankas izveidotās drošības sistēmas drošības līmeni. Jo gadījumā, ja kādam citam būs pieeja Tava datora cietajam diskam, viņš(a) varēs nolasīt failu, kur glabājas kodi un tad viņam(ai) atliks TIKAI UZMINĒT TAVU LIETOTĀJA VĀRDU UN PAROLI, lai iekļūtu bankas lapā!