浏览代码

Events fixes and refinements (#51)

* Bug fixes and docs/updates to event feature. Adding additional content_panel layout options in CoderedPage. Tweaking css and content_panel layouts

* Fixing bug when generating ical

* Removing `get_description` from events because it does not exist

* Additional sorting/occurrence madness and bug fixes. Changing calendar ajax call to GET so it can benefit from caching
Vince Salvino 6 年之前
父节点
当前提交
2d71fa58cc

文件差异内容过多而无法显示
+ 22 - 0
coderedcms/migrations/0006_auto_20181213_1753.py


+ 63 - 39
coderedcms/models/page_models.py

@@ -335,10 +335,15 @@ class CoderedPage(Page, metaclass=CoderedPageMeta):
         Page.content_panels +
         [
             ImageChooserPanel('cover_image'),
-            FieldPanel('tags'),
         ]
     )
 
+    body_content_panels = []
+
+    bottom_content_panels = [
+        FieldPanel('tags'),
+    ]
+
     layout_panels = [
         MultiFieldPanel(
             [
@@ -427,7 +432,7 @@ class CoderedPage(Page, metaclass=CoderedPageMeta):
         Override to "lazy load" the panels overriden by subclasses.
         """
         return TabbedInterface([
-            ObjectList(cls.content_panels, heading='Content'),
+            ObjectList(cls.content_panels + cls.body_content_panels + cls.bottom_content_panels, heading='Content'),
             ObjectList(cls.layout_panels, heading='Layout'),
             ObjectList(cls.promote_panels, heading='SEO', classname="seo"),
             ObjectList(cls.settings_panels, heading='Settings', classname="settings"),
@@ -514,6 +519,9 @@ class CoderedPage(Page, metaclass=CoderedPageMeta):
         context['content_walls'] = self.get_content_walls(check_child_setting=False)
         return context
 
+
+
+
 ###############################################################################
 # Abstract pages providing pre-built common website functionality, suitable for subclassing.
 # These are abstract so subclasses can override fields if desired.
@@ -542,10 +550,9 @@ class CoderedWebPage(CoderedPage):
     )
 
     # Panels
-    content_panels = (
-        CoderedPage.content_panels +
-        [StreamFieldPanel('body'),]
-    )
+    body_content_panels = [
+        StreamFieldPanel('body'),
+    ]
 
     @property
     def body_preview(self):
@@ -569,7 +576,7 @@ class CoderedWebPage(CoderedPage):
 
     @page_ptr.setter
     def page_ptr(self, value):
-        self.base_page_ptr = value    
+        self.base_page_ptr = value
 
 
 class CoderedArticlePage(CoderedWebPage):
@@ -654,12 +661,7 @@ class CoderedArticlePage(CoderedWebPage):
     content_panels = (
         CoderedWebPage.content_panels +
         [
-            MultiFieldPanel(
-                [
-                    FieldPanel('caption'),
-                ],
-                _('Additional Content')
-            ),
+            FieldPanel('caption'),
             MultiFieldPanel(
                 [
                     FieldPanel('author'),
@@ -739,13 +741,14 @@ class CoderedEventPage(CoderedWebPage, BaseEvent):
             MultiFieldPanel(
                 [
                     FieldPanel('calendar_color'),
+                    FieldPanel('address'),
                 ],
                 heading=_('Event information')
             ),
-            FieldPanel('address'),
             InlinePanel(
                 'occurrences',
-                heading="Occurrences",
+                min_num=1,
+                heading=_("Dates and times"),
             ),
         ]
     )
@@ -754,11 +757,26 @@ class CoderedEventPage(CoderedWebPage, BaseEvent):
     def upcoming_occurrences(self):
         """
         Returns the next x occurrences for this event.
-
         By default, it returns 10.
         """
         return self.query_occurrences(num_of_instances_to_return=10)
 
+    @property
+    def most_recent_occurrence(self):
+        """
+        Gets the next upcoming, or last occurrence if the event has no more occurrences.
+        """
+        noc = self.next_occurrence()
+        if noc:
+            return noc
+        else:
+            aoc = []
+            for occurrence in self.occurrences.all():
+                aoc += [instance for instance in occurrence.all_occurrences()]
+            if len(aoc) > 0:
+                return aoc[-1] # last one in the list
+            return None
+
     def query_occurrences(self, num_of_instances_to_return=None, **kwargs):
         """
         Returns a list of all upcoming event instances for the specified query.
@@ -790,6 +808,9 @@ class CoderedEventPage(CoderedWebPage, BaseEvent):
     def convert_to_ical_format(self, dt_start=None, dt_end=None, occurrence=None):
         ical_event = ICalEvent()
         ical_event.add('summary', self.title)
+        if self.address:
+            ical_event.add('location', self.address)
+
         if dt_start:
             ical_event.add('dtstart', dt_start)
 
@@ -829,10 +850,11 @@ class DefaultCalendarViewChoices():
     LIST_MONTH = 'listMonth'
 
     CHOICES = (
-            (MONTH, 'Monthly Calendar'),
-            (AGENDA_WEEK, 'Weekly Calendar'),
-            (AGENDA_DAY, 'Daily Calendar'),
-            (LIST_MONTH, 'Monthly List'),
+            ('', _('No calendar')),
+            (MONTH, _('Monthly Calendar')),
+            (AGENDA_WEEK, _('Weekly Calendar')),
+            (AGENDA_DAY, _('Daily Calendar')),
+            (LIST_MONTH, _('Calendar List View')),
         )
 
 class CoderedEventIndexPage(CoderedWebPage):
@@ -857,7 +879,7 @@ class CoderedEventIndexPage(CoderedWebPage):
         blank=True,
         choices=DefaultCalendarViewChoices.CHOICES,
         max_length=255,
-        verbose_name=_('Default Calendar View'),
+        verbose_name=_('Calendar Style'),
         help_text=_('The default look of the calendar on this page.')
     )
 
@@ -872,19 +894,22 @@ class CoderedEventIndexPage(CoderedWebPage):
         if self.index_query_pagemodel and self.index_order_by == 'next_occurrence':
             querymodel = resolve_model_string(self.index_query_pagemodel, self._meta.app_label)
             qs = querymodel.objects.child_of(self).live()
-            qs = sorted(qs.all(), key=lambda e: e.next_occurrence())
-            return qs
+            # filter out events that don't have a next_occurrence
+            upcoming = []
+            for event in qs.all():
+                if event.next_occurrence():
+                    upcoming.append(event)
+            # sort the events by next_occurrence
+            return sorted(upcoming, key=lambda e: e.next_occurrence())
 
         return super().get_index_children()
 
     def get_calendar_events(self, start, end):
-        events = set()
-
-        for event_page in self.get_index_children():
-            events.add(event_page)
-
+        # start with all child events, regardless of get_index_children rules.
+        querymodel = resolve_model_string(self.index_query_pagemodel, self._meta.app_label)
+        qs = querymodel.objects.child_of(self).live()
         event_instances = []
-        for event in events:
+        for event in qs:
             occurrences = event.query_occurrences(limit=None, from_date=start, to_date=end)
             for occurrence in occurrences:
                 event_data = {
@@ -1002,8 +1027,8 @@ class CoderedFormPage(CoderedWebPage):
         help_text=_('Date and time when the FORM will no longer be available on the page.'),
     )
 
-    content_panels = (
-        CoderedWebPage.content_panels +
+    body_content_panels = (
+        CoderedWebPage.body_content_panels +
         [
             FormSubmissionsPanel(),
             InlinePanel('form_fields', label="Form fields"),
@@ -1328,13 +1353,12 @@ class CoderedLocationPage(CoderedWebPage):
     )
 
     content_panels = (
-        CoderedWebPage.content_panels[:1] + 
+        CoderedWebPage.content_panels +
         [
             FieldPanel('address'),
             FieldPanel('website'),
             FieldPanel('phone_number'),
-        ] +
-        CoderedWebPage.content_panels[1:]
+        ]
     )
 
     layout_panels = (
@@ -1351,7 +1375,7 @@ class CoderedLocationPage(CoderedWebPage):
     )
 
     settings_panels = (
-        CoderedWebPage.settings_panels + 
+        CoderedWebPage.settings_panels +
         [
             MultiFieldPanel(
                 [
@@ -1389,7 +1413,7 @@ class CoderedLocationPage(CoderedWebPage):
                 'page': self
             }
         )
-    
+
     def to_geojson(self):
         return {
             "type": "Feature",
@@ -1436,13 +1460,13 @@ class CoderedLocationIndexPage(CoderedWebPage):
 
     center_latitude = models.FloatField(
         null=True,
-        blank=True, 
+        blank=True,
         help_text=_('The default latitude you want the map set to.'),
         default=0
     )
     center_longitude = models.FloatField(
         null=True,
-        blank=True, 
+        blank=True,
         help_text=_('The default longitude you want the map set to.'),
         default=0
     )
@@ -1468,7 +1492,7 @@ class CoderedLocationIndexPage(CoderedWebPage):
             ),
         ]
     )
-        
+
     def geojson_data(self, viewport=None):
         """
         function that will return all locations under this index as geoJSON compliant data.

+ 6 - 1
coderedcms/project_template/project_name/settings/base.py

@@ -187,4 +187,9 @@ BOOTSTRAP4 = {
     'base_url': '',
     # remove green highlight on inputs
     'success_css_class': ''
-}
+}
+
+
+# Tags
+
+TAGGIT_CASE_INSENSITIVE = True

文件差异内容过多而无法显示
+ 22 - 0
coderedcms/project_template/website/migrations/0005_auto_20181213_1753.py


+ 4 - 3
coderedcms/static/css/codered-admin.css

@@ -52,7 +52,7 @@ body.ready input[type='checkbox'], body.ready input[type='radio'] {
 
 .halloeditor, .tagit, input, select, textarea {
     background-color:#eee;
-    border:1px solid #eee;
+    border:1.5px solid #ddd;
     padding-top:0.5em;
     padding-bottom:0.5em;
     font-weight:normal;
@@ -60,7 +60,7 @@ body.ready input[type='checkbox'], body.ready input[type='radio'] {
 
 .halloeditor:hover, .tagit:hover, input:hover, select:hover, textarea:hover {
     background-color:#eee;
-    border:1px solid #eee;
+    border:1.5px solid #ddd;
 }
 
 input[type='checkbox'], input[type='radio'] {
@@ -78,7 +78,8 @@ input[type='checkbox']::before, input[type='radio']::before {
 .full input {
     background-color:white;
 }
-.full input:focus, .halloeditor:focus, .tagit:focus, input:focus, select:focus, textarea:focus {
+.full input:focus, .halloeditor:focus, .tagit:focus, input:focus, select:focus, textarea:focus,
+.tag_field.focused .tagit {
     border-color:#00b0b1;
     background-color:#f2fcfc;
 }

+ 3 - 2
coderedcms/static/css/codered-editor.css

@@ -35,7 +35,7 @@ label,
 .sequence-member .richtext,
 .sequence-member .tagit {
     background-color:white;
-    border:1px solid #ddd;
+    border:1.5px solid #ddd;
 }
 
 .sequence-member input:focus,
@@ -67,6 +67,7 @@ label,
 }
 .input input[type='color']{
     height: 40px;
+    padding: 5px;
 }
 
 
@@ -215,7 +216,7 @@ li.sequence-member li > .field .Draftail-Editor,
 li.sequence-member li.sequence-member .Draftail-Editor {
     background-color: #fff !important;
     border-radius:5px;
-    border:1px solid #ddd !important;
+    border:1.5px solid #ddd !important;
     padding: 0.5em 1em;
 }
 li.sequence-member li.sequence-member .Draftail-Editor {

+ 2 - 2
coderedcms/static/js/codered-front.js

@@ -166,9 +166,9 @@ $(document).ready(function()
                                 fixedWeekCount: false,
                                 events: {
                                     url: '/ajax/calendar/events/',
-                                    type: 'POST',
+                                    type: 'GET',
                                     data: {
-                                        'page_id': pageId
+                                        'pid': pageId
                                     }
                                 }
                             });

+ 1 - 1
coderedcms/templates/coderedcms/includes/ical/calendar_ical_button.html

@@ -1,5 +1,5 @@
 <form data-attribute="calendar-ical-form" class="calendar-ical-form" action="{% url 'event_generate_ical_for_calendar' %}" method="POST">
         <input name="page_id" type="number" hidden value="{{ page.id }}" />
         {% csrf_token %}
-    <button class="btn btn-primary" type="submit">{% block button_text %}Download Calendar{% endblock %}</button>
+    <button class="btn btn-primary" title="Download calendar in .ical format" type="submit">{% block button_text %}Download Calendar{% endblock %}</button>
 </form>

+ 1 - 1
coderedcms/templates/coderedcms/includes/ical/recurring_ical_button.html

@@ -1,5 +1,5 @@
 <form data-attribute="recurring-ical-form-{{event.pk}}" class="recurring-ical-form" action="{% url 'event_generate_recurring_ical' %}" method="POST">
     {% csrf_token %}
     <input name="event_pk" type="number" hidden value="{{event.pk}}" />
-    <button class="btn btn-primary" type="submit">{% block button_text %}Add All Events To Calendar{% endblock %}</button>
+    <button class="btn btn-primary" title="Download events in .ical format" type="submit">{% block button_text %}Add All Events To Calendar{% endblock %}</button>
 </form>

+ 4 - 4
coderedcms/templates/coderedcms/includes/ical/single_ical_button.html

@@ -1,7 +1,7 @@
-<form data-attribute="single-ical-form-{{event.pk}}-{{datetime_start}}" class="single-ical-form" action="{% url 'event_generate_single_ical' %}" method="POST">
+<form data-attribute="single-ical-form-{{event.pk}}-{{datetime_start}}" class="single-ical-form d-inline" action="{% url 'event_generate_single_ical' %}" method="POST">
     {% csrf_token %}
     <input name="event_pk" type="number" hidden value="{{event.pk}}" />
-    <input name="datetime_start" type="text" hidden value="{{datetime_start}}" />
-    <input name="datetime_end" type="text" hidden value="{{datetime_end}}" />
-    <button class="btn btn-primary" type="submit">{% block button_text %}Add to Calendar{% endblock %}</button>
+    <input name="datetime_start" type="text" hidden value="{{start|date:"c"}}" />
+    <input name="datetime_end" type="text" hidden value="{{end|date:"c"}}" />
+    <button class="btn btn-primary btn-sm" title="Download event in .ical format" type="submit">{% block button_text %}Add to Calendar{% endblock %}</button>
 </form>

+ 1 - 1
coderedcms/templates/coderedcms/includes/iframe_gmap.html

@@ -1,5 +1,5 @@
 {% if address %}
-<div class="row mb-2 mt-2">
+<div class="my-2">
     <div class="embed-responsive embed-responsive-16by9 mb-5">
         <iframe class="embed-responsive-item" width="100%" style="border:0" src="https://maps.google.com/maps?q={{ address }}&output=embed" allowfullscreen></iframe>
     </div>

+ 14 - 5
coderedcms/templates/coderedcms/includes/struct_data_event.json

@@ -2,11 +2,14 @@
 
 {
   "@context": "http://schema.org",
- "mainEntityOfPage": {
+  "mainEntityOfPage": {
     "@type": "WebPage",
     "@id": "{{page.get_full_url}}"
   },
-  "description": "{{page.get_description}}",
+
+  {% if page.search_description %}
+  "description": "{{page.search_description}}",
+  {% endif %}
 
   {# Get different aspect ratios. Use huge numbers because wagtail will not upscale, #}
   {# but will max out at the image's original resultion using the specified aspect ratio. #}
@@ -31,6 +34,7 @@
     ],
   {% endif %}
 
+  {% if self.address %}
   "location":{
     "@type": "Place",
     "name": "{{self.title}}",
@@ -39,11 +43,16 @@
       "streetAddress": "{{self.address}}",
     }
   },
+  {% endif %}
+
   "name": "{{self.title}}",
-  "startDate": "{{self.next_occurrence.0|structured_data_datetime}}",
-  {% if self.next_occurrence.1 != '' %}
-  "endDate": "{{self.next_occurrence.1|structured_data_datetime}}",
+
+  {% with self.most_recent_occurrence as nextup %}
+  "startDate": "{{nextup.0|structured_data_datetime}}",
+  {% if nextup.1 %}
+  "endDate": "{{nextup.1|structured_data_datetime}}",
   {% endif %}
+  {% endwith %}
 
   "@type": "Event"
 }

+ 0 - 8
coderedcms/templates/coderedcms/pages/article_page.html

@@ -15,14 +15,6 @@
 {% if settings.coderedcms.SeoSettings.og_meta %}
     {% block og_description %}{{self.get_description}}{% endblock %}
     {% block og_type %}article{% endblock %}
-    {% block og_image %}
-        {% if self.cover_image %}
-            {% image self.cover_image fill-2000x1000 as cover_image %}
-            {{self.get_site.root_url}}{{cover_image.url}}
-        {% else %}
-            {{block.super}}
-        {% endif %}
-    {% endblock %}
     {% block og_seo_extra %}
         <meta property="og:article:published_time" content="{{self.get_pub_date}}" />
         <meta property="og:article:modified_time" content="{{self.last_published_at}}" />

+ 3 - 0
coderedcms/templates/coderedcms/pages/base.html

@@ -42,6 +42,9 @@
                 {% if self.og_image %}
                     {% image self.og_image original as og_logo %}
                     {{og_logo.url}}
+                {% elif self.cover_image %}
+                    {% image self.cover_image original as og_logo %}
+                    {{og_logo.url}}
                 {% elif settings.coderedcms.LayoutSettings.logo %}
                     {% image settings.coderedcms.LayoutSettings.logo original as og_logo %}
                     {{og_logo.url}}

+ 3 - 1
coderedcms/templates/coderedcms/pages/event_index_page.html

@@ -3,6 +3,7 @@
 {% load wagtailcore_tags wagtailimages_tags coderedcms_tags %}
 
 {% block content_post_body %}
+{% if self.default_calendar_view %}
     <div class="container">
         <div class="row">
             <div class="col pt-5 pb-5">
@@ -17,6 +18,7 @@
             </div>
         </div>
     </div>
+{% endif %}
 {% endblock %}
 
 {% block index_content %}
@@ -34,7 +36,7 @@
                 {% block event_body_preview %}
                     <div class="col-md">
                         <h3><a href="{{ event.url }}">{{ event.title }}</a></h3>
-                        <p>{{ event.next_occurrence.0 }}</p>
+                        <p>{{ event.most_recent_occurrence.0 }}</p>
                         <p>{{ event.body_preview }}</p>
                     </div>
                 {% endblock %}

+ 35 - 46
coderedcms/templates/coderedcms/pages/event_page.html

@@ -1,27 +1,26 @@
 {% extends "coderedcms/pages/web_page.html" %}
-
 {% load wagtailadmin_tags wagtailcore_tags wagtailimages_tags coderedcms_tags %}
 
-{% block description %}{{self.get_description}}{% endblock %}
-
-
-{% if settings.coderedcms.SeoSettings.og_meta %}
-    {% block og_description %}{{self.get_description}}{% endblock %}
-    {% block og_image %}
-        {% if self.cover_image %}
-            {% image self.cover_image fill-2000x1000 as cover_image %}
-            {{self.get_site.root_url}}{{cover_image.url}}
-        {% else %}
-            {{block.super}}
-        {% endif %}
-    {% endblock %}
-{% endif %}
-
 {% if settings.coderedcms.SeoSettings.twitter_meta %}
     {% block twitter_card %}{% if self.cover_image %}summary_large_image{% else %}{{block.super}}{% endif %}{% endblock %}
 {% endif %}
 
+{% block content_pre_body %}
+    {{ block.super }}
+    {% with self.most_recent_occurrence as nextup %}
+    <div class="container my-5">
+        <p class="d-md-inline-block mr-4"><b>When:</b> {{nextup.0}}</p>
+        {% if self.address %}
+        <p class="d-md-inline-block mr-4"><b>Where:</b> {{self.address}}</p>
+        {% endif %}
+        <div class="float-lg-right">{% include "coderedcms/includes/ical/single_ical_button.html" with event=self start=nextup.0 end=nextup.1 %}</div>
+        <hr>
+    </div>
+    {% endwith %}
+{% endblock %}
+
 {% block content_post_body %}
+
     {% block map %}
         <div class="container">
             {% include 'coderedcms/includes/iframe_gmap.html' with address=page.address %}
@@ -29,42 +28,32 @@
     {% endblock %}
 
     {% block upcoming_dates %}
-        {% if self.upcoming_occurrences %}
+        {% if self.upcoming_occurrences|length > 1 %}
             <div class="container">
-                <div class="row">
-                    <div class="col">
-                        <h3 style="display:inline-block;">Upcoming Dates</h3>
-                        <div class="table-responsive">
-                            <table class="table table-striped">
-                                <tr>
-                                    <th>Starting Time</th>
-                                    <th>Ending Time</th>
-                                    <th>Actions</th>
-                                </tr>
-                                {% for date in self.upcoming_occurrences %}
-                                <tr>
-                                    <td>{{date.0}}</td>
-                                    <td>{% if date.0 != date.1 %}{{date.1}}{% endif %}</td>
-                                    <td>{% include "coderedcms/includes/ical/single_ical_button.html" with event=self datetime_start=date.0|date:"c" datetime_end=date.1|date:"c" %}</td>
-                                </tr>
-                                {% endfor %}
-                            </table>
-                        </div>
-                    </div>
+                <h2>Upcoming Dates</h2>
+                <div class="table-responsive">
+                    <table class="table table-striped">
+                        <tr>
+                            <th>Start Time</th>
+                            <th>End Time</th>
+                            <th>Actions</th>
+                        </tr>
+                        {% for date in self.upcoming_occurrences %}
+                        <tr>
+                            <td>{{date.0}}</td>
+                            <td>{% if date.0 != date.1 %}{{date.1}}{% endif %}</td>
+                            <td>{% include "coderedcms/includes/ical/single_ical_button.html" with event=self start=date.0 end=date.1 %}</td>
+                        </tr>
+                        {% endfor %}
+                    </table>
                 </div>
+                {% block ical %}
+                    {% include "coderedcms/includes/ical/recurring_ical_button.html" with event=self %}
+                {% endblock %}
             </div>
         {% endif %}
     {% endblock %}
 
-    {% block ical %}
-        <div class="container">
-            <div class="row">
-                <div class="col">
-                    {% include "coderedcms/includes/ical/recurring_ical_button.html" with event=self %}
-                </div>
-            </div>
-        </div>
-    {% endblock %}
 {% endblock %}
 
 {% if settings.coderedcms.SeoSettings.struct_meta %}

+ 12 - 3
coderedcms/views.py

@@ -17,6 +17,7 @@ from wagtail.core.models import Page
 from wagtail.search.backends import db, get_search_backend
 from wagtail.search.models import Query
 
+from coderedcms import utils
 from coderedcms.forms import SearchForm
 from coderedcms.models import CoderedPage, CoderedEventPage, get_page_models, GeneralSettings
 from coderedcms.importexport import convert_csv_to_json, import_pages, ImportPagesFromCSVFileForm
@@ -24,6 +25,7 @@ from coderedcms.settings import cr_settings
 
 
 
+
 def search(request):
     """
     Searches pages across the entire site.
@@ -99,6 +101,7 @@ def search(request):
         'results_paginated': results_paginated
     })
 
+
 @login_required
 def serve_protected_file(request, path):
     """
@@ -115,6 +118,7 @@ def serve_protected_file(request, path):
         return response
     raise Http404()
 
+
 def robots(request):
     robots = GeneralSettings.for_site(request.site).robots
     return render(
@@ -124,6 +128,7 @@ def robots(request):
         content_type='text/plain'
     )
 
+
 def event_generate_single_ical_for_event(request):
     if request.method == "POST":
         event_pk = request.POST['event_pk']
@@ -147,6 +152,7 @@ def event_generate_single_ical_for_event(request):
         return response
     raise Http404()
 
+
 def event_generate_recurring_ical_for_event(request):
     if request.method == "POST":
         event_pk = request.POST['event_pk']
@@ -166,6 +172,7 @@ def event_generate_recurring_ical_for_event(request):
         return response
     raise Http404()
 
+
 def event_generate_ical_for_calendar(request):
     if request.method == "POST":
         try:
@@ -184,19 +191,21 @@ def event_generate_ical_for_calendar(request):
         return response
     raise Http404()
 
+
 def event_get_calendar_events(request):
     if request.is_ajax():
         try:
-            page = CoderedPage.objects.get(id=request.POST.get('page_id')).specific
+            page = CoderedPage.objects.get(id=request.GET.get('pid')).specific
         except ValueError:
             raise Http404
-        start_str = request.POST.get('start')
+        start_str = request.GET.get('start')
         start = datetime.strptime(start_str[:10], "%Y-%m-%d") if start_str else None
-        end_str = request.POST.get('end')
+        end_str = request.GET.get('end')
         end = datetime.strptime(end_str[:10], "%Y-%m-%d") if end_str else None
         return JsonResponse(page.get_calendar_events(start=start, end=end), safe=False)
     raise Http404()
 
+
 @login_required
 def import_pages_from_csv_file(request):
     """

+ 33 - 8
docs/features/events.rst

@@ -1,15 +1,40 @@
 Events
 =============
 
-Event pages allow users to create a calendar or list of events.  These lists/calendars allow
-users to download ical invitations to their own calendars.
+Create a calendar or list of events. Visitors can download ical invitations to their own calendars
+for each event, recurring events, or all events.
 
-There are two abstract pages when dealing with events.  The first ``CoderedEventPage`` holds 
-the information regarding the event.  Dates, location, etc all will fall under this page.  The
-``CoderedEventIndexPage`` will aggregate its children ``CoderedEventPage`` and display them in a calendar.  
 
-The event functionality is built-in to Codered CMS but it is not enabled by default.  To implement,
-add the following to your ``website/models.py``::
+Usage
+-----
+
+If events are implemented on your site (see instructions below), first start by creating an
+"Event Landing Page" (may be named differently on your specific website). Add content to this
+page as usual. Under the **Layout** tab, you can choose a few options:
+
+* Show list of child pages: Check this box to show a list of all events.
+* Calendar style: There are several options here. Choose one that fits your needs.
+
+Next, save the Event Landing Page. Now create a child page. Each child page here represents
+an individual event. Events can be one time, or recurring, similar to Outlook or other
+calendar software.
+
+When creating an event page, fill out the relevant information, and click the **+** icon next
+to "Dates and times" to add an event occurrence. You can create multiple occurrences, or set
+an occurrence to repeat.
+
+
+Implementation
+--------------
+
+The event functionality is built-in to CodeRed CMS but it is not enabled by default.
+
+There are two abstract pages when dealing with events.  The first ``CoderedEventPage`` holds
+the information regarding the event.  Dates, location, etc. all will fall under this page.  The
+``CoderedEventIndexPage`` will aggregate its children ``CoderedEventPage`` and display them in a
+calendar or list.
+
+To implement, add the following to your ``website/models.py``::
 
     from modelcluster.fields import ParentalKey
     from coderedcms.models import (
@@ -49,4 +74,4 @@ add the following to your ``website/models.py``::
 Next run ``python manage.py makemigrations`` and ``python manage.py migrate`` to create the new pages
 in your project.
 
-Now when going to the wagtail admin, you can create an EventIndexPage, and child EventPages.
+Now when going to the wagtail admin, you can create an Event Landing Page, and child Event Pages.

部分文件因为文件数量过多而无法显示