diff --git a/README.md b/README.md
index ec937158fe6c6d0a87e523e94c756b5102b72081..f12ebec23dd852f41254d6301cc04c517224e376 100644
--- a/README.md
+++ b/README.md
@@ -36,6 +36,20 @@ jako přehled pluginů a rozšíření pro Wagtail.
 Appky v sobě mají modely pro stránky a statické soubory a templaty. Momentálně se
 mezi weby nic z toho nesdílí.
 
+### Kalendáře
+
+Pro práci s kalendáři v iCal formátu je připravena appka `calendar_utils`.
+
+Poskytuje `CalendarMixin` do modelu, který přidá fieldy `calendar_url` pro
+editaci a `calendar` pro vazbu na model `Calendar` (který se plní a automaticky
+spravuje na pozadí). Typicky se použije ve Wagtail settings pro web, kde stačí
+`calendar_url` zpřístupnit pro editaci.
+
+Kalendář se stáhne při uložení modelu obsahujícího `CalendarMixin`.
+
+Appka přidává management command `update_callendars`, který stahuje a updatuje
+kalendáře. Je třeba ho pravidelně volat na pozadí (přes CRON).
+
 ## Deployment
 
 ### Konfigurace
diff --git a/senat_campaign/migrations/0003_auto_20200523_0241.py b/senat_campaign/migrations/0003_auto_20200523_0241.py
new file mode 100644
index 0000000000000000000000000000000000000000..f56e67570500c4accc46fafbfec7bbe99c3fda2d
--- /dev/null
+++ b/senat_campaign/migrations/0003_auto_20200523_0241.py
@@ -0,0 +1,31 @@
+# Generated by Django 3.0.6 on 2020-05-23 00:41
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("calendar_utils", "0001_initial"),
+        ("senat_campaign", "0002_senatcampaignwebsettings_matomo_id"),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name="senatcampaignwebsettings",
+            name="calendar",
+            field=models.ForeignKey(
+                null=True,
+                on_delete=django.db.models.deletion.PROTECT,
+                to="calendar_utils.Calendar",
+            ),
+        ),
+        migrations.AddField(
+            model_name="senatcampaignwebsettings",
+            name="calendar_url",
+            field=models.URLField(
+                blank=True, null=True, verbose_name="URL kalendáře ve formátu iCal"
+            ),
+        ),
+    ]
diff --git a/senat_campaign/models.py b/senat_campaign/models.py
index f36fee3c80389645c42460f9ececb6872fc96f64..e2a066c07158012316ff80ed3f20adf5fa04ea90 100644
--- a/senat_campaign/models.py
+++ b/senat_campaign/models.py
@@ -13,6 +13,8 @@ from wagtail.documents.blocks import DocumentChooserBlock
 from wagtail.images.blocks import ImageChooserBlock
 from wagtail.images.edit_handlers import ImageChooserPanel
 
+from calendar_utils.models import CalendarMixin
+
 
 class SenatCampaignHomePage(Page):
     # top section
@@ -81,7 +83,7 @@ class SenatCampaignHomePage(Page):
     is_home = True
 
     class Meta:
-        verbose_name = f"Senát kampaň"
+        verbose_name = "Senát kampaň"
 
     def get_context(self, request):
         context = super().get_context(request)
@@ -128,7 +130,7 @@ class SenatCampaignNewsIndexPage(Page):
     is_home = False
 
     class Meta:
-        verbose_name = f"Aktuality"
+        verbose_name = "Aktuality"
 
     def get_context(self, request):
         context = super().get_context(request)
@@ -163,7 +165,7 @@ class SenatCampaignNewsPage(Page):
     is_home = False
 
     class Meta:
-        verbose_name = f"Aktualita"
+        verbose_name = "Aktualita"
 
     def get_context(self, request):
         context = super().get_context(request)
@@ -208,7 +210,7 @@ class SenatCampaignProgramPage(Page):
     is_home = False
 
     class Meta:
-        verbose_name = f"Program"
+        verbose_name = "Program"
 
 
 class SenatCampaignCookiesPage(Page):
@@ -225,7 +227,7 @@ class SenatCampaignCookiesPage(Page):
     is_home = False
 
     class Meta:
-        verbose_name = f"Cookies"
+        verbose_name = "Cookies"
 
 
 class ContactBlock(blocks.StructBlock):
