<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>JiKra.Name</title>
	<atom:link href="http://jikra.name/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://jikra.name/blog</link>
	<description>O vývoji a správě aplikací</description>
	<lastBuildDate>Wed, 09 May 2012 17:10:03 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Konstruktor a výjimky &#8211; Tertium non datur</title>
		<link>http://jikra.name/blog/2012/konstruktor-a-vyjimky-tertium-non-datur/</link>
		<comments>http://jikra.name/blog/2012/konstruktor-a-vyjimky-tertium-non-datur/#comments</comments>
		<pubDate>Wed, 09 May 2012 17:10:03 +0000</pubDate>
		<dc:creator>JiKra</dc:creator>
				<category><![CDATA[Nezařazené]]></category>

		<guid isPermaLink="false">http://jikra.name/blog/?p=149</guid>
		<description><![CDATA[Měl jsem pochyby. Zvažoval jsem pro a proti. Četl jsem různé články a diskuse na toto téma. Ale již mám jasno.

Konstruktor může v případě potřeby vyhodit výjimku a ukončit tak vytvoření objektu, pokud pro to nejsou splněny základní podmínky. Pěkně to <a href="https://twitter.com/#!/renestein/status/200207451352600578">vysvětlil</a> René Stein na Twitteru.
<blockquote>Od toho konstruktory jsou. Konstruktor buď dovolí vznik objektu v legálním stavu, anebo vyhodí výjimku. Tertium non datur (většinou)</blockquote>
Ze světa Lotusscriptu jsem byl zvyklý spíš na Initialize pattern. Objekt se vytvoří ze všech okolností a pak se volá nějaká Init metoda, která vrací true, nebo false. Tak je to nutné ve světě programovacích jazyků, které neznají výjimky. A je to špatný způsob, neboť předpokládá spolupráci programátora, který bude jednou vaše knihovny používat. Dává mu to na výběr - buď zavolá Init, anebo to nechá koňovi a program pak buď proběhne, anebo zbuchne, aniž by dal vědět, proč. V objektovém světě to však funguje s výjimkami lépe.

Ideální pro tento případ je, vzít si příklad ze skutečného světa. Vznikl by automobil, kdyby místo kovu a plastů přišla do fabriky hlína a kamení? Materiál je parametr vznikajícího objektu. Pokud je null, anebo je jiný, než je pro vytvoření objektu potřeba - objekt prostě nevznikne. Místo toho vznikne výjimka - new ArgumentException("Byl očekáván materiál typu kov a plasty!").

A stejně jako nevznikne automobil bez kovu a plastů, neměl by vzniknout ani váš objekt, pokud nejsou splněny všechny výchozí podmínky.]]></description>
		<wfw:commentRss>http://jikra.name/blog/2012/konstruktor-a-vyjimky-tertium-non-datur/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WPF DataGrid a grafický styl</title>
		<link>http://jikra.name/blog/2012/wpf-datagrid-a-graficky-styl/</link>
		<comments>http://jikra.name/blog/2012/wpf-datagrid-a-graficky-styl/#comments</comments>
		<pubDate>Fri, 02 Mar 2012 13:50:22 +0000</pubDate>
		<dc:creator>JiKra</dc:creator>
				<category><![CDATA[Vývoj]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[.net]]></category>
		<category><![CDATA[vývoj]]></category>
		<category><![CDATA[wpf]]></category>
		<category><![CDATA[xaml]]></category>

		<guid isPermaLink="false">http://jikra.name/blog/?p=138</guid>
		<description><![CDATA[Grafické styly, resp. témata jsou ve WPF silným nástrojem, který umožňuje snadno převléknout view aplikace, aniž by se muselo zasahovat do aplikační logiky. Funguje to na stejném principu jako CSS na webu, jen je to o poznání propracovanější. Témata ve WPF umí různé grafické vychytávky, od stínování či kulatých rožků, až po pokročilé animace či vázání vlastností.

V základu vypadá aplikace ve WPF obyčejně - defaultně je aplikován styl podle prostředí. Tedy s aktivním Aerem je použit styl Aero, v opačném případě styl Classic. Bavíme se o .NET 4.0, kde už je WPF Toolkit obsažen v základu.

[caption id="attachment_140" align="aligncenter" width="300" caption="WPF Aero styl"]<a href="http://jikra.name/blog/wp-content/uploads/2012/03/wpf-aerostyle.png"><img class="size-medium wp-image-140" title="wpf-aerostyle" src="http://jikra.name/blog/wp-content/uploads/2012/03/wpf-aerostyle-300x200.png" alt="WPF Aero" width="300" height="200" /></a>[/caption]

Vypadá to úplně normálně a dá se to normálně používat. Ale na druhou stranu, proč bychom používali prezentační framework s takovou silou, kdybychom chtěli pracovat s aplikacemi, které vypadají obyčejně. To je jako vzít si na sebe luxusní hadry za dvacet tisíc a pak si přes ně přehodit obyčejný pršiplášť. Klidně můžeme tu aplikaci udělat ve Windows Forms a bude to sloužit stejně. My ale chceme, aby ta aplikace vypadala cool.

Vytvořit styl, aby vypadal k světu, však není sranda. Potřebujete grafika.
Můžete si samozřejmě koupit nějaký profesionální styl, ale ty stojí nemalé peníze.
Ještě je však možnost použít styl, který někdo dal k dispozici zdarma. <a href="https://www.google.com/search?q=wpf%20expression%20dark%20codeplex#hl=cs&#38;sclient=psy-ab&#38;q=free%20wpf%20themes&#38;pbx=1&#38;oq=free%20wpf%20&#38;aq=1&#38;aqi=g4&#38;aql=&#38;gs_sm=11&#38;gs_upl=467695l471124l0l472929l13l12l0l1l1l0l144l1123l8.4l13l0&#38;gs_l=&#38;bav=on.2,or.r_gc.r_pw.r_cp.r_qf.,cf.osb&#38;fp=2b55dec719eece5a&#38;biw=1526&#38;bih=748&#38;pf=p&#38;pdl=300">Pár se jich najít dá</a>. Microsoft vydal už před pár lety pro WPF sadu základních stylů, resp. jsou to konverze ze Silverlightu, ale zcela dostačují. Dají se stáhnout z <a href="http://wpf.codeplex.com/wikipage?title=WPF%20Themes">Codeplexu</a>, anebo od léta už i přes <a href="http://nuget.org/packages/WPFToolkit">NuGet</a>. Vypadají dobře, svou roli plní a jsou plně funkční... tedy až na pár drobností - o tom však jindy...

Zásadní problém je v jedné věci - neobsahují styl pro komponentu DataGrid, což je blbé, neboť DataGrid je dost důležitá komponenta.

Já si oblíbil téma ExpressionDark a používám ho ve všech aplikacích. Bez stylů pro DataGrid to však vypadá divně.

[caption id="attachment_141" align="aligncenter" width="300" caption="WPF ExpressionDark bez stylu pro DataGrid"]<a href="http://jikra.name/blog/wp-content/uploads/2012/03/wpf-expressiondark.png"><img class="size-medium wp-image-141" title="wpf-expressiondark" src="http://jikra.name/blog/wp-content/uploads/2012/03/wpf-expressiondark-300x200.png" alt="WPF ExpressionDark bez stylu pro DataGrid" width="300" height="200" /></a>[/caption]

Chvíli jsem pátral a <a href="http://datagridthemesfromsl.codeplex.com/">vypátral</a> práci nějakého nadšence, který loni zjevně řešil stejný problém. Téma WhistlerBlue, ExpressionDark a ExpressionLight jen pro DataGrid jako doplňek ke stávajícím stylům. Na první pohled to vypadalo dobře, tak jsem to vyzkoušel. Ale ouha, musel jsem od toho upustit. Jednak to není samostantý styl, ale rozšířená komponenta, druhak nezdokumentovaná, třeťak takřka bez podpory. Taky co chci zadarmo, že jo. A přitom jsem potřeboval jenom trochu obarvit stávající DataGrid. Zkusil jsem použít jen to téma, chvíli to vypadalo dobře, ale jen do doby, než jsem zkusil zobrazit tabulku o třech tisících řádků.

DataGrid má v základním nastavení zapnutou takzvanou Virtualizaci UI. Notesáci to jistě také znají, stejně jako vývojáři ve Flexu. Jde o to, že vygenerovat najednou UI grid pro velké množství dat je velmi náročný proces jak pro paměť, tak i na čas. A navíc je to naprosto zbytečné. Proč zobrazovat data, která jsou mimo viewport uživatele, takže ten je stejně neuvidí? Proto se zobrazí určité množství řádek nad, uvnitř a pod viewportem, třeba 50 a jak uživatel scrolluje, generují se další a další řádky. V Lotus Notes je to vidět při rychlém scrollování, nebo při PageUp/Down - není to plynulé, občas se to zasekává, jak si pohled fetchuje další záznamy. Ve WPF DataGridu je to vidět když necháte šířku sloupců na Auto a sledujete, jak se při scrollování šířka postupně přizpůsobuje nejširším záznamům, jak se postupně objevují.
Jinak hezky popsaná je UI Virtualizace WPF DataGridu popsána <a href="http://blogs.microsoft.co.il/blogs/tomershamam/archive/2009/09/06/ui-virtualization-vs-data-virtualization.aspx">zde</a>.

S tématem, které jsem použil, se tato virtualizace vypnula a grid na počátku renderoval všechny řádky najednou - se třemi tisíci řádků to na mém notebooku s 8G paměti a i7 procesorem trvalo skoro minutu. To je nepoužitelné.

Tak jsem si sednul a splácal vlastní téma pro WPF DataGrid, based on ExpressionDark. Zabralo mi pár hodin, než jsem do toho proniknul a navíc to téma není hotové - dal jsem tam jen to, co jsem aktuálně potřeboval. Tudíž tam není třeba RowHeader a návazná funkcionalita. Ale prozatím to stačí.

[caption id="attachment_142" align="aligncenter" width="300" caption="WPF ExpressionDark + DataGrid"]<a href="http://jikra.name/blog/wp-content/uploads/2012/03/wpf-expressiondark-plusgrid.png"><img class="size-medium wp-image-142" title="wpf-expressiondark-plusgrid" src="http://jikra.name/blog/wp-content/uploads/2012/03/wpf-expressiondark-plusgrid-300x200.png" alt="WPF ExpressionDark + DataGrid" width="300" height="200" /></a>[/caption]

Vypadá to docela dobře, ne? Pokud má někdo zájem, může si ten styl <a href="https://skydrive.live.com/?cid=5C4048C0B4ED9028&#38;id=5C4048C0B4ED9028%21208">stáhnout</a>. Pak ho stačí přiložit k aplikaci a přidat další řádek do App.xaml... enjoy.

[crayon-4fb9c9bec4ef1 lang="XML" title="App.xaml" mark="4,5"]
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Themes\ExpressionDark.xaml" />
                <ResourceDictionary Source="Themes\ExpressionDarkDataGrid.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
[/crayon]]]></description>
		<wfw:commentRss>http://jikra.name/blog/2012/wpf-datagrid-a-graficky-styl/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Jak ve WPF zbavit DatePicker otravného hintu</title>
		<link>http://jikra.name/blog/2012/jak-ve-wpf-zbavit-datepicker-otravneho-hintu/</link>
		<comments>http://jikra.name/blog/2012/jak-ve-wpf-zbavit-datepicker-otravneho-hintu/#comments</comments>
		<pubDate>Tue, 21 Feb 2012 11:02:27 +0000</pubDate>
		<dc:creator>JiKra</dc:creator>
				<category><![CDATA[Vývoj]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[.net]]></category>
		<category><![CDATA[c#]]></category>
		<category><![CDATA[vývoj]]></category>
		<category><![CDATA[workaround]]></category>
		<category><![CDATA[wpf]]></category>

		<guid isPermaLink="false">http://jikra.name/blog/?p=129</guid>
		<description><![CDATA[Pokud jste vyvíjeli nějaký formulář ve WPF, jistě jste si všimli komponenty DatePicker. To je pole s ikonkou kalendáře, které po kliknutí rozbalí kalendář pro pohodlný výběr data. V základu ta komponenta vypadá takhle:

[caption id="attachment_131" align="aligncenter" width="119" caption="Tradiční DatePicker s hintem"]<a href="http://jikra.name/blog/wp-content/uploads/2012/02/WPFDatePicker.png"><img class="size-full wp-image-131" title="WPFDatePicker" src="http://jikra.name/blog/wp-content/uploads/2012/02/WPFDatePicker.png" alt="Tradiční DatePicker s hintem" width="119" height="30" /></a>[/caption]

Jak vidíte, je v něm hint v angličtině, co s tím máte dělat. Tyto hinty jsou v některých polích docela užitečné, ale zrovna tady je to naprosto zbytečná věc, navíc v angličtině. A co je opravdu blbé, nedá se to změnit normálním atributem. Takže to budeme muset obejít.

Nejprve si vytvoříme statickou třídu, které předáme náš DatePicker, aby se postarala o odstranění toho vodoznaku.

[crayon-4fb9c9bec9cb2 lang="C#" title="DatePickerWatermark" mark="11"]
    public static class DatePickerWatermark
    {
        public static void Remove(object sender)
        {
            var dp = sender as DatePicker;
            if (dp == null) return;

            var tb = GetChildOfType<System.Windows.Controls.Primitives.DatePickerTextBox>(dp);
            if (tb == null) return;

            var wm = tb.Template.FindName("PART_Watermark", tb) as ContentControl;
            if (wm == null) return;

            wm.Content = null;
        }

        public static T GetChildOfType<T>(DependencyObject depObj) where T : DependencyObject
        {
            if (depObj == null) return null;

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                var child = VisualTreeHelper.GetChild(depObj, i);

                var result = (child as T) ?? GetChildOfType<T>(child);
                if (result != null) return result;
            }
            return null;
        }
    }
[/crayon]

Třida si na pomoc zavolala VisualTreeHelper, našla s jeho pomocí TextBox, vložený do komponenty a pak si našla Template s názvem PART_Watermark, jehož obsah odstranila.
Mějme tedy DatePicker, vložený do našeho formuláře...

[crayon-4fb9c9bec9cf5 lang="xml" title="DatePicker"]
<DatePicker Name="Date" Width="120" Loaded="Date_Loaded"  />
[/crayon]

Kromě názvu a velikosti jsem ještě definoval reakci na událost Loaded. V této metodě zavoláme statickou třídu DatePickerWatermark s metodou Remove a předáme jí tu komponentu.

[crayon-4fb9c9bec9d34 lang="C#" title="Date_Loaded"]
        private void Date_Loaded(object sender, RoutedEventArgs e)
        {
            DatePickerWatermark.Remove(sender);
        }
[/crayon]

No a to je vše. Vodoznak je pryč. Třídu lze libovolně rozšířit, aby třeba psala vodoznak česky, nebo tam vložila obrázek - fantazii se meze nekladou. Enjoy...]]></description>
		<wfw:commentRss>http://jikra.name/blog/2012/jak-ve-wpf-zbavit-datepicker-otravneho-hintu/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WPF: Vázání dat na property</title>
		<link>http://jikra.name/blog/2012/wpf-vazani-dat-na-property/</link>
		<comments>http://jikra.name/blog/2012/wpf-vazani-dat-na-property/#comments</comments>
		<pubDate>Wed, 15 Feb 2012 14:06:47 +0000</pubDate>
		<dc:creator>JiKra</dc:creator>
				<category><![CDATA[Vývoj]]></category>
		<category><![CDATA[WPF]]></category>
		<category><![CDATA[.net]]></category>
		<category><![CDATA[c#]]></category>
		<category><![CDATA[vývoj]]></category>
		<category><![CDATA[wpf]]></category>

		<guid isPermaLink="false">http://jikra.name/blog/?p=92</guid>
		<description><![CDATA[Data binding koncept ve WPF frameworku je postaven na skutečnosti, která by se dala vyjádřit jednoduchou větou - cokoliv se dá jednoduše provázat s čímkoliv. A to jak jednosměrně, tak i obousměrně.

Díky tomuto konceptu může být prezentační logika striktně oddělena od aplikační logiky a od dat. Koncept WPF je postaven na návrhovém vzoru <a href="http://en.wikipedia.org/wiki/Model_View_ViewModel">MVVM</a> a umožňuje vyvíjet aplikační front-end vývojářům, kteří nepotřebují znát aplikační pozadí ani způsob uložení a tahání dat.

Vázat data lze jak na business objekty View-Model vrstvy, ale lze vázat i na jiné prvky v prezentační vrstvě. Já teď ukážu vázání na property aktuální třídy, což je problém, který jsem před nedávnem řešil.

Vezměme si následující případ...
Uživatel v nějakém datagridu klikne na položku a očekává, že se otevře nějaké dialogové okno, kde bude detail daného záznamu se všemi položkami, on bude moci ty položky upravit, uložit a zavřít dialog. Jak na to?

Náš DataGrid máme nabindovaný na nějaký objekt typu List&#60;T&#62;, nebo IObservable&#60;T&#62;, a to pomocí CollectionViewSource komponenty. Nějak takhle...

[crayon-4fb9c9beca6a8 lang="xml" title="DataGrid binding" mark="3,4,8"]
<Window.Resources>
    <ResourceDictionary>
        <Data:MyRecords x:Key="DataSource"/>
        <CollectionViewSource x:Key="DataCollection" Source="{StaticResource DataSource}"/>
    </ResourceDictionary>
</Window.Resources>
<DataGrid Name="GridData"
               ItemsSource="{Binding Source={StaticResource DataCollection}}">
[/crayon]

V každém řádku je tlačítko, kterým vyvoláme otevření dialogu a předáme mu přes property set daný záznam.
[crayon-4fb9c9beca6e7 lang="c#" mark="5,9,14"]private void EditDetail_Click(object sender, RoutedEventArgs e)
{
    var dataCollection = this.FindResource("DataCollection") as CollectionViewSource;
    var data = dataCollection.Source as IMyRecords;
    var record = (IMyRecord)((FrameworkElement)sender).DataContext;

    var form = new MyDetailForm();
    form.Owner = (Window)this.Parent;
    form.Record = record as MyRecord;
    form.ShowDialog();

    if (form.DialogResult == true)
    {
        record = (IMyRecord)form.Record;
        data.UpdateRecord(record);
        ReloadDataSources();
    }
}[/crayon]
Teď už je potřeba jenom si daný záznam v té cílové třídě převzít a zaregistrovat. Potřebuji k tomu třídu DependencyProperty a její metodu Register()...

[crayon-4fb9c9beca723 lang="c#" title="DependencyProperty"]
public static readonly DependencyProperty FormProperty
        = DependencyProperty.Register("Record", typeof(MyRecord),
           typeof(MyDetailForm));
[/crayon]

a pak samotnou property Record...

[crayon-4fb9c9beca75e lang="c#" title="property Record"]
public MyRecord Record
{
    get
    {
        return (MyRecord)GetValue(FormProperty);
    }
    set
    {
        SetValue(FormProperty, value);
        DataContext = value;
    }
}
[/crayon]

Nakonec už stačí jednotlivým prvkům v okně nastavit Path na příslušné pole daného záznamu...

[crayon-4fb9c9beca79a lang="xml" title="Path binding"]
<Label Content="Název">
</Label>
<TextBox Name="MyRecordName"
         Text="{Binding Path=Name}">
</TextBox>
[/crayon]

A to je v podstatě vše. Po submitu formuláře si pak nadřazené okno převezme property Record z dialogu, který má díky provázanosti na pole aktualizované hodnoty a naloží s ním podle svého - v tomto případě zavolá příkaz Update v obslužném objektu a aktualizuje grid.]]></description>
		<wfw:commentRss>http://jikra.name/blog/2012/wpf-vazani-dat-na-property/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Podepisování a šifrování zpráv v Lotus Notes – zkušenost</title>
		<link>http://jikra.name/blog/2012/podepisovani-a-sifrovani-zprav-v-lotus-notes-zkusenost-4/</link>
		<comments>http://jikra.name/blog/2012/podepisovani-a-sifrovani-zprav-v-lotus-notes-zkusenost-4/#comments</comments>
		<pubDate>Thu, 09 Feb 2012 14:59:40 +0000</pubDate>
		<dc:creator>JiKra</dc:creator>
				<category><![CDATA[Administrace]]></category>
		<category><![CDATA[Domino]]></category>
		<category><![CDATA[certifikáty]]></category>
		<category><![CDATA[domino]]></category>
		<category><![CDATA[lotus]]></category>
		<category><![CDATA[šifrování]]></category>

		<guid isPermaLink="false">http://jikra.name/blog/?p=87</guid>
		<description><![CDATA[Nedávno jsem řešil požadavek v jednom oddělení, podepisovat a šifrovat komunikaci s jednou partnerskou firmou. Ta organizace má vlastní certifikační autoritu a vydává vlastní privátní klíče pro daného člověka.

První zádrhel nastal již na počátku. Oni sice vytváří PK pro daného člověka, ale distribuuje jej na proprietárním USB tokenu, odkud nejde exportovat. Při připojení tohoto tokenu do systému se PK uloží dočasně do systémového úložiště certifikátů Windows, odkud ho lze po dobu připojení tokenu využívat. Problém je, že Lotus Notes nepoužívají systémové úložiště certifikátů. Mají vlastní systém. Privátní i veřejné klíče se ukládají jednak do ID souboru uživatele, druhak na server, do Person dokumentu tohoto uživatele. Odtud se pak používají pro šifrování a podepisování.

Po dlouhém vyjednávání vydali PK proti podpisu na CD a já ho mohl připojit k certifikátům uživatele. A zde nastal druhý zádrhel.

Lotus Notes mají od verze 8.5.2 nové kryptografické jádro, které <a href="http://www.svetnotes.cz/sn/discussion.nsf/resp/F539251508691E8BC12575BE002A3FF0?Open&#38;start=1&#38;count=50">nepodporuje šifrovací klíče, dlouhé 2048 bytů</a>, tedy SHA2. IBM to zatím neřeší, jejich odpověď je, že tuto funkci nikdy nepodporovala a fakt, že starší verze klientů to umožňovaly, nic neznamená. Naštěstí existuje docela jednoduchý workaround, a to přiložit ty klíče ve starším klientovi. Shodou okolností zde mám ještě nainstalovaného sedmičkového klienta, takže s tím nebyl problém.

Pro uložení veřejného a privátního klíče je potřeba vytvořit kontejner ve formátu PKCS12. Lze to udělat buď exportem z úložiště Windows, pomocí Windows Exploreru, anebo nějakou utilitkou, která umí tyto klíče zkombinovat. Já použil OpenSSL na linuxu.

Výsledný kontejner (.p12) je pak potřeba <a href="http://www.hansgut.com/20040120-podepisovani-a-sifrovani-notesovske-posty/">mnohokrát popsaným postupem</a> přiložit k certifikátům uživatele. Dále je potřeba přidat do osobního adresáře certifikát CA dané organizace a pak už jen stačí při psaní zprávy zatrhnout položku Podepsat.

A zde nastal další zádrhel. Klíč se zpravidla vydává na konkrétní e-mail adresu daného uživatele. Takže pokud je vydán pro e-mail frantisek.vomacka@prvni-poskytujici.cz, nelze podepsanou zprávu odeslat jako frantisek.vomacka@pr-po.cz, i když je to alias původní domény, používaný stejnou firmou. Uživatelé Lotus Notes mají svou e-mail adresu uloženu na serveru ve svém Person dokumentu, avšak tuto hodnotu lze přebít osobním Location profilem, uloženým v osobním adresáři, na který je uživatel aktuálně přihlášen. Pro případ, že aktuální adresa odesílatele tedy koliduje s adresou, uvedenou v certifikátu, je potřeba vytvořit uživateli Location profil a tam zapsat přesný tvar adresy v certifikátu. Uživatel se pro odeslání zprávy přepne do tohoto profilu a bude odesílat zprávu s tam uvedenou adresou.

Když mi to už pěkně u dané uživatelky ty zprávy podepisovalo, nastal čas vyzkoušet šifrování. A zde nastal další, naštěstí už poslední zádrhel.

K šifrování zprávy pro nějakého konkrétního adresáta potřebujete veřejný klíč, vydaný stejnou organizací, která vydala ten váš. Šifrujete veřejným klíčem adresáta, on zprávu otevírá pomocí svého privátního klíče. Tím se zamezí tomu, že si zprávu přečte někdo jiný, kdo nevlastní daný privátní klíč.

Abyste mohli šifrovat veřejným klíčem někoho jiného, potřebujete logicky, aby vám ho nějak předal. k tomu postačí, když vám pošle podepsanou zprávu. Tuto zprávu si otevřete a v nástrojovém pruhu vyberete položku Více (More) a v menu pod ní pak položku Přidat odesílatele do kontaktů... (Add Sender to Contacts...). Vyskočí na vás dialogové okno, kde se přesvědčíte, že je zatržena volba Přiložit X.509 certifikát... Ostatní položky lze upravit a po stisknutí OK se vám tento kontakt objeví v osobním adresáři, kde si můžete na poslední záložce ověřit, že je přiložen i certifikát od správného vydavatele.

A teď, v čem je ten zádrhel. Pokud tomu člověku píšete, musíte ho explicitně vybrat ze svého adresáře - kliknout na položku Pro: (To:) --&#62; Přepnout na osobní adresář --&#62; Vyhledat, vybrat a potvrdit. Jen tak zajistíte, že klient Lotus Notes daný certifikát najde a použije. A to je potřeba udělat i v případě, že odpovídáte na zprávu od toho člověka. Pokud tam je třeba předvyplněný e-mail a ten e-mail i koresponduje s tím, který je uložený v kontaktu, ten kontakt nemusí být nalezen. Vyhodí vám to ošklivé dialogové okno s hláškou, že buď nebyl nalezen příslušný kontakt, anebo byl nalezen, ale neobsahoval potřebný certifikát.

Takže vybrat, nevyplňovat ručně. V případě odpovědi raději adresu vymazat a vybrat znovu.

To jsou moje zkušenosti z cca týdenního porodu, kdy jsem se snažil nastavit jedné uživatelce možnost podepisovat a šifrovat komunikaci s nějakou firmou. Jak vidíte, není to jednoduché a upřímně říkám, že PKI architektura a navíc v kombinaci s klientem Lotus Notes, je pro obyčejné uživatele naprosto nepochopitelný obser a nedivím se, že o to téměř nikdo nestojí.]]></description>
		<wfw:commentRss>http://jikra.name/blog/2012/podepisovani-a-sifrovani-zprav-v-lotus-notes-zkusenost-4/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Stavíme wrapper nad OpenOffice.org s použitím OLE/COM I.</title>
		<link>http://jikra.name/blog/2011/stavime-wrapper-nad-openoffice-org-s-pouzitim-olecom-i/</link>
		<comments>http://jikra.name/blog/2011/stavime-wrapper-nad-openoffice-org-s-pouzitim-olecom-i/#comments</comments>
		<pubDate>Fri, 16 Dec 2011 20:19:49 +0000</pubDate>
		<dc:creator>JiKra</dc:creator>
				<category><![CDATA[LotusScript]]></category>
		<category><![CDATA[Vývoj]]></category>
		<category><![CDATA[lotus]]></category>
		<category><![CDATA[OpenOffice.org]]></category>
		<category><![CDATA[vývoj]]></category>
		<category><![CDATA[wrapper]]></category>

		<guid isPermaLink="false">http://jikra.name/blog/?p=71</guid>
		<description><![CDATA[Ač je databáze v Lotus Notes dokumentová a vývoj nám umožňuje poměrně solidní práci s formuláři a pohledy, zhusta potřebujeme pracovat s daty takovým způsobem, který se z hlubin Lotus Notes doluje velmi nesnadno. Spoustu operací provedete v nějakém kancelářském balíku lépe a rychleji. Není tedy od věci, mít možnost rychle vyexpedovat data ven, popř. je naopak importovat do databáze.

Kancelářské balíky, které stojí za úvahu, máme v současnosti na trhu v podstatě dva. V tomto článku popíšu zásadní body vývoje wrapperu pro ten druhý – OpenOffice.org. Použijeme OLE/COM model, takže hned zapomeňte na linuxové či macovské klienty. :-)
Zaměřím se na záseky, best practice, problémy, které jinde než v Lotus Scriptu nenajdete atd.
<h3>Základem všeho je služba</h3>
Open Office API je striktně typové a konvenční. Základní strukturou jsou services, neboli služby, které se volají při otevírání dokumentu. Jednoduše řečeno, o vše se stará nějaká služba. Pro inicializaci služeb potřebujeme třídu <strong>ServiceManager</strong>. Instanci této třídy zavoláme takto:

[crayon-4fb9c9becda11 lang="vb" title="ServiceManager"]Dim SM as Variant
Set SM = CreateObject(“com.sun.star.ServiceManager”)[/crayon]

CreateObject je univerzální funkce, která inicializuje OLE objekt s danou signaturou. Aplikace s volaným API musí být vždy na daném počítači nainstalována. V případě, že tento OLE objekt není nalezen, vrátí funkce chybu číslo <em>208 – Cannot create automation object</em>. Poté, co zavoláme tuto funkci, máme v proměnné SM ServiceManager, který sám o sobě neznamená nic, ale umožní nám volat potřebné služby pro práci s dokumenty OpenOffice.
<h3>Základem všeho ostatního je dokument</h3>
Základem práce s kancelářským balíkem je dokument. Pro práci s dokumentem je OOo potřeba iniciovat několik služeb.

[caption id="attachment_73" align="aligncenter" width="300" caption="Služby"]<a href="http://jikra.name/blog/wp-content/uploads/2011/12/Loadcomp.png"><img class="size-medium wp-image-73" title="OO.o services" src="http://jikra.name/blog/wp-content/uploads/2011/12/Loadcomp-300x293.png" alt="Služby" width="300" height="293" /></a>[/caption]

Jak je vidět, těch služeb je celá řada, ale naštěstí to není tak komplikované, jak to na první pohled vypadá. Postačí zavolat službu <strong>Desktop </strong>a ta se postará o zbytek.

[crayon-4fb9c9becda8f lang="vb" title="com.sun.star.frame.Desktop"]Dim Desktop as Variant
Set Desktop = SM.createInstance(„com.sun.star.frame.Desktop“)[/crayon]

O inicializaci dokumentu se stará třída <strong>com.sun.star.frame.Desktop</strong>, která implementuje rozhraní <strong>com.sun.star.frame.XComponentLoader</strong>, jednou jedinou metodou. Open Office API nerozlišuje otevírání stávajícího dokumentu od vytváření nového dokumentu jinak než parametrem této metody.

[crayon-4fb9c9becdad5 lang="vb" title="loadComponentFromURL"]Dim Document as Variant
Dim Args() as variant
Set Document = Desktop.loadComponentFromURL(URL, TargetFrameName, SearchFlags, Args())[/crayon]

Parametr URL je string, který určuje buď cestu k dokumentu, anebo typ nového dokumentu. Další dva parametry (název okna a příznak pro typ vyhledávání) není potřeba příliš řešit, v drtivé většině případů stačí zadat „_blank“ a 0. Poslední parametr je sada argumentů, které určují chování a vzhled otevíraného dokumentu. Je to pole objektů typu <strong>com.sun.star.beans.PropertyValue</strong>, kterým se jednotlivé parametry předávají formou vlastností <strong>Name </strong>a <strong>Value</strong>. Těch parametrů je celá řada a zhusta není potřeba je vůbec využívat. Pro takový případ postačí poslat do tohoto parametru prázdné pole. Ovšem občas tyto argumenty budeme potřebovat a tudíž si musíme umět vytvořit objekt <strong>PropertyValue</strong>. Budeme k tomu potřebovat objekt typu <strong>CoreReflection </strong>pro inicializaci vnitřních struktur, na které nevedou přímé reference. K tomuto účelu si zavedeme jednoduchou funkci CreateStruct.

[crayon-4fb9c9becdb15 lang="vb" title="CreateStruct"]Function CreateStruct(StructTypeName as Variant) as Variant
  Dim CR as Variant
  Dim ClassSize As Variant
  Dim Struct As Variant

  Set CR = SM.createInstance(„com.sun.star.reflextion.CoreReflextion“)
  Set ClassSize = CR.forName(StructTypeName)
  Call ClassSize.CreateObject(Struct)
  Set CreateStruct = Struct
End Function[/crayon]

Dále si pomocí této funkce vytvoříme instanci třídy PropertyValue, do které nasázíme potřebné parametry startu dokumentu.

[crayon-4fb9c9becdb53 lang="vb" title="MakePropertyValue"]Public Function MakePropertyValue(pName As Variant, pValue As Variant) As Variant
  Dim PropertyValue As Variant

  Set PropertyValue = CreateStruct("com.sun.star.beans.PropertyValue")
  Let PropertyValue.Name = pName
  Let PropertyValue.Value = pValue
  Set MakePropertyValue = PropertyValue
End Function[/crayon]

<em>Jak je vidět, nemít Variant, byl by Lotus script ztracený :-)</em>
<h3>Vytvořit nový, nebo otevřít stávající? Vše v jednom</h3>
A teď si můžeme ukázat jednoduchou funkci pro vytvoření nového dokumentu OpenOffice Calc, který otevřeme skrytý.

[crayon-4fb9c9becdb91 lang="vb" title="CreateNewDocument"]Sub CreateNewDocument
  Dim Args(0) as Variant
  Set Args(0) = MakePropertyValue("Hidden", True)
  Set Document = Desktop.loadComponentFromURL(„private:factory/scalc“, "_blank", 0, Args)
End Sub[/crayon]

Jak jsem řekl, rozdíl mezi otevíráním a vytvářením dokumentu je pouze v parametru URL. Funkce pro otevření dokumentu je tedy velmi podobná jako funkce pro vytvoření nového. Abych to nekomplikoval, předpokládejme, že opět chceme dokument otevřít skrytě.

[crayon-4fb9c9becdbcf lang="vb" title="OpenDocument"]Sub OpenDocument(URL as String)
  Dim Args(0) as Variant
  Set Args(0) = MakePropertyValue(„Hidden“, True)
  Set Document = Desktop.loadComponentFromURL(ConvertToURL(URL), „_blank“, 0, Args)
End Sub[/crayon]

Jak jste si všimli, prohnal jsem zadanou cestu jakousi záhadnou funkcí ConvertToURL. API Open Office je totiž velmi citlivé na zadané cesty, nesnese některé znaky, potřebuje speciální prefix a co je hlavní, zásadně neuznává zpětné lomítko, neboli backslash, čemuž naprosto rozumím :-) . A protože každá cesta na OS Windows je tvořena zpětnými lomítky, je potřeba zadanou cestu upravit.

[crayon-4fb9c9becdc0d lang="vb" title="ConvertToURL"]Function ConvertToURL(ByVal URL as String) as String
  Let URL = Replace(URL, "\", "/")
  Let URL = Replace(URL, ":", "&#124;")
  Let URL = Replace(URL, " ", "%20")
  If Not Left$(URL, 8) = "file:///" Then
    Let URL = "file:///" &#38; URL
  End If
  Let ConvertToURL = URL
End Function[/crayon]

Jak vidíte, práce s API Open Office má svou logiku. Vše je zde čistě objektové. Na závěr prvního dílu vytvoříme bázovou třídu, která bude mít za úkol obsluhovat základní operace s dokumenty Open Office.
<h3>Co všechno budeme do hrnečku potřebovat?</h3>
Popsali jsme si základní mechanismy vytváření a otevírání dokumentů. Popsali jsme si práci se službami a strukturami. Zbývá určit si operace, které budeme po našem objektu požadovat.

Bude to bázová třída, chceme tedy pouze jednoduché operace Open, Create, Save, SaveAs, Close. Také by se hodil Print, i když to už je trochu za hranicí základních operačních primitiv. Určitě chceme umět nastavit viditelnost dokumentu. Pak možná nějaké drobnosti jako SelectAll, CopyToClipboard, Paste apod., abychom si ukázali jak na to.

OpenDocument a CreateDocument jsem již nastínil, zaměřím se tedy rovnou na Save a SaveAs. To je v Lotus Scriptu trochu tricky záležitost. Nebo alespoň bývala ve verzi 8.0 - na 8.5.x jsem netestoval.

O co jde? Jde o to, že uložení dokumentu bez udání cesty - tedy klasický Save, nikoliv SaveAs - se volá document.store(). Ten však v LS z neznámého důvodu nefunguje. Je potřeba tedy volat SaveAs a zadat cestu. Cestu si musíme pamatovat v globální proměnné třídy. V případě, že jde o nový dokument, zachováme se správně a neuděláme nic :-) .

[crayon-4fb9c9becdc4d lang="vb" title="Save"]Public Sub Save
	If Not IsEmpty(Me.oDocument) Then
		If Me.oDocument.hasLocation() Then
			' Have to use SaveAs, because simple Doc.store() doesn't work in LS
			Call Me.SaveAs(Me.FilePath)
		End If
	End If
End Sub

Public Sub SaveAs(FilePath As String)
	Dim Args(0) As Variant

	If Not IsEmpty(Me.oDocument) Then
		Set Args(0) = Me.OOMakePropertyValue("Overwrite", True)
		Call Me.oDocument.StoreAsURL(Me.OOConvertToURL(FilePath), Args)
	End If
End Sub[/crayon]

Všimněte si vlastnosti dokumentu hasLocation(). Ta vrací true/false, zda jde o existující dokument, nebo jestli pracujeme s novým. PropertyValue jsme si již představili, já jen k názvu funkce přidal OO, abych rozlišil pomocné metody. A jinak se samozřejmě SaveAs v OpenOffice nejmenuje SaveAs, ale StoreAsURL. Jak jinak, že? :-)

Dále stojí za povšimnutí metoda, jak se sestavuje prázdné pole argumentů. V Lotus Scriptu neexistuje klasický null, je tedy potřeba vytvořit inicializované pole s jednou hodnotou, která je prázdná.

Jak takový otevřený dokument zavřeme? Jednoduše.

[crayon-4fb9c9becdc79 lang="vb" title="Close"]Public Sub Close
	If Not IsEmpty(Me.oDocument) Then
		Call Me.oDocument.Close(True)
	End If
End Sub[/crayon]

A co ten tisk? Není nic jednoduššího.

[crayon-4fb9c9becdcb8 lang="vb" title="Print"]Public Sub Print
	Dim Args() As Variant

	If Not IsEmpty(Me.oDocument) Then
		Call Me.oDocument.Print(Args)
	End If
End Sub[/crayon]

Visibility, neboli viditelnost dokumentu. Tedy atribut, zda je při práci ten dokument vidět nebo ne. Ptáte se nač to? Pro pochopení problematiky je potřeba vědět, co se stane, když uživatel spustí akci, která pomocí OLE zobrazí třeba OpenOffice Calc a začne do něj cpát data. Ten skript běží třeba 20 sekund, uživatel nevydrží a začne do toho dokumentu chudákovi skriptovi hamtat kurzorem. Začne prostě s tím dokumentem pracovat, aniž by počkal, než export doběhne. V tu ránu celá operace spadne a skript uraženě zahlásí <em>OLE Automation object error</em>. Proto je lepší provést operaci na pozadí a zobrazit uživateli dokument až nakonec. Mezitím mu můžete přehrát nějakou animaci, třeba s pomocí klasického NEMProgressBaru.

[crayon-4fb9c9becdcfd lang="vb" title="SetVisible"]Public Sub SetVisible(IsVisible As Boolean)
	Dim oDocCtrl As Variant
	Dim oDocFrame As Variant
	Dim oDocWindow As Variant
	Dim oDocBook As Variant

	If Not IsEmpty(Me.oDocument) Then
		Set oDocCtrl = Me.oDocument.getCurrentController()
		Set oDocFrame = oDocCtrl.getFrame()
		Set oDocWindow = oDocFrame.getContainerWindow()
		Set oDocBook = oDocFrame.getComponentWindow()

		Call oDocWindow.setVisible(IsVisible)
		Call oDocBook.setVisible(IsVisible)
	End If
End Sub[/crayon]

Pro volání operací jako je SelectAll, Copy, Cut, nebo Paste je potřeba vytvořit instanci dispatcheru - tedy čas na další OO helper.

[crayon-4fb9c9becdd42 lang="vb" title="Dispatcher"]Public Sub OODocumentDispatch(URL As String, Args As Variant)
	Dim oController As Variant
	Dim oFrame As Variant
	Dim oDispatcher As Variant

	If Not IsEmpty(Me.oDocument) Then
		Set oDispatcher = Me.OOCreateService("com.sun.star.frame.DispatchHelper")
		Set oController = Me.oDocument.getCurrentController()
		Set oFrame = oController.getFrame()
	End If

	Call oDispatcher.executeDispatch(oFrame, URL, "", 0, Args)
End Sub[/crayon]

URL je v tomto případě opět nějaký příkaz, který má dispatcher nad dokumentem vykonat a vykonává je nad framem, což není nic jiného, než viewport našeho dokumentu.

Zavolat zmíněné oparece je pak už triviální.

[crayon-4fb9c9becdd82 lang="vb" title="Clipboard operations"]Public Sub SelectAll
	Dim Args() As Variant

	Call Me.OODocumentDispatch(".uno:SelectAll", Args)
End Sub

Public Sub ClipboardCopy
	Dim Args() As Variant

	Call Me.OODocumentDispatch(".uno:Copy", Args)
End Sub

Public Sub ClipboardCut
	Dim Args() As Variant

	Call Me.OODocumentDispatch(".uno:Cut", Args)
End Sub

Public Sub ClipboardPaste
	Dim Args() As Variant

	Call Me.OODocumentDispatch(".uno:Paste", Args)
End Sub[/crayon]

No a to by pro dnešek bylo vše. Příště bychom si mohli popsat pokročilejší operace, konstanty, které OpenOffice používá a zkusit postavit nějaký sofistikovanější framework pro exporty a importy dat.

Na závěr přidám na ukázku celou bázovou třídu OpenOfficeBase. Enjoy. :-)

