From 4576f3135ba88c160b960dcbd0a44fe05b2ff6f5 Mon Sep 17 00:00:00 2001 From: Marcin Wojas Date: Sat, 16 May 2026 17:56:33 +0200 Subject: [PATCH] action-result.md --- soneta-programming/SKILL.md | 2 + .../references/action-result.md | 593 ++++++++++++++++++ 2 files changed, 595 insertions(+) create mode 100644 soneta-programming/references/action-result.md diff --git a/soneta-programming/SKILL.md b/soneta-programming/SKILL.md index 0427784..4638094 100644 --- a/soneta-programming/SKILL.md +++ b/soneta-programming/SKILL.md @@ -410,6 +410,8 @@ class Test { - **[references/datapack-guidedrow.md](references/datapack-guidedrow.md)** - Paczki danych, GuidedRow, ExportedRow, synchronizacja - **[references/context.md](references/context.md)** - Klasa Context, komunikacja UI ↔ logika - **[references/examples.md](references/examples.md)** - Przykłady kodu i wzorce użycia +- **[references/action-result.md](references/action-result.md)** - Action result zwracane przez worker/extender/Command + (typy obsługiwane przez ResultHandler) i własne handlery ## Szybki start - wzorce kodu diff --git a/soneta-programming/references/action-result.md b/soneta-programming/references/action-result.md new file mode 100644 index 0000000..0667dcd --- /dev/null +++ b/soneta-programming/references/action-result.md @@ -0,0 +1,593 @@ +# Action result zwracany przez akcje (worker, extender, Command) + +Akcje (metody worker, akcje extender, handlery Command, callbacki) zwracają **action result** — obiekt +sterujący tym, co stanie się w UI po wykonaniu logiki biznesowej. Typ zwróconego obiektu decyduje +o sposobie obsługi. + +## Najważniejsza zasada + +**Nie wywołuj sam UI z poziomu worker/extender.** Zamiast pokazywać MessageBox, otwierać formularz +czy generować plik — **zwróć odpowiedni action result**. Framework zrobi resztę, panując nad sesją, +transakcją i wątkiem UI. + +```csharp +// ŹLE - kod biznesowy nie powinien znać UI +[Action("Sprawdź")] +public void Sprawdz() { + MessageBoxWindow.Show("Niepoprawne dane".Translate()); // <- nie tak +} + +// DOBRZE - zwracamy action result +[Action("Sprawdź")] +public object Sprawdz() { + return new MessageBoxInformation("Walidacja".Translate(), "Niepoprawne dane".Translate()); +} +``` + +## Wartości specjalne + +| Zwrócona wartość | Działanie | +|---|---| +| `null` | Brak akcji. Transakcja jest commitowana. | +| `void` (metoda bez return) | Brak akcji. | +| `Task` / `Task` | Framework czeka na zakończenie i obsługuje zwróconą wartość. | +| `Exception` | Pokazuje okno błędu (`MessageBoxWindow.ShowException`). Transakcja jest commitowana (rollback w wyjątku robi się wcześniej). | + +## Sterowanie aktualnym formularzem + +### `FormAction` (enum) + +Najprostsza forma — sama wartość enum jako action result. Handler konwertuje to +do `FormActionResult` z ustawioną akcją. + +| Wartość | Działanie | +|---|---| +| `None` | Brak akcji. | +| `Save` | Zapis bez zamykania, bez potwierdzeń warningów. | +| `SaveWithConfirmation` | Zapis bez zamykania, z potwierdzeniem warningów. | +| `SaveAndClose` | Zapis i zamknięcie, bez potwierdzeń. | +| `SaveAndCloseWithConfirmation` | Zapis i zamknięcie, z potwierdzeniem warningów. | +| `Refresh` | Odczyt danych formularza z bazy. | +| `RefreshOwner` | Odświeżenie formularza-rodzica. | +| `Close` | Zamknięcie bez zapisu, bez ostrzeżeń. | + +```csharp +[Action("Zatwierdź")] +public FormAction Zatwierdz() { + Status = Status.Zatwierdzony; + return FormAction.SaveAndClose; +} +``` + +### `FormActionResult` + +Rozszerzona wersja `FormAction` — pozwala dodać `EditValue` (kontynuacja po zapisie), `Context`, +`CommittedHandler` (kod wywoływany po commit transakcji). + +```csharp +return new FormActionResult { + Action = FormAction.SaveAndClose, + EditValue = nowyDokument, // otwarcie kolejnego obiektu po zapisie + CommittedHandler = context => null, +}; +``` + +## Okna informacyjne i pytania do użytkownika + +### `string` + +Krótki komunikat — handler konwertuje do `MessageBoxInformation` z domyślnym tytułem "Informacja". + +```csharp +return "Operacja zakończona pomyślnie".Translate(); +``` + +### `MessageBoxInformation` + +Pełna kontrola nad okienkiem dialogowym. Przyciski (OK / Anuluj / Tak / Nie) pojawiają się +automatycznie na podstawie ustawionych handlerów (`OKHandler`, `CancelHandler`, `YesHandler`, +`NoHandler`). Handler każdego przycisku jest typu `Func` — **może zwrócić kolejny action result**, +który zostanie obsłużony rekurencyjnie (mechanizm `DelayedHandler`). + +| Property | Znaczenie | +|---|---| +| `Caption` | Tytuł okna (NULL = standardowy zależny od `Type`). | +| `Text` / `TextMarkdown` | Treść (czysty tekst lub markdown). | +| `Type` | `Information` / `Warning` / `Error` — kolorystyka. | +| `OKHandler` / `CancelHandler` / `YesHandler` / `NoHandler` | Akcje po wybraniu przycisku. | +| `IsSecondDefault` | `Enter` wywołuje drugą akcję (No/Cancel zamiast Yes/OK). | + +```csharp +return new MessageBoxInformation("Potwierdzenie".Translate(), "Czy na pewno usunąć?".Translate()) { + Type = MessageBoxInformationType.Warning, + YesHandler = () => { + Usun(); + return FormAction.RefreshOwner; + }, + NoHandler = () => null, +}; +``` + +## Otwieranie obiektów i okien + +### Zwrócenie `Row` lub dowolnego `object` + +Domyślny handler otwiera obiekt w nowym formularzu +(`ObjectWindow` w HTML). Działa to dla każdego typu nieobsługiwanego przez specyficzne handlery — w tym +`GuidedRow`, kreatorów, dialogów. + +```csharp +[Action("Otwórz kontrahenta")] +public Kontrahent OtworzKontrahenta() => Wystawca; +``` + +## Nawigacja po programie + +### `NavigationResult` + +Przejście do innego folderu danych lub bazy. Działa jak kliknięcie w drzewie folderów. + +| Property | Znaczenie | +|---|---| +| `Address` | Ścieżka folderu (np. `"Handel/Sprzedaż/Faktury sprzedaży"`) lub `//<ścieżka>` dla innej bazy. | +| `Context` | Kontekst (filtry) folderu. | +| `Target` | `Self` / `NewWindow` / `NewTab`. | +| `KeepCurrentCredentials` | Logowanie do innej bazy aktualnymi danymi (JWT). | +| `KeepSessionLiving` | Przeniesienie żywej sesji do folderu docelowego (`Self` only). | +| `KeepCurrentViewInHistory` | Aktualny widok zostaje w historii nawigacji. | + +Najważniejsze ścieżki do folderów aplikacji znajdują się w skill `/soneta-mcp-ui/common-folders.md`. + +```csharp +return new NavigationResult("Handel/Sprzedaż/Faktury sprzedaży") { + Target = NavigationTarget.NewTab +}; +``` + +### `HyperlinkResult` + +Otwarcie URL w przeglądarce. + +```csharp +return new HyperlinkResult { + Address = "https://soneta.pl", + Target = NavigationTarget.NewWindow, +}; +``` + +### `CommandMenu` + +Menu poleceń wyświetlone użytkownikowi (drop-down z opcjami). Po wybraniu wykonuje się akcja +przypisana do polecenia. + +### `LoginParameters` + +Wymusza przelogowanie (np. po zmianie operatora). Framework wywołuje `LoginService.Relogin`. + +## Pliki i strumienie + +### `NamedStream` + +Pojedynczy plik do pobrania przez przeglądarkę lub ramkę SonetaFrame. + +```csharp +return new NamedStream("raport.pdf", () => GenerujPdfStream()); +``` + +### `NamedStream[]` + +Tablica plików → spakowane jako ZIP (`Pliki_yyyyMMddHHmmssfff.zip`) i pobrane jako jeden plik lub zapisywane pojedynczo +przez ramkę SonetaFrame. + +### `StorageFileEditor` + +Edycja pliku przechowywanego w storage bazy danych (XML, DOCX, REPX, itp.) + +## Raporty + +### `ReportResult` + +Główny action result raportu — uruchamia mechanizm wydruków. Działa w **trzech trybach**: + +1. **Tryb interaktywny (menu)** — bez `TemplateFileName`, framework otwiera okno raportu i ewentualne + okno parametrów (`UseReportMenu == true`). Stosować, gdy chcemy pokazać użytkownikowi listę + dostępnych wzorców dla danego typu/ViewInfo. +2. **Tryb automatyczny (z kodu)** — z `TemplateFileName`, bez UI raportu. Wzorzec wskazany jawnie, + wydruk od razu generowany. Można dodać `OutputHandler` do przejęcia strumienia wynikowego. +3. **Tryb serwisowy (bez rezultatu)** — przez `IReportService.GenerateReport / GenerateReportStr / + PrintReport`. Stosować w testach, taskach tła i kodzie nie-UI. + +#### Najważniejsze property + +| Property | Znaczenie | +|---|---| +| `TemplateFileName` | Nazwa lub ścieżka wzorca (`.repx`, `.repx.cs`, `.aspx`, `.dotx`) lub XML z ustawieniami wydruku. Ustawienie wyklucza `ReportName`. | +| `TemplateFileSource` | `AspxSource.Local` (plik na dysku) / `Storage` (konfiguracja w bazie — pliki w `XtraReports/...`). | +| `ReportName` | Nazwa raportu z menu (tryb interaktywny). Wyklucza `TemplateFileName`. | +| `DataType` | Typ danych — `typeof(Towar)` (jeden obiekt), `typeof(Towary)` (cały View), `typeof(Towar[])` (zaznaczone wiersze). Steruje też ładowaniem listy dostępnych wydruków. | +| `ViewInfo` | ViewInfo źródłowego widoku (np. dla raportów listy z customowym ViewInfo). | +| `ViewNames` | Nazwy widoków, w których wyszukać raport po nazwie. | +| `Rows` | `IEnumerable` z wierszami źródłowymi (zamiast pobierania z `Context`). **Nie działa** w trybie menu. | +| `Context` | Kontekst raportu — źródło parametrów (`Context.Set(params)`), zaznaczeń, kultury. | +| `OutputFormat` | `HTML` (domyślnie) / `PDF` / `TXT`. | +| `Target` | `File` / `Preview` / `Printer`. Z `OutputHandler` traktowany jak `File`. | +| `PrinterName` | Drukarka dla `Target = Printer`. | +| `CultureInfo` | Język wydruku (nadpisuje kulturę sesji — patrz `DxReport_Generator_Dx_Zakup_English_ReportResult`). | +| `AskForParameters` | `false` = wydruk bez pytania o parametry (wymaga ustawionych parametrów w `Context`). | +| `Sign` / `VisibleSignature` / `Encrypt` | Podpis certyfikatem i hasło PDF (tylko tryb interaktywny w wersji desktop). | +| `OutputHandler` | `Func` — przejmuje wygenerowany strumień, jego wynik staje się rezultatem akcji. Tylko z `TemplateFileName`. | +| `ParametersHandler` | `Action` wywoływany przed pytaniem o parametry — sposób na zainicjowanie obiektów parametrów (oddzielnie dla każdego typu). | +| `Caption` | Tytuł okna. | + +#### Wzorzec: wydruk z akcji (worker / extender) + +```csharp +[Action("Drukuj fakturę")] +public object DrukujFV() { + Context.Set(new SprzedazSnippet.MyParametryWydruku(Context)); + return new ReportResult { + TemplateFileName = "Sprzedaz.repx", + DataType = typeof(DokumentHandlowy), + Context = Context, + AskForParameters = false, + }; +} +``` + +#### Wzorzec: wydruk z testu / taska (bez UI) + +```csharp +var rs = Session.GetRequiredService(); + +Context.Set(new SprzedazSnippet.MyParametryWydruku(Context)); + +var rr = new ReportResult { + TemplateFileName = "Sprzedaz.repx", + DataType = typeof(DokumentHandlowy), + Context = Context, + AskForParameters = false, +}; + +string html = rs.GenerateReportStr(rr); // HTML jako string +// lub +using var stream = rs.GenerateReport(rr); // strumień (PDF / inny) +// lub +rs.PrintReport(rr, archive: true, archivePath: "C:\\Archive"); +``` + +#### Wzorzec: wzorzec ze storage (XtraReports/Wzorce użytkownika) + +```csharp +var rr = new ReportResult { + TemplateFileName = "XtraReports/Wzorce użytkownika/EmptyReport.repx.cs", + TemplateFileSource = AspxSource.Storage, + Context = Context, + AskForParameters = false, +}; +``` + +#### Wzorzec: zaznaczone wiersze + custom params + +```csharp +Context.Set(towary); // tablica zaznaczonych Row +Context.Set(new CennikViewInfo.CennikParams(Context)); + +var rr = new ReportResult { + DataType = typeof(Towar[]), // l.mn. = zaznaczone + TemplateFileName = "...", + TemplateFileSource = AspxSource.Storage, + Context = Context, + OutputFormat = ReportFormats.TXT, + AskForParameters = false, +}; +``` + +#### Wzorzec: jeden obiekt + powiązane konteksty + +```csharp +var fvNewSession = Session.Get(fv); +Context.Set(fvNewSession); // single +Context.Set(new[] { fvNewSession }); // jako tablica (dla DataType = Towar[]) +Context.Set(new SprzedazSnippet.MyParametryWydruku(Context)); + +var rr = new ReportResult { + TemplateFileName = "Sprzedaz.repx", + DataType = typeof(DokumentHandlowy), + Context = Context, + AskForParameters = false, +}; +``` + +#### Wzorzec: wymuszenie języka wydruku + +```csharp +var rr = new ReportResult { + TemplateFileName = "Zakup.repx", + DataType = typeof(DokumentHandlowy), + Context = Context, + OutputFormat = ReportFormats.TXT, + CultureInfo = CoreTools.CultureEN, + AskForParameters = false, +}; +``` + +#### Sprawdzenie wymaganych typów parametrów + +Przykład uzupełnia context standardowymi wartościami parametrów potrzebnymi dla raportu. + +```csharp +Type[] paramTypes = rs.GetParameterTypes("Sprzedaz.repx", Context); +foreach (Type t in paramTypes) { + Context.Set(Activator.CreateInstance(t, Context)); +} +``` + +#### Deprecated property + +- `TemplateType` → używać `TemplateFileName`. +- `Format` (`ReportResultFormat`) → używać `OutputFormat` (`ReportFormats`). +- `Preview` → używać `Target = ReportTargets.Preview`. +- `SilentProgress` → używać `!AskForParameters`. + +### `ReportOrganizerResult` + +Menedżer raportów (lista raportów dla tabeli/widoku) — okno zarządzania zestawem raportów dla +podanego typu danych. + +### `ReportEditorResult` + +Otwarcie edytora raportu (DOTX, DX) — w zależności od `Format` wybierany jest odpowiedni handler. + +### `PdfResult` + +Pojedynczy PDF (do podglądu, druku lub pobrania) sterowany przez `Action`: +`Preview` / `Print` / `Save` / `OpenInBrowser`. + +### `PdfResult[]` + +Tablica PDF — handler (`PdfsResultHandler`) wysyła wszystkie na drukarkę przez `IPrintingService`. + +### `DotxReportPrintResult`, `TextReportPrintResult`, `DxReportPrintResult` + +Specjalizowane action result wydruku / podglądu konkretnych formatów (DOTX edytor, raport tekstowy, +DevExpress). + +## Dialogi parametrów + +### `QueryContextInformation` + +Otwiera okno z parametrami uzupełniającymi `Context` przed wywołaniem właściwej operacji. To samo +okno pojawia się przed niektórymi czynnościami z menu lub przed raportami. Po `OK` wywoływany jest +handler, który dostaje uzupełnione obiekty parametrów jako argumenty. Po `Cancel` — `CancelHandler`. + +#### Mechanizm + +1. Akcja zwraca `QueryContextInformation` z handlerem typu `Func`. +2. Framework patrzy na typy parametrów handlera (`T1`, `T2`, ...). +3. Dla każdego typu sprawdza, czy `Context` zawiera już instancję — jeśli **tak**, pomija + pytanie i od razu wywołuje handler. +4. Jeśli czegoś brakuje, otwiera okno parametrów (renderowane przez `QueryContextRenderer`). +5. Po `OK` wywołuje handler z parametrami pobranymi z `Context`. +6. Wartość zwrócona z handlera staje się **kolejnym action result** (rekurencyjnie obsługiwana). + +#### Tworzenie — fabryki `Create` + +```csharp +// Jeden typ parametrów +QueryContextInformation.Create(p => DoWork(p)); + +// Kilka typów (do 6) +QueryContextInformation.Create((p1, p2) => DoWork(p1, p2)); + +// Context + parametry (Context można dostać jako pierwszy parametr) +QueryContextInformation.Create((cx, p) => DoWork(cx, p)); + +// Bez handlera — sama informacja "wypełnij te typy w kontekście" +QueryContextInformation.CreateForTypes(typeof(Params1), typeof(Params2)); + +// Z dowolnego Delegate +QueryContextInformation.CreateFromHandler(myDelegate); +``` + +#### Klasa parametrów + +Typowo dziedziczy z `ContextBase` (przyjmuje `Context` w konstruktorze, ma `OnChanged()` do +odświeżania), używa atrybutów Soneta i `System.ComponentModel`: + +```csharp +[Caption("Parametry wydruku")] +class MyParams(Context context) : ContextBase(context) { + + [Category("Daty")] // grupowanie wizualne + [Caption("Data od")] + [Priority(1)] // kolejność w grupie + [Accessor(AutoChange=true)] + public Date DataOd { get; set; } + + [Category("Daty")] + [Caption("Zakres")] + [Priority(3)] + [Accessor(AutoChange=true)] + public FromTo Okres { get; set; } + + [Category("Informacje")] + [Caption("Opis")] + [Required] + [Accessor(AutoChange=true)] + public string Info { get; set; } + + [Hidden] // ukrycie pola + public int Internal { get; set; } +} +``` + +Renderer: + +- grupuje pola po `[Category]` (`GroupContainer` z `CaptionHtml = nazwa kategorii`), +- sortuje wewnątrz grupy po `[Priority]` (rosnąco), +- pomija pola z `[Hidden]` / `[Browsable(false)]`, +- `[Accessor(AutoChange=true)]` zadba o automatycznie wywołanie `Session.InvokeChanged()` na `set`. +- może renderować tę samą kategorię wielokrotnie, jeśli pola są przeplatane innymi. + +#### Wzorzec: zapytanie o parametr w workerze + +```csharp +[Action("Zmień podstawę zwolnienia")] +public object Zmien() { + return QueryContextInformation.Create(p => { + using var t = Towar.Session.Logout(true); + Towar.PodstawaZwolnienia = p.Podstawa; + t.Commit(); + return null; + }); +} +``` + +#### Wzorzec: łańcuch dialogów (wynik handlera = kolejny `QueryContextInformation`) + +```csharp +[Action("Dwuetapowy dialog")] +public object Wieloetapowo() { + return QueryContextInformation.Create(p1 => + QueryContextInformation.Create(p2 => { + using var t = Session.Logout(true); + // wykorzystaj p1 i p2 + t.Commit(); + return null; + }) + ); +} +``` + +#### Wzorzec: dialog wewnątrz `FormActionResult.CommittedHandler` + +```csharp +return new FormActionResult { + EditValue = new SecondTestObject { Context = Context }, + UseDialog = true, + CommittingHandler = cx => FormAction.Close, + CommittedHandler = cx => QueryContextInformation.Create(p => { + using var t = Towar.Session.Logout(true); + Towar.PodstawaZwolnienia = p.Podstawa; + t.Commit(); + return Towar; // otworzy formularz dla Towar po pytaniu + }) +}; +``` + +#### Wzorzec: dialog z `MessageBoxInformation` jako wynikiem + +```csharp +public object Pokaz() { + return QueryContextInformation.Create((MessageParams p) => + new MessageBoxInformation { + Caption = "Test".Translate(), + Text = p.Message, + Type = p.IsWarning + ? MessageBoxInformationType.Warning + : MessageBoxInformationType.Information + }); +} +``` + +#### Wzorzec: dialog z plikami (`NamedStream[]`) + +`QueryContextInformation` obsługuje też typy z plikami — w klasie parametrów `NamedStream` lub +`NamedStream[]` z `GetListXxx()` zwracającym `FileDialogInfo`: + +```csharp +class NsArgs : ContextBase { + public NsArgs(Context cx) : base(cx) {} + + public NamedStream[] Streams { get; set; } + + public FileDialogInfo GetListStreams() + => new FileDialogInfo { InitialDirectory = "x:\\" } + .AddTxtFilter() + .AddXmlFilter(); +} + +// użycie: +return QueryContextInformation.Create((NsArgs ns) => ns.Streams[0].FileName); +``` + +#### Property obiektu + +| Property | Znaczenie | +|---|---| +| `Context` | Kontekst, na którym pracuje okno. Standardowo `null` → użyty zostanie kontekst wywołania. | +| `Caption` | Tytuł okna. | +| `Data` | Dane przekazane do konstruktora (rzadko używane bezpośrednio). | +| `Types` | Tablica typów do uzupełnienia w kontekście — gdy używamy `CreateForTypes`. | +| `AcceptHandler` | `Func` bez parametrów. Alias na handler ustawiony przez `Create<>`. | +| `CancelHandler` | `Func` — wynik traktowany jak kolejny action result. | +| `ResultHandler` | `Func` — postprocessing wartości zwróconej z handlera. | +| `ObjectConstructor` | `ConstructorInfo` do tworzenia obiektu wynikowego (zaawansowane). | +| `GetHandler()` | Zwraca `Delegate` przekazany do `Create<>` (dla refleksji w testach). | +| `SetCaption(c)` / `SetCancelHandler(h)` | Fluent setter. | + +#### `TryInvoke(Context)` + +Pozwala uniknąć okna, gdy parametry **są już** w kontekście: + +```csharp +// 1) gdy w kontekście brakuje Params - zwraca self (otwarcie okna) +object r = QueryContextInformation + .Create(MyAction) + .TryInvoke(cx); +// r is QueryContextInformation == true + +// 2) gdy Params jest w kontekście - wywołuje handler natychmiast +cx.Set(new Params(cx) { ... }); +object r = QueryContextInformation + .Create(MyAction) + .TryInvoke(cx); +// r == wynik MyAction +``` + +#### Walidacja typu propertysów + +Pola z atrybutem `[Context(nameof(T.Prop))]` muszą mieć właściwy typ wartości docelowej (typ +property `T.Prop`). Niezgodność → `Context.InvalidValueException` przy wywołaniu +`ContextInvoker.CreateObject<>()`. + +### `StartActionInformation` + +Wewnętrzny action result — uruchomienie akcji w nowym kontekście (używany przez framework +do delegowania wykonania workera). Rzadko zwracany ręcznie. + +## Operacje klienta i bezpieczeństwo + +### `ClientActionResult` + +Wywołanie operacji w aplikacji klienckiej **SonetaFrame** (desktop wrapper). Działa tylko gdy +`BusTools.DeviceType.IsBrowserApp()`. Pozwala wykonać akcję po stronie klienta (otwarcie pliku +lokalnego, integracja z systemem operacyjnym) i opcjonalnie odebrać odpowiedź przez `ResponseHandler`. + +### `MfaRequest` / `MfaSourceBase` + +2FA (Multi-Factor Authentication) — uruchamia okno weryfikacji tożsamości użytkownika. Handler +deleguje do serwisu `IMfaHandleResolver`. Stosować dla akcji wymagających dodatkowego potwierdzenia. + +### `ClipboardStream` + +Operacja na schowku przeglądarki (kopiowanie do / odczyt z). + +## Tabela szybkiego wyboru + +| Co chcę zrobić | Co zwrócić | +|---|---| +| Pokazać komunikat | `string` lub `MessageBoxInformation` | +| Pokazać błąd | `Exception` (throw) lub `MessageBoxInformation` (Type = Error) | +| Zapisać/zamknąć formularz | `FormAction.Save*` / `FormAction.Close` | +| Zadać pytanie tak/nie | `MessageBoxInformation` z `YesHandler`/`NoHandler` | +| Otworzyć obiekt do edycji | sam obiekt (`Row` lub inny) | +| Otworzyć inny folder | `NavigationResult` | +| Otworzyć stronę WWW | `HyperlinkResult` | +| Pobrać plik | `NamedStream` | +| Pobrać archiwum plików | `NamedStream[]` | +| Wydruk PDF | `PdfResult` lub `PdfResult[]` | +| Raport | `ReportResult` | +| Zapytać o parametry | `QueryContextInformation` | +| Nic nie robić | `null` / `void` | +