Pokud chystáme nějakou událost, určitě se nám bude hodit aplikace, díky které si bude moci uživatel vložit naši událost do kalendáře pouhým kliknutím na odkaz. Vytvoříme aplikaci, která bude přímo za běhu generovat soubor ke stažení ve formátu vCalendar s příponou .vcs.

Vložit událost do kalendáře je možné pomocí souboru zvláštního formátu. Naše aplikace bude tento soubor generovat na vyžádání, podobně jako jsme se již naučili generovat na vyžádání obrázkové nadpisy. Důležité je dodržet odesílaný Content Type i hlavičku, která zajistí, že soubor bude nabízen jako příloha ke stažení s příponou „vcs“, jinak MS Outlook neidentifikuje záznam do kalendáře správně a vložení události nenabídne. Údaje, podle kterých se bude generovat soubor k importu do kalendáře, by se hodilo načítat třeba z databáze, v naší ukázce pro jednoduchost využijeme sice funkční ale ne příliš čisté metody předání v QueryStringu. Máte-li instalovaný MS Outlook nebo jinou aplikaci, podporující import do kalendáře (například Lotus Notes), vyzkoušejte si ukázku (zdrojový kód), která naplánuje „Neexistující schůzku serveru Interval.cz“ od „13. 5. 2020 12:20“ do „20. 8. 2020 12:20“ v místě „Koželužská 7, Brno – Jižní Morava“.

Formát pro výměnu údajů v kalendáři vCalendar definuje standard RFC 2447, nebudeme jej zde nijak pitvat a prostě jen připravíme aplikaci tak, aby výsledek byl v souladu s normou – vyjdeme z příkladu na webu Microsoftu.

Základem aplikace je šablona formátu vCalendar:

<%@ Page ContentType=“text/x-vCalendar“ ResponseEncoding=“Windows-1250″ AutoEventWireUp=“False“ Language=“C#“ EnableViewState=“False“ EnableSessionState=“False“ Trace=“False“ Debug=“False“ Inherits=“MakeEventLink“ CodeBehind=“MakeEventLink.aspx.cs“ %>
<%@ OutputCache Duration=“360″ VaryByParam=“*“ %>
BEGIN:VCALENDAR
VERSION:1.0
BEGIN:VEVENT
DTSTART:<asp:Literal Id=“DTSTART“ RunAt=“Server“ />
DTEND:<asp:Literal Id=“DTEND“ RunAt=“Server“ />
LOCATION;ENCODING=QUOTED-PRINTABLE:<asp:Literal Id=“LOCATION“ RunAt=“Server“ />
UID:<asp:Literal Id=“UID“ RunAt=“Server“ />
DESCRIPTION;ENCODING=QUOTED-PRINTABLE:<asp:Literal Id=“DESCRIPTION“ RunAt=“Server“ />
SUMMARY;ENCODING=QUOTED-PRINTABLE:<asp:Literal Id=“SUMMARY“ RunAt=“Server“ />
PRIORITY:3
END:VEVENT
END:VCALENDAR

V direktivách stránky vidíme mimo jiné i nastavení správného ContentType, pro údaj do kalendáře je to text/x-vCalendar. Protože převážná většina klientů, kteří pak mohou naši aplikaci využít, budou mít nejspíše MS Outlook, nastavíme také výstupní kódování na Windows-1250 pomocí direktivy Response.Encoding. Vypnuté jsou nepotřebné funkce ViewState a SessionState, zapnuté je naopak výstupní cachování – to se hodí zvláště pro případ, kdy někdo rozešle hromadný mail spoustě klientů a ti takřka současně kliknou na odkaz, což je bez cachování leckdy schopno položit na několik minut celý server.