@@ -241,7 +243,7 @@ class ContactBlock(blocks.StructBlock):
 
 
 @register_setting
-class SenatCampaignWebSettings(BaseSetting):
+class SenatCampaignWebSettings(BaseSetting, CalendarMixin):
     first_name = models.CharField("jméno kandidáta", max_length=250)
     last_name = models.CharField("příjmení kandidáta", max_length=250)
     facebook = models.URLField("Facebook URL", blank=True, null=True)
@@ -255,13 +257,13 @@ class SenatCampaignWebSettings(BaseSetting):
         "Matomo ID pro sledování návštěvnosti", blank=True, null=True
     )
 
-    # TODO Matomo
     # TODO donations
 
     first_tab_panels = [
         FieldPanel("first_name"),
         FieldPanel("last_name"),
         FieldPanel("matomo_id"),
+        FieldPanel("calendar_url"),
     ]
 
     second_tab_panels = [
@@ -283,8 +285,10 @@ class SenatCampaignWebSettings(BaseSetting):
         ]
     )
 
+    select_related = ["calendar"]
+
     class Meta:
-        verbose_name = f"Senát kampaň"
+        verbose_name = "Senát kampaň"
 
     @property
     def full_name(self):
@@ -299,9 +303,8 @@ class SenatCampaignWebSettings(BaseSetting):
         return SenatCampaignNewsIndexPage.objects.in_site(self.site).live().exists()
 
     @property
-    def has_callendar(self):
-        # TODO
-        return False
+    def has_calendar(self):
+        return self.calendar_id is not None
 
     @property
     def has_donations(self):
diff --git a/senat_campaign/templates/senat_campaign/base.html b/senat_campaign/templates/senat_campaign/base.html
index 0fa291ebc09fc0688eb071d086cd8b3a648f4801..8ea1c7cd0497930302cb0c7d12aabed671aa1167 100644
--- a/senat_campaign/templates/senat_campaign/base.html
+++ b/senat_campaign/templates/senat_campaign/base.html
@@ -146,7 +146,7 @@
             {% endif %}
           </li>
           {% endif %}
-          {% if web_settings.has_callendar %}
+          {% if web_settings.has_calendar %}
           <li class="nav-item">
             {% if page.is_home %}
             <a class="nav-link js-scroll-anchor" href="#kalendar">Kalendář</a>
diff --git a/senat_campaign/templates/senat_campaign/calendar_event_snippet.html b/senat_campaign/templates/senat_campaign/calendar_event_snippet.html
new file mode 100644
index 0000000000000000000000000000000000000000..b8cee4a8074b33486b4da805cb54781e7118a79d
--- /dev/null
+++ b/senat_campaign/templates/senat_campaign/calendar_event_snippet.html
@@ -0,0 +1,13 @@
+<div class="calendar__row">
+  <div class="calendar__row__date1">
+    <h3>{{ event.begin|date:"d" }}.</h3>
+  </div>
+  <div class="calendar__row__date2">
+    <h6>{{ event.begin|date:"j.n.Y" }}</h6>
+    <p>{{ event.duration }}</p>
+  </div>
+  <div class="calendar__row__content">
+    <h6>{{ event.name }}</h6>
+    <p>{{ event.location }}</p>
+  </div>
+</div><!-- /calendar__row -->
diff --git a/senat_campaign/templates/senat_campaign/senat_campaign_home_page.html b/senat_campaign/templates/senat_campaign/senat_campaign_home_page.html
index a3bddbc395928f384ff27b36352f15fb4ae0ef4a..f233b2bdbbdf43258b57d2cc820c25fbe704195e 100644
--- a/senat_campaign/templates/senat_campaign/senat_campaign_home_page.html
+++ b/senat_campaign/templates/senat_campaign/senat_campaign_home_page.html
@@ -151,7 +151,7 @@
 
 
   {% if web_settings.has_news %}
-  <section class="section--primary{% if web_settings.has_callendar %} section--no-bottom-padding{% endif %}" id="aktuality">
+  <section class="section--primary{% if web_settings.has_calendar %} section--no-bottom-padding{% endif %}" id="aktuality">
     <div class="container">
 
       <h2 class="lead page-subheading mb-4">Aktuality</h2>
