cone.calendar 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cone/calendar/__init__.py +20 -0
- cone/calendar/browser/__init__.py +88 -0
- cone/calendar/browser/calendar.pt +16 -0
- cone/calendar/browser/calendar.py +370 -0
- cone/calendar/browser/static/calendar/cone.calendar.css +23 -0
- cone/calendar/browser/static/calendar/cone.calendar.js +241 -0
- cone/calendar/browser/static/calendar/cone.calendar.min.js +1 -0
- cone/calendar/browser/static/fullcalendar/fullcalendar.css +1404 -0
- cone/calendar/browser/static/fullcalendar/fullcalendar.js +14206 -0
- cone/calendar/browser/static/fullcalendar/fullcalendar.min.css +5 -0
- cone/calendar/browser/static/fullcalendar/fullcalendar.min.js +10 -0
- cone/calendar/browser/static/fullcalendar/fullcalendar.print.css +208 -0
- cone/calendar/browser/static/fullcalendar/fullcalendar.print.min.css +5 -0
- cone/calendar/browser/static/fullcalendar/gcal.js +180 -0
- cone/calendar/browser/static/fullcalendar/gcal.min.js +6 -0
- cone/calendar/browser/static/fullcalendar/locale/af.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/ar-dz.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/ar-ly.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/ar-ma.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/ar-sa.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/ar-tn.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/ar.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/bg.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/ca.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/cs.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/da.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/de-at.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/de.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/el.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/en-au.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/en-ca.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/en-gb.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/en-ie.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/en-nz.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/es-do.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/es.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/eu.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/fa.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/fi.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/fr-ca.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/fr-ch.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/fr.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/gl.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/he.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/hi.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/hr.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/hu.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/id.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/is.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/it.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/ja.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/kk.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/ko.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/lb.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/lt.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/lv.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/mk.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/ms-my.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/ms.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/nb.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/nl-be.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/nl.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/nn.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/pl.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/pt-br.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/pt.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/ro.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/ru.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/sk.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/sl.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/sr-cyrl.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/sr.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/sv.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/th.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/tr.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/uk.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/vi.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/zh-cn.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale/zh-tw.js +1 -0
- cone/calendar/browser/static/fullcalendar/locale-all.js +5 -0
- cone/calendar/browser/static/moment/locale/af.js +73 -0
- cone/calendar/browser/static/moment/locale/ar-dz.js +59 -0
- cone/calendar/browser/static/moment/locale/ar-ly.js +126 -0
- cone/calendar/browser/static/moment/locale/ar-ma.js +60 -0
- cone/calendar/browser/static/moment/locale/ar-sa.js +105 -0
- cone/calendar/browser/static/moment/locale/ar-tn.js +59 -0
- cone/calendar/browser/static/moment/locale/ar.js +142 -0
- cone/calendar/browser/static/moment/locale/az.js +105 -0
- cone/calendar/browser/static/moment/locale/be.js +134 -0
- cone/calendar/browser/static/moment/locale/bg.js +90 -0
- cone/calendar/browser/static/moment/locale/bn.js +119 -0
- cone/calendar/browser/static/moment/locale/bo.js +119 -0
- cone/calendar/browser/static/moment/locale/br.js +108 -0
- cone/calendar/browser/static/moment/locale/bs.js +143 -0
- cone/calendar/browser/static/moment/locale/ca.js +81 -0
- cone/calendar/browser/static/moment/locale/cs.js +172 -0
- cone/calendar/browser/static/moment/locale/cv.js +63 -0
- cone/calendar/browser/static/moment/locale/cy.js +81 -0
- cone/calendar/browser/static/moment/locale/da.js +60 -0
- cone/calendar/browser/static/moment/locale/de-at.js +79 -0
- cone/calendar/browser/static/moment/locale/de.js +78 -0
- cone/calendar/browser/static/moment/locale/dv.js +100 -0
- cone/calendar/browser/static/moment/locale/el.js +98 -0
- cone/calendar/browser/static/moment/locale/en-au.js +67 -0
- cone/calendar/browser/static/moment/locale/en-ca.js +63 -0
- cone/calendar/browser/static/moment/locale/en-gb.js +67 -0
- cone/calendar/browser/static/moment/locale/en-ie.js +67 -0
- cone/calendar/browser/static/moment/locale/en-nz.js +67 -0
- cone/calendar/browser/static/moment/locale/eo.js +73 -0
- cone/calendar/browser/static/moment/locale/es-do.js +80 -0
- cone/calendar/browser/static/moment/locale/es.js +81 -0
- cone/calendar/browser/static/moment/locale/et.js +80 -0
- cone/calendar/browser/static/moment/locale/eu.js +66 -0
- cone/calendar/browser/static/moment/locale/fa.js +107 -0
- cone/calendar/browser/static/moment/locale/fi.js +107 -0
- cone/calendar/browser/static/moment/locale/fo.js +60 -0
- cone/calendar/browser/static/moment/locale/fr-ca.js +60 -0
- cone/calendar/browser/static/moment/locale/fr-ch.js +64 -0
- cone/calendar/browser/static/moment/locale/fr.js +64 -0
- cone/calendar/browser/static/moment/locale/fy.js +73 -0
- cone/calendar/browser/static/moment/locale/gd.js +76 -0
- cone/calendar/browser/static/moment/locale/gl.js +77 -0
- cone/calendar/browser/static/moment/locale/he.js +99 -0
- cone/calendar/browser/static/moment/locale/hi.js +124 -0
- cone/calendar/browser/static/moment/locale/hr.js +145 -0
- cone/calendar/browser/static/moment/locale/hu.js +109 -0
- cone/calendar/browser/static/moment/locale/hy-am.js +95 -0
- cone/calendar/browser/static/moment/locale/id.js +83 -0
- cone/calendar/browser/static/moment/locale/is.js +127 -0
- cone/calendar/browser/static/moment/locale/it.js +70 -0
- cone/calendar/browser/static/moment/locale/ja.js +76 -0
- cone/calendar/browser/static/moment/locale/jv.js +83 -0
- cone/calendar/browser/static/moment/locale/ka.js +89 -0
- cone/calendar/browser/static/moment/locale/kk.js +87 -0
- cone/calendar/browser/static/moment/locale/km.js +58 -0
- cone/calendar/browser/static/moment/locale/ko.js +65 -0
- cone/calendar/browser/static/moment/locale/ky.js +88 -0
- cone/calendar/browser/static/moment/locale/lb.js +137 -0
- cone/calendar/browser/static/moment/locale/lo.js +70 -0
- cone/calendar/browser/static/moment/locale/lt.js +117 -0
- cone/calendar/browser/static/moment/locale/lv.js +97 -0
- cone/calendar/browser/static/moment/locale/me.js +111 -0
- cone/calendar/browser/static/moment/locale/mi.js +64 -0
- cone/calendar/browser/static/moment/locale/mk.js +90 -0
- cone/calendar/browser/static/moment/locale/ml.js +81 -0
- cone/calendar/browser/static/moment/locale/mr.js +159 -0
- cone/calendar/browser/static/moment/locale/ms-my.js +83 -0
- cone/calendar/browser/static/moment/locale/ms.js +82 -0
- cone/calendar/browser/static/moment/locale/my.js +96 -0
- cone/calendar/browser/static/moment/locale/nb.js +63 -0
- cone/calendar/browser/static/moment/locale/ne.js +123 -0
- cone/calendar/browser/static/moment/locale/nl-be.js +86 -0
- cone/calendar/browser/static/moment/locale/nl.js +86 -0
- cone/calendar/browser/static/moment/locale/nn.js +60 -0
- cone/calendar/browser/static/moment/locale/pa-in.js +124 -0
- cone/calendar/browser/static/moment/locale/pl.js +105 -0
- cone/calendar/browser/static/moment/locale/pt-br.js +61 -0
- cone/calendar/browser/static/moment/locale/pt.js +65 -0
- cone/calendar/browser/static/moment/locale/ro.js +75 -0
- cone/calendar/browser/static/moment/locale/ru.js +183 -0
- cone/calendar/browser/static/moment/locale/se.js +61 -0
- cone/calendar/browser/static/moment/locale/si.js +71 -0
- cone/calendar/browser/static/moment/locale/sk.js +150 -0
- cone/calendar/browser/static/moment/locale/sl.js +162 -0
- cone/calendar/browser/static/moment/locale/sq.js +70 -0
- cone/calendar/browser/static/moment/locale/sr-cyrl.js +110 -0
- cone/calendar/browser/static/moment/locale/sr.js +110 -0
- cone/calendar/browser/static/moment/locale/ss.js +89 -0
- cone/calendar/browser/static/moment/locale/sv.js +69 -0
- cone/calendar/browser/static/moment/locale/sw.js +59 -0
- cone/calendar/browser/static/moment/locale/ta.js +130 -0
- cone/calendar/browser/static/moment/locale/te.js +89 -0
- cone/calendar/browser/static/moment/locale/tet.js +68 -0
- cone/calendar/browser/static/moment/locale/th.js +67 -0
- cone/calendar/browser/static/moment/locale/tl-ph.js +62 -0
- cone/calendar/browser/static/moment/locale/tlh.js +120 -0
- cone/calendar/browser/static/moment/locale/tr.js +90 -0
- cone/calendar/browser/static/moment/locale/tzl.js +91 -0
- cone/calendar/browser/static/moment/locale/tzm-latn.js +58 -0
- cone/calendar/browser/static/moment/locale/tzm.js +58 -0
- cone/calendar/browser/static/moment/locale/uk.js +146 -0
- cone/calendar/browser/static/moment/locale/uz.js +58 -0
- cone/calendar/browser/static/moment/locale/vi.js +79 -0
- cone/calendar/browser/static/moment/locale/x-pseudo.js +68 -0
- cone/calendar/browser/static/moment/locale/yo.js +60 -0
- cone/calendar/browser/static/moment/locale/zh-cn.js +127 -0
- cone/calendar/browser/static/moment/locale/zh-hk.js +105 -0
- cone/calendar/browser/static/moment/locale/zh-tw.js +104 -0
- cone/calendar/browser/static/moment/locales.js +8624 -0
- cone/calendar/browser/static/moment/locales.min.js +663 -0
- cone/calendar/browser/static/moment/moment-with-locales.js +12910 -0
- cone/calendar/browser/static/moment/moment-with-locales.min.js +1216 -0
- cone/calendar/browser/static/moment/moment.min.js +551 -0
- cone/calendar/interfaces.py +14 -0
- cone/calendar/locale/cone.calendar.pot +0 -0
- cone/calendar/locale/de/LC_MESSAGES/cone.calendar.po +0 -0
- cone/calendar/locale/en/LC_MESSAGES/cone.calendar.po +0 -0
- cone/calendar/tests/test_calendar.py +340 -0
- cone_calendar-1.1.0.dist-info/METADATA +252 -0
- cone_calendar-1.1.0.dist-info/RECORD +202 -0
- cone_calendar-1.1.0.dist-info/WHEEL +4 -0
- cone_calendar-1.1.0.dist-info/licenses/LICENSE.rst +27 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from cone.app import main_hook
|
|
2
|
+
from cone.calendar.browser import configure_resources
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger('cone.calendar')
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@main_hook
|
|
10
|
+
def initialize_calendar(config, global_config, settings):
|
|
11
|
+
# application startup initialization
|
|
12
|
+
|
|
13
|
+
# static resources
|
|
14
|
+
configure_resources(config, settings)
|
|
15
|
+
|
|
16
|
+
# add translation
|
|
17
|
+
config.add_translation_dirs('cone.calendar:locale/')
|
|
18
|
+
|
|
19
|
+
# scan browser package
|
|
20
|
+
config.scan('cone.calendar.browser')
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from .calendar import CalendarEvents # noqa
|
|
2
|
+
from .calendar import CalendarTile # noqa
|
|
3
|
+
import os
|
|
4
|
+
import webresource as wr
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
resources_dir = os.path.join(os.path.dirname(__file__), 'static')
|
|
8
|
+
|
|
9
|
+
# moment js
|
|
10
|
+
moment_resources = wr.ResourceGroup(
|
|
11
|
+
name='cone.calendar-moment',
|
|
12
|
+
directory=os.path.join(resources_dir, 'moment'),
|
|
13
|
+
path='moment'
|
|
14
|
+
)
|
|
15
|
+
moment_resources.add(wr.ScriptResource(
|
|
16
|
+
name='moment-js',
|
|
17
|
+
resource='moment.min.js'
|
|
18
|
+
))
|
|
19
|
+
|
|
20
|
+
# fullcalendar
|
|
21
|
+
fullcalendar_resources = wr.ResourceGroup(
|
|
22
|
+
name='cone.calendar-fullcalendar',
|
|
23
|
+
directory=os.path.join(resources_dir, 'fullcalendar'),
|
|
24
|
+
path='fullcalendar'
|
|
25
|
+
)
|
|
26
|
+
fullcalendar_resources.add(wr.ScriptResource(
|
|
27
|
+
name='fullcalendar-js',
|
|
28
|
+
depends=['moment-js', 'jquery-js'],
|
|
29
|
+
resource='fullcalendar.js',
|
|
30
|
+
compressed='fullcalendar.min.js'
|
|
31
|
+
))
|
|
32
|
+
fullcalendar_resources.add(wr.StyleResource(
|
|
33
|
+
name='fullcalendar-css',
|
|
34
|
+
resource='fullcalendar.css',
|
|
35
|
+
compressed='fullcalendar.min.css'
|
|
36
|
+
))
|
|
37
|
+
# XXX: print CSS needs to be delivered dynamically in dedicated print view
|
|
38
|
+
# fullcalendar_resources.add(wr.StyleResource(
|
|
39
|
+
# name='fullcalendar-print-css',
|
|
40
|
+
# resource='fullcalendar.print.css',
|
|
41
|
+
# compressed='fullcalendar.print.min.css'
|
|
42
|
+
# ))
|
|
43
|
+
|
|
44
|
+
# cone calendar
|
|
45
|
+
cone_calendar_resources = wr.ResourceGroup(
|
|
46
|
+
name='cone.calendar-calendar',
|
|
47
|
+
directory=os.path.join(resources_dir, 'calendar'),
|
|
48
|
+
path='calendar'
|
|
49
|
+
)
|
|
50
|
+
cone_calendar_resources.add(wr.ScriptResource(
|
|
51
|
+
name='cone-calendar-js',
|
|
52
|
+
depends='fullcalendar-js',
|
|
53
|
+
resource='cone.calendar.js',
|
|
54
|
+
compressed='cone.calendar.min.js'
|
|
55
|
+
))
|
|
56
|
+
cone_calendar_resources.add(wr.StyleResource(
|
|
57
|
+
name='cone-calendar-css',
|
|
58
|
+
depends='fullcalendar-css',
|
|
59
|
+
resource='cone.calendar.css'
|
|
60
|
+
))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def configure_resources(config, settings):
|
|
64
|
+
config.register_resource(moment_resources)
|
|
65
|
+
config.set_resource_include('moment-js', 'authenticated')
|
|
66
|
+
|
|
67
|
+
config.register_resource(fullcalendar_resources)
|
|
68
|
+
config.set_resource_include('fullcalendar-js', 'authenticated')
|
|
69
|
+
config.set_resource_include('fullcalendar-css', 'authenticated')
|
|
70
|
+
|
|
71
|
+
config.register_resource(cone_calendar_resources)
|
|
72
|
+
config.set_resource_include('cone-calendar-js', 'authenticated')
|
|
73
|
+
config.set_resource_include('cone-calendar-css', 'authenticated')
|
|
74
|
+
|
|
75
|
+
locales = settings.get('cone.calendar.locales')
|
|
76
|
+
if not locales:
|
|
77
|
+
return
|
|
78
|
+
locales_directory = os.path.join(resources_dir, 'fullcalendar', 'locale')
|
|
79
|
+
for locale in [loc.strip() for loc in locales.split(',') if loc]:
|
|
80
|
+
locale_name = 'fullcalendar-{}-js'.format(locale)
|
|
81
|
+
fullcalendar_resources.add(wr.ScriptResource(
|
|
82
|
+
name=locale_name,
|
|
83
|
+
depends='fullcalendar-js',
|
|
84
|
+
directory=locales_directory,
|
|
85
|
+
path='fullcalendar/locale',
|
|
86
|
+
resource='{}.js'.format(locale)
|
|
87
|
+
))
|
|
88
|
+
config.set_resource_include(locale_name, 'authenticated')
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<tal:block xmlns:tal="http://xml.zope.org/namespaces/tal"
|
|
2
|
+
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
|
|
3
|
+
xmlns:ajax="http://namespaces.conestack.org/ajax"
|
|
4
|
+
i18n:domain="cone.calendar"
|
|
5
|
+
omit-tag="True">
|
|
6
|
+
|
|
7
|
+
<tal:contextmenu condition="context.show_contextmenu"
|
|
8
|
+
replace="structure tile('contextmenu')" />
|
|
9
|
+
|
|
10
|
+
<div id="calendar"
|
|
11
|
+
data-calendar_target='${context.target}'
|
|
12
|
+
data-calendar_options='${context.options}'
|
|
13
|
+
data-calendar_sources='${context.sources}'
|
|
14
|
+
data-calendar_actions='${context.actions}'></div>
|
|
15
|
+
|
|
16
|
+
</tal:block>
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
from cone.app.browser import render_main_template
|
|
2
|
+
from cone.app.browser.utils import format_traceback
|
|
3
|
+
from cone.tile import Tile
|
|
4
|
+
from cone.tile import tile
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from pyramid.i18n import TranslationStringFactory
|
|
7
|
+
from pyramid.view import view_config
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger('cone.calendar')
|
|
13
|
+
_ = TranslationStringFactory('cone.calendar')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def parse_date(seconds):
|
|
17
|
+
"""Parse datetime from value received from fullcalendar.
|
|
18
|
+
"""
|
|
19
|
+
return datetime.utcfromtimestamp(int(seconds))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def format_date(dt):
|
|
23
|
+
"""Format datetime suitable for fullcalendar.
|
|
24
|
+
"""
|
|
25
|
+
return dt.isoformat()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@tile(name='calendar', path='calendar.pt', permission='view')
|
|
29
|
+
class CalendarTile(Tile):
|
|
30
|
+
"""Tile rendering the fullcalendar widget.
|
|
31
|
+
|
|
32
|
+
Reference: https://fullcalendar.io/docs/v3
|
|
33
|
+
|
|
34
|
+
Configuration of the calendar is done via ``model.properties`` object.
|
|
35
|
+
|
|
36
|
+
Available configuration properties:
|
|
37
|
+
|
|
38
|
+
``calendar_locale``:
|
|
39
|
+
Corresponds to ``locale`` option passed to fullcalendar.
|
|
40
|
+
|
|
41
|
+
See https://fullcalendar.io/docs/v3/locale
|
|
42
|
+
|
|
43
|
+
Note: Locale files must be preloaded in order to provide the desored
|
|
44
|
+
locales. The available locales are configured in the application ini
|
|
45
|
+
file.
|
|
46
|
+
|
|
47
|
+
``calendar_header``:
|
|
48
|
+
Corresponds to ``header`` option passed to fullcalendar.
|
|
49
|
+
|
|
50
|
+
See https://fullcalendar.io/docs/v3/header
|
|
51
|
+
|
|
52
|
+
``calendar_footer``:
|
|
53
|
+
Corresponds to ``footer`` option passed to fullcalendar.
|
|
54
|
+
|
|
55
|
+
See https://fullcalendar.io/docs/v3/footer
|
|
56
|
+
|
|
57
|
+
``calendar_first_day``:
|
|
58
|
+
Corresponds to ``firstDay`` option passed to fullcalendar.
|
|
59
|
+
|
|
60
|
+
See https://fullcalendar.io/docs/v3/firstDay
|
|
61
|
+
|
|
62
|
+
``calendar_weekends``:
|
|
63
|
+
Corresponds to ``weekends`` option passed to fullcalendar.
|
|
64
|
+
|
|
65
|
+
See https://fullcalendar.io/docs/v3/weekends
|
|
66
|
+
|
|
67
|
+
``calendar_week_numbers``:
|
|
68
|
+
Corresponds to ``weekNumbers`` option passed to fullcalendar.
|
|
69
|
+
|
|
70
|
+
See https://fullcalendar.io/docs/v3/weekNumbers
|
|
71
|
+
|
|
72
|
+
``calendar_week_numbers_within_days``:
|
|
73
|
+
Corresponds to ``weekNumbersWithinDays`` option passed to fullcalendar.
|
|
74
|
+
|
|
75
|
+
See https://fullcalendar.io/docs/v3/weekNumbersWithinDays
|
|
76
|
+
|
|
77
|
+
``calendar_business_hours``:
|
|
78
|
+
Corresponds to ``businessHours`` option passed to fullcalendar.
|
|
79
|
+
|
|
80
|
+
See https://fullcalendar.io/docs/v3/businessHours
|
|
81
|
+
|
|
82
|
+
``calendar_sources``:
|
|
83
|
+
A list of dictionaries containing event source configurations.
|
|
84
|
+
|
|
85
|
+
See https://fullcalendar.io/docs/v3/event-source-object
|
|
86
|
+
|
|
87
|
+
As ``events`` attribute, a JSON view name is expected. On the client
|
|
88
|
+
side a callback handler gets created using this view name for fetching
|
|
89
|
+
event data. The base class for implementing JSON event data is
|
|
90
|
+
``CalendarEvents``.
|
|
91
|
+
|
|
92
|
+
If ``calendar_sources`` is not defined on model properties,
|
|
93
|
+
``CalendarTile.default_sources`` is used.
|
|
94
|
+
|
|
95
|
+
``calendar_actions``:
|
|
96
|
+
List of actions for clicking empty day/time in calendar.
|
|
97
|
+
|
|
98
|
+
Action definition format and behavior is the same as for the ``actions``
|
|
99
|
+
attribute of event data.
|
|
100
|
+
|
|
101
|
+
See ``CalendarEvents.events`` documentation for details.
|
|
102
|
+
|
|
103
|
+
As default target the calendar context target is used if not defined
|
|
104
|
+
explicitely on actions.
|
|
105
|
+
|
|
106
|
+
If ``calendar_actions`` is not defined on model properties,
|
|
107
|
+
``CalendarTile.default_actions`` is used.
|
|
108
|
+
"""
|
|
109
|
+
show_contextmenu = False
|
|
110
|
+
option_mapping = {
|
|
111
|
+
'calendar_locale': 'locale',
|
|
112
|
+
'calendar_header': 'header',
|
|
113
|
+
'calendar_footer': 'footer',
|
|
114
|
+
'calendar_first_day': 'firstDay',
|
|
115
|
+
'calendar_weekends': 'weekends',
|
|
116
|
+
'calendar_week_numbers': 'weekNumbers',
|
|
117
|
+
'calendar_week_numbers_within_days': 'weekNumbersWithinDays',
|
|
118
|
+
'calendar_business_hours': 'businessHours'
|
|
119
|
+
}
|
|
120
|
+
default_options = {
|
|
121
|
+
'calendar_locale': 'en'
|
|
122
|
+
}
|
|
123
|
+
default_sources = [{
|
|
124
|
+
'events': 'calendar_events'
|
|
125
|
+
}]
|
|
126
|
+
default_actions = []
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def target(self):
|
|
130
|
+
"""Calendar context target. Gets used for querying event sources.
|
|
131
|
+
"""
|
|
132
|
+
return self.nodeurl
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def options(self):
|
|
136
|
+
options = {}
|
|
137
|
+
for prop_name, option_name in self.option_mapping.items():
|
|
138
|
+
value = getattr(self.model.properties, prop_name)
|
|
139
|
+
if value is None:
|
|
140
|
+
value = self.default_options.get(prop_name)
|
|
141
|
+
if value is not None:
|
|
142
|
+
options[option_name] = value
|
|
143
|
+
return json.dumps(options, sort_keys=True) if options else None
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def sources(self):
|
|
147
|
+
sources = self.model.properties.calendar_sources
|
|
148
|
+
if not sources:
|
|
149
|
+
sources = self.default_sources
|
|
150
|
+
return json.dumps(sources)
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def actions(self):
|
|
154
|
+
actions = self.model.properties.calendar_actions
|
|
155
|
+
if not actions:
|
|
156
|
+
actions = self.default_actions
|
|
157
|
+
return json.dumps(actions)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@view_config(name='calendar', permission='view')
|
|
161
|
+
def calendar(model, request):
|
|
162
|
+
"""Traversable view rendering the ``calendar`` tile to content area.
|
|
163
|
+
"""
|
|
164
|
+
return render_main_template(model, request, 'calendar')
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class JSONView(object):
|
|
168
|
+
"""Abstract JSON view.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
def __init__(self, model, request):
|
|
172
|
+
self.model = model
|
|
173
|
+
self.request = request
|
|
174
|
+
|
|
175
|
+
def __call__(self):
|
|
176
|
+
"""Return dict with JSON data result.
|
|
177
|
+
|
|
178
|
+
If success, dict contains data at key 'data'.
|
|
179
|
+
|
|
180
|
+
If an exception is raised while computing, dict contains flag indicating
|
|
181
|
+
error happends at key 'error' and the formatted traceback at key
|
|
182
|
+
'message'.
|
|
183
|
+
"""
|
|
184
|
+
try:
|
|
185
|
+
return dict(data=self.data())
|
|
186
|
+
except Exception as e:
|
|
187
|
+
logger.exception(e)
|
|
188
|
+
return dict(
|
|
189
|
+
error=True,
|
|
190
|
+
message=format_traceback()
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
def data(self):
|
|
194
|
+
"""Supposed to be implemented on subclass. Responsible to compute JSON
|
|
195
|
+
data delivered to client.
|
|
196
|
+
"""
|
|
197
|
+
raise NotImplementedError(
|
|
198
|
+
'Abstract ``JSONView`` does not implement ``data``'
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@view_config(
|
|
203
|
+
name='calendar_events',
|
|
204
|
+
accept='application/json',
|
|
205
|
+
renderer='json',
|
|
206
|
+
permission='view')
|
|
207
|
+
class CalendarEvents(JSONView):
|
|
208
|
+
"""Abstract JSON event data view for fullcalendar.
|
|
209
|
+
|
|
210
|
+
Concrete even data providers must derive from this object and implement
|
|
211
|
+
``events`` function.
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
@property
|
|
215
|
+
def range_start(self):
|
|
216
|
+
"""Event range start as datetime.
|
|
217
|
+
"""
|
|
218
|
+
start = self.request.params.get('start')
|
|
219
|
+
return parse_date(start)
|
|
220
|
+
|
|
221
|
+
@property
|
|
222
|
+
def range_end(self):
|
|
223
|
+
"""Event range end as datetime.
|
|
224
|
+
"""
|
|
225
|
+
end = self.request.params.get('end')
|
|
226
|
+
return parse_date(end)
|
|
227
|
+
|
|
228
|
+
def events(self, start, end):
|
|
229
|
+
"""Return events for requested range.
|
|
230
|
+
|
|
231
|
+
Return format:
|
|
232
|
+
|
|
233
|
+
[{
|
|
234
|
+
'id': uuid.UUID,
|
|
235
|
+
'title': 'Event Title',
|
|
236
|
+
'start': datetime.datetime,
|
|
237
|
+
'end': datetime.datetime,
|
|
238
|
+
'target': 'https://example.com/path/to/event',
|
|
239
|
+
'actions': [{
|
|
240
|
+
'title': 'Edit',
|
|
241
|
+
'icon': 'glyphicons glyphicons-pencil',
|
|
242
|
+
'target': 'https://example.com/path/to/event?param=value',
|
|
243
|
+
'overlay': {
|
|
244
|
+
'action': 'overlayedit',
|
|
245
|
+
'selector': '#ajax-form',
|
|
246
|
+
'content_selector': '.overlay_content',
|
|
247
|
+
'css': 'overlay-form'
|
|
248
|
+
}
|
|
249
|
+
}, {
|
|
250
|
+
'title': 'Delete',
|
|
251
|
+
'icon': 'glyphicons glyphicons-remove-circle',
|
|
252
|
+
'confirm': 'Do you really want to delete this event?',
|
|
253
|
+
'action': {
|
|
254
|
+
'name': 'delete',
|
|
255
|
+
'selector': 'NONE',
|
|
256
|
+
'mode': 'NONE'
|
|
257
|
+
}
|
|
258
|
+
}]
|
|
259
|
+
}]
|
|
260
|
+
|
|
261
|
+
For a full list of fullcalendar event attributes see
|
|
262
|
+
https://fullcalendar.io/docs/v3/event-object
|
|
263
|
+
|
|
264
|
+
The ``actions`` and ``target`` attributes are cone specific.
|
|
265
|
+
|
|
266
|
+
``target`` defines the default target where actions get executed.
|
|
267
|
+
Target can be overwritten by specific actions. Target represents the URL of
|
|
268
|
+
the event without trailing view name and may contain a query string.
|
|
269
|
+
Target is mandatory if event is editable or actions are defined which
|
|
270
|
+
do not provide a target on their own.
|
|
271
|
+
|
|
272
|
+
``actions`` defines a list of actions available for the specific event.
|
|
273
|
+
|
|
274
|
+
If one action is specified, it gets executed directly when event gets
|
|
275
|
+
clicked.
|
|
276
|
+
|
|
277
|
+
If multiple actions are specified, a dropdown menu containing the actions
|
|
278
|
+
as menu items gets rendered.
|
|
279
|
+
|
|
280
|
+
Action options:
|
|
281
|
+
|
|
282
|
+
``title``
|
|
283
|
+
The action title as string.
|
|
284
|
+
|
|
285
|
+
``icon``
|
|
286
|
+
Icon CSS class. Optional.
|
|
287
|
+
|
|
288
|
+
``target``
|
|
289
|
+
Target on which the action gets executed. Target represents the URL
|
|
290
|
+
of the event without trailing view name and may contain a query
|
|
291
|
+
string. Optional, if skipped the default target is used.
|
|
292
|
+
|
|
293
|
+
``action``
|
|
294
|
+
bdajax action definitions as dict containing:
|
|
295
|
+
|
|
296
|
+
{
|
|
297
|
+
'name': 'action_name',
|
|
298
|
+
'selector': '.some_selector',
|
|
299
|
+
'mode': 'replace'
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
At least one option out of ``action``, ``event`` or ``overlay`` must
|
|
303
|
+
be defined. An arbitrary combination is possible.
|
|
304
|
+
|
|
305
|
+
``event``
|
|
306
|
+
bdajax event definitions as dict containing:
|
|
307
|
+
|
|
308
|
+
{
|
|
309
|
+
'name': 'eventname',
|
|
310
|
+
'selector': '.some_selector'
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
At least one option out of ``action``, ``event`` or ``overlay`` must
|
|
314
|
+
be defined. An arbitrary combination is possible.
|
|
315
|
+
|
|
316
|
+
``overlay``
|
|
317
|
+
bdajax overlay definitions as dict containing:
|
|
318
|
+
|
|
319
|
+
{
|
|
320
|
+
'action': 'action_name',
|
|
321
|
+
'selector': '#ajax-form',
|
|
322
|
+
'content_selector': '.overlay_content',
|
|
323
|
+
'css': 'additional_overlay_css_class'
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
At least one option out of ``action``, ``event`` or ``overlay`` must
|
|
327
|
+
be defined. An arbitrary combination is possible.
|
|
328
|
+
|
|
329
|
+
``confirm``
|
|
330
|
+
Confirmation message as string. Optional.
|
|
331
|
+
"""
|
|
332
|
+
raise NotImplementedError(
|
|
333
|
+
'Abstract ``CalendarEvents`` does not implement ``events``'
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
def data(self):
|
|
337
|
+
events = self.events(self.range_start, self.range_end)
|
|
338
|
+
for event in events:
|
|
339
|
+
event['id'] = str(event['id'])
|
|
340
|
+
event['start'] = format_date(event['start'])
|
|
341
|
+
event['end'] = format_date(event['end'])
|
|
342
|
+
return events
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
@view_config(
|
|
346
|
+
name='calendar_event_drop',
|
|
347
|
+
# context=IScheduled,
|
|
348
|
+
accept='application/json',
|
|
349
|
+
renderer='json',
|
|
350
|
+
permission='view')
|
|
351
|
+
class CalendarEventDrop(JSONView):
|
|
352
|
+
"""JSON view for modifying event start time.
|
|
353
|
+
"""
|
|
354
|
+
|
|
355
|
+
def data(self):
|
|
356
|
+
return 'DROPPED'
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
@view_config(
|
|
360
|
+
name='calendar_event_resize',
|
|
361
|
+
# context=IScheduled,
|
|
362
|
+
accept='application/json',
|
|
363
|
+
renderer='json',
|
|
364
|
+
permission='view')
|
|
365
|
+
class CalendarEventResize(JSONView):
|
|
366
|
+
"""JSON view for modifying event duration.
|
|
367
|
+
"""
|
|
368
|
+
|
|
369
|
+
def data(self):
|
|
370
|
+
return 'RESIZED'
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* contextmenu */
|
|
2
|
+
.calendar-contextmenu-wrapper {
|
|
3
|
+
position:absolute;
|
|
4
|
+
z-index:1000;
|
|
5
|
+
top:0;
|
|
6
|
+
left:0;
|
|
7
|
+
width:100%;
|
|
8
|
+
}
|
|
9
|
+
.calendar-contextmenu {
|
|
10
|
+
background-color:#fff;
|
|
11
|
+
position:absolute;
|
|
12
|
+
z-index:1000;
|
|
13
|
+
}
|
|
14
|
+
.calendar-contextmenu li {
|
|
15
|
+
padding-left:10px;
|
|
16
|
+
cursor:pointer;
|
|
17
|
+
}
|
|
18
|
+
.calendar-contextmenu li:hover {
|
|
19
|
+
background-color:#ccc;
|
|
20
|
+
}
|
|
21
|
+
.calendar-contextmenu li span {
|
|
22
|
+
margin-right:10px;
|
|
23
|
+
}
|