Na dva kliky
14.09.2007
Lotus Notes
Richtext v Lotus Notes je známá tragédie. Nedá se s ním téměř nic dělat.
V Lotusscriptu je sada tříd, které dokážou pracovat s Notes Richtext formátem, ale řekněme si upřímě, je to naprosto nepoužitelné. Zoufalí uživatelé volají po známých funkcích z MS Word, anebo alespoň nějaké integraci, ale stále nic. Notes Richtext neumí styly. Neumí kontinuální zanořené seznamy. Neumí skoro nic. Máte jakýsi NotesRichTextNavigator, ale koukněte se na seznam druhů elementů, které zvládne najít. Je jich pět. Jenom pět ze všech možných. Objekty typu picture ignoruje, stejně jako jiné speciální objekty. Takže přenést obsah RT pole i s formátováním třeba do MS Word, nebo naopak se v podstatě nedá.
Nedávno jsem dělal jednoduchou workflow aplikaci pro oběhy ISO dokumentace. Všechny dokumenty jsou napsány v MS Word, nebo OO.o Writer. Požadavek byl jednak možnost uchování attachmentu původního dokumentu, tak i jeho hrubý přepis se základním formátováním do richtext pole. Což znamenalo napsat nějakou směrnici, uložit ji, CTRL+A, CTRL+C, přesun do pole Body a CTRL+V. Pak následovaly úpravy, protože přenosem se ztratila většina formátování, jako třeba číslované seznamy apod.
Anebo také ne. Objevil jsem metodu Import třídy NotesUIDocument, které jsem si nikdy dříve nevšiml. A to v helpu tvrdí, že je implementována už od verze 5. Stačí přesunout kurzor do cílového richtext pole a zavolat tuto metodu s parametrem filter$, což je druh souboru a filename$, tedy cesta k souboru. Výsledné formátování není úplně přesné, ale pořád lepší, než přes copy&paste.
Takže to bychom měli jednu část operace. Ale zbývá další. Je potřeba vložit příslušnou směrnici do jiného RT pole, jako attachment. Nehledě na to, že by bylo fajn už při otevření formuláře pro zadání směrnice nabídnout uživateli ke stažení šablonu příslušné směrnice pro otevření ve Wordu, resp. Writeru. A to celé na jeden, maximálně dva kliky.
Výsledek vypadá následovně:
Uživatel v LWF nastartuje proces pro příslušnou ISO dokumentaci. Otevře se mu formulář s předvyplněnými poli.