Hodnoty „elementů“ v šabloně nastavujeme prostřednictvím webových ovládacích prvků Literal, které nepodporují žádné formátování, mají jen prostou vlastnost Text. Každopádně je ale třeba si všimnout, že v šabloně se vyskytuje zápis ENCODING=QUOTED-PRINTABLE, což skutečně znamená, že další údaje jsou kódovány jako Quoted Printable. Problém je, že samotný .NET nemá vlastní třídu pro toto kódování – je tedy třeba si napsat vlastní. Já jsem trochu hledal na webu a našel jsem QuotedPrintable Class. Tato sice funguje, ale jak jsem později zjistil, je potřeba při kompilaci povolit vytváření nebezpečného kódu direktivou /unsafe a navíc jsou konvertovány úplně všechny znaky, přestože znaky, jejichž kód je nižší než 127, mohou být uváděny přímo bez kódování. I přesto, že ukázka funguje a já sám tuto knihovnu používám, doporučuji vám najít si jinou knihovnu nebo si zde uvedenou upravit. Pro začátečníky připomínám, že knihovnu je třeba po stažení buďto přidat do projektu ve Visual Studiu, nebo zkompilovat řádkovým příkazem csc.exe /t:library QuotedPrintable.cs a výsledný dll soubor pak uložit do složky „Bin“.

Výkonný kód aplikace jsem pro jednoduchost ani nezařazoval do zvláštního namespace:

using System;
using System.Web;
using System.Text;
using mimelib;
public abstract class MakeEventLink : System.Web.UI.Page
{
  protected String Utf2Win(String input)
  {
    byte[] bytes1250 = Encoding.GetEncoding(1250).GetBytes(input);
    StringBuilder stringBuilder1250 = new StringBuilder(String.Empty);
    foreach(byte b in bytes1250)
    {
      char ch = Convert.ToChar(b);
      stringBuilder1250.Append(ch.ToString());
    }
    return stringBuilder1250.ToString();
  }
  protected System.Web.UI.WebControls.Literal DTSTART,DTEND,SUMMARY,DESCRIPTION,LOCATION,UID;
  private void Page_Load (Object sender, EventArgs e)
  {
    if (Request.QueryString[„Summary“] != null && Request.QueryString[„DtStart“] != null && Request.QueryString[„DtEnd“] != null && Request.QueryString[„Summary“].Length > 0 && Request.QueryString[„Summary“].Length > 0 && Request.QueryString[„DtEnd“].Length > 0)
    {
      try
      {
        DTSTART.Text = DateTime.Parse(Request.QueryString[„DtStart“].Trim()).ToUniversalTime().ToString(„yyyyMMdd\\THHmmss\\Z“);
      }
      catch
      {
        DTSTART.Text = DateTime.MinValue.ToUniversalTime().ToString(„yyyyMMdd\\THHmmss\\Z“);
      }
      try
      {
        DTEND.Text = DateTime.Parse(Request.QueryString[„DtEnd“].Trim()).ToUniversalTime().ToString(„yyyyMMdd\\THHmmss\\Z“);;
      }
      catch
      {
        DTEND.Text = DateTime.MaxValue.ToUniversalTime().ToString(„yyyyMMdd\\THHmmss\\Z“);
      }
      // DTSTAMP.Text = DateTime.Now.ToUniversalTime().ToString(„yyyyMMdd\\THHmmss\\Z“);
      SUMMARY.Text = QuotedPrintable.Encode(Utf2Win(Request.QueryString[„Summary“].Trim()));
      if (Request.QueryString[„Description“] != null && Request.QueryString[„Description“].Length > 0)
        DESCRIPTION.Text = QuotedPrintable.Encode(Utf2Win(Request.QueryString[„Description“].Trim()));
      if (Request.QueryString[„Location“] != null && Request.QueryString[„Location“].Length > 0)
        LOCATION.Text = QuotedPrintable.Encode(Utf2Win(Request.QueryString[„Location“].Trim()));
      if (Request.QueryString[„Uid“] != null && Request.QueryString[„Uid“].Length > 0)
        UID.Text = Request.QueryString[„Uid“].Trim();
      Response.AppendHeader („Content-Disposition“,“; filename=“ + HttpUtility.UrlEncode(Request.QueryString[„Summary“].Trim()) + „.vcs“);
    }
    else
    {
      Response.End();
    }
  }
  #region Web Form Designer generated code
  override protected void OnInit(EventArgs e)
  {
//
// CODEGEN: This call is required by the ASP.NET Web Form Designer.
//
    InitializeComponent();
    base.OnInit(e);
  }
/// Required method for Designer support – do not modify
/// the contents of this method with the code editor.
/// </summary>
  private void InitializeComponent()
  {
    this.Load += new System.EventHandler(this.Page_Load);
  }
#endregion
}