@@ -184,7 +184,7 @@
   {% endif %}
 
 
-  {% if web_settings.has_callendar %}
+  {% if web_settings.has_calendar %}
   <section class="section--primary" id="kalendar">
     <div class="container">
 
@@ -201,95 +201,13 @@
         </div><!-- /calendar__left -->
         <div class="calendar__right">
 
-
-          <div class="calendar__row">
-            <div class="calendar__row__date1">
-              <h3>18.</h3>
-            </div>
-            <div class="calendar__row__date2">
-              <h6>18.4.2020</h6>
-              <p>celý den</p>
-            </div>
-            <div class="calendar__row__content">
-              <h6>Rozdávání novin na vlakovém nádraží Beroun</h6>
-              <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sollicitudin. Aliquam erat volutpat.
-              </p>
-            </div>
-          </div><!-- /calendar__row -->
-
-          <div class="calendar__row">
-            <div class="calendar__row__date1">
-              <h3>17.</h3>
-            </div>
-            <div class="calendar__row__date2">
-              <h6>17.4.2020</h6>
-              <p>13:00 - 17:00</p>
-            </div>
+          {% for event in web_settings.calendar.actual_events %}
+            {% include "senat_campaign/calendar_event_snippet.html" %}
+          {% empty %}
             <div class="calendar__row__content">
-              <h6>Stánkování Náměstí Republiky</h6>
-              <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sollicitudin. </p>
+              <p>Žádné události.</p>
             </div>
-          </div><!-- /calendar__row -->
-
-          <div class="calendar__row">
-            <div class="calendar__row__date1">
-              <h3>16.</h3>
-            </div>
-            <div class="calendar__row__date2">
-              <h6>16.4.2020</h6>
-              <p>20:00 - 22:00</p>
-            </div>
-            <div class="calendar__row__content">
-              <h6>Posezení se senátorem</h6>
-              <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sollicitudin. Aliquam erat volutpat.
-                Maecenas sollicitudin. Aliquam erat volutpat.</p>
-            </div>
-          </div><!-- /calendar__row -->
-
-          <div class="calendar__row">
-            <div class="calendar__row__date1">
-              <h3>15.</h3>
-            </div>
-            <div class="calendar__row__date2">
-              <h6>15.4.2020</h6>
-              <p>celý den</p>
-            </div>
-            <div class="calendar__row__content">
-              <h6>Pivobraní Mikulovice náměstí</h6>
-              <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sollicitudin. Aliquam erat volutpat.
-              </p>
-            </div>
-          </div><!-- /calendar__row -->
-
-          <div class="calendar__row">
-            <div class="calendar__row__date1">
-              <h3>14.</h3>
-            </div>
-            <div class="calendar__row__date2">
-              <h6>14.4.2020</h6>
-              <p>celý den</p>
-            </div>
-            <div class="calendar__row__content">
-              <h6>Vinobraní na náměstí Chrudim</h6>
-              <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sollicitudin. Aliquam erat volutpat.
-              </p>
-            </div>
-          </div><!-- /calendar__row -->
-
-          <div class="calendar__row">
-            <div class="calendar__row__date1">
-              <h3>13.</h3>
-            </div>
-            <div class="calendar__row__date2">
-              <h6>13.4.2020</h6>
-              <p>celý den</p>
-            </div>
-            <div class="calendar__row__content">
-              <h6>Stánkování na farmářských trzích Bohnice</h6>
-              <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sollicitudin. Aliquam erat volutpat.
-              </p>
-            </div>
-          </div><!-- /calendar__row -->
+          {% endfor %}
 
         </div><!-- /calendar__right -->
 
@@ -329,7 +247,7 @@
   {% endif %}
 
 
-  {% if web_settings.has_callendar %}
+  {% if web_settings.has_calendar %}
   <!-- Calendar modal -->
   <div class="modal fade calendar__modal" id="calendarModal" tabindex="-1" role="dialog"
     aria-labelledby="calendarModalTitle" aria-hidden="true">
@@ -359,122 +277,28 @@
             aria-labelledby="upcomingEvents-tab">
 
             <div class="calendar__right">
