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,14 @@
|
|
|
1
|
+
from zope.interface import Attribute
|
|
2
|
+
from zope.interface import Interface
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class IEvent(Interface):
|
|
6
|
+
start = Attribute('Date as datetime.datetime instance')
|
|
7
|
+
end = Attribute('Date as datetime.datetime instance')
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ICron(Interface):
|
|
11
|
+
effective = Attribute('Date as datetime.datetime instance')
|
|
12
|
+
expires = Attribute('Date as datetime.datetime instance')
|
|
13
|
+
rule = Attribute('Crontab rule as string')
|
|
14
|
+
duration = Attribute('Duration in minutes')
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
from cone.app import testing
|
|
2
|
+
from cone.app.model import BaseNode
|
|
3
|
+
from cone.app.model import Properties
|
|
4
|
+
from cone.calendar import browser
|
|
5
|
+
from cone.calendar.browser import CalendarEvents
|
|
6
|
+
from cone.calendar.browser import CalendarTile
|
|
7
|
+
from cone.calendar.browser.calendar import JSONView
|
|
8
|
+
from cone.tile import render_tile
|
|
9
|
+
from cone.tile.tests import TileTestCase
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from node.utils import instance_property
|
|
12
|
+
from pyramid.httpexceptions import HTTPForbidden
|
|
13
|
+
import json
|
|
14
|
+
import os
|
|
15
|
+
import sys
|
|
16
|
+
import unittest
|
|
17
|
+
import uuid
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class CalendarLayer(testing.Security):
|
|
21
|
+
|
|
22
|
+
def make_app(self, **kw):
|
|
23
|
+
super(CalendarLayer, self).make_app(**{
|
|
24
|
+
'cone.plugins': 'cone.calendar',
|
|
25
|
+
'cone.calendar.locales': 'de,it'
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
calendar_layer = CalendarLayer()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CalendarNode(BaseNode):
|
|
33
|
+
|
|
34
|
+
@instance_property
|
|
35
|
+
def properties(self):
|
|
36
|
+
return Properties()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class TestCalendarTile(TileTestCase):
|
|
40
|
+
layer = calendar_layer
|
|
41
|
+
|
|
42
|
+
def test_calendar_rendering(self):
|
|
43
|
+
model = CalendarNode(name='calendar')
|
|
44
|
+
request = self.layer.new_request()
|
|
45
|
+
|
|
46
|
+
with self.assertRaises(HTTPForbidden):
|
|
47
|
+
render_tile(model, request, 'calendar')
|
|
48
|
+
|
|
49
|
+
with self.layer.authenticated('admin'):
|
|
50
|
+
rendered = render_tile(model, request, 'calendar')
|
|
51
|
+
self.checkOutput("""
|
|
52
|
+
<div id="calendar"
|
|
53
|
+
data-calendar_target='http://example.com/calendar'
|
|
54
|
+
data-calendar_options='{"locale": "en"}'
|
|
55
|
+
data-calendar_sources='[{"events": "calendar_events"}]'
|
|
56
|
+
data-calendar_actions='[]'></div>
|
|
57
|
+
""", rendered)
|
|
58
|
+
|
|
59
|
+
def test_calendar_target(self):
|
|
60
|
+
root = BaseNode(name='container')
|
|
61
|
+
model = CalendarNode(name='cal', parent=root)
|
|
62
|
+
calendar = CalendarTile()
|
|
63
|
+
calendar.model = model
|
|
64
|
+
calendar.request = self.layer.new_request()
|
|
65
|
+
self.assertEqual(calendar.target, 'http://example.com/container/cal')
|
|
66
|
+
|
|
67
|
+
def test_calendar_options(self):
|
|
68
|
+
calendar = CalendarTile()
|
|
69
|
+
calendar.model = CalendarNode(name='calendar')
|
|
70
|
+
calendar.request = self.layer.new_request()
|
|
71
|
+
|
|
72
|
+
self.assertEqual(calendar.option_mapping, {
|
|
73
|
+
'calendar_locale': 'locale',
|
|
74
|
+
'calendar_header': 'header',
|
|
75
|
+
'calendar_footer': 'footer',
|
|
76
|
+
'calendar_first_day': 'firstDay',
|
|
77
|
+
'calendar_weekends': 'weekends',
|
|
78
|
+
'calendar_week_numbers': 'weekNumbers',
|
|
79
|
+
'calendar_week_numbers_within_days': 'weekNumbersWithinDays',
|
|
80
|
+
'calendar_business_hours': 'businessHours'
|
|
81
|
+
})
|
|
82
|
+
self.assertEqual(calendar.default_options, {
|
|
83
|
+
'calendar_locale': 'en'
|
|
84
|
+
})
|
|
85
|
+
self.assertTrue(isinstance(calendar.options, str))
|
|
86
|
+
self.assertEqual(json.loads(calendar.options), {'locale': 'en'})
|
|
87
|
+
|
|
88
|
+
properties = calendar.model.properties
|
|
89
|
+
properties.calendar_locale = 'de'
|
|
90
|
+
properties.calendar_header = {
|
|
91
|
+
'left': 'title',
|
|
92
|
+
'center': 'today',
|
|
93
|
+
'right': 'prev,next'
|
|
94
|
+
}
|
|
95
|
+
properties.calendar_footer = {
|
|
96
|
+
'left': 'title',
|
|
97
|
+
'center': 'today',
|
|
98
|
+
'right': 'prev,next'
|
|
99
|
+
}
|
|
100
|
+
properties.calendar_first_day = 2
|
|
101
|
+
properties.calendar_weekends = False
|
|
102
|
+
properties.calendar_week_numbers = True
|
|
103
|
+
properties.calendar_week_numbers_within_days = True
|
|
104
|
+
properties.calendar_business_hours = [{
|
|
105
|
+
'dow': [1, 2, 3, 4, 5],
|
|
106
|
+
'start': '08:00',
|
|
107
|
+
'end': '18:00'
|
|
108
|
+
}]
|
|
109
|
+
self.assertEqual(json.loads(calendar.options), {
|
|
110
|
+
'locale': 'de',
|
|
111
|
+
'header': {
|
|
112
|
+
'left': 'title',
|
|
113
|
+
'center': 'today',
|
|
114
|
+
'right': 'prev,next'
|
|
115
|
+
},
|
|
116
|
+
'footer': {
|
|
117
|
+
'left': 'title',
|
|
118
|
+
'center': 'today',
|
|
119
|
+
'right': 'prev,next'
|
|
120
|
+
},
|
|
121
|
+
'firstDay': 2,
|
|
122
|
+
'weekends': False,
|
|
123
|
+
'weekNumbers': True,
|
|
124
|
+
'weekNumbersWithinDays': True,
|
|
125
|
+
'businessHours': [{
|
|
126
|
+
'dow': [1, 2, 3, 4, 5],
|
|
127
|
+
'start': '08:00',
|
|
128
|
+
'end': '18:00'
|
|
129
|
+
}]
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
def test_calendar_sources(self):
|
|
133
|
+
calendar = CalendarTile()
|
|
134
|
+
calendar.model = CalendarNode(name='calendar')
|
|
135
|
+
calendar.request = self.layer.new_request()
|
|
136
|
+
|
|
137
|
+
self.assertEqual(calendar.default_sources, [
|
|
138
|
+
{'events': 'calendar_events'}
|
|
139
|
+
])
|
|
140
|
+
self.assertTrue(isinstance(calendar.sources, str))
|
|
141
|
+
self.assertEqual(json.loads(calendar.sources), [{
|
|
142
|
+
'events': 'calendar_events'
|
|
143
|
+
}])
|
|
144
|
+
|
|
145
|
+
properties = calendar.model.properties
|
|
146
|
+
properties.calendar_sources = [{
|
|
147
|
+
'events': 'calendar_events_1'
|
|
148
|
+
}, {
|
|
149
|
+
'events': 'calendar_events_2'
|
|
150
|
+
}]
|
|
151
|
+
self.assertEqual(json.loads(calendar.sources), [{
|
|
152
|
+
'events': 'calendar_events_1'
|
|
153
|
+
}, {
|
|
154
|
+
'events': 'calendar_events_2'
|
|
155
|
+
}])
|
|
156
|
+
|
|
157
|
+
def test_calendar_actions(self):
|
|
158
|
+
calendar = CalendarTile()
|
|
159
|
+
calendar.model = CalendarNode(name='calendar')
|
|
160
|
+
calendar.request = self.layer.new_request()
|
|
161
|
+
|
|
162
|
+
self.assertEqual(calendar.default_actions, [])
|
|
163
|
+
self.assertTrue(isinstance(calendar.actions, str))
|
|
164
|
+
self.assertEqual(json.loads(calendar.actions), [])
|
|
165
|
+
|
|
166
|
+
calendar.default_actions = [{
|
|
167
|
+
'title': 'Default Action',
|
|
168
|
+
}]
|
|
169
|
+
self.assertEqual(json.loads(calendar.actions), [{
|
|
170
|
+
'title': 'Default Action',
|
|
171
|
+
}])
|
|
172
|
+
|
|
173
|
+
properties = calendar.model.properties
|
|
174
|
+
properties.calendar_actions = [{
|
|
175
|
+
'title': 'Context Action',
|
|
176
|
+
}]
|
|
177
|
+
self.assertEqual(json.loads(calendar.actions), [{
|
|
178
|
+
'title': 'Context Action',
|
|
179
|
+
}])
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class TestJSONView(TileTestCase):
|
|
183
|
+
layer = calendar_layer
|
|
184
|
+
|
|
185
|
+
def test_json_view(self):
|
|
186
|
+
model = CalendarNode(name='calendar')
|
|
187
|
+
request = self.layer.new_request()
|
|
188
|
+
|
|
189
|
+
view = JSONView(model, request)
|
|
190
|
+
with self.assertRaises(NotImplementedError):
|
|
191
|
+
view.data()
|
|
192
|
+
|
|
193
|
+
res = view()
|
|
194
|
+
self.assertEqual(sorted(res.keys()), ['error', 'message'])
|
|
195
|
+
self.assertTrue(res['error'])
|
|
196
|
+
self.checkOutput("""
|
|
197
|
+
<pre>Traceback (most recent call last):
|
|
198
|
+
...
|
|
199
|
+
NotImplementedError:
|
|
200
|
+
Abstract ``JSONView`` does not implement ``data``\n</pre>
|
|
201
|
+
""", res['message'])
|
|
202
|
+
|
|
203
|
+
class MyJSONView(JSONView):
|
|
204
|
+
def data(self):
|
|
205
|
+
return 'data'
|
|
206
|
+
|
|
207
|
+
view = MyJSONView(model, request)
|
|
208
|
+
res = view()
|
|
209
|
+
self.assertEqual(sorted(res.keys()), ['data'])
|
|
210
|
+
self.assertEqual(res['data'], 'data')
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class TestCalendarEvents(TileTestCase):
|
|
214
|
+
layer = calendar_layer
|
|
215
|
+
|
|
216
|
+
def test_calendar_events(self):
|
|
217
|
+
model = CalendarNode(name='calendar')
|
|
218
|
+
request = self.layer.new_request()
|
|
219
|
+
|
|
220
|
+
events = CalendarEvents(model, request)
|
|
221
|
+
|
|
222
|
+
request.params['start'] = '1588312800'
|
|
223
|
+
self.assertEqual(events.range_start, datetime(2020, 5, 1, 6, 0))
|
|
224
|
+
|
|
225
|
+
request.params['end'] = '1588320000'
|
|
226
|
+
self.assertEqual(events.range_end, datetime(2020, 5, 1, 8, 0))
|
|
227
|
+
|
|
228
|
+
with self.assertRaises(NotImplementedError):
|
|
229
|
+
events.events(events.range_start, events.range_end)
|
|
230
|
+
|
|
231
|
+
with self.assertRaises(NotImplementedError):
|
|
232
|
+
events.data()
|
|
233
|
+
|
|
234
|
+
res = events()
|
|
235
|
+
self.assertEqual(sorted(res.keys()), ['error', 'message'])
|
|
236
|
+
self.assertTrue(res['error'])
|
|
237
|
+
self.checkOutput("""
|
|
238
|
+
<pre>Traceback (most recent call last):
|
|
239
|
+
...
|
|
240
|
+
NotImplementedError:
|
|
241
|
+
Abstract ``CalendarEvents`` does not implement ``events``\n</pre>
|
|
242
|
+
""", res['message'])
|
|
243
|
+
|
|
244
|
+
class MyEvents(CalendarEvents):
|
|
245
|
+
def events(self, start, end):
|
|
246
|
+
return [{
|
|
247
|
+
'id': uuid.UUID('08148acd-edda-4e37-9d43-662d4b2d5f59'),
|
|
248
|
+
'title': 'Title',
|
|
249
|
+
'start': datetime(2020, 5, 1, 7, 0),
|
|
250
|
+
'end': datetime(2020, 5, 1, 7, 30),
|
|
251
|
+
}]
|
|
252
|
+
|
|
253
|
+
events = MyEvents(model, request)
|
|
254
|
+
self.assertEqual(events(), {
|
|
255
|
+
'data': [{
|
|
256
|
+
'id': '08148acd-edda-4e37-9d43-662d4b2d5f59',
|
|
257
|
+
'title': 'Title',
|
|
258
|
+
'start': '2020-05-01T07:00:00',
|
|
259
|
+
'end': '2020-05-01T07:30:00'
|
|
260
|
+
}]
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def np(path):
|
|
265
|
+
return path.replace('/', os.path.sep)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
class TestResources(unittest.TestCase):
|
|
269
|
+
layer = calendar_layer
|
|
270
|
+
|
|
271
|
+
def test_moment_resources(self):
|
|
272
|
+
resources_ = browser.moment_resources
|
|
273
|
+
self.assertTrue(resources_.directory.endswith(np('/static/moment')))
|
|
274
|
+
self.assertEqual(resources_.name, 'cone.calendar-moment')
|
|
275
|
+
self.assertEqual(resources_.path, 'moment')
|
|
276
|
+
|
|
277
|
+
scripts = resources_.scripts
|
|
278
|
+
self.assertEqual(len(scripts), 1)
|
|
279
|
+
|
|
280
|
+
self.assertTrue(scripts[0].directory.endswith(np('/static/moment')))
|
|
281
|
+
self.assertEqual(scripts[0].path, 'moment')
|
|
282
|
+
self.assertEqual(scripts[0].file_name, 'moment.min.js')
|
|
283
|
+
self.assertTrue(os.path.exists(scripts[0].file_path))
|
|
284
|
+
|
|
285
|
+
styles = resources_.styles
|
|
286
|
+
self.assertEqual(len(styles), 0)
|
|
287
|
+
|
|
288
|
+
def test_fullcalendar_resources(self):
|
|
289
|
+
resources_ = browser.fullcalendar_resources
|
|
290
|
+
self.assertTrue(resources_.directory.endswith(np('/static/fullcalendar')))
|
|
291
|
+
self.assertEqual(resources_.name, 'cone.calendar-fullcalendar')
|
|
292
|
+
self.assertEqual(resources_.path, 'fullcalendar')
|
|
293
|
+
|
|
294
|
+
scripts = resources_.scripts
|
|
295
|
+
self.assertEqual(len(scripts), 3)
|
|
296
|
+
|
|
297
|
+
self.assertTrue(scripts[0].directory.endswith(np('/static/fullcalendar')))
|
|
298
|
+
self.assertEqual(scripts[0].path, 'fullcalendar')
|
|
299
|
+
self.assertEqual(scripts[0].file_name, 'fullcalendar.min.js')
|
|
300
|
+
self.assertTrue(os.path.exists(scripts[0].file_path))
|
|
301
|
+
|
|
302
|
+
self.assertTrue(scripts[1].directory.endswith(np('/static/fullcalendar/locale')))
|
|
303
|
+
self.assertEqual(scripts[1].path, 'fullcalendar/locale')
|
|
304
|
+
self.assertEqual(scripts[1].file_name, 'de.js')
|
|
305
|
+
self.assertTrue(os.path.exists(scripts[1].file_path))
|
|
306
|
+
|
|
307
|
+
self.assertTrue(scripts[2].directory.endswith(np('/static/fullcalendar/locale')))
|
|
308
|
+
self.assertEqual(scripts[2].path, 'fullcalendar/locale')
|
|
309
|
+
self.assertEqual(scripts[2].file_name, 'it.js')
|
|
310
|
+
self.assertTrue(os.path.exists(scripts[2].file_path))
|
|
311
|
+
|
|
312
|
+
styles = resources_.styles
|
|
313
|
+
self.assertEqual(len(styles), 1)
|
|
314
|
+
|
|
315
|
+
self.assertTrue(styles[0].directory.endswith(np('/static/fullcalendar')))
|
|
316
|
+
self.assertEqual(styles[0].path, 'fullcalendar')
|
|
317
|
+
self.assertEqual(styles[0].file_name, 'fullcalendar.min.css')
|
|
318
|
+
self.assertTrue(os.path.exists(styles[0].file_path))
|
|
319
|
+
|
|
320
|
+
def test_cone_calendar_resources(self):
|
|
321
|
+
resources_ = browser.cone_calendar_resources
|
|
322
|
+
self.assertTrue(resources_.directory.endswith(np('/static/calendar')))
|
|
323
|
+
self.assertEqual(resources_.name, 'cone.calendar-calendar')
|
|
324
|
+
self.assertEqual(resources_.path, 'calendar')
|
|
325
|
+
|
|
326
|
+
scripts = resources_.scripts
|
|
327
|
+
self.assertEqual(len(scripts), 1)
|
|
328
|
+
|
|
329
|
+
self.assertTrue(scripts[0].directory.endswith(np('/static/calendar')))
|
|
330
|
+
self.assertEqual(scripts[0].path, 'calendar')
|
|
331
|
+
self.assertEqual(scripts[0].file_name, 'cone.calendar.min.js')
|
|
332
|
+
self.assertTrue(os.path.exists(scripts[0].file_path))
|
|
333
|
+
|
|
334
|
+
styles = resources_.styles
|
|
335
|
+
self.assertEqual(len(styles), 1)
|
|
336
|
+
|
|
337
|
+
self.assertTrue(styles[0].directory.endswith(np('/static/calendar')))
|
|
338
|
+
self.assertEqual(styles[0].path, 'calendar')
|
|
339
|
+
self.assertEqual(styles[0].file_name, 'cone.calendar.css')
|
|
340
|
+
self.assertTrue(os.path.exists(styles[0].file_path))
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cone.calendar
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: jQuery Fullcalendar integration for cone.app
|
|
5
|
+
Project-URL: Homepage, http://github.com/conestack/cone.calendar
|
|
6
|
+
Author-email: Cone Contributors <dev@conestack.org>
|
|
7
|
+
License: Simplified BSD
|
|
8
|
+
License-File: LICENSE.rst
|
|
9
|
+
Keywords: cone,node,pyramid,web
|
|
10
|
+
Classifier: Environment :: Web Environment
|
|
11
|
+
Classifier: Programming Language :: Python
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Requires-Dist: cone-app<2.0.0,>1.0.99
|
|
20
|
+
Requires-Dist: yafowil-widget-cron>1.99
|
|
21
|
+
Provides-Extra: test
|
|
22
|
+
Requires-Dist: pytest; extra == 'test'
|
|
23
|
+
Requires-Dist: zope-pytestlayer; extra == 'test'
|
|
24
|
+
Description-Content-Type: text/x-rst
|
|
25
|
+
|
|
26
|
+
.. image:: https://img.shields.io/pypi/v/cone.calendar.svg
|
|
27
|
+
:target: https://pypi.python.org/pypi/cone.calendar
|
|
28
|
+
:alt: Latest PyPI version
|
|
29
|
+
|
|
30
|
+
.. image:: https://img.shields.io/pypi/dm/cone.calendar.svg
|
|
31
|
+
:target: https://pypi.python.org/pypi/cone.calendar
|
|
32
|
+
:alt: Number of PyPI downloads
|
|
33
|
+
|
|
34
|
+
.. image:: https://github.com/conestack/cone.calendar/actions/workflows/test.yml/badge.svg
|
|
35
|
+
:target: https://github.com/conestack/cone.calendar/actions/workflows/test.yml
|
|
36
|
+
:alt: Test cone.calendar
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
This package provides a calendar integration in to cone.app.
|
|
40
|
+
It utilizes jQuery Fullcalendar
|
|
41
|
+
(https://github.com/fullcalendar/fullcalendar).
|
|
42
|
+
|
|
43
|
+
Currently, tag 3.1.0 is included. See
|
|
44
|
+
(https://github.com/fullcalendar/fullcalendar/releases).
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
Calendar Widget
|
|
48
|
+
---------------
|
|
49
|
+
|
|
50
|
+
The calendar widget integration is done via
|
|
51
|
+
``cone.calendar.browser.CalendarTile`` which is registered by name ``calendar``.
|
|
52
|
+
|
|
53
|
+
The overall calendar configuration is done via model properties.
|
|
54
|
+
|
|
55
|
+
.. code-block:: python
|
|
56
|
+
|
|
57
|
+
from cone.app.model import BaseNode
|
|
58
|
+
from cone.app.model import Properties
|
|
59
|
+
|
|
60
|
+
class MyCalendar(BaseNode):
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def properties(self):
|
|
64
|
+
props = Properties()
|
|
65
|
+
props.default_content_tile = 'calendar'
|
|
66
|
+
props.calendar_header = {
|
|
67
|
+
'left': 'month,agendaWeek,agendaDay',
|
|
68
|
+
'center': 'title',
|
|
69
|
+
'right': 'today prev,next'
|
|
70
|
+
}
|
|
71
|
+
return props
|
|
72
|
+
|
|
73
|
+
For a full list of available calendar options read
|
|
74
|
+
``cone.calendar.browser.CalendarTile`` documentation.
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
Calendar Events
|
|
78
|
+
---------------
|
|
79
|
+
|
|
80
|
+
In order to display events in the calendar, one or more event data sources
|
|
81
|
+
must be provided.
|
|
82
|
+
|
|
83
|
+
Concrete event data implementation must derive from
|
|
84
|
+
``cone.calendar.browser.CalendarEvents`` and provide ``events`` function.
|
|
85
|
+
|
|
86
|
+
The default event data source is expected as JSON view registered by name
|
|
87
|
+
``calendar_events`` for a dedictaed model context.
|
|
88
|
+
|
|
89
|
+
.. code-block:: python
|
|
90
|
+
|
|
91
|
+
from cone.calendar.browser import CalendarEvents
|
|
92
|
+
from datetime import datetime
|
|
93
|
+
from pyramid.view import view_config
|
|
94
|
+
import uuid
|
|
95
|
+
|
|
96
|
+
@view_config(
|
|
97
|
+
name='calendar_events',
|
|
98
|
+
context=MyCalendar,
|
|
99
|
+
accept='application/json',
|
|
100
|
+
renderer='json',
|
|
101
|
+
permission='view')
|
|
102
|
+
class MyEvents(CalendarEvents):
|
|
103
|
+
|
|
104
|
+
def events(self, start, end):
|
|
105
|
+
events = [{
|
|
106
|
+
'id': uuid.uuid4(),
|
|
107
|
+
'title': 'Title',
|
|
108
|
+
'start': datetime(2020, 4, 29, 17, 40),
|
|
109
|
+
'end': datetime(2020, 4, 29, 18, 0),
|
|
110
|
+
}]
|
|
111
|
+
return events
|
|
112
|
+
|
|
113
|
+
For a full list of available event options read
|
|
114
|
+
``cone.calendar.browser.CalendarEvents`` documentation.
|
|
115
|
+
|
|
116
|
+
Multiple event data sources are defined via model properties.
|
|
117
|
+
|
|
118
|
+
.. code-block:: python
|
|
119
|
+
|
|
120
|
+
class MyCalendar(BaseNode):
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def properties(self):
|
|
124
|
+
props = Properties()
|
|
125
|
+
props.calendar_sources = [{
|
|
126
|
+
'events': 'event_data_1',
|
|
127
|
+
'color': 'green'
|
|
128
|
+
}, {
|
|
129
|
+
'events': 'event_data_2',
|
|
130
|
+
'color': 'red'
|
|
131
|
+
}]
|
|
132
|
+
return props
|
|
133
|
+
|
|
134
|
+
For a full list of available event source options read
|
|
135
|
+
``cone.calendar.browser.CalendarTile`` documentation.
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
Locales
|
|
139
|
+
-------
|
|
140
|
+
|
|
141
|
+
The calendar locale can be set via ``calendar_locale`` on model properties.
|
|
142
|
+
|
|
143
|
+
.. code-block:: python
|
|
144
|
+
|
|
145
|
+
class MyCalendar(BaseNode):
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def properties(self):
|
|
149
|
+
props = Properties()
|
|
150
|
+
props.calendar_locale = 'de'
|
|
151
|
+
return props
|
|
152
|
+
|
|
153
|
+
Default calendar locale is ``en``. The desired locales must be delivered to
|
|
154
|
+
the browser in order to work correctly. Locales to deliver are defined in the
|
|
155
|
+
application ini config as comma separated list of locale names.
|
|
156
|
+
|
|
157
|
+
.. code-block:: ini
|
|
158
|
+
|
|
159
|
+
cone.calendar.locales = de,fr
|
|
160
|
+
|
|
161
|
+
For a complete list of available locales see ``browser/static/fullcalendar/locales``
|
|
162
|
+
folder of this package.
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
Contributors
|
|
166
|
+
============
|
|
167
|
+
|
|
168
|
+
- Robert Niederreiter
|
|
169
|
+
- Johannes Raggam
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
Changes
|
|
173
|
+
=======
|
|
174
|
+
|
|
175
|
+
1.1.0 (2026-02-03)
|
|
176
|
+
------------------
|
|
177
|
+
|
|
178
|
+
- Refactor package layout to use ``pyproject.toml`` and implicit namespace packages.
|
|
179
|
+
[rnix]
|
|
180
|
+
|
|
181
|
+
- Setup Makefile.
|
|
182
|
+
[lenadax]
|
|
183
|
+
|
|
184
|
+
- Run tests with pytest.
|
|
185
|
+
[lenadax]
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
1.0a1 (2023-05-15)
|
|
189
|
+
------------------
|
|
190
|
+
|
|
191
|
+
- Use ``webresource`` for resource registration.
|
|
192
|
+
[rnix]
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
0.4 (2025-10-25)
|
|
196
|
+
----------------
|
|
197
|
+
|
|
198
|
+
- Pin upper versions of dependencies.
|
|
199
|
+
[rnix]
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
0.3 (2022-10-06)
|
|
203
|
+
----------------
|
|
204
|
+
|
|
205
|
+
- Modernize JavaScript.
|
|
206
|
+
[rnix]
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
0.2 (2020-07-09)
|
|
210
|
+
----------------
|
|
211
|
+
|
|
212
|
+
- Add ``show_contextmenu`` to ``CalendarTile``. Defaults to ``False``.
|
|
213
|
+
[rnix]
|
|
214
|
+
|
|
215
|
+
- Also check for action length when handling actions dropdown in JS.
|
|
216
|
+
[rnix]
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
0.1 (2020-05-30)
|
|
220
|
+
----------------
|
|
221
|
+
|
|
222
|
+
- Initial.
|
|
223
|
+
[rnix, thet]
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
License
|
|
227
|
+
=======
|
|
228
|
+
|
|
229
|
+
Copyright (c) 2017-2021, BlueDynamics Alliance, Austria
|
|
230
|
+
Copyright (c) 2021-2025, Cone Contributors
|
|
231
|
+
All rights reserved.
|
|
232
|
+
|
|
233
|
+
Redistribution and use in source and binary forms, with or without
|
|
234
|
+
modification, are permitted provided that the following conditions are met:
|
|
235
|
+
|
|
236
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
237
|
+
list of conditions and the following disclaimer.
|
|
238
|
+
|
|
239
|
+
* Redistributions in binary form must reproduce the above copyright notice, this
|
|
240
|
+
list of conditions and the following disclaimer in the documentation and/or
|
|
241
|
+
other materials provided with the distribution.
|
|
242
|
+
|
|
243
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
244
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
245
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
246
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
247
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
248
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
249
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
250
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
251
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
252
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|