[crayon-4fb9c9becddc2 lang="vb" title="OpenOfficeBase"]%REM
%	Class OpenOfficeBase
%		Base handler for OpenOffice.org wrapper

%	@author Jiri Krakora aka Lokutus
%	@date 20.01.2010
%	@uses TObject
%	@revision 1.00.038 20.01.2010   Release
%END REM
Public Class OpenOfficeBase
	Private oSM As Variant			' ServiceManager
	Private oDesktop As Variant		' Desktop
	Private oDocument As Variant	' Office document
	Private oCR As Variant			' Core Reflection object to create structs
	Private oIsOpened As Boolean
	Private oIsVisible As Boolean
	Private oError As String

	Public Sub New
		' First, create a primary object to create necessary services
		Set Me.oSM = CreateObject("com.sun.star.ServiceManager")

		' Then create a Desktop service to handle document loading
		Set Me.oDesktop = Me.OOCreateService("com.sun.star.frame.Desktop")

		' To create objects, use a CoreReflection class
		Set oCR = Me.OOCreateService("com.sun.star.reflection.CoreReflection")
	End Sub

	%REM
	%	Create new open office document object, either calc or writer
	%
	%	@param type of the new document
	%			private:factory/swriter for Writer object
	%			private:factory/scalc for Calc object
	%	@return true/false
	%END REM
	Public Function CreateDocument(DocumentType As String) As Boolean
		Dim Args(0) As Variant				' Open params

		On Error 208 GoTo eh208

		' If there is another document opened, it must be first saved and closed
		If Me.oIsOpened Then
			Let Me.oError = "Již je otevřen jiný sešit!"
			Exit Function
		End If

		Set Args(0) = Me.OOMakePropertyValue("Hidden", Not Me.oIsVisible)
		Set Me.oDocument = Me.oDesktop.loadComponentFromURL _
		(DocumentType, "_blank", 0, Args)

		Let Me.oIsOpened = True
		Let CreateDocument = True