-              <div class="calendar__row">
-                <div class="calendar__row__date1">
-                  <h3>18.</h3>
-                </div>
-                <div class="calendar__row__date2">
-                  <h6>18.4.2020</h6>
-                  <p>celý den</p>
-                </div>
-                <div class="calendar__row__content">
-                  <h6>Rozdávání novin na vlakovém nádraží Beroun</h6>
-                  <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sollicitudin. Aliquam erat
-                    volutpat.
-                  </p>
-                </div>
-              </div><!-- /calendar__row -->
-
-              <div class="calendar__row">
-                <div class="calendar__row__date1">
-                  <h3>17.</h3>
-                </div>
-                <div class="calendar__row__date2">
-                  <h6>17.4.2020</h6>
-                  <p>13:00 - 17:00</p>
-                </div>
-                <div class="calendar__row__content">
-                  <h6>Stánkování Náměstí Republiky</h6>
-                  <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sollicitudin. </p>
-                </div>
-              </div><!-- /calendar__row -->
-
-              <div class="calendar__row">
-                <div class="calendar__row__date1">
-                  <h3>16.</h3>
-                </div>
-                <div class="calendar__row__date2">
-                  <h6>16.4.2020</h6>
-                  <p>20:00 - 22:00</p>
-                </div>
-                <div class="calendar__row__content">
-                  <h6>Posezení se senátorem</h6>
-                  <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sollicitudin. Aliquam erat
-                    volutpat.
-                    Maecenas sollicitudin. Aliquam erat volutpat.</p>
-                </div>
-              </div><!-- /calendar__row -->
-
-              <div class="calendar__row">
-                <div class="calendar__row__date1">
-                  <h3>15.</h3>
-                </div>
-                <div class="calendar__row__date2">
-                  <h6>15.4.2020</h6>
-                  <p>celý den</p>
-                </div>
-                <div class="calendar__row__content">
-                  <h6>Pivobraní Mikulovice náměstí</h6>
-                  <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sollicitudin. Aliquam erat
-                    volutpat.
-                  </p>
-                </div>
-              </div><!-- /calendar__row -->
 
-              <div class="calendar__row">
-                <div class="calendar__row__date1">
-                  <h3>14.</h3>
-                </div>
-                <div class="calendar__row__date2">
-                  <h6>14.4.2020</h6>
-                  <p>celý den</p>
-                </div>
+              {% for event in web_settings.calendar.future_events %}
+                {% include "senat_campaign/calendar_event_snippet.html" %}
+              {% empty %}
                 <div class="calendar__row__content">
-                  <h6>Vinobraní na náměstí Chrudim</h6>
-                  <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sollicitudin. Aliquam erat
-                    volutpat.
-                  </p>
+                  <p>Žádné události.</p>
                 </div>
-              </div><!-- /calendar__row -->
+              {% endfor %}
 
-              <div class="calendar__row">
-                <div class="calendar__row__date1">
-                  <h3>13.</h3>
-                </div>
-                <div class="calendar__row__date2">
-                  <h6>13.4.2020</h6>
-                  <p>celý den</p>
-                </div>
-                <div class="calendar__row__content">
-                  <h6>Stánkování na farmářských trzích Bohnice</h6>
-                  <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sollicitudin. Aliquam erat
-                    volutpat.
-                  </p>
-                </div>
-              </div><!-- /calendar__row -->
             </div><!-- /calendar__right -->
 
-
           </div><!-- /tab-pane -->
           <div class="tab-pane fade" id="pastEvents" role="tabpanel" aria-labelledby="pastEvents-tab">
 
-
             <div class="calendar__right">
-              <div class="calendar__row">
-                <div class="calendar__row__date1">
-                  <h3>18.</h3>
-                </div>
-                <div class="calendar__row__date2">
-                  <h6>18.4.2020</h6>
-                  <p>celý den</p>
-                </div>
+              {% for event in web_settings.calendar.past_events|slice:":10" %}
+                {% include "senat_campaign/calendar_event_snippet.html" %}
+              {% empty %}
                 <div class="calendar__row__content">
-                  <h6>Rozdávání novin na vlakovém nádraží Beroun</h6>
-                  <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sollicitudin. Aliquam erat
-                    volutpat.
-                  </p>
+                  <p>Žádné události.</p>
                 </div>
-              </div><!-- /calendar__row -->
+              {% endfor %}
             </div><!-- /calendar__right -->
 
           </div><!-- /tab-pane -->