Všiměte si malého wizardu na pravé straně formuláře. Nabádá uživatele k otevření šablony, úpravě obsahu, následnému uložení a přiložení souboru. Na úrovni tohoto formuláře pouze dva kroky.
Tedy v prvním kroku provedeme pouze dvě věci.
- Nakonektíme se někam, kde máme uložené šablony, podle klíče, který tvoří třeba název LWF procesu, dohledat příslušnou šablonu, uložit ten attachment do Temp adresáře uživatele do složky s unikátním názvem a otevřít přes asociovaný program.
- Uložit si do nějakého dočasného pole ve formuláři cestu k souboru na disku pro pozdější využití. Ale lze využít i notes.ini, či uživatelův profil, pokud ho aplikace používá.
Můj zdroják pro tuto akci vypadá následovně:
Sub Click(Source As Button)
Dim Sys As New NotesUIBase ' Pomocna trida pro praci s UI
Dim Kw As New KeywordUI(Sys.Db) ' API pro praci s ciselniky a ulozistem sablon
Dim File As New FileHandle ' Trida pro praci se soubory
Dim UI As NotesUIDocument
Dim KwRepID As String
Set UI = Sys.Ws.CurrentDocument
If UI.FieldGetText("ISOTitle") = "" Then ' Nejake validace...
Msgbox "Nejprve vyplňte položku Název!", 16, "Chyba"
Exit Sub
End If
Let KwRepID = GetConfigValue(Sys.Db, "CnfKeywordRepID") ' z konfigurace nacteme replica id databaze sablon
If KwRepID = "" Then
Msgbox "Cesta k šablonám ISO není nastavena." & CRLF & "Kontaktujte technickou podporu.", 16, "Chyba"
Exit Sub
End If
If Not Kw.OpenByreplicaID(KwRepID) Then ' podle zadane rep id otevreme databazi sablon
Msgbox "Šablony ISO nejsou dostupné." & CRLF & "Kontaktujte technickou podporu.", 16, "Chyba"
Exit Sub
End If
If Not Kw.GetTemplateByCode("", "ISOTEMPLATES", UI.FieldGetText("ISOID")) Then ' podle typu iso dokumentace najdeme prislusny attachment
Msgbox "Šablona ISO pro ISO: " & UI.FieldGetText("DocName") & " nebyla nalezena." & CRLF & "Kontaktujte technickou podporu.", 16, "Chyba"
Exit Sub
End If
Call File.Save(Kw.Template) ' ulozime na disk
Let FilePath = File.Path
Let UI.Document.FilePath = FilePath ' ulozime si cestu do smluvniho pole ve formulari
Call UI.Document.Save(True, False) ' ulozime uidocument
Call UI.Refresh ' pro jistotu ho zaktualizujeme
Call File.Drop ' aby nam nas soubor destruktor tridy file nezlikvidoval – jsme totiz cistotni a obvykle po sobe uklizime
If Not File.Open Then ' pokusime se otevrit soubor pres asociovany program
Msgbox "Šablony ISO nejsou dostupné." & CRLF & "Kontaktujte technickou podporu.", 16, "Chyba"
Exit Sub
End If
End Sub
Pro otevření souboru přes asociovaný program postačí známá funkce:
Function ShellExecuteA Lib "shell32.dll" _
(Byval hwnd As Any, Byval operation As Any, Byval filename As String, _
Byval parms As Any, Byval defdir As Any, Byval show As Integer) As Long
Pokud se podaří a soubor se otevře, nic nebrání uživateli jej zeditovat a uložit. Následně se přesune do našeho formuláře a použije druhý odkaz, Přiložit soubor, který naimportuje obsah souboru do Body a spustí agenta:
Sub Click(Source As Button)
Dim Ws As New NotesUIWorkspace
Dim UI As NotesUIDocument
Dim Doc As NotesDocument
Dim RTI As NotesRichTextItem
Dim Ag As NotesAgent
Set UI = Ws.CurrentDocument
If UI.FieldGetText("FilePath") = "" Then
Msgbox "Nejprve musíte otevřít šablonu!", 16, "Chyba"
Exit Sub
End If
Call UI.GotoField("Body")
Call UI.Import("Microsoft Word", FilePath)
Call UI.Save
Call UI.Close
Set Doc = UI.Document
Let Doc.SaveOptions = "0"
Call Doc.Save(True, False)
Set Ag = Ws.CurrentDatabase.Database.GetAgent("ISOAddAttachment")
If Ag.Run(Doc.NoteID) <> 0 Then
Msgbox "Nepodařilo se přiložit soubor."
End If
End Sub
Nakonec se rozjede agent, který v backendu přiloží attachment a otevře dokument zpět do frontendu, neboť jak známo, ve frontendu nelze tuto operaci skriptem provést.
Sub Initialize
Dim Sys As New NotesUIBase
Dim Ag As NotesAgent
Dim cDb As NotesDatabase
Dim cDoc As NotesDocument
Dim UI As NotesUIDocument
Dim cRTI As NotesRichTextItem
Set Ag = Sys.Ses.CurrentAgent
Set cDb = Sys.Ses.CurrentDatabase
Set cDoc = cDb.GetDocumentByID(Ag.ParameterDocID)
Set cRTI = cDoc.GetFirstItem("ISOAttachment")
Call cRTI.EmbedObject(EMBED_ATTACHMENT, "", cDoc.FilePath(0))
Let cDoc.FilePath = ""
Call cDoc.Save(True, False)
Set UI = Sys.Ws.EditDocument(True, cDoc)
End Sub
A nakonec je potřeba ošetřit pole SaveOptions:
Sub Postopen(Source As Notesuidocument)
If Source.Document.HasItem("SaveOptions") Then
Call Source.Document.RemoveItem("SaveOptions")
Call Source.Document.Save(True, False)
End If
End Sub
A to je vše. Uživatel ušetří několik operací.