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.
Files changed (202) hide show
  1. cone/calendar/__init__.py +20 -0
  2. cone/calendar/browser/__init__.py +88 -0
  3. cone/calendar/browser/calendar.pt +16 -0
  4. cone/calendar/browser/calendar.py +370 -0
  5. cone/calendar/browser/static/calendar/cone.calendar.css +23 -0
  6. cone/calendar/browser/static/calendar/cone.calendar.js +241 -0
  7. cone/calendar/browser/static/calendar/cone.calendar.min.js +1 -0
  8. cone/calendar/browser/static/fullcalendar/fullcalendar.css +1404 -0
  9. cone/calendar/browser/static/fullcalendar/fullcalendar.js +14206 -0
  10. cone/calendar/browser/static/fullcalendar/fullcalendar.min.css +5 -0
  11. cone/calendar/browser/static/fullcalendar/fullcalendar.min.js +10 -0
  12. cone/calendar/browser/static/fullcalendar/fullcalendar.print.css +208 -0
  13. cone/calendar/browser/static/fullcalendar/fullcalendar.print.min.css +5 -0
  14. cone/calendar/browser/static/fullcalendar/gcal.js +180 -0
  15. cone/calendar/browser/static/fullcalendar/gcal.min.js +6 -0
  16. cone/calendar/browser/static/fullcalendar/locale/af.js +1 -0
  17. cone/calendar/browser/static/fullcalendar/locale/ar-dz.js +1 -0
  18. cone/calendar/browser/static/fullcalendar/locale/ar-ly.js +1 -0
  19. cone/calendar/browser/static/fullcalendar/locale/ar-ma.js +1 -0
  20. cone/calendar/browser/static/fullcalendar/locale/ar-sa.js +1 -0
  21. cone/calendar/browser/static/fullcalendar/locale/ar-tn.js +1 -0
  22. cone/calendar/browser/static/fullcalendar/locale/ar.js +1 -0
  23. cone/calendar/browser/static/fullcalendar/locale/bg.js +1 -0
  24. cone/calendar/browser/static/fullcalendar/locale/ca.js +1 -0
  25. cone/calendar/browser/static/fullcalendar/locale/cs.js +1 -0
  26. cone/calendar/browser/static/fullcalendar/locale/da.js +1 -0
  27. cone/calendar/browser/static/fullcalendar/locale/de-at.js +1 -0
  28. cone/calendar/browser/static/fullcalendar/locale/de.js +1 -0
  29. cone/calendar/browser/static/fullcalendar/locale/el.js +1 -0
  30. cone/calendar/browser/static/fullcalendar/locale/en-au.js +1 -0
  31. cone/calendar/browser/static/fullcalendar/locale/en-ca.js +1 -0
  32. cone/calendar/browser/static/fullcalendar/locale/en-gb.js +1 -0
  33. cone/calendar/browser/static/fullcalendar/locale/en-ie.js +1 -0
  34. cone/calendar/browser/static/fullcalendar/locale/en-nz.js +1 -0
  35. cone/calendar/browser/static/fullcalendar/locale/es-do.js +1 -0
  36. cone/calendar/browser/static/fullcalendar/locale/es.js +1 -0
  37. cone/calendar/browser/static/fullcalendar/locale/eu.js +1 -0
  38. cone/calendar/browser/static/fullcalendar/locale/fa.js +1 -0
  39. cone/calendar/browser/static/fullcalendar/locale/fi.js +1 -0
  40. cone/calendar/browser/static/fullcalendar/locale/fr-ca.js +1 -0
  41. cone/calendar/browser/static/fullcalendar/locale/fr-ch.js +1 -0
  42. cone/calendar/browser/static/fullcalendar/locale/fr.js +1 -0
  43. cone/calendar/browser/static/fullcalendar/locale/gl.js +1 -0
  44. cone/calendar/browser/static/fullcalendar/locale/he.js +1 -0
  45. cone/calendar/browser/static/fullcalendar/locale/hi.js +1 -0
  46. cone/calendar/browser/static/fullcalendar/locale/hr.js +1 -0
  47. cone/calendar/browser/static/fullcalendar/locale/hu.js +1 -0
  48. cone/calendar/browser/static/fullcalendar/locale/id.js +1 -0
  49. cone/calendar/browser/static/fullcalendar/locale/is.js +1 -0
  50. cone/calendar/browser/static/fullcalendar/locale/it.js +1 -0
  51. cone/calendar/browser/static/fullcalendar/locale/ja.js +1 -0
  52. cone/calendar/browser/static/fullcalendar/locale/kk.js +1 -0
  53. cone/calendar/browser/static/fullcalendar/locale/ko.js +1 -0
  54. cone/calendar/browser/static/fullcalendar/locale/lb.js +1 -0
  55. cone/calendar/browser/static/fullcalendar/locale/lt.js +1 -0
  56. cone/calendar/browser/static/fullcalendar/locale/lv.js +1 -0
  57. cone/calendar/browser/static/fullcalendar/locale/mk.js +1 -0
  58. cone/calendar/browser/static/fullcalendar/locale/ms-my.js +1 -0
  59. cone/calendar/browser/static/fullcalendar/locale/ms.js +1 -0
  60. cone/calendar/browser/static/fullcalendar/locale/nb.js +1 -0
  61. cone/calendar/browser/static/fullcalendar/locale/nl-be.js +1 -0
  62. cone/calendar/browser/static/fullcalendar/locale/nl.js +1 -0
  63. cone/calendar/browser/static/fullcalendar/locale/nn.js +1 -0
  64. cone/calendar/browser/static/fullcalendar/locale/pl.js +1 -0
  65. cone/calendar/browser/static/fullcalendar/locale/pt-br.js +1 -0
  66. cone/calendar/browser/static/fullcalendar/locale/pt.js +1 -0
  67. cone/calendar/browser/static/fullcalendar/locale/ro.js +1 -0
  68. cone/calendar/browser/static/fullcalendar/locale/ru.js +1 -0
  69. cone/calendar/browser/static/fullcalendar/locale/sk.js +1 -0
  70. cone/calendar/browser/static/fullcalendar/locale/sl.js +1 -0
  71. cone/calendar/browser/static/fullcalendar/locale/sr-cyrl.js +1 -0
  72. cone/calendar/browser/static/fullcalendar/locale/sr.js +1 -0
  73. cone/calendar/browser/static/fullcalendar/locale/sv.js +1 -0
  74. cone/calendar/browser/static/fullcalendar/locale/th.js +1 -0
  75. cone/calendar/browser/static/fullcalendar/locale/tr.js +1 -0
  76. cone/calendar/browser/static/fullcalendar/locale/uk.js +1 -0
  77. cone/calendar/browser/static/fullcalendar/locale/vi.js +1 -0
  78. cone/calendar/browser/static/fullcalendar/locale/zh-cn.js +1 -0
  79. cone/calendar/browser/static/fullcalendar/locale/zh-tw.js +1 -0
  80. cone/calendar/browser/static/fullcalendar/locale-all.js +5 -0
  81. cone/calendar/browser/static/moment/locale/af.js +73 -0
  82. cone/calendar/browser/static/moment/locale/ar-dz.js +59 -0
  83. cone/calendar/browser/static/moment/locale/ar-ly.js +126 -0
  84. cone/calendar/browser/static/moment/locale/ar-ma.js +60 -0
  85. cone/calendar/browser/static/moment/locale/ar-sa.js +105 -0
  86. cone/calendar/browser/static/moment/locale/ar-tn.js +59 -0
  87. cone/calendar/browser/static/moment/locale/ar.js +142 -0
  88. cone/calendar/browser/static/moment/locale/az.js +105 -0
  89. cone/calendar/browser/static/moment/locale/be.js +134 -0
  90. cone/calendar/browser/static/moment/locale/bg.js +90 -0
  91. cone/calendar/browser/static/moment/locale/bn.js +119 -0
  92. cone/calendar/browser/static/moment/locale/bo.js +119 -0
  93. cone/calendar/browser/static/moment/locale/br.js +108 -0
  94. cone/calendar/browser/static/moment/locale/bs.js +143 -0
  95. cone/calendar/browser/static/moment/locale/ca.js +81 -0
  96. cone/calendar/browser/static/moment/locale/cs.js +172 -0
  97. cone/calendar/browser/static/moment/locale/cv.js +63 -0
  98. cone/calendar/browser/static/moment/locale/cy.js +81 -0
  99. cone/calendar/browser/static/moment/locale/da.js +60 -0
  100. cone/calendar/browser/static/moment/locale/de-at.js +79 -0
  101. cone/calendar/browser/static/moment/locale/de.js +78 -0
  102. cone/calendar/browser/static/moment/locale/dv.js +100 -0
  103. cone/calendar/browser/static/moment/locale/el.js +98 -0
  104. cone/calendar/browser/static/moment/locale/en-au.js +67 -0
  105. cone/calendar/browser/static/moment/locale/en-ca.js +63 -0
  106. cone/calendar/browser/static/moment/locale/en-gb.js +67 -0
  107. cone/calendar/browser/static/moment/locale/en-ie.js +67 -0
  108. cone/calendar/browser/static/moment/locale/en-nz.js +67 -0
  109. cone/calendar/browser/static/moment/locale/eo.js +73 -0
  110. cone/calendar/browser/static/moment/locale/es-do.js +80 -0
  111. cone/calendar/browser/static/moment/locale/es.js +81 -0
  112. cone/calendar/browser/static/moment/locale/et.js +80 -0
  113. cone/calendar/browser/static/moment/locale/eu.js +66 -0
  114. cone/calendar/browser/static/moment/locale/fa.js +107 -0
  115. cone/calendar/browser/static/moment/locale/fi.js +107 -0
  116. cone/calendar/browser/static/moment/locale/fo.js +60 -0
  117. cone/calendar/browser/static/moment/locale/fr-ca.js +60 -0
  118. cone/calendar/browser/static/moment/locale/fr-ch.js +64 -0
  119. cone/calendar/browser/static/moment/locale/fr.js +64 -0
  120. cone/calendar/browser/static/moment/locale/fy.js +73 -0
  121. cone/calendar/browser/static/moment/locale/gd.js +76 -0
  122. cone/calendar/browser/static/moment/locale/gl.js +77 -0
  123. cone/calendar/browser/static/moment/locale/he.js +99 -0
  124. cone/calendar/browser/static/moment/locale/hi.js +124 -0
  125. cone/calendar/browser/static/moment/locale/hr.js +145 -0
  126. cone/calendar/browser/static/moment/locale/hu.js +109 -0
  127. cone/calendar/browser/static/moment/locale/hy-am.js +95 -0
  128. cone/calendar/browser/static/moment/locale/id.js +83 -0
  129. cone/calendar/browser/static/moment/locale/is.js +127 -0
  130. cone/calendar/browser/static/moment/locale/it.js +70 -0
  131. cone/calendar/browser/static/moment/locale/ja.js +76 -0
  132. cone/calendar/browser/static/moment/locale/jv.js +83 -0
  133. cone/calendar/browser/static/moment/locale/ka.js +89 -0
  134. cone/calendar/browser/static/moment/locale/kk.js +87 -0
  135. cone/calendar/browser/static/moment/locale/km.js +58 -0
  136. cone/calendar/browser/static/moment/locale/ko.js +65 -0
  137. cone/calendar/browser/static/moment/locale/ky.js +88 -0
  138. cone/calendar/browser/static/moment/locale/lb.js +137 -0
  139. cone/calendar/browser/static/moment/locale/lo.js +70 -0
  140. cone/calendar/browser/static/moment/locale/lt.js +117 -0
  141. cone/calendar/browser/static/moment/locale/lv.js +97 -0
  142. cone/calendar/browser/static/moment/locale/me.js +111 -0
  143. cone/calendar/browser/static/moment/locale/mi.js +64 -0
  144. cone/calendar/browser/static/moment/locale/mk.js +90 -0
  145. cone/calendar/browser/static/moment/locale/ml.js +81 -0
  146. cone/calendar/browser/static/moment/locale/mr.js +159 -0
  147. cone/calendar/browser/static/moment/locale/ms-my.js +83 -0
  148. cone/calendar/browser/static/moment/locale/ms.js +82 -0
  149. cone/calendar/browser/static/moment/locale/my.js +96 -0
  150. cone/calendar/browser/static/moment/locale/nb.js +63 -0
  151. cone/calendar/browser/static/moment/locale/ne.js +123 -0
  152. cone/calendar/browser/static/moment/locale/nl-be.js +86 -0
  153. cone/calendar/browser/static/moment/locale/nl.js +86 -0
  154. cone/calendar/browser/static/moment/locale/nn.js +60 -0
  155. cone/calendar/browser/static/moment/locale/pa-in.js +124 -0
  156. cone/calendar/browser/static/moment/locale/pl.js +105 -0
  157. cone/calendar/browser/static/moment/locale/pt-br.js +61 -0
  158. cone/calendar/browser/static/moment/locale/pt.js +65 -0
  159. cone/calendar/browser/static/moment/locale/ro.js +75 -0
  160. cone/calendar/browser/static/moment/locale/ru.js +183 -0
  161. cone/calendar/browser/static/moment/locale/se.js +61 -0
  162. cone/calendar/browser/static/moment/locale/si.js +71 -0
  163. cone/calendar/browser/static/moment/locale/sk.js +150 -0
  164. cone/calendar/browser/static/moment/locale/sl.js +162 -0
  165. cone/calendar/browser/static/moment/locale/sq.js +70 -0
  166. cone/calendar/browser/static/moment/locale/sr-cyrl.js +110 -0
  167. cone/calendar/browser/static/moment/locale/sr.js +110 -0
  168. cone/calendar/browser/static/moment/locale/ss.js +89 -0
  169. cone/calendar/browser/static/moment/locale/sv.js +69 -0
  170. cone/calendar/browser/static/moment/locale/sw.js +59 -0
  171. cone/calendar/browser/static/moment/locale/ta.js +130 -0
  172. cone/calendar/browser/static/moment/locale/te.js +89 -0
  173. cone/calendar/browser/static/moment/locale/tet.js +68 -0
  174. cone/calendar/browser/static/moment/locale/th.js +67 -0
  175. cone/calendar/browser/static/moment/locale/tl-ph.js +62 -0
  176. cone/calendar/browser/static/moment/locale/tlh.js +120 -0
  177. cone/calendar/browser/static/moment/locale/tr.js +90 -0
  178. cone/calendar/browser/static/moment/locale/tzl.js +91 -0
  179. cone/calendar/browser/static/moment/locale/tzm-latn.js +58 -0
  180. cone/calendar/browser/static/moment/locale/tzm.js +58 -0
  181. cone/calendar/browser/static/moment/locale/uk.js +146 -0
  182. cone/calendar/browser/static/moment/locale/uz.js +58 -0
  183. cone/calendar/browser/static/moment/locale/vi.js +79 -0
  184. cone/calendar/browser/static/moment/locale/x-pseudo.js +68 -0
  185. cone/calendar/browser/static/moment/locale/yo.js +60 -0
  186. cone/calendar/browser/static/moment/locale/zh-cn.js +127 -0
  187. cone/calendar/browser/static/moment/locale/zh-hk.js +105 -0
  188. cone/calendar/browser/static/moment/locale/zh-tw.js +104 -0
  189. cone/calendar/browser/static/moment/locales.js +8624 -0
  190. cone/calendar/browser/static/moment/locales.min.js +663 -0
  191. cone/calendar/browser/static/moment/moment-with-locales.js +12910 -0
  192. cone/calendar/browser/static/moment/moment-with-locales.min.js +1216 -0
  193. cone/calendar/browser/static/moment/moment.min.js +551 -0
  194. cone/calendar/interfaces.py +14 -0
  195. cone/calendar/locale/cone.calendar.pot +0 -0
  196. cone/calendar/locale/de/LC_MESSAGES/cone.calendar.po +0 -0
  197. cone/calendar/locale/en/LC_MESSAGES/cone.calendar.po +0 -0
  198. cone/calendar/tests/test_calendar.py +340 -0
  199. cone_calendar-1.1.0.dist-info/METADATA +252 -0
  200. cone_calendar-1.1.0.dist-info/RECORD +202 -0
  201. cone_calendar-1.1.0.dist-info/WHEEL +4 -0
  202. 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.