es:
		Exit Function
eh208:
		Let Me.oError = "Balík OpenOffice.org není pravděpodobně nainstalován!"
		Resume es
	End Function

	%REM
	%	Open existing open office document object, either calc or writer
	%
	%	@param URL of the document to be opened
	%	@return true/false
	%END REM
	Public Function OpenDocument(FilePath As String) As Boolean
		Dim Args(0) As Variant				' Open params

		On Error 208 GoTo eh208

		' If there is another document opened, it must be first saved and closed
		If Me.oIsOpened Then
			Let Me.oError = "Již je otevřen jiný sešit!"
			Exit Function
		End If

		' Check FilePath if it exists
		If Not Me.FileExists(FilePath) Then
			Let Me.oError = "Soubor nebyl nalezen!"
			Exit Function
		End If

		Set Args(0) = Me.OOMakePropertyValue("Hidden", Not Me.oIsVisible)
		Set Me.oDocument = Me.oDesktop.loadComponentFromURL _
		(Me.OOConvertToURL(FilePath), "_blank", 0, Args)

		Let Me.oIsOpened = True
		Let OpenDocument = True
es:
		Exit Function
eh208:
		Let Me.oError = "Balík OpenOffice.org není pravděpodobně nainstalován!"
		Resume es
	End Function

	%REM
	%	Save current document
	%END REM
	Public Sub Save
		If Not IsEmpty(Me.oDocument) Then
			If Me.oDocument.hasLocation() Then
				' Have to use SaveAs, because simple Doc.store() doesn't work in LS
				Call Me.SaveAs(Me.FilePath)
			End If
		End If
	End Sub

	%REM
	%	Save current document - set new filepath
	%
	%	@param filepath
	%END REM
	Public Sub SaveAs(FilePath As String)
		Dim Args(0) As Variant

		If Not IsEmpty(Me.oDocument) Then
			Set Args(0) = Me.OOMakePropertyValue("Overwrite", True)
			Call Me.oDocument.StoreAsURL(Me.OOConvertToURL(FilePath), Args)
		End If
	End Sub

	%REM
	%	Send current document to a printer
	%END REM
	Public Sub Print
		Dim Args() As Variant

		If Not IsEmpty(Me.oDocument) Then
			Call Me.oDocument.Print(Args)
		End If
	End Sub

	%REM
	%	Close current document
	%END REM
	Public Sub Close
		If Not IsEmpty(Me.oDocument) Then
			Call Me.oDocument.Close(True)
		End If
	End Sub

	%REM
	%	Set user visibility of the current document
	%
	%	@param true/false if visible or not
	%END REM
	Public Sub SetVisible(IsVisible As Boolean)
		Dim oDocCtrl As Variant
		Dim oDocFrame As Variant
		Dim oDocWindow As Variant
		Dim oDocBook As Variant

		If Not IsEmpty(Me.oDocument) Then
			Set oDocCtrl = Me.oDocument.getCurrentController()
			Set oDocFrame = oDocCtrl.getFrame()
			Set oDocWindow = oDocFrame.getContainerWindow()
			Set oDocBook = oDocFrame.getComponentWindow()

			Call oDocWindow.setVisible(IsVisible)
			Call oDocBook.setVisible(IsVisible)
		End If
	End Sub

	%REM
	%	Select whole content of the current document
	%		simulate Ctrl+A
	%END REM
	Public Sub SelectAll
		Dim Args() As Variant

		Call Me.OODocumentDispatch(".uno:SelectAll", Args)
	End Sub

	%REM
	%	Copy selection into a clipboard
	%		simulate Ctrl+C
	%END REM
	Public Sub ClipboardCopy
		Dim Args() As Variant

		Call Me.OODocumentDispatch(".uno:Copy", Args)
	End Sub

	%REM
	%	Copy selection into a clipboard and delete content
	%		simulate Ctrl+X
	%END REM
	Public Sub ClipboardCut
		Dim Args() As Variant

		Call Me.OODocumentDispatch(".uno:Cut", Args)
	End Sub

	%REM
	%	Paste content of the clipboard into document
	%		simulate Ctrl+V
	%END REM
	Public Sub ClipboardPaste
		Dim Args() As Variant

		Call Me.OODocumentDispatch(".uno:Paste", Args)
	End Sub