V kódu vidíme import prostoru názvů mimelib – to je prostor, do kterého je zařazena již zmíněná knihovna obsahující třídu pro konverzi formátu Quoted Printable. Článek o Codebehind stránky obsahuje také triviální metodu pro konverzi kódování z UTF-8 na Windows-1250. Ta se hodí, když requesty do naší aplikace přicházejí v UTF-8, potřebujeme totiž metodě konvertující do Quoted Printable předávat řetězec kódovaný ve Windows-1250. Dále máme deklarované použité webové ovládací prvky typu Literal, a to je téměř vše, co aplikace potřebuje.

Výkonný kód v naší ukázce sestává z prostého převzetí údajů z QueryStringu v obsluze události Page_Load. Pokud jsou povinné hodnoty zadány, jsou překonvertovány do patřičné podoby a naplněny do šablony. Předtím ovšem testujeme, zda skutečně jsou zadány všechny povinné údaje (tedy data a předmět). Pokud nejsou, ukončíme provádnění skriptu pomocí Response.End(). Pokud potřebné údaje jsou zadány, zkusíme metodou DateTime.Parse() získat korektní údaj času. Tento následně upravíme pro kalendář – převedeme na takzvaný Universal Time (UTC), který je nezávislý na časových pásmech, a kompletní čas pak získáme zformátováním při převodu na řetězec pomocí ToString("yyyyMMdd\\THHmmss\\Z").

Následně proběhne naplnění pole Summary a případně dalších nepovinných polí při současné konverzi na formát Quoted Printable. Všimněte si, že z každého pole zahazujeme metodou Trim() nepatřičné prázdné znaky. Před odesláním klientovi přidáme ještě pomocí AppendHeader() korektní hlavičku Content-Disposition, aby vybavení klienta „vědělo“, jak s títmto formátem nakládat.

Jednoduchá ukázka odkazu pro vložení do kalendáře (URL je kódováno v UTF-8):

MakeEventLink.aspx?DtStart=13.5.2020+12%3A20&DtEnd=20.8.2020+12%3A20&Summary=Sch%C5%AFzka&Location=Brno

Ideální by bylo, pokud bychom věděli, zda klient je schopen zpracovat soubor kalendáře. Bohužel, žádný vhodný způsob se mi nepodařilo objevit, jediným řešením je tedy dát k odkazu vhodný popis. Je jasné, že tato aplikace nikdy nebude dostatečně univerzální. Chápejme ji tedy spíš jen jako užitečný doplněk, který je s to uspokojit tu část uživatelů internetu, kteří využívají MS Outlook ve Windows (mám na mysli zejména závislost na kódování Windows, které je v aplikaci napevno nastaveno na Windows-1250, což nemusí být úplně vhodné pro jiné systémy). A samozřejmě ji lze s výhodou použít především v intranetech, kde víme, jací klienti naši aplikaci používají.

Hotovou aplikaci můžeme navázat na nějakou naši aplikaci zobrazující termín(y) událostí, aby generovala i odkazy pro vložení události do kalendáře. Nebo můžeme vytvořit formulář, který nám či autorovi nějakého článku ze zadaných údajů pomůže odkaz sestavit. Aplikaci můžeme také využívat čistě jako generátor souborů .vcs. V takovém případě soubor pouze uložíme na disk a dále jej můžeme přikládat jak do stránek, tak do mailů, kde by odkaz s dlouhým QueryStringem působil nepřehledně a neesteticky. K odkazu na vložení události do kalendáře se ještě hodí dát upozornění, aby uživatel zvolil „Otevřít“ po kliknutí na odkaz, protože volbou „Uložit“ se událost neuloží, uloží se jen soubor, který událost popisuje.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Žádný příspěvek v diskuzi

Odpovědět