'---+----+10--+----+20--+----+30--+----+40--+----+50--+----+60--+----+70--+----+
'           ____  ____        ___   ___  ______ __    __
'          / __ \/ __ \ ___  / _ &#124; / _ \/  _/ // /__ / /__ ___ _______
'         / /_/ / /_/ // _ \/ __ &#124;/ ___// // _  / -_) / _ Y -_) __(_-&#60;
'         \____/\____(_)___/_/ &#124;_/_/  /___/_//_/\__/_/ .__&#124;__/_/ /___/
'                                                   /_/
'---+----+10--+----+20--+----+30--+----+40--+----+50--+----+60--+----+70--+----+
	%REM
	%	Create new service using service manager
	%
	%	@param service type name (e.g. com.sun.star.frame.Desktop)
	%	@return newly create instance of the required service
	%END REM
	Public Function OOCreateService(ServiceTypeName As String) As Variant
		Set OOCreateService = Me.oSM.createInstance(ServiceTypeName)
	End Function

	%REM
	%	Create new structure using CoreReflexion class
	%
	%	@param structure type name (e.g. com.sun.star.beans.PropertyValue)
	%	@return newly create instance of the required structure
	%END REM
	Public Function OOCreateStruct(StructTypeName As String) As Variant
		Dim ClassSize As Variant
		Dim oStruct As Variant

		Set ClassSize = Me.oCR.forName(StructTypeName)
		Call ClassSize.CreateObject(oStruct)

		Set OOCreateStruct = oStruct
	End Function

	%REM
	%	Create command dispatcher and perform a dispatch action
	%
	%	@param url or the command (e.g. .uno:Copy)
	%	@param arguments
	%END REM
	Public Sub OODocumentDispatch(URL As String, Args As Variant)
		Dim oController As Variant
		Dim oFrame As Variant
		Dim oDispatcher As Variant

		If Not IsEmpty(Me.oDocument) Then
			Set oDispatcher = Me.OOCreateService("com.sun.star.frame.DispatchHelper")
			Set oController = Me.oDocument.getCurrentController()
			Set oFrame = oController.getFrame()
		End If

		Call oDispatcher.executeDispatch(oFrame, URL, "", 0, Args)
	End Sub

	%REM
	%	Create an instance of the property object with key and value
	%
	%	@param required property name
	%	@param required property value
	%	@return instance of the PropertyValue object
	%END REM
	Public Function OOMakePropertyValue(pName As Variant, pValue As Variant) _
	As Variant
		Dim oPropertyValue As Variant

		Set oPropertyValue _
		= Me.OOCreateStruct("com.sun.star.beans.PropertyValue")
		Let oPropertyValue.Name = pName
		Let oPropertyValue.Value = pValue 

		Set OOMakePropertyValue = oPropertyValue
	End Function

	%REM
	%	Return converted url
	%
	%	@param url to be converted
	%	@return converted url
	%END REM
	Public Function OOConvertToURL(ByVal URL As String) As String
		Let URL = Replace(URL, "\", "/")
		Let URL = Replace(URL, ":", "&#124;")
		Let URL = Replace(URL, " ", "%20")

		If Not Left$(URL, 8) = "file:///" Then
			Let URL = "file:///" &#38; URL
		End If

		Let OOConvertToURL = URL
	End Function

	%REM
	%	Return current instance of the service manager object
	%
	%	@return current instance of the service manager object
	%END REM
	Public Property Get Application As Variant
		Set Application = Me.oSM
	End Property

	%REM
	%	Return current instance of the desktop object
	%
	%	@return current instance of the desktop object
	%END REM
	Public Property Get Desktop As Variant
		Set Desktop = Me.oDesktop
	End Property

	%REM
	%	Return current instance of the document object
	%
	%	@return current instance of the document object
	%END REM
	Public Property Get Document As Variant
		Set Document = Me.oDocument
	End Property

	Public Property Get Error As String
		Let ~Error = Me.oError
	End Property

	%REM
	%	Set current instance of the document object
	%
	%	@param document object
	%END REM
	Public Property Set Document As Variant
		Set Me.oDocument = Document
	End Property

	%REM
	%	Return filepath of the current document
	%
	%	@return filepath of the current document
	%END REM
	Public Property Get FilePath As String
		If Not IsEmpty(Me.oDocument) Then
			If Me.oDocument.hasLocation() Then
				Let FilePath = Me.oDocument.getURL()
			End If
		End If
	End Property

	%REM
	%	Return filename of the current document
	%
	%	@return filename of the current document
	%END REM
	Public Property Get FileName As String
		Let FileName = StrRightBack(Me.FilePath, "/")
	End Property

	%REM
	%	Return current instance of the core reflection object
	%
	%	@return current instance of the core reflection object
	%END REM
	Public Property Get CoreReflection As Variant
		Set CoreReflection = Me.oCR
	End Property

	%REM
	%	Return current instance of the core reflection object
	%
	%	@return current instance of the core reflection object
	%END REM
	Public Property Get CR As Variant
		Set CR = Me.oCR
	End Property

	%REM
	%	Return true/false if current document is modified
	%
	%	@return true/false
	%END REM
	Public Property Get IsModified As Variant
		Set IsModified = Me.oDocument.isModified()
	End Property

	%REM
	%	Return true/false if current document is visible
	%
	%	@return true/false
	%END REM
	Public Property Get IsVisible As Boolean
		Let IsVisible = Me.oIsVisible
	End Property

	%REM
	%	Set user visibility of the current document
	%
	%	@return true/false
	%END REM
	Public Property Set IsVisible As Boolean
		Let Me.oIsVisible = IsVisible
		Call Me.SetVisible(IsVisible)
	End Property

	%REM
	%	Check if Open Office is installed on the current computer
	%		throw OLE Automation error if not
	%
	%	@return true/false
	%END REM
	Public Property Get IsOfficeInstalled As Boolean
		Dim tmp As Variant

		On Error 208 GoTo eh208
		Set tmp = CreateObject("com.sun.star.ServiceManager")
		Let IsOfficeInstalled = True
es:
		Exit Property
eh208:
		Let Me.oError = "Balík OpenOffice.org není pravděpodobně nainstalován!"
		Resume es
	End Property

	%REM
	%	Check if the file exists
	%
	%	@param filepath
	%	@return true/false
	%END REM
	Private Function FileExists(Path As String) As Boolean
		On Error 53 GoTo eh53
		If (GetFileAttr(Path) And 16) = 0 Then
			Let FileExists = True
		End If
es:
		Exit Function
eh53:
		Resume es
	End Function
End Class[/crayon]]]></description>
		<wfw:commentRss>http://jikra.name/blog/2011/stavime-wrapper-nad-openoffice-org-s-pouzitim-olecom-i/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Jak zrekonstruovat ztracený proces v Lotus Workflow</title>
		<link>http://jikra.name/blog/2011/jak-zrekonstruovat-ztraceny-proces-v-lotus-workflow/</link>
		<comments>http://jikra.name/blog/2011/jak-zrekonstruovat-ztraceny-proces-v-lotus-workflow/#comments</comments>
		<pubDate>Fri, 16 Dec 2011 16:52:29 +0000</pubDate>
		<dc:creator>JiKra</dc:creator>
				<category><![CDATA[Lotus Workfow]]></category>
		<category><![CDATA[Vývoj]]></category>
		<category><![CDATA[domino]]></category>
		<category><![CDATA[lotus]]></category>
		<category><![CDATA[vývoj]]></category>
		<category><![CDATA[workflow]]></category>

		<guid isPermaLink="false">http://jikra.name/blog/?p=65</guid>
		<description><![CDATA[Stala se mi nemilá věc.  Ztratil se mi workflow proces. Tedy neztratil se celý, jen nešel vyvolat v programu Lotus Workflow Architect, což je, vzhledem ke způsobu jeho uložení, dost průser. Proces se dá rozumě upravit v podstatě jen v této aplikaci.

První, co jsem udělal, bylo, že jsem si ověřil, zda se ztratil celý proces (sada dokumentů v modulu <strong>Process Definition</strong>, které ho určují).  Proces byl v pořádku a po krátkém testu, kdy jsem ho spustil a celý prošel, jsem se ujistil, že tento problém nehoří. Následně jsem zjistil, že i dokumenty, které definují design procesního diagramu (<strong>Design Repository</strong>) jsou přítomny. Jediné, co chybělo, byl jediný dokument, typu <strong>Process</strong> (v pohledu <strong>ProcessCurrent</strong>), který funguje jako definiční. V podstatě udržuje informace o názvu procesu a vazbách na ostatní designové dokumenty. Tento dokument je klíčový pro aplikaci Lotus Workflow Architect, neboť podle něj dokáže načíst celý proces. Není definiční dokument, není proces.

A teď, co s tím? Proces byl dost komplikovaný a provázaný s mnoha prvky ve formulářích, nemohl jsem ho jen tak, z hlavy, vystřihnout znovu. Bylo potřeba ho zrekonstruovat z údajů, které jsem měl.

Nabízely se dva postupy. Jednak jsem mohl zkusit zrekonstruovat samotný dokument typu Process, druhak zrekonstruovat celý proces a udělat ho podle vodítek znovu. Zvolil jsem druhou možnost. Samotný dokument v sobě udržuje desítky polí, o kterých nevím, k čemu jsou, a která se mi nechtělo rozebírat.  A navíc jsem neměl tušení, zda nechybí i další procesní dokumenty.

První krok byl nasnadě. Zrekonstruovat jednotlivé kroky procesu a cesty mezi nimi. K tomu mi výborně posloužil další nástroj – Lotus Workflow Viewer. Ten vám nad dokumentem v procesu zobrazí diagram s cestou, kterou dokument doposud urazil (Více informací/Zobrazit diagram úlohy).

[caption id="attachment_60" align="aligncenter" width="286" caption="Náhled na proces v Lotus Workflow Viewer"]<a href="http://jikra.name/blog/wp-content/uploads/2011/12/Viewer.png"><img class="size-medium wp-image-60" title="Lotus Workflow Viewer" src="http://jikra.name/blog/wp-content/uploads/2011/12/Viewer-286x300.png" alt="Náhled na proces v Lotus Workflow Viewer" width="286" height="300" /></a>[/caption]

V tomto diagramu lze přečíst, i komu konkrétně v kterém kroku daný dokument patří, popř. kdo ho může číst. Jsou zde vidět podmínky, formuláře, jednotlivá rozhodnutí, tedy většina věcí.

[caption id="attachment_61" align="aligncenter" width="300" caption="Properties"]<a href="http://jikra.name/blog/wp-content/uploads/2011/12/Viewer-Properties.png"><img class="size-medium wp-image-61" title="Viewer Properties" src="http://jikra.name/blog/wp-content/uploads/2011/12/Viewer-Properties-300x222.png" alt="Workflow Viewer Properties" width="300" height="222" /></a>[/caption]

[caption id="attachment_62" align="aligncenter" width="300" caption="Advanced Properties"]<a href="http://jikra.name/blog/wp-content/uploads/2011/12/Viewer-AdvancedProperties.png"><img class="size-medium wp-image-62" title="Viewer Advanced Properties" src="http://jikra.name/blog/wp-content/uploads/2011/12/Viewer-AdvancedProperties-300x223.png" alt="Workflow Viewer Advanced Properties" width="300" height="223" /></a>[/caption]

Poté, co jsem takto zrekonstruoval syrový proces, zbývala jedna věc.  Doplnit vlastní pole a jejich hodnoty v každém kroku, kde se nějaké plní. Ty nejsou v prohlížeči zobrazeny, ale pro oběh dokumentu jsou velmi důležité, neboť si do nich vývojář zpravidla ukládá dočasné proměnné.

K tomu mi pro změnu posloužil modul Process Definition, kde jsem si rozbalil aktuální verzi procesu a pro každý krok si zobrazil vlastnosti dokumentu. Na druhé záložce jsou informace o uložených polích. Seznam vlastních polí je pak uložen v prvním poli CustomFieldsOS a samotná pole najdete pak v seznamu i s jejich hodnotami.

[caption id="attachment_63" align="aligncenter" width="300" caption="Pole CustomFieldsOS - seznam polí"]<a href="http://jikra.name/blog/wp-content/uploads/2011/12/CustomFieldsOS.png"><img class="size-medium wp-image-63" title="Pole CustomFieldsOS" src="http://jikra.name/blog/wp-content/uploads/2011/12/CustomFieldsOS-300x202.png" alt="Pole CustomFieldsOS" width="300" height="202" /></a>[/caption]

[caption id="attachment_64" align="aligncenter" width="300" caption="CustomFieldOS - vzorec pro výpočet hodnoty pole"]<a href="http://jikra.name/blog/wp-content/uploads/2011/12/CustomFieldsOS-DocHistory.png"><img class="size-medium wp-image-64" title="CustomFieldsOS-DocHistory" src="http://jikra.name/blog/wp-content/uploads/2011/12/CustomFieldsOS-DocHistory-300x205.png" alt="Pole CustomFieldOS - hodnoty polí" width="300" height="205" /></a>[/caption]

Pak není nic jednoduššího, než do každého kroku patřičná pole doplnit, aktivovat proces a se stávajícími formuláři ho otestovat.

Nakonec doporučuji v Lotus Workflow Architect vybrat File/Export/LWF File/Cesta a výsledný soubor uložit jako zálohu. Ať nemusíte trávit neplodné odpoledne rekonstrukcí procesu jako já. ;-)

[caption id="attachment_66" align="aligncenter" width="295" caption="Hotový proces"]<a href="http://jikra.name/blog/wp-content/uploads/2011/12/ProcessFinal.png"><img class="size-medium wp-image-66" title="ProcessFinal" src="http://jikra.name/blog/wp-content/uploads/2011/12/ProcessFinal-295x300.png" alt="Hotový proces" width="295" height="300" /></a>[/caption]]]></description>
		<wfw:commentRss>http://jikra.name/blog/2011/jak-zrekonstruovat-ztraceny-proces-v-lotus-workflow/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Winmail.dat a Domino</title>
		<link>http://jikra.name/blog/2011/winmail-dat-a-domino/</link>
		<comments>http://jikra.name/blog/2011/winmail-dat-a-domino/#comments</comments>
		<pubDate>Fri, 16 Dec 2011 16:01:23 +0000</pubDate>
		<dc:creator>JiKra</dc:creator>
				<category><![CDATA[Administrace]]></category>
		<category><![CDATA[Domino]]></category>
		<category><![CDATA[administrace]]></category>
		<category><![CDATA[domino]]></category>
		<category><![CDATA[lotus]]></category>
		<category><![CDATA[microsoft]]></category>
		<category><![CDATA[tnef]]></category>

		<guid isPermaLink="false">http://jikra.name/blog/?p=54</guid>
		<description><![CDATA[To jsem zase objevil Ameriku. Zjistil jsem, že si Domino už od verze 6.5.6 umí poradit s otravnými soubory v obskurním Microsoftím formátu <a href="http://en.wikipedia.org/wiki/Transport_Neutral_Encapsulation_Format">TNEF</a>. Co to je, a jak to neodesílat, je dobře popsáno <a href="http://myego.cz/item/jak-neposilat-z-outlooku-prijemcum-winmail-dat">všude možně</a>. Dříve se to i na Dominu řešilo <a href="http://www.hansgut.com/20041110-jak-na-winmaildat/">řadou utilitek</a>.

Od verze Domino 6.5.6, která měla release date 27.3.2007 existuje v notes.ini několik nových parametrů, které ten problém řeší. Pečlivě popsáno je to <a href="http://www.lntoolbox.com/en/categories/notes-domino/222-howto-configure-domino-to-autom-convert-the-winmaildat-files.html">zde</a>, a to i s ukázkovým nastavením v notes.ini. Po následném restartu serveru už chodí winmail.dat pěkně rozbalené.

Parametrů je 5.
<div id="_mcePaste"><a href="http://www.lntoolbox.com/en/notesini-reference/bycategory/smtp-configuration/20-SMTP_Configuration/2181-TNEFEnableConversion.html">TNEFEnableConversion</a>=1</div>
<div>Říká serveru, zda má winmail.dat přílohy konvertovat.</div>
<div>1=Ano, 0=Ne.</div>
<div id="_mcePaste"><a href="http://www.lntoolbox.com/en/notesini-reference/bycategory/smtp-configuration/20-SMTP_Configuration/2179-TNEFAttachRTF.html">TNEFAttachRTF</a>=1</div>
<div>Určuje, zda se má přiložit příloha, kterou obsahuje winmail.dat.</div>
<div>1=Ano, 0=Ne.</div>
<div id="_mcePaste"><a href="http://www.lntoolbox.com/en/notesini-reference/bycategory/smtp-configuration/20-SMTP_Configuration/1860-TNEFConverter_Log_Level.html">TNEFConverter_Log_Level</a>=20</div>
<div>Nastavuje úroveň logování.</div>
<div>
<div>10: minimal (pouze chyby)</div>
<div>20: normal (chyby a základní informace)</div>
<div>30: informational (chyby, základní informace plus nějaké dodatečné informace)</div>
<div>40: verbose (ukecaný log)</div>
</div>
<div id="_mcePaste"><a href="http://www.lntoolbox.com/en/notesini-reference/bycategory/smtp-configuration/20-SMTP_Configuration/1861-TNEFKeepAttachment.html">TNEFKeepAttachment</a>=0</div>
<div>Nastavuje, zda se má přiložit i samotný winmail.dat soubor.</div>
<div>1=Ano, 0=Ne.</div>
<div id="_mcePaste"><a href="http://www.lntoolbox.com/en/notesini-reference/bycategory/smtp-configuration/20-SMTP_Configuration/2180-TNEFBreakSMIME.html">TNEFBreakSMIME</a>=1</div>
<div>Nastaví zpracování příloh jako S/MIME.</div>
<span style="line-height: 19px;">1=Ano, 0=Ne.</span>

<span style="line-height: 19px;">Po tomto ukázkovém nastavení začne server rozbalovat winmail.dat soubory, přiloží ke zprávě původní soubor a samotný winmail.dat vyčutne. Enjoy.</span>

Mimochodem. Tohle je pěkná ukázka toho, kterak lze dominantním postavením v nějaké oblasti vnutit konkurenci vlastní implementaci nějakého formátu, byť je sebeidiotštější. :-)]]></description>
		<wfw:commentRss>http://jikra.name/blog/2011/winmail-dat-a-domino/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Multitail &#8211; barvičky v logu Domina</title>
		<link>http://jikra.name/blog/2011/multitail-barvicky-v-logu-domina/</link>
		<comments>http://jikra.name/blog/2011/multitail-barvicky-v-logu-domina/#comments</comments>
		<pubDate>Tue, 06 Dec 2011 15:12:52 +0000</pubDate>
		<dc:creator>JiKra</dc:creator>
				<category><![CDATA[Administrace]]></category>
		<category><![CDATA[Domino]]></category>
		<category><![CDATA[administrace]]></category>
		<category><![CDATA[domino]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[lotus]]></category>

		<guid isPermaLink="false">http://jikra.name/blog/?p=27</guid>
		<description><![CDATA[Pokud provozujete Domino server na Linuxu, jistě sledujete výstup z logu přes nějakého telnetového klienta, jako je PuTTY.
Log si pak necháváte zobrazit typicky pomocí příkazu <strong>tail -f /var/log/domino/notes.log</strong>. Výstupem je pak klasické bílé písmo na černém pozadí, což je nepřehledné a špatně se v tom hledají informace. Obzvláště když vám log běží na druhém monitoru, uvítali byste jistě lepší vypovídací schopnosti toho výpisu.

Řešení existuje, jmenuje se Multitail. Pokud zavoláte klasický tail přes tuto utilitku, zobrazí vám log barevně, a to podle konfigurace.

[caption id="attachment_25" align="aligncenter" width="300" caption="Multitail v praxi (kliknutím zvětšíte)"]<a href="http://jikra.name/blog/wp-content/uploads/2011/12/Screenshot.png"><img class="size-medium wp-image-25  " title="Multitail" src="http://jikra.name/blog/wp-content/uploads/2011/12/Screenshot-300x178.png" alt="Multitail v praxi" width="300" height="178" /></a>[/caption]

Program můžete stáhnout <a href="http://www.vanheusden.com/multitail/">zde</a>. Je to klasický rpm balíček, provozujeme Domino na SLES. Instalátor vám vytvoří konfigurační soubor v <strong>/etc/multitail.conf</strong>, kde jsou pro každý typ logu definována barevná schémata. Konfigurační soubor si otevřete pro editaci třeba přes mcedit a vložte novou sekci colorscheme pro domino.

[crayon-4fb9c9bed56fb title="multitail.conf" mark="7,8"]# domino
colorscheme:domino:IBM Lotus Domino
#cs_re:blue&#124;blue,,bold:..:..:..
#cs_re_s:,,bold:..:..:.. [^ ]* ([A-z0-9]*)
cs_re:red,,inverse:(.*error.*)?(.*Error.*)?(.*Entry not found in index.*)?(.* .nable.*)?(.*arning.*)?(.*not listed in Domino Directory)?
cs_re:magenta,,inverse:.*ATTEMPT TO ACCESS DATABASE .* was denied.*
cs_re:red,,bold:(Jiri Krakora)?(.*krakora@.*)?
cs_re:yellow:( [a-zA-Z0-9]+ [a-zA-Z0-9]+/[a-zA-Z0-9]+/Alliance/CZ)+
cs_re:magenta,,bold:([a-zA-Z0-9_\.-]+@[a-zA-Z0-9_\.-]+)
cs_re:green,,bold:(added [0-9]+ document\(s\))?
cs_re:red,,bold:(deleted [0-9]+ document\(s\))?
cs_re:white,,bold:(updated [0-9]+ document\(s\))?
cs_re:cyan:(.*Replicator.*)?(.* Closed session for .*)?
cs_re:cyan,,bold:(.*Pushing.*)?(.*Pulling.*)?
cs_re:cyan,,inverse:(.* Opened session for .*)?
cs_re:green:.*SMTP Server.*
cs_re:magenta:.*emote console.*
cs_re:yellow,,inverse:(^sh.*)?(^tell.*)?(^restart.*)?(^dbcache.*)?(^load.*)?(^set.*)?(^stop.*)?(^start.*)?(^trace.*)?
cs_re:yellow:.*Admin Process: .*
cs_re:red:(.*Agent Manager: .*)?(.*AMgr: .*)?
cs_re:red,white:.*Agent printing: .*
cs_re:blue,,bold:.*Router.*
cs_re:white,,bold:^ .*
cs_re:green:(.*tarted.*)?(.*oaded.*)?(.*nitialized.*)?(.*Done.*)?(.*done.*)?[/crayon]

Jak sami vidíte, schéma se definuje pomocí klasických unix-like regulárních výrazů, nic složitého.

Každá definice je umístěna na svém řádku, uvozena je řetězcem <strong><em>cs_re:</em></strong>, který je povinný. Následuje definice barvy písma, těch je osm - blue, red, yellow, white, magenta, green, cyan, . Další je tučnost textu a definice inverze - tedy zda se z barvy písma má stát barva pozadí. Pak dvojtečka a samotný regulární výraz, který ovlivňuje výběr.
Mám tam dva řádky, které definují mé jméno v organizační struktuře a v e-mail adrese a organizační strukturu firmy jako takovou. Mé jméno se ve výpisu zobrazí výraznou červenou  barvou, jakékoliv jméno v hierarchickém formátu zase žlutě. Ostatní e-mail adresy budou zvýrazněny modře. To jen na ukázku, co všechno to dovede, samozřejmě si to změňte, nebo smažte - já se asi ve vašem logu vyskytovat nebudu. :-)

Pak stačí zavolat místo
<blockquote>tail -f /var/log/domino/notes.log</blockquote>
příkaz
<blockquote>multitail -cS domino /var/log/domino/notes.log</blockquote>
K tomu se také hodí jednoduchý skriptík, abyste nemuseli neustále psát tak dlouhý příkaz. Vytvořte si <strong>/usr/local/bin/tail-log</strong>, do kterého vepište následující kód.

[crayon-4fb9c9bed57aa title="multi-tail"]#!/bin/sh
. /etc/profile
DOMINO_LOG='-cS domino /var/log/domino/notes.log'
SYSTEM_LOG='-cS syslog /var/log/messages'

MYCMD=`which multitail`
MYCMD="${MYCMD} -s 2 "

case $1 in
"domino")&#60;-&#62;MYCMD="${MYCMD} ${DOMINO_LOG}" ;;
"system")&#60;-&#62;MYCMD="${MYCMD} ${SYSTEM_LOG}" ;;
"all")&#60;----&#62;for i in "${DOMINO_LOG}" "${SYSTEM_LOG}" ; do
&#60;------&#62;&#60;------&#62;    MYCMD="${MYCMD} ${i}"
&#60;------&#62;&#60;------&#62;done
&#60;------&#62;&#60;------&#62;;;
*)&#60;&#62;echo "Usage: `basename $0` ( domino &#124; system &#124; all )"
&#60;------&#62;exit 0
&#60;------&#62;;;
esac

eval $MYCMD[/crayon]

Pak je potřeba udělat ho spustitelným --&#62; <strong>chmod 755 /usr/local/bin/tail-log</strong>, no a pak můžete spouštět log domina pohodlně:
<blockquote>tail-log domino</blockquote>]]></description>
		<wfw:commentRss>http://jikra.name/blog/2011/multitail-barvicky-v-logu-domina/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

