WP Simple Booking Calendar - Version 2.0

Version Description

Download this release

Release Info

Developer murgroland
Plugin Icon 128x128 WP Simple Booking Calendar
Version 2.0
Comparing to
See all releases

Code changes from version 1.5.5 to 2.0

Files changed (336) hide show
  1. assets/css/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  2. assets/css/images/ui-icons_222222_256x240.png +0 -0
  3. assets/css/jquery-ui.css +1 -0
  4. assets/css/style-admin.css +611 -0
  5. assets/css/style-front-end.css +173 -0
  6. assets/css/style-front-end.min.css +1 -0
  7. assets/img/date-icon.svg +1 -0
  8. assets/img/flags/ad.png +0 -0
  9. assets/img/flags/ae.png +0 -0
  10. assets/img/flags/af.png +0 -0
  11. assets/img/flags/ag.png +0 -0
  12. assets/img/flags/ai.png +0 -0
  13. assets/img/flags/al.png +0 -0
  14. assets/img/flags/am.png +0 -0
  15. assets/img/flags/an.png +0 -0
  16. assets/img/flags/ao.png +0 -0
  17. assets/img/flags/ar.png +0 -0
  18. assets/img/flags/as.png +0 -0
  19. assets/img/flags/at.png +0 -0
  20. assets/img/flags/au.png +0 -0
  21. assets/img/flags/aw.png +0 -0
  22. assets/img/flags/ax.png +0 -0
  23. assets/img/flags/az.png +0 -0
  24. assets/img/flags/ba.png +0 -0
  25. assets/img/flags/bb.png +0 -0
  26. assets/img/flags/bd.png +0 -0
  27. assets/img/flags/be.png +0 -0
  28. assets/img/flags/bf.png +0 -0
  29. assets/img/flags/bg.png +0 -0
  30. assets/img/flags/bh.png +0 -0
  31. assets/img/flags/bi.png +0 -0
  32. assets/img/flags/bj.png +0 -0
  33. assets/img/flags/bm.png +0 -0
  34. assets/img/flags/bn.png +0 -0
  35. assets/img/flags/bo.png +0 -0
  36. assets/img/flags/br.png +0 -0
  37. assets/img/flags/bs.png +0 -0
  38. assets/img/flags/bt.png +0 -0
  39. assets/img/flags/bv.png +0 -0
  40. assets/img/flags/bw.png +0 -0
  41. assets/img/flags/by.png +0 -0
  42. assets/img/flags/bz.png +0 -0
  43. assets/img/flags/ca.png +0 -0
  44. assets/img/flags/cc.png +0 -0
  45. assets/img/flags/cd.png +0 -0
  46. assets/img/flags/cf.png +0 -0
  47. assets/img/flags/cg.png +0 -0
  48. assets/img/flags/ch.png +0 -0
  49. assets/img/flags/ci.png +0 -0
  50. assets/img/flags/ck.png +0 -0
  51. assets/img/flags/cl.png +0 -0
  52. assets/img/flags/cm.png +0 -0
  53. assets/img/flags/cn.png +0 -0
  54. assets/img/flags/co.png +0 -0
  55. assets/img/flags/cr.png +0 -0
  56. assets/img/flags/cs.png +0 -0
  57. assets/img/flags/cu.png +0 -0
  58. assets/img/flags/cv.png +0 -0
  59. assets/img/flags/cx.png +0 -0
  60. assets/img/flags/cy.png +0 -0
  61. assets/img/flags/cz.png +0 -0
  62. assets/img/flags/da.png +0 -0
  63. assets/img/flags/de.png +0 -0
  64. assets/img/flags/dj.png +0 -0
  65. assets/img/flags/dm.png +0 -0
  66. assets/img/flags/do.png +0 -0
  67. assets/img/flags/dz.png +0 -0
  68. assets/img/flags/ec.png +0 -0
  69. assets/img/flags/ee.png +0 -0
  70. assets/img/flags/eg.png +0 -0
  71. assets/img/flags/eh.png +0 -0
  72. assets/img/flags/el.png +0 -0
  73. assets/img/flags/en.png +0 -0
  74. assets/img/flags/england.png +0 -0
  75. assets/img/flags/er.png +0 -0
  76. assets/img/flags/es.png +0 -0
  77. assets/img/flags/et.png +0 -0
  78. assets/img/flags/eu.png +0 -0
  79. assets/img/flags/europeanunion.png +0 -0
  80. assets/img/flags/fam.png +0 -0
  81. assets/img/flags/fi.png +0 -0
  82. assets/img/flags/fj.png +0 -0
  83. assets/img/flags/fk.png +0 -0
  84. assets/img/flags/fm.png +0 -0
  85. assets/img/flags/fo.png +0 -0
  86. assets/img/flags/fr.png +0 -0
  87. assets/img/flags/ga.png +0 -0
  88. assets/img/flags/gb.png +0 -0
  89. assets/img/flags/gd.png +0 -0
  90. assets/img/flags/ge.png +0 -0
  91. assets/img/flags/gf.png +0 -0
  92. assets/img/flags/gh.png +0 -0
  93. assets/img/flags/gi.png +0 -0
  94. assets/img/flags/gl.png +0 -0
  95. assets/img/flags/gm.png +0 -0
  96. assets/img/flags/gn.png +0 -0
  97. assets/img/flags/gp.png +0 -0
  98. assets/img/flags/gq.png +0 -0
  99. assets/img/flags/gs.png +0 -0
  100. assets/img/flags/gt.png +0 -0
  101. assets/img/flags/gu.png +0 -0
  102. assets/img/flags/gw.png +0 -0
  103. assets/img/flags/gy.png +0 -0
  104. assets/img/flags/hk.png +0 -0
  105. assets/img/flags/hm.png +0 -0
  106. assets/img/flags/hn.png +0 -0
  107. assets/img/flags/hr.png +0 -0
  108. assets/img/flags/ht.png +0 -0
  109. assets/img/flags/hu.png +0 -0
  110. assets/img/flags/id.png +0 -0
  111. assets/img/flags/ie.png +0 -0
  112. assets/img/flags/il.png +0 -0
  113. assets/img/flags/in.png +0 -0
  114. assets/img/flags/io.png +0 -0
  115. assets/img/flags/iq.png +0 -0
  116. assets/img/flags/ir.png +0 -0
  117. assets/img/flags/is.png +0 -0
  118. assets/img/flags/it.png +0 -0
  119. assets/img/flags/jm.png +0 -0
  120. assets/img/flags/jo.png +0 -0
  121. assets/img/flags/jp.png +0 -0
  122. assets/img/flags/ke.png +0 -0
  123. assets/img/flags/kg.png +0 -0
  124. assets/img/flags/kh.png +0 -0
  125. assets/img/flags/ki.png +0 -0
  126. assets/img/flags/km.png +0 -0
  127. assets/img/flags/kn.png +0 -0
  128. assets/img/flags/kp.png +0 -0
  129. assets/img/flags/kr.png +0 -0
  130. assets/img/flags/kw.png +0 -0
  131. assets/img/flags/ky.png +0 -0
  132. assets/img/flags/kz.png +0 -0
  133. assets/img/flags/la.png +0 -0
  134. assets/img/flags/lb.png +0 -0
  135. assets/img/flags/lc.png +0 -0
  136. assets/img/flags/li.png +0 -0
  137. assets/img/flags/lk.png +0 -0
  138. assets/img/flags/lr.png +0 -0
  139. assets/img/flags/ls.png +0 -0
  140. assets/img/flags/lt.png +0 -0
  141. assets/img/flags/lu.png +0 -0
  142. assets/img/flags/lv.png +0 -0
  143. assets/img/flags/ly.png +0 -0
  144. assets/img/flags/ma.png +0 -0
  145. assets/img/flags/mc.png +0 -0
  146. assets/img/flags/md.png +0 -0
  147. assets/img/flags/me.png +0 -0
  148. assets/img/flags/mg.png +0 -0
  149. assets/img/flags/mh.png +0 -0
  150. assets/img/flags/mk.png +0 -0
  151. assets/img/flags/ml.png +0 -0
  152. assets/img/flags/mm.png +0 -0
  153. assets/img/flags/mn.png +0 -0
  154. assets/img/flags/mo.png +0 -0
  155. assets/img/flags/mp.png +0 -0
  156. assets/img/flags/mq.png +0 -0
  157. assets/img/flags/mr.png +0 -0
  158. assets/img/flags/ms.png +0 -0
  159. assets/img/flags/mt.png +0 -0
  160. assets/img/flags/mu.png +0 -0
  161. assets/img/flags/mv.png +0 -0
  162. assets/img/flags/mw.png +0 -0
  163. assets/img/flags/mx.png +0 -0
  164. assets/img/flags/my.png +0 -0
  165. assets/img/flags/mz.png +0 -0
  166. assets/img/flags/na.png +0 -0
  167. assets/img/flags/nc.png +0 -0
  168. assets/img/flags/ne.png +0 -0
  169. assets/img/flags/nf.png +0 -0
  170. assets/img/flags/ng.png +0 -0
  171. assets/img/flags/ni.png +0 -0
  172. assets/img/flags/nl.png +0 -0
  173. assets/img/flags/no.png +0 -0
  174. assets/img/flags/np.png +0 -0
  175. assets/img/flags/nr.png +0 -0
  176. assets/img/flags/nu.png +0 -0
  177. assets/img/flags/nz.png +0 -0
  178. assets/img/flags/om.png +0 -0
  179. assets/img/flags/pa.png +0 -0
  180. assets/img/flags/pe.png +0 -0
  181. assets/img/flags/pf.png +0 -0
  182. assets/img/flags/pg.png +0 -0
  183. assets/img/flags/ph.png +0 -0
  184. assets/img/flags/pk.png +0 -0
  185. assets/img/flags/pl.png +0 -0
  186. assets/img/flags/pm.png +0 -0
  187. assets/img/flags/pn.png +0 -0
  188. assets/img/flags/pr.png +0 -0
  189. assets/img/flags/ps.png +0 -0
  190. assets/img/flags/pt.png +0 -0
  191. assets/img/flags/pw.png +0 -0
  192. assets/img/flags/py.png +0 -0
  193. assets/img/flags/qa.png +0 -0
  194. assets/img/flags/re.png +0 -0
  195. assets/img/flags/ro.png +0 -0
  196. assets/img/flags/ru.png +0 -0
  197. assets/img/flags/rw.png +0 -0
  198. assets/img/flags/sa.png +0 -0
  199. assets/img/flags/sb.png +0 -0
  200. assets/img/flags/sc.png +0 -0
  201. assets/img/flags/scotland.png +0 -0
  202. assets/img/flags/sd.png +0 -0
  203. assets/img/flags/se.png +0 -0
  204. assets/img/flags/sg.png +0 -0
  205. assets/img/flags/sh.png +0 -0
  206. assets/img/flags/si.png +0 -0
  207. assets/img/flags/sj.png +0 -0
  208. assets/img/flags/sk.png +0 -0
  209. assets/img/flags/sl.png +0 -0
  210. assets/img/flags/sm.png +0 -0
  211. assets/img/flags/sn.png +0 -0
  212. assets/img/flags/so.png +0 -0
  213. assets/img/flags/sr.png +0 -0
  214. assets/img/flags/srn.png +0 -0
  215. assets/img/flags/st.png +0 -0
  216. assets/img/flags/sv.png +0 -0
  217. assets/img/flags/sy.png +0 -0
  218. assets/img/flags/sz.png +0 -0
  219. assets/img/flags/tc.png +0 -0
  220. assets/img/flags/td.png +0 -0
  221. assets/img/flags/tf.png +0 -0
  222. assets/img/flags/tg.png +0 -0
  223. assets/img/flags/th.png +0 -0
  224. assets/img/flags/tj.png +0 -0
  225. assets/img/flags/tk.png +0 -0
  226. assets/img/flags/tl.png +0 -0
  227. assets/img/flags/tm.png +0 -0
  228. assets/img/flags/tn.png +0 -0
  229. assets/img/flags/to.png +0 -0
  230. assets/img/flags/tr.png +0 -0
  231. assets/img/flags/tt.png +0 -0
  232. assets/img/flags/tv.png +0 -0
  233. assets/img/flags/tw.png +0 -0
  234. assets/img/flags/tz.png +0 -0
  235. assets/img/flags/ua.png +0 -0
  236. assets/img/flags/ug.png +0 -0
  237. assets/img/flags/um.png +0 -0
  238. assets/img/flags/us.png +0 -0
  239. assets/img/flags/uy.png +0 -0
  240. assets/img/flags/uz.png +0 -0
  241. assets/img/flags/va.png +0 -0
  242. assets/img/flags/vc.png +0 -0
  243. assets/img/flags/ve.png +0 -0
  244. assets/img/flags/vg.png +0 -0
  245. assets/img/flags/vi.png +0 -0
  246. assets/img/flags/vn.png +0 -0
  247. assets/img/flags/vu.png +0 -0
  248. assets/img/flags/wales.png +0 -0
  249. assets/img/flags/wf.png +0 -0
  250. assets/img/flags/ws.png +0 -0
  251. assets/img/flags/ye.png +0 -0
  252. assets/img/flags/yt.png +0 -0
  253. assets/img/flags/za.png +0 -0
  254. assets/img/flags/zm.png +0 -0
  255. assets/img/flags/zw.png +0 -0
  256. assets/js/script-admin-edit-calendar.js +368 -0
  257. assets/js/script-admin-uninstaller.js +44 -0
  258. assets/js/script-admin-upgrader.js +148 -0
  259. assets/js/script-admin.js +363 -0
  260. assets/js/script-front-end.js +283 -0
  261. assets/js/script-front-end.min.js +1 -0
  262. assets/libs/chosen/chosen-sprite.png +0 -0
  263. assets/libs/chosen/chosen-sprite@2x.png +0 -0
  264. assets/libs/chosen/chosen.css +448 -0
  265. assets/libs/chosen/chosen.jquery.min.js +2 -0
  266. blocks/sbc/assets/js/script-block-sbc.js +0 -71
  267. blocks/sbc/functions.php +0 -152
  268. css/sbc-controller.css +0 -98
  269. css/sbc-shortcode.css +0 -17
  270. css/sbc.css +0 -171
  271. images/button-bg.png +0 -0
  272. images/checked-icon.jpg +0 -0
  273. images/checked.png +0 -0
  274. includes/abstracts/abstract-class-db.php +66 -0
  275. includes/abstracts/abstract-class-list-table.php +1373 -0
  276. includes/abstracts/abstract-class-object-db.php +351 -0
  277. includes/abstracts/abstract-class-object-meta-db.php +95 -0
  278. includes/abstracts/abstract-class-object.php +58 -0
  279. includes/abstracts/abstract-class-submenu-page.php +167 -0
  280. includes/base/admin/backup/class-submenu-page-backup.php +45 -0
  281. includes/base/admin/backup/functions-actions-backup.php +148 -0
  282. includes/base/admin/backup/functions.php +50 -0
  283. includes/base/admin/backup/views/view-backup.php +78 -0
  284. includes/base/admin/calendar/class-calendar-editor-outputter.php +454 -0
  285. includes/base/admin/calendar/class-list-table-calendars.php +294 -0
  286. includes/base/admin/calendar/class-submenu-page-calendar.php +130 -0
  287. includes/base/admin/calendar/functions-actions-ajax-calendar.php +241 -0
  288. includes/base/admin/calendar/functions-actions-calendar.php +299 -0
  289. includes/base/admin/calendar/functions-actions-legend-item.php +396 -0
  290. includes/base/admin/calendar/functions-shortcode-generator.php +69 -0
  291. includes/base/admin/calendar/functions.php +105 -0
  292. includes/base/admin/calendar/views/view-add-calendar.php +51 -0
  293. includes/base/admin/calendar/views/view-calendars.php +34 -0
  294. includes/base/admin/calendar/views/view-edit-calendar.php +219 -0
  295. includes/base/admin/calendar/views/view-shortcode-generator.php +167 -0
  296. includes/base/admin/class-admin-notices.php +154 -0
  297. includes/base/admin/functions.php +76 -0
  298. includes/base/admin/settings/class-submenu-page-settings.php +90 -0
  299. includes/base/admin/settings/functions.php +46 -0
  300. includes/base/admin/settings/views/view-settings-tab-languages.php +43 -0
  301. includes/base/admin/settings/views/view-settings.php +87 -0
  302. includes/base/admin/upgrade-to-premium.php +93 -0
  303. includes/base/calendar/class-calendar-outputter.php +820 -0
  304. includes/base/calendar/class-calendar.php +67 -0
  305. includes/base/calendar/class-object-db-calendars.php +157 -0
  306. includes/base/calendar/class-object-meta-db-calendars.php +85 -0
  307. includes/base/calendar/functions-ajax.php +41 -0
  308. includes/base/calendar/functions.php +290 -0
  309. includes/base/class-shortcodes.php +94 -0
  310. includes/base/class-widget-calendar.php +349 -0
  311. includes/base/event/class-event.php +85 -0
  312. includes/base/event/class-object-db-events.php +193 -0
  313. includes/base/event/class-object-meta-db-events.php +85 -0
  314. includes/base/event/functions.php +189 -0
  315. includes/base/functions-languages.php +231 -0
  316. includes/base/functions-utils.php +72 -0
  317. includes/base/functions.php +83 -0
  318. includes/base/legend/class-legend-item.php +106 -0
  319. includes/base/legend/class-object-db-legend-items.php +156 -0
  320. includes/base/legend/class-object-meta-db-legend-items.php +85 -0
  321. includes/base/legend/functions.php +387 -0
  322. includes/libs/iCal/iCalcreator.php +67 -0
  323. includes/libs/iCal/lib/calendarComponent.class.php +4661 -0
  324. includes/libs/iCal/lib/iCal.XML.inc.php +897 -0
  325. includes/libs/iCal/lib/iCal.tz.inc.php +292 -0
  326. includes/libs/iCal/lib/iCal.vCard.inc.php +166 -0
  327. includes/libs/iCal/lib/iCalBase.class.php +427 -0
  328. includes/libs/iCal/lib/iCalUtilityFunctions.class.php +2510 -0
  329. includes/libs/iCal/lib/iCaldateTime.class.php +129 -0
  330. includes/libs/iCal/lib/valarm.class.php +135 -0
  331. includes/libs/iCal/lib/vcalendar.class.php +2132 -0
  332. includes/libs/iCal/lib/vevent.class.php +269 -0
  333. includes/libs/iCal/lib/vfreebusy.class.php +149 -0
  334. includes/libs/iCal/lib/vjournal.class.php +222 -0
  335. includes/libs/iCal/lib/vtimezone.class.php +165 -0
  336. includes/libs/iCal/lib/vtodo.class.php +207 -0
assets/css/images/ui-bg_flat_75_ffffff_40x100.png ADDED
Binary file
assets/css/images/ui-icons_222222_256x240.png ADDED
Binary file
assets/css/jquery-ui.css ADDED
@@ -0,0 +1 @@
 
1
+ .ui-helper-hidden{display:none}.ui-helper-hidden-accessible{position:absolute;left:-99999999px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:after{content:".";display:block;height:0;clear:both;visibility:hidden}.ui-helper-clearfix{display:inline-block}* html .ui-helper-clearfix{height:1%}.ui-helper-clearfix{display:block}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:absolute;top:0;left:0;width:100%;height:100%}.ui-widget{font-family:Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget button,.ui-widget input,.ui-widget select,.ui-widget textarea{font-family:Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #aaa;background:#fff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x;color:#222}.ui-widget-content a{color:#222}.ui-widget-header{border:1px solid #aaa;background:#ccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x;color:#222;font-weight:700}.ui-widget-header a{color:#222}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #d3d3d3;background:#e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x;font-weight:400;color:#555}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#555;text-decoration:none}.ui-state-focus,.ui-state-hover,.ui-widget-content .ui-state-focus,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-focus,.ui-widget-header .ui-state-hover{border:1px solid #999;background:#dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x;font-weight:400;color:#212121}.ui-state-hover a,.ui-state-hover a:hover{color:#212121;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #aaa;background:#fff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x;font-weight:400;color:#212121}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#212121;text-decoration:none}.ui-widget :active{outline:0}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fcefa1;background:#fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x;color:#cd0a0a}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#cd0a0a}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#cd0a0a}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:700}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:400}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-icon{width:16px;height:16px;background-image:url(images/ui-icons_222222_256x240.png)}.ui-widget-content .ui-icon{background-image:url(images/ui-icons_222222_256x240.png)}.ui-widget-header .ui-icon{background-image:url(images/ui-icons_222222_256x240.png)}.ui-state-default .ui-icon{background-image:url(images/ui-icons_888888_256x240.png)}.ui-state-focus .ui-icon,.ui-state-hover .ui-icon{background-image:url(images/ui-icons_454545_256x240.png)}.ui-state-active .ui-icon{background-image:url(images/ui-icons_454545_256x240.png)}.ui-state-highlight .ui-icon{background-image:url(images/ui-icons_2e83ff_256x240.png)}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url(images/ui-icons_cd0a0a_256x240.png)}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-off{background-position:-96px -144px}.ui-icon-radio-on{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-tl{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px}.ui-corner-tr{-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px}.ui-corner-bl{-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px}.ui-corner-br{-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px}.ui-corner-top{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px}.ui-corner-bottom{-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px}.ui-corner-right{-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px}.ui-corner-left{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px}.ui-corner-all{-moz-border-radius:4px;-webkit-border-radius:4px;border-radius:4px}.ui-widget-overlay{background:#aaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{margin:-8px 0 0 -8px;padding:8px;background:#aaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30);-moz-border-radius:8px;-webkit-border-radius:8px;border-radius:8px}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:.1px;z-index:99999;display:block}.ui-resizable-autohide .ui-resizable-handle,.ui-resizable-disabled .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable-helper{border:1px dotted #000}.ui-accordion .ui-accordion-header{cursor:pointer;position:relative;margin-top:1px;zoom:1}.ui-accordion .ui-accordion-li-fix{display:inline}.ui-accordion .ui-accordion-header-active{border-bottom:0!important}.ui-accordion .ui-accordion-header a{display:block;font-size:1em;padding:.5em .5em .5em .7em}.ui-accordion a{zoom:1}.ui-accordion-icons .ui-accordion-header a{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;margin-top:-2px;position:relative;top:1px;margin-bottom:2px;overflow:auto;display:none;zoom:1}.ui-accordion .ui-accordion-content-active{display:block}.ui-autocomplete{position:absolute;cursor:default}.ui-autocomplete-loading{background:#fff url(images/ui-anim_basic_16x16.gif) right center no-repeat}* html .ui-autocomplete{width:1px}.ui-menu{list-style:none;padding:2px;margin:0;display:block}.ui-menu .ui-menu{margin-top:-3px}.ui-menu .ui-menu-item{margin:0;padding:0;zoom:1;float:left;clear:left;width:100%}.ui-menu .ui-menu-item a{text-decoration:none;display:block;padding:.2em .4em;line-height:1.5;zoom:1}.ui-menu .ui-menu-item a.ui-state-active,.ui-menu .ui-menu-item a.ui-state-hover{font-weight:400;margin:-1px}.ui-button{display:inline-block;position:relative;padding:0;margin-right:.1em;text-decoration:none!important;cursor:pointer;text-align:center;zoom:1;overflow:visible}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:1.4}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-icons-only .ui-icon,.ui-button-text-icon .ui-icon,.ui-button-text-icons .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-icons-only .ui-button-icon-primary,.ui-button-text-icon .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary{left:.5em}.ui-button-icons-only .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-dialog{position:absolute;padding:.2em;width:300px;overflow:hidden}.ui-dialog .ui-dialog-titlebar{padding:.5em 1em .3em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 16px .2em 0}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:19px;margin:-10px 0 0 0;padding:1px;height:18px}.ui-dialog .ui-dialog-titlebar-close span{display:block;margin:1px}.ui-dialog .ui-dialog-titlebar-close:focus,.ui-dialog .ui-dialog-titlebar-close:hover{padding:0}.ui-dialog .ui-dialog-content{border:0;padding:.5em 1em;background:0 0;overflow:auto;zoom:1}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin:.5em 0 0 0;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane button{float:right;margin:.5em .4em .5em 0;cursor:pointer;padding:.2em .6em .3em .6em;line-height:1.4em;width:auto;overflow:visible}.ui-dialog .ui-resizable-se{width:14px;height:14px;right:3px;bottom:3px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-tabs{position:relative;padding:.2em;zoom:1}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:1px;margin:0 .2em 1px 0;border-bottom:0!important;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav li a{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-selected{margin-bottom:0;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-state-disabled a,.ui-tabs .ui-tabs-nav li.ui-state-processing a,.ui-tabs .ui-tabs-nav li.ui-tabs-selected a{cursor:text}.ui-tabs .ui-tabs-nav li a,.ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:0 0}.ui-tabs .ui-tabs-hide{display:none!important}.ui-datepicker{width:17em;padding:.2em .2em 0}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-next,.ui-datepicker .ui-datepicker-prev{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-next-hover,.ui-datepicker .ui-datepicker-prev-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-next span,.ui-datepicker .ui-datepicker-prev span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month-year{width:100%}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:49%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:700;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td a,.ui-datepicker td span{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current{float:right}.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-datepicker-cover{display:none;display:block;position:absolute;z-index:-1;filter:mask();top:-4px;left:-4px;width:200px;height:200px}.ui-progressbar{height:2em;text-align:left}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}
assets/css/style-admin.css ADDED
@@ -0,0 +1,611 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /******************************************************************************************/
2
+ /* General
3
+ /******************************************************************************************/
4
+ a.wpsbc-inactive { color: #b1b1b1; }
5
+ a.wpsbc-inactive:hover { color: #b1b1b1; }
6
+
7
+ .wpsbc-float-right { float: right; }
8
+
9
+ .wpsbc-clear {display:block; clear:both;}
10
+
11
+ .button-primary.wpsbc-button-large { height: 38px; line-height: 36px; padding: 0 30px; }
12
+
13
+ .postbox .wpsbc-plugin-card-bottom { padding-left: 12px; padding-right: 12px; overflow: visible; }
14
+
15
+ .wrap.wpsbc-wrap .postbox .hndle { cursor: auto; }
16
+
17
+ /******************************************************************************************/
18
+ /* Grid
19
+ /******************************************************************************************/
20
+ .wpsbc-row { margin-left: -10px; margin-right: -10px; }
21
+ .wpsbc-row:after { content: ''; display: block; clear: both; }
22
+ .wpsbc-row.wpsbc-last { margin-bottom: -20px; }
23
+
24
+ [class*="wpsbc-col-"] { display: block; float: left; padding: 0 10px; margin-bottom: 20px; box-sizing: border-box; }
25
+
26
+ .wpsbc-col-1-1 { width: 100%; }
27
+ .wpsbc-col-1-2 { width: 50%; }
28
+ .wpsbc-col-1-3 { width: 33.3333%; }
29
+ .wpsbc-col-1-4 { width: 25%; }
30
+ .wpsbc-col-3-4 { width: 75%; }
31
+ .wpsbc-col-1-5 { width: 20%; }
32
+
33
+
34
+ /******************************************************************************************/
35
+ /* Error notices
36
+ /******************************************************************************************/
37
+ .wpsbc-notice-error { background: #fff; border-left: 4px solid #dc3232; box-shadow: 0 1px 1px 0 rgba(0,0,0,.1); margin: 5px 0 15px 0; padding: 1px 12px; }
38
+
39
+ /******************************************************************************************/
40
+ /* Page Headings
41
+ /******************************************************************************************/
42
+ .wpsbc-heading-button-secondary.button-secondary { position: relative; top: -3px; vertical-align: initial; }
43
+
44
+ .wpsbc-heading-actions { display: block; float: right; margin-top: 9px; }
45
+
46
+ .wp-header-end {clear: both;}
47
+
48
+ .wpsbc-heading-tag { position: relative; top: -2px; font-size: 11px; margin-left: 15px; background: #34495e; color: #fff; padding: 6px 11px; border-radius: 4px; vertical-align: middle; }
49
+
50
+ /******************************************************************************************/
51
+ /* Tab Navigation
52
+ /******************************************************************************************/
53
+ .wpsbc-nav-tab:focus { outline: 0; box-shadow: none; }
54
+
55
+ .wpsbc-tab { display: none; margin-top: 2em; margin-bottom: 2em; }
56
+ .wpsbc-tab.wpsbc-active { display: block; }
57
+
58
+ /******************************************************************************************/
59
+ /* Settings Fields
60
+ /******************************************************************************************/
61
+ .wpsbc-settings-field-wrapper { position: relative; margin-bottom: 15px; }
62
+ .wpsbc-settings-field-wrapper.wpsbc-settings-field-inline { padding-left: 200px; }
63
+
64
+ .wpsbc-settings-field-wrapper.wpsbc-settings-field-wrapper-left {width:49%; float:left; margin-right:2%;}
65
+ .wpsbc-settings-field-wrapper.wpsbc-settings-field-wrapper-right {width:49%; float:right;}
66
+
67
+ .wpsbc-settings-field-wrapper.wpsbc-last { margin-bottom: 0; }
68
+
69
+ .wpsbc-settings-field-wrapper .wpsbc-settings-field-label { display: block; margin-bottom: 5px; }
70
+ .wpsbc-settings-field-wrapper.wpsbc-settings-field-inline .wpsbc-settings-field-label { position: absolute; top: 5px; left: 0; width: 180px; margin-bottom: 0; }
71
+
72
+ .wpsbc-settings-field-inner > input,
73
+ .wpsbc-settings-field-inner > textarea,
74
+ .wpsbc-settings-field-inner > select { width: 100%; margin-left: 0; height: 30px; }
75
+
76
+ .wpsbc-settings-field-small .wpsbc-settings-field-inner > input,
77
+ .wpsbc-settings-field-small .wpsbc-settings-field-inner > textarea,
78
+ .wpsbc-settings-field-small .wpsbc-settings-field-inner > select { max-width: 110px; }
79
+
80
+ .wpsbc-settings-field-medium .wpsbc-settings-field-inner > input,
81
+ .wpsbc-settings-field-medium .wpsbc-settings-field-inner > textarea,
82
+ .wpsbc-settings-field-medium .wpsbc-settings-field-inner > select { max-width: 225px; }
83
+
84
+ .wpsbc-settings-field-large .wpsbc-settings-field-inner > input,
85
+ .wpsbc-settings-field-large .wpsbc-settings-field-inner > textarea,
86
+ .wpsbc-settings-field-large .wpsbc-settings-field-inner > select { max-width: 400px; }
87
+
88
+ .wpsbc-settings-field-translations {display: none; padding-bottom:20px;}
89
+ .wpsbc-settings-field-translations .wpsbc-settings-field-wrapper.wpsbc-settings-field-inline.wpsbc-settings-field-large {margin-left:200px; padding-left:100px; }
90
+ .wpsbc-settings-field-translations .wpsbc-settings-field-wrapper.wpsbc-settings-field-inline.wpsbc-settings-field-large-label {margin-left:300px; }
91
+ .wpsbc-settings-field-translations .wpsbc-settings-field-wrapper.wpsbc-settings-field-inline.wpsbc-settings-field-large .wpsbc-settings-field-label {width:80px;}
92
+
93
+ .wpsbc-settings-field-show-translations {text-decoration:none; margin-top: 3px; display: block; max-width:400px;}
94
+ .wpsbc-settings-field-show-translations:focus {outline:none; box-shadow:none;}
95
+ .wpsbc-settings-field-show-translations span {position: relative; top:-1px; left:-7px;}
96
+ .wpsbc-settings-field-show-translations.open span {transform:rotate(180deg); left:-5px; top:0; }
97
+
98
+ /* Chosen */
99
+ .wpsbc-chosen-wrapper .chosen-container { width: 100% !important; }
100
+ .wpsbc-chosen-wrapper .chosen-container .chosen-choices { box-shadow: inset 0 1px 2px rgba(0,0,0,.07); background-image: none; border-color: #ddd; min-height: 32px; padding: 0 3px; }
101
+ .wpsbc-chosen-wrapper .chosen-container .chosen-choices .search-choice { box-shadow: none; background: #34495e; color: #fff; border: 0; padding: 5px 24px 6px 8px; border-radius: 2px; }
102
+ .wpsbc-chosen-wrapper .chosen-container .chosen-choices li.search-field input[type="text"] { height: 28px; padding-left: 3px; }
103
+ .wpsbc-chosen-wrapper .chosen-container .chosen-choices li.search-choice .search-choice-close { top: 7px; right: 6px; opacity: 0.7; transition: opacity 0.2s ease-in-out; }
104
+ .wpsbc-chosen-wrapper .chosen-container .chosen-choices li.search-choice .search-choice-close:hover { background-position: -42px 1px; opacity: 1; }
105
+
106
+ .wpsbc-chosen-wrapper .chosen-container.chosen-with-drop .chosen-drop { border: 0; }
107
+ .wpsbc-chosen-wrapper .chosen-container.chosen-with-drop .chosen-drop .chosen-results li { padding: 8px 10px; }
108
+ .wpsbc-chosen-wrapper .chosen-container.chosen-with-drop .chosen-drop .chosen-results li.highlighted { background: #34495e; }
109
+
110
+
111
+
112
+ /******************************************************************************************/
113
+ /* Legend Item Icon
114
+ /******************************************************************************************/
115
+ .wpsbc-legend-item-icon { position: relative; display: inline-block; width: 24px; height: 24px; border-radius: 2px; vertical-align: middle; box-sizing: border-box; overflow: hidden; }
116
+
117
+ .wpsbc-legend-item-icon[data-type="single"] div { display: none; }
118
+ .wpsbc-legend-item-icon[data-type="single"] svg { display:none; }
119
+ .wpsbc-legend-item-icon[data-type="single"] div:first-of-type { position: absolute; top: 0; left: 0; display: block; width: 100%; height: 100%; }
120
+
121
+ .wpsbc-legend-item-icon[data-type="split"] div:first-of-type { position: absolute; top: -115%; left: -115%; width: 200%; height: 200%; transform: rotate(45deg); }
122
+ .wpsbc-legend-item-icon[data-type="split"] div:nth-of-type(2) { position: absolute; top: 20.75%; left: 20.75%; width: 200%; height: 200%; transform: rotate(45deg); }
123
+
124
+ /******************************************************************************************/
125
+ /* Admin Fields General
126
+ /******************************************************************************************/
127
+ .wpsbc-admin-field-wrapper { margin-bottom: 15px; }
128
+ .wpsbc-admin-field-wrapper label { font-weight: bold; }
129
+
130
+ .wpsbc-admin-field-wrapper.wpsbc-field-inline { position: relative; padding-left: 150px; }
131
+ .wpsbc-admin-field-wrapper.wpsbc-field-inline > label { position: absolute; top: 4px; left: 0; }
132
+
133
+ .wpsbc-admin-field-wrapper input.wpsbc-medium,
134
+ .wpsbc-admin-field-wrapper select.wpsbc-medium { min-width: 300px; }
135
+
136
+ /* Color picker */
137
+ .wpsbc-admin-field-wrapper .wp-picker-container { position: relative; display: inline-block; }
138
+ .wpsbc-admin-field-wrapper .wp-picker-container button { width: 145px; }
139
+ .wpsbc-admin-field-wrapper .wp-picker-container .wp-picker-input-wrap { position: absolute; z-index: 1; top: 27px; left: 0; width: 100%; padding: 5px 0; background: #fff; }
140
+ .wpsbc-admin-field-wrapper .wp-picker-container .wp-picker-input-wrap input[type="text"] { width: 93px; font-weight: normal; }
141
+ .wpsbc-admin-field-wrapper .wp-picker-container .wp-picker-holder { position: absolute; z-index: 1; top: 55px; left: 0; }
142
+
143
+
144
+ /******************************************************************************************/
145
+ /* Tooltips
146
+ /******************************************************************************************/
147
+ .wpsbc-tooltip-wrapper { position: relative; margin-left: 2px; top:1px; }
148
+
149
+ .wpsbc-tooltip-wrapper .wpsbc-tooltip-icon { display: inline-block; width: 16px; height: 16px; line-height: 16px; text-align: center; font-size: 10px; vertical-align: text-top; cursor: pointer; background: #6B8CAD; color: #fff; border-radius: 10px; }
150
+
151
+ .wpsbc-tooltip-wrapper .wpsbc-tooltip-message { opacity: 0; visibility: hidden; position: absolute; z-index: 9999; top: 45px; left: 50%; width: 220px; margin-left: -110px; background: #6B8CAD; padding: 10px; line-height: 18px; font-size: 12px; border-radius: 3px; font-weight: normal; color: #fff; box-shadow: 0 2px 5px rgba( 0, 0, 0, 0.1 ), 0 0 56px rgba( 0, 0, 0, 0.075 ); box-sizing: border-box; transition: all 0.2s ease-in-out; }
152
+ .wpsbc-tooltip-wrapper:hover .wpsbc-tooltip-message { opacity: 1; visibility: visible; top: 28px; }
153
+
154
+ .wpsbc-tooltip-wrapper .wpsbc-tooltip-arrow { position: absolute; z-index: 1; top: -7px; left: 50%; margin-left: -10px; width: 0; height: 0; line-height: 0; border: 10px solid #6B8CAD; border-left-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent; }
155
+
156
+ .wpsbc-tooltip-wrapper a { color: #fff; }
157
+ .wpsbc-tooltip-wrapper hr { border: 0; border-bottom: 1px solid #e1e1e1; margin: 12px 0; }
158
+
159
+ /******************************************************************************************/
160
+ /* List Table General
161
+ /******************************************************************************************/
162
+ .wpsbc-list-table-no-items { text-align: center; padding: 35px; }
163
+
164
+ .wpsbc-list-table-icon.wpsbc-list-table-icon-star.wpsbc-list-table-icon-active .dashicons { color: #f0932b; }
165
+ .wpsbc-list-table-icon.wpsbc-list-table-icon-star.wpsbc-list-table-icon-inactive .dashicons { color: #95afc0; opacity: 0.55; }
166
+ .wpsbc-list-table-icon.wpsbc-list-table-icon-star.wpsbc-list-table-icon-inactive:hover .dashicons { color: #f0932b; }
167
+
168
+ .wpsbc-list-table-icon.wpsbc-list-table-icon-yes .dashicons { position: relative; top: 1px; border-radius: 50%; color: #fff; width: 18px; height: 18px; line-height: 18px; font-size: 16px; text-align: center; text-indent: -2px; }
169
+ .wpsbc-list-table-icon.wpsbc-list-table-icon-yes.wpsbc-list-table-icon-active .dashicons { background: #27ae60; }
170
+ .wpsbc-list-table-icon.wpsbc-list-table-icon-yes.wpsbc-list-table-icon-inactive .dashicons { background: #95afc0; color: #95afc0; opacity: 0.55; }
171
+ .wpsbc-list-table-icon.wpsbc-list-table-icon-yes.wpsbc-list-table-icon-inactive:hover .dashicons { background: #27ae60; color: #fff; }
172
+
173
+ /******************************************************************************************/
174
+ /* Calendars List Table
175
+ /******************************************************************************************/
176
+ .wp-list-table.wpsbc_calendars .column-id { width: 55px; }
177
+ .wp-list-table.wpsbc_calendars .column-name { width: 50%; }
178
+ @media screen and (max-width: 782px) {
179
+ .wpsbc-wrap-calendars p.search-box {bottom:-40px}
180
+ }
181
+
182
+ /******************************************************************************************/
183
+ /* Legend Items List Table
184
+ /******************************************************************************************/
185
+ .wrap.wpsbc-wrap-legend-items .tablenav.top { display: none; }
186
+
187
+ .wpsbc-wp-list-table-wrapper { position: relative; }
188
+
189
+ .wp-list-table.wpsbc_legend_items { margin-top: 10px; }
190
+ .wp-list-table.wpsbc_legend_items .column-sort { width: 40px; text-align: center; }
191
+ .wp-list-table.wpsbc_legend_items .column-color { width: 40px; text-align: center; }
192
+ .wp-list-table.wpsbc_legend_items td { vertical-align: middle; }
193
+
194
+ .wp-list-table.wpsbc_legend_items .wpsbc-legend-item-icon { margin-top: 5px; }
195
+
196
+ .wpsbc-move-legend-item { position: relative; display: block; width: 40px; height: 40px; cursor: move; }
197
+ .wpsbc-move-legend-item .wpsbc-inner,
198
+ .wpsbc-move-legend-item .wpsbc-inner:before,
199
+ .wpsbc-move-legend-item .wpsbc-inner:after { content: ''; display: block; position: absolute; left: 50%; margin-left: -12px; width: 24px; height: 1px; background: #c1c1c1; }
200
+ .wpsbc-move-legend-item .wpsbc-inner { top: 50%; }
201
+ .wpsbc-move-legend-item .wpsbc-inner:before { top: -6px; }
202
+ .wpsbc-move-legend-item .wpsbc-inner:after { top: 6px; }
203
+
204
+ .wp-list-table.wpsbc_legend_items .ui-sortable-helper { background: #fff; box-shadow: 0 5px 10px 2px #f1f1f1; }
205
+ .wp-list-table.wpsbc_legend_items .wpsbc-list-table-sort-placeholder { display: table-row; width: 100%; height: 60px; box-sizing: border-box; box-shadow: inset 0 0 10px #d9d9d9; background: #f9f9f9 !important; }
206
+
207
+ .wpsbc-overlay { position: absolute; z-index: 3; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255,255,255,0.65); }
208
+ .wpsbc-overlay .spinner { float: none; position: absolute; top: 50%; left: 50%; margin: -10px 0 0 -10px; visibility: visible; }
209
+
210
+ /******************************************************************************************/
211
+ /* Add New Calendar Page
212
+ /******************************************************************************************/
213
+
214
+ /* Admin notices */
215
+ .wrap.wpsbc-wrap-add-calendar div.notice { max-width: 500px; margin-left: auto; margin-right: auto; box-sizing: border-box; }
216
+
217
+ /* Icon */
218
+ #wpsbc-add-new-calendar-icon { text-align: center; }
219
+ #wpsbc-add-new-calendar-icon .wpsbc-icon-wrap { position: relative; background: #e6e6e6; width: 70px; height: 70px; border-radius: 50%; margin: 50px auto 10px; }
220
+
221
+ #wpsbc-add-new-calendar-icon .wpsbc-icon-wrap .dashicons-calendar-alt { font-size: 38px; vertical-align: middle; text-align: center; width: 70px; height: 70px; line-height: 68px; }
222
+ #wpsbc-add-new-calendar-icon .wpsbc-icon-wrap .dashicons-plus { position: absolute; top: -10px; right: -10px; background: #c1c1c1; border-radius: 50%; width: 30px; height: 30px; text-align: center; line-height: 32px; vertical-align: middle; }
223
+
224
+ /* Heading */
225
+ #wpsbc-add-new-calendar-heading { text-align: center; margin-bottom: 35px; }
226
+
227
+ /* Postbox */
228
+ #wpsbc-add-new-calendar-postbox { max-width: 500px; margin: 0 auto; }
229
+ #wpsbc-add-new-calendar-postbox .inside { padding: 30px 20px; margin: 0; }
230
+
231
+ #wpsbc-add-new-calendar-postbox h1 { text-align: center; margin-bottom: 35px; }
232
+
233
+ #wpsbc-add-new-calendar-postbox label { display: block; font-weight: bold; margin-bottom: 5px; }
234
+ #wpsbc-add-new-calendar-postbox input[type="text"],
235
+ #wpsbc-add-new-calendar-postbox select { width: 100%; height: 32px; }
236
+
237
+ #wpsbc-add-new-calendar-postbox label[for="wpsbc-new-calendar-legend"] { margin-top: 25px; }
238
+ #wpsbc-add-new-calendar-postbox select { margin-bottom: 5px; }
239
+
240
+ #wpsbc-add-new-calendar-postbox input[type=submit] { display: inline-block; margin: 0 auto; }
241
+
242
+ #wpsbc-add-new-calendar-postbox #major-publishing-actions { padding: 20px; text-align: right; }
243
+ #wpsbc-add-new-calendar-postbox #major-publishing-actions:after { content: ''; display: block; clear: both; }
244
+ #wpsbc-add-new-calendar-postbox #major-publishing-actions a { float: left; color: #777; line-height: 36px; outline: 0; box-shadow: none; }
245
+ #wpsbc-add-new-calendar-postbox #major-publishing-actions a:hover { color: #444; }
246
+
247
+ /******************************************************************************************/
248
+ /* Edit Calendar Page
249
+ /******************************************************************************************/
250
+ .wrap.wpsbc-wrap-edit-calendar #titlediv { margin-bottom: 20px; }
251
+ .wrap.wpsbc-wrap-edit-calendar #titlediv #titlewrap:after {content:""; display:block; clear:both;}
252
+ .wrap.wpsbc-wrap-edit-calendar #titlediv .titlewrap-toggle {float:right;}
253
+ .wrap.wpsbc-wrap-edit-calendar #titlediv .titlewrap-toggle svg {width:8px; margin-left:1px;}
254
+ .wrap.wpsbc-wrap-edit-calendar #titlediv .titlewrap-toggle.open svg {transform:rotate(180deg);}
255
+ .wrap.wpsbc-wrap-edit-calendar #titlediv .titlewrap-translations {display:none; clear:both; width:100%; float:right; padding-top: 10px;}
256
+ .wrap.wpsbc-wrap-edit-calendar #titlediv .titlewrap-translations input {padding: 3px 8px 3px 34px; font-size: 1.7em; line-height: 100%; height: 1.7em; width: 100%; outline: 0; margin: 0 0 3px; background-color: #fff;}
257
+ .wrap.wpsbc-wrap-edit-calendar #titlediv .titlewrap-translation {position:relative;}
258
+ .wrap.wpsbc-wrap-edit-calendar #titlediv .titlewrap-translation-flag img {position:absolute; left: 10px; top: 14px;}
259
+
260
+
261
+
262
+ .wrap.wpsbc-wrap-edit-calendar #post-body-content { float: right; margin-right: 0; }
263
+ .wrap.wpsbc-wrap-edit-calendar #poststuff #post-body.columns-2 { margin-left: 255px; margin-right: 0; }
264
+ .wrap.wpsbc-wrap-edit-calendar #post-body.columns-2 #postbox-container-1 { float: left; width: 235px; margin-left: -255px; margin-right: 0; }
265
+ .wrap.wpsbc-wrap-edit-calendar #post-body.columns-2 #postbox-container-1 .postbox { min-width: 225px; }
266
+ .wrap.wpsbc-wrap-edit-calendar #poststuff .postbox .inside { margin-top: 12px; }
267
+ .wrap.wpsbc-wrap-edit-calendar #poststuff:after { content: ''; display: block; clear: both; }
268
+
269
+ /* Calendar */
270
+ .wrap.wpsbc-wrap-edit-calendar .wpsbc-container .wpsbc-calendars .wpsbc-calendar { border: 0; margin-top: 10px; margin-bottom: 0; }
271
+ .wrap.wpsbc-wrap-edit-calendar .wpsbc-container .wpsbc-calendars .wpsbc-calendar-wrapper { padding: 10px 0 0 0; }
272
+ .wrap.wpsbc-wrap-edit-calendar .wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header { padding: 0 0 10px; background: transparent; border-bottom: 1px solid #f1f1f1; }
273
+
274
+ /* Legend */
275
+ .wpsbc-legend-item { margin-bottom: 10px; }
276
+ .wpsbc-legend-item .wpsbc-legend-item-icon { margin-right: 5px; }
277
+ .wpsbc-legend-item .wpsbc-legend-item-name { display: inline-block; vertical-align: middle; }
278
+
279
+ /* Calendar Editor */
280
+ #wpsbc-calendar-events { position: relative; }
281
+
282
+ #wpsbc-calendar-editor .wpsbc-calendar-date { padding: 3px 0; }
283
+
284
+ .wpsbc-calendar-date-legend-item,
285
+ .wpsbc-calendar-date-legend-item-header { position: relative; display: inline-block; width: 180px; padding-left: 35px; margin-right: 7px; box-sizing: border-box; }
286
+ .wpsbc-calendar-date-legend-item-header { padding-left: 0; }
287
+ .wpsbc-calendar-date-legend-item select { width: 100%; height: 30px; margin: 0; }
288
+ .wpsbc-calendar-date-legend-item .wpsbc-legend-item-icon-wrapper,
289
+ .wpsbc-calendar-date-legend-item .wpsbc-legend-item-icon { position: absolute; top: 0; left: 0; width: 30px; height: 30px; }
290
+ .wpsbc-calendar-date-legend-item .wpsbc-date-inner { position: relative; z-index: 2; line-height: 30px; width: 100%; text-align: center; }
291
+
292
+ .wpsbc-calendar-date-description-ical { position: relative; display: inline-block; width: calc( 100% - 200px ); margin-right:7px; }
293
+ .wpsbc-calendar-date-description,
294
+ .wpsbc-calendar-date-tooltip,
295
+ .wpsbc-calendar-date-description-header,
296
+ .wpsbc-calendar-date-tooltip-header { position: relative; display: inline-block; width: calc( 100% - 200px ); }
297
+ .wpsbc-calendar-date-description-ical .dashicons,
298
+ .wpsbc-calendar-date-description .dashicons,
299
+ .wpsbc-calendar-date-tooltip .dashicons { position: absolute; left: 5px; top: 5px; color: #d6d6d6; }
300
+ .wpsbc-calendar-date-description-ical input,
301
+ .wpsbc-calendar-date-description input,
302
+ .wpsbc-calendar-date-tooltip input { width: 100%; height: 30px; margin: 0; vertical-align: middle; padding-left: 30px; }
303
+
304
+ .wpsbc-calendar-date-description,
305
+ .wpsbc-calendar-date-description-header { margin-right: 7px; }
306
+
307
+ .wpsbc-calendar-date-legend-item-header,
308
+ .wpsbc-calendar-date-description-header,
309
+ .wpsbc-calendar-date-tooltip-header { margin-bottom: 7px; font-weight: bold; }
310
+
311
+ .wpsbc-save-calendar-spinner { float: none; margin-top: 0; }
312
+
313
+
314
+ /* Export CSV */
315
+ .wrap.wpsbc-wrap-edit-calendar .postbox .button-secondary .dashicons {position: relative; top:2px; left:-2px;}
316
+
317
+ .wp-admin .ui-datepicker.wpsbc-datepicker { border: 1px solid #ccc; -webkit-box-shadow: 0 3px 6px rgba(0, 0, 0, 0.4); box-shadow: 0 3px 6px rgba(0, 0, 0, 0.4); }
318
+
319
+
320
+ /******************************************************************************************/
321
+ /* iCalendar Import/Export Page
322
+ /******************************************************************************************/
323
+ .wpsbc-wrap #dashboard-widgets-wrap { overflow: visible; }
324
+
325
+ .wpsbc-wrap .wpsbc-settings-field-inner .wpsbc-warning {padding-top:6px; font-weight: bold; display:none;}
326
+ .wpsbc-wrap .wpsbc-settings-field-inner .wpsbc-warning span.dashicons {color: #ffba00;}
327
+
328
+ #wpsbc-settings-field-ical-import,
329
+ #wpsbc-settings-field-ical-export { font-size: 12px; height: 32px; }
330
+
331
+ #wpsbc-ical-url-import .wpsbc-settings-field-conditional {display: none;}
332
+ #ical_url_import_split_days {position:relative; top:2px;}
333
+
334
+ .wpsbc-ical-feed-calendar { position: relative; padding: 15px 80px 15px 40px; border-bottom: 1px solid #eee; box-sizing: border-box; }
335
+ .wpsbc-ical-feed-calendar:after { clear: both; display: block; }
336
+
337
+ .wpsbc-ical-feed-calendar p { margin-top: 5px; margin-bottom: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: #757575; }
338
+ .wpsbc-ical-feed-calendar small { color: #757575; }
339
+
340
+ .wpsbc-ical-feed-calendar .wpsbc-trash { position: absolute; right: 12px; top: 50%; margin-top: -10px; line-height: 20px; vertical-align: middle; color: #a00; }
341
+ .wpsbc-ical-feed-calendar .wpsbc-trash:hover { color: #dc3232; }
342
+
343
+ .wpsbc-ical-feed-calendar .wpsbc-legend-item-icon { position: absolute; left: 12px; top: 15px; width: 16px; height: 16px; }
344
+
345
+
346
+ /******************************************************************************************/
347
+ /* Modals
348
+ /******************************************************************************************/
349
+ .wpsbc-modal { display: none; position: fixed; z-index: 85462256; background-color: #fff; border: 1px solid rgba(0,0,0,.08); margin: 0 auto; max-height: 600px; max-width: 950px; top: 7.5%; bottom: 7.5%; left: calc(45% - 400px ); right: calc(45% - 400px ); box-shadow: 0 1px 3px 0 rgba(0,0,0,.2), 0 1px 1px 0 rgba(0,0,0,.14), 0 2px 1px -1px rgba(0,0,0,.12); overflow: hidden; }
350
+ .wpsbc-modal.wpsbc-active { display: block; }
351
+
352
+ .wpsbc-modal .wpsbc-modal-header { position: relative; padding: 20px 15px; box-shadow: 0 1px 3px 0 rgba(0,0,0,.2), 0 1px 1px 0 rgba(0,0,0,.14), 0 2px 1px -1px rgba(0,0,0,.12); }
353
+ .wpsbc-modal .wpsbc-modal-header h2 { margin: 0; font-size: 1.6em; font-weight: normal; }
354
+ .wpsbc-modal .wpsbc-modal-header h2 .dashicons { margin-right: 5px; }
355
+ .wpsbc-modal .wpsbc-modal-header .wpsbc-modal-close { position: absolute; right: 15px; top: 50%; margin-top: -15px; width: 30px; height: 30px; line-height: 30px; font-size: 30px; cursor: pointer; }
356
+
357
+ .wpsbc-modal .wpsbc-modal-nav-tab-wrapper { list-style: none; background: rgba( 0, 0, 0, 0.08 ); padding: 0; margin: 0; }
358
+ .wpsbc-modal .wpsbc-modal-nav-tab-wrapper:after { content: ''; display: block; clear: both; }
359
+ .wpsbc-modal .wpsbc-modal-nav-tab-wrapper li { display: block; margin: 0; float: left; }
360
+ .wpsbc-modal .wpsbc-modal-nav-tab-wrapper li a { position: relative; display: block; padding: 14px 16px; color: #444; text-decoration: none; font-weight: bold; outline: 0; box-shadow: none; }
361
+ .wpsbc-modal .wpsbc-modal-nav-tab-wrapper li a:after { content: ''; display: block; position: absolute; bottom: 0; left: 50%; width: 0; height: 2px; background: #66b3ff; transition: all 0.2s ease-in-out; }
362
+ .wpsbc-modal .wpsbc-modal-nav-tab-wrapper li a:hover:after { left: 0%; width: 100%; }
363
+ .wpsbc-modal .wpsbc-modal-nav-tab-wrapper li.wpsbc-active a { background: rgba( 0, 0, 0, 0.15 ); }
364
+ .wpsbc-modal .wpsbc-modal-nav-tab-wrapper li.wpsbc-active a:after { left: 0%; width: 100%; }
365
+
366
+ .wpsbc-modal .wpsbc-modal-body .wpsbc-modal-inner { padding: 15px; overflow-y: scroll; }
367
+
368
+ .wpsbc-modal .wpsbc-modal-body .wpsbc-modal-tab { margin: 0; }
369
+ .wpsbc-modal .wpsbc-modal-body .wpsbc-modal-tab.wpsbc-active { padding-bottom: 5px; }
370
+
371
+ .wpsbc-modal-overlay { display: none; position: fixed; z-index: 85462255; top: 0; left: 0; width: 100%; height: 100%; background: rgba( 0, 0, 0, 0.5 ); }
372
+ .wpsbc-modal-overlay.wpsbc-active { display: block; }
373
+
374
+ .wpsbc-modal .wpsbc-tooltip-message { width: 240px; margin-left: -120px; }
375
+
376
+ .wpsbc-modal .wpsbc-modal-body .wpsbc-modal-tab .wpsbc-element-disabled { position: relative; }
377
+ .wpsbc-modal .wpsbc-modal-body .wpsbc-modal-tab .wpsbc-element-disabled:before { content: ''; position: absolute; z-index: 1; top: 0; right: 0; bottom: 0; left: 0; background: #fff; opacity: 0.6; }
378
+
379
+
380
+ /******************************************************************************************/
381
+ /* Shortcode Generator
382
+ /******************************************************************************************/
383
+ #wpsbc-shortcode-generator-button span { position: relative; top: -2px; display: inline-block; width: 18px; height: 18px; vertical-align: text-top; margin: 0 4px 0 0; font-size: 18px; }
384
+
385
+ #wpsbc-modal-add-calendar-shortcode .wpsbc-tab { display: block; height: 0; position: relative; top: 50px; visibility: hidden; opacity: 0; transition: all 0.2s ease-in-out; overflow: hidden; }
386
+ #wpsbc-modal-add-calendar-shortcode .wpsbc-tab.wpsbc-active { height: auto; top: 0; opacity: 1; visibility: visible; overflow: visible; }
387
+
388
+ #wpsbc-modal-add-calendar-shortcode h3 { margin-bottom: 0; }
389
+ #wpsbc-modal-add-calendar-shortcode h4 { font-size: 1.1em; margin-bottom: 0; }
390
+ #wpsbc-modal-add-calendar-shortcode hr { margin: 1.33em 0; }
391
+ #wpsbc-modal-add-calendar-shortcode p:first-of-type { margin-top: 0.5em; margin-bottom: 2.2em; }
392
+ #wpsbc-modal-add-calendar-shortcode label { display: block; margin-bottom: 5px; font-weight: bold; }
393
+ #wpsbc-modal-add-calendar-shortcode select { width: 100%; }
394
+
395
+
396
+
397
+ /******************************************************************************************/
398
+ /* Settings Page
399
+ /******************************************************************************************/
400
+
401
+ /* Languages Tab */
402
+ .wpsbc-tab.wpsbc-tab-languages .wpsbc-settings-field-inner > div { margin-bottom: 7px; }
403
+ .wpsbc-tab.wpsbc-tab-languages .wpsbc-settings-field-inner > div img { margin: 0 5px 0 5px; }
404
+
405
+ /* Register Website Tab */
406
+ #wpsbc-wpsbc-settings-field-register-website input { max-width: 300px; margin: 0; }
407
+ #wpsbc-wpsbc-settings-field-register-website .button-primary,
408
+ #wpsbc-wpsbc-settings-field-register-website .button-secondary { vertical-align: top; height: 29px; line-height: 27px; }
409
+
410
+ /* Uninstaller Tab */
411
+ #wpsbc-uninstaller-confirmation { display: none; }
412
+
413
+ .wpsbc-settings-field-inner.wpsbc-settings-field-inner-inline small {display: inline-block; position: relative; top: 3px; font-style:italic;}
414
+
415
+
416
+ /******************************************************************************************/
417
+ /* Upgrader Page
418
+ /******************************************************************************************/
419
+ #wpsbc-upgrader-content { max-width: 800px; margin: 0 auto; text-align: center; }
420
+
421
+ #wpsbc-upgrader-content-inner { min-height: 85px; }
422
+
423
+ #wpsbc-upgrader-content-inner :first-child { margin-top: 0; }
424
+ #wpsbc-upgrader-content-inner :last-child { margin-bottom: 0; }
425
+
426
+ #wpsbc-upgrader-button-wrapper { position: relative; display: inline-block; margin-top: 40px; padding: 0 30px; }
427
+
428
+ #wpsbc-upgrader-button-wrapper .spinner { position: absolute; margin: 0; right: 0; top: 10px; }
429
+
430
+ #wpsbc-upgrader-button-start-upgrade span { display: none; }
431
+ #wpsbc-upgrader-button-start-upgrade span:nth-of-type(1) { display: block; }
432
+
433
+ #wpsbc-upgrader-button-continue { display: none; }
434
+
435
+ #wpsbc-upgrader-loading-bar-wrapper { display: none; min-height: 85px; }
436
+
437
+ #wpsbc-upgrader-loading-bar { position: relative; top: 42px; background: #d5d5d5; height: 3px; width: 100%; }
438
+ #wpsbc-upgrader-loading-bar:after { content: ''; display: block; position: absolute; z-index: 1; top: 0; left: 0; width: 0%; height: 3px; background: #27ae60; transition: width 0.4s ease-in-out; }
439
+
440
+ .wpsbc-upgrader-loading-bar-step,
441
+ #wpsbc-upgrader-loading-bar .spinner { visibility: visible; opacity: 1; position: absolute; z-index: 2; top: -9px; margin-left: -10px; width: 20px; height: 20px; background-color: #d5d5d5; border-radius: 50%; }
442
+
443
+ #wpsbc-upgrader-loading-bar .spinner { background-color: #f1f1f1; margin: 0; margin-left: -10px; left: 0; }
444
+
445
+ #wpsbc-upgrader-loading-bar-step-1,
446
+ #wpsbc-upgrader-message-doing-step-1,
447
+ #wpsbc-upgrader-message-step-1 { left: 0; }
448
+ #wpsbc-upgrader-loading-bar-step-2,
449
+ #wpsbc-upgrader-message-doing-step-2,
450
+ #wpsbc-upgrader-message-step-2 { left: 33.3333%; }
451
+ #wpsbc-upgrader-loading-bar-step-3,
452
+ #wpsbc-upgrader-message-doing-step-3,
453
+ #wpsbc-upgrader-message-step-3 { left: 66.6666%; }
454
+ #wpsbc-upgrader-loading-bar-step-4,
455
+ #wpsbc-upgrader-message-doing-step-4,
456
+ #wpsbc-upgrader-message-step-4 { left: 100%; }
457
+
458
+ .wpsbc-upgrader-message-step,
459
+ .wpsbc-upgrader-message-doing-step { position: absolute; top: 20px; width: 250px; margin-left: -125px !important; text-align: center; }
460
+
461
+ .wpsbc-upgrader-message-step { opacity: 0.35; }
462
+ .wpsbc-upgrader-message-doing-step { display: none; }
463
+
464
+ /* Upgrader Step 1 */
465
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="1"] #wpsbc-upgrader-loading-bar .spinner { left: 0; }
466
+
467
+ /* Upgrader Step 2 */
468
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="2"] #wpsbc-upgrader-loading-bar .spinner { left: 33.3333%; }
469
+
470
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="2"] #wpsbc-upgrader-message-step-1 { opacity: 1; color: #27ae60; }
471
+
472
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="2"] #wpsbc-upgrader-loading-bar-step-1 { background-color: #27ae60; }
473
+
474
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="2"] #wpsbc-upgrader-loading-bar:after { width: 33.3333%; }
475
+
476
+ /* Upgrader Step 3 */
477
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="3"] #wpsbc-upgrader-loading-bar .spinner { left: 66.6666%; }
478
+
479
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="3"] #wpsbc-upgrader-message-step-1,
480
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="3"] #wpsbc-upgrader-message-step-2 { opacity: 1; color: #27ae60; }
481
+
482
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="3"] #wpsbc-upgrader-loading-bar-step-1,
483
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="3"] #wpsbc-upgrader-loading-bar-step-2 { background-color: #27ae60; }
484
+
485
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="3"] #wpsbc-upgrader-loading-bar:after { width: 66.6666%; }
486
+
487
+ /* Upgrader Step 4 */
488
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="4"] #wpsbc-upgrader-loading-bar .spinner { left: 100%; }
489
+
490
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="4"] #wpsbc-upgrader-message-step-1,
491
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="4"] #wpsbc-upgrader-message-step-2,
492
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="4"] #wpsbc-upgrader-message-step-3 { opacity: 1; color: #27ae60; }
493
+
494
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="4"] #wpsbc-upgrader-loading-bar-step-1,
495
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="4"] #wpsbc-upgrader-loading-bar-step-2,
496
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="4"] #wpsbc-upgrader-loading-bar-step-3 { background-color: #27ae60; }
497
+
498
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="4"] #wpsbc-upgrader-loading-bar:after { width: 100%; }
499
+
500
+ /* Upgrader Step 5 */
501
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="5"] #wpsbc-upgrader-loading-bar .spinner { display: none; }
502
+
503
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="5"] #wpsbc-upgrader-message-step-1,
504
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="5"] #wpsbc-upgrader-message-step-2,
505
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="5"] #wpsbc-upgrader-message-step-3,
506
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="5"] #wpsbc-upgrader-message-step-4 { opacity: 1; color: #27ae60; }
507
+
508
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="5"] #wpsbc-upgrader-loading-bar-step-1,
509
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="5"] #wpsbc-upgrader-loading-bar-step-2,
510
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="5"] #wpsbc-upgrader-loading-bar-step-3,
511
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="5"] #wpsbc-upgrader-loading-bar-step-4 { background-color: #27ae60; }
512
+
513
+ #wpsbc-upgrader-loading-bar-wrapper[data-step="5"] #wpsbc-upgrader-loading-bar:after { width: 100%; }
514
+
515
+ /* Skip Migration */
516
+
517
+ #wpsbc-upgrader-skip-wrapper { margin-top: 20px; }
518
+
519
+ /* Print Styles */
520
+ @media print {
521
+ #adminmenumain {display: none;}
522
+ #wpcontent {margin: 0 !important;}
523
+ }
524
+
525
+
526
+
527
+ .wpsbc-page-notice { background: #fff; border-left-style: solid; border-left-width: 4px; box-shadow: 0 1px 1px 0 rgba(0,0,0,.1); margin: 5px 0 15px; padding: 1px 12px;}
528
+ .wpsbc-page-notice p {font-size:13px; line-height:1.5; margin: .5em 0; padding: 2px; }
529
+
530
+ /*** Premium CTA ***/
531
+ .wpsbc-wrap-upgrade-to-premium h3 {margin:30px 0;}
532
+ .wpsbc-wrap-upgrade-to-premium ul {float:left; width:75%; margin:0;}
533
+ .wpsbc-wrap-upgrade-to-premium ul:after {display: block; clear: both; content:"";}
534
+ .wpsbc-wrap-upgrade-to-premium ul li {display:block; float:left; width:24%; margin:0 1% 1% 0; background:#fff; padding:15px; box-sizing:border-box;}
535
+ .wpsbc-wrap-upgrade-to-premium ul li:nth-child(4n+1) {clear:both;}
536
+ .wpsbc-wrap-upgrade-to-premium ul li strong {font-size:16px;}
537
+ .wpsbc-wrap-upgrade-to-premium ul li strong span {color: #3cc33c; font-size: 30px; position: relative; top: -4px; left: -7px;}
538
+ .wpsbc-wrap-upgrade-to-premium ul li p {margin:0; padding-top:10px;}
539
+
540
+ .wpsbc-wrap-upgrade-to-premium .cta {float:right; width:25%; background-color:#9aca41; padding:30px; box-sizing:border-box; color:#fff; margin-bottom:1%;}
541
+ .wpsbc-wrap-upgrade-to-premium .cta h3 {color:#fff; font-size:22px; line-height:28px; margin:0;}
542
+ .wpsbc-wrap-upgrade-to-premium .cta a.wpsbc-button {padding: 20px 30px 20px 30px; border-radius: 2px; text-align: center; font-size:20px; font-weight: bold; display:block; background:#1075a0; color:#fff; text-decoration:none; margin-top:20px; }
543
+ .wpsbc-wrap-upgrade-to-premium .cta a.wpsbc-button:hover {color:#000; background:#fff;}
544
+ .wpsbc-wrap-upgrade-to-premium .cta p.prices {text-align:center; margin:2px 0; font-size:12px;}
545
+
546
+
547
+ .wpsbc-wrap-upgrade-to-premium .cta-2 {float:right; width:25%; background-color:#fff; padding:30px; box-sizing:border-box; color:#fff; margin-bottom:20px;}
548
+ .wpsbc-wrap-upgrade-to-premium .cta-2 h4 {color:#000; font-size:18px; line-height:28px; margin:0; text-align: center;}
549
+ .wpsbc-wrap-upgrade-to-premium .cta-2 p.discount {text-align: center;}
550
+ .wpsbc-wrap-upgrade-to-premium .cta-2 p.discount strong {display:inline-block; background:#1075a0; color:#fff; padding:4px 10px; font-size:18px; }
551
+
552
+ .wpsbc-wrap-upgrade-cta {width:100%; background:#fff; padding:20px; box-sizing: border-box; border-radius:2px; display:block; text-decoration: none;}
553
+ .wpsbc-modal-inner .wpsbc-wrap-upgrade-cta {background:#f7f7f7;}
554
+ .wpsbc-wrap-upgrade-cta:hover .wpsbc-wrap-upgrade-cta-button {background:#1075a0;}
555
+ .wpsbc-wrap-upgrade-cta:after {display: block; content:""; clear: both;}
556
+ .wpsbc-wrap-upgrade-cta .wpsbc-wrap-upgrade-cta-button {padding: 15px 20px; border-radius: 2px; text-align: center; font-size:16px; font-weight: bold; display:block; float:right; background:#9aca41; color:#fff; text-decoration:none; }
557
+ .wpsbc-wrap-upgrade-cta .wpsbc-wrap-upgrade-cta-heading {color: #23282d; font-size: 1.3em; line-height:28px; padding:10px 0; display: block;}
558
+
559
+
560
+ /* 5.3 Styles */
561
+
562
+ .branch-5-3 .wpsbc-wrap select {max-width:none; border-radius:4px; min-height:28px;}
563
+
564
+ .branch-5-3 .wpsbc-ical-feed-calendar {border-bottom:1px solid #ccd0d4;}
565
+ .branch-5-3 .postbox .wpsbc-plugin-card-bottom {border-top:1px solid #ccd0d4;}
566
+
567
+ .branch-5-3 .wpsbc-chosen-wrapper .chosen-container .chosen-choices {border-color:#7e8993; border-radius:4px; box-shadow:none; background: #fff url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20width%3D%2220%22%20height%3D%2220%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M5%206l5%205%205-5%202%201-7%207-7-7%202-1z%22%20fill%3D%22%23555%22%2F%3E%3C%2Fsvg%3E') no-repeat right 5px top 55%; background-size: 16px 16px; cursor: pointer;}
568
+ .branch-5-3 .wpsbc-chosen-wrapper .chosen-container.chosen-container-active .chosen-choices {border-color: #007cba; box-shadow: 0 0 0 1px #007cba;}
569
+ .branch-5-3 .wpsbc-chosen-wrapper .chosen-container .chosen-choices .search-choice {background: #007cba;}
570
+ .branch-5-3 .wpsbc-chosen-wrapper .chosen-container.chosen-with-drop .chosen-drop .chosen-results li.highlighted {background: #007cba;}
571
+ .branch-5-3 .wpsbc-chosen-wrapper .chosen-container.chosen-with-drop .chosen-drop {top: calc(100% + 1px);}
572
+ .branch-5-3 .wpsbc-chosen-wrapper .chosen-container .chosen-choices li.search-field input[type="text"] {min-height:28px; margin:0;}
573
+ .branch-5-3 .wpsbc-chosen-wrapper .chosen-container .chosen-choices {min-height:30px;}
574
+ .branch-5-3 .wpsbc-chosen-wrapper .chosen-container-multi .chosen-choices li.search-choice,
575
+ .branch-5-3 .wpsbc-chosen-wrapper .chosen-container-multi .chosen-choices li.search-choice {margin:2px 3px 2px 0;}
576
+
577
+
578
+
579
+ @media screen and ( max-width : 850px ) {
580
+
581
+ /* Hide the headers of the availability table */
582
+ .wpsbc-calendar-date-legend-item-header,
583
+ .wpsbc-calendar-date-description-header,
584
+ .wpsbc-calendar-date-tooltip-header { display: none; }
585
+
586
+ /* Make the availability fields 100% in width */
587
+ .wpsbc-calendar-date-legend-item,
588
+ .wpsbc-calendar-date-description-ical,
589
+ .wpsbc-calendar-date-description,
590
+ .wpsbc-calendar-date-tooltip { width: 100%; margin-top: 10px; }
591
+
592
+ /* Match the heights for all availability fields */
593
+ .wpsbc-calendar-date-legend-item { padding-left: 45px; }
594
+
595
+ .wpsbc-calendar-date-description-ical input,
596
+ .wpsbc-calendar-date-description input,
597
+ .wpsbc-calendar-date-tooltip input { height: 36px; }
598
+
599
+ .wpsbc-calendar-date-description-ical .dashicons,
600
+ .wpsbc-calendar-date-description .dashicons,
601
+ .wpsbc-calendar-date-tooltip .dashicons { top: 8px; left: 8px; }
602
+
603
+ .wpsbc-calendar-date-legend-item .wpsbc-legend-item-icon-wrapper,
604
+ .wpsbc-calendar-date-legend-item .wpsbc-legend-item-icon { width: 36px; height: 36px; }
605
+ .wpsbc-calendar-date-legend-item .wpsbc-date-inner { line-height: 36px; }
606
+
607
+ /* Position sidebar */
608
+ .wrap.wpsbc-wrap-edit-calendar #post-body.columns-2 #postbox-container-1 { margin-left: 0; }
609
+
610
+ .branch-5-3 #wpsbc-calendar-editor .wpsbc-wrap select {min-height:36px; line-height:1;}
611
+ }
assets/css/style-front-end.css ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /******************************************************************************************/
2
+ /* Single Calendar Resets
3
+ /******************************************************************************************/
4
+ .wpsbc-container { position: relative; visibility: hidden; }
5
+ .wpsbc-container .wpsbc-calendars-wrapper { position: relative; }
6
+
7
+ .wpsbc-container .wpsbc-calendars:after { content: ''; display: block; clear: both; }
8
+
9
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar table { table-layout: fixed; width: 100%; border-collapse: collapse; margin: 0 !important; padding: 0 !important; border: 0 !important; }
10
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar table thead,
11
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar table tbody,
12
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar table tr { border: 0 !important; padding: 0 !important; margin: 0 !important; background: transparent; }
13
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar table tr th,
14
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar table tr td { position: relative; border: 0 !important; padding: 0 !important; margin: 0 !important; vertical-align: middle; background: transparent; text-shadow: none; box-shadow: none; box-sizing: border-box; }
15
+
16
+
17
+
18
+
19
+ /******************************************************************************************/
20
+ /* Single Calendar Default Styles
21
+ /******************************************************************************************/
22
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar { position: relative; border: 1px solid #f1f1f1; background: #fff; margin-bottom: 1em; }
23
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar table thead tr th {padding-bottom: 8px !important; -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box;}
24
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar-wrapper { padding: 8px 7px 7px 7px; -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; }
25
+
26
+ /******************************************************************************************/
27
+ /* Calendar Loading Overlay
28
+ /******************************************************************************************/
29
+ .wpsbc-container .wpsbc-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba( 255,255,255,0.6 ); }
30
+
31
+ .wpsbc-overlay-spinner { position: absolute; top: 50%; left: 50%; margin-top: -6px; margin-left: -35px; width: 70px; text-align: center; }
32
+ .wpsbc-overlay-spinner > div { width: 12px; height: 12px; background-color: #333; border-radius: 100%; display: inline-block; -webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both; animation: sk-bouncedelay 1.4s infinite ease-in-out both; }
33
+ .wpsbc-overlay-spinner .wpsbc-overlay-bounce1 { -webkit-animation-delay: -0.32s; animation-delay: -0.32s; }
34
+ .wpsbc-overlay-spinner .wpsbc-overlay-bounce2 { -webkit-animation-delay: -0.16s; animation-delay: -0.16s; }
35
+ @-webkit-keyframes sk-bouncedelay { 0%, 80%, 100% { -webkit-transform: scale(0) } 40% { -webkit-transform: scale(1.0) } }
36
+ @keyframes sk-bouncedelay { 0%, 80%, 100% { -webkit-transform: scale(0); transform: scale(0); } 40% { -webkit-transform: scale(1.0); transform: scale(1.0); } }
37
+
38
+ /******************************************************************************************/
39
+ /* Single Calendar Header
40
+ /******************************************************************************************/
41
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header { text-align: center; padding: 8px; background: #f5f5f5 }
42
+
43
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-calendar-header-navigation { position: relative; padding: 0 30px; min-height: 26px; font-size: 13px; line-height: 26px; }
44
+
45
+ /******************************************************************************************/
46
+ /* Single Calendar Header Button Navigation
47
+ /******************************************************************************************/
48
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-calendar-header-navigation .wpsbc-prev,
49
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-calendar-header-navigation .wpsbc-next { position: absolute; display: block; top: 50%; height: 26px; width: 26px; line-height: 26px; margin-top: -13px; text-decoration: none; border: 0; box-shadow: none; color: #fff; background: #bdc3c7; border-radius: 2px; }
50
+
51
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-calendar-header-navigation .wpsbc-prev { left: 0; }
52
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-calendar-header-navigation .wpsbc-next { right: 0; }
53
+
54
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-calendar-header-navigation .wpsbc-next .wpsbc-arrow,
55
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-calendar-header-navigation .wpsbc-prev .wpsbc-arrow { position: absolute; top: 50%; margin-top: -5px; display: inline-block; width: 0; height: 0; border-style: solid; }
56
+
57
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-calendar-header-navigation .wpsbc-next .wpsbc-arrow { right: 50%; margin-right: -5px; border-width: 5px 0 5px 8.7px; border-color: transparent transparent transparent #ffffff; }
58
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-calendar-header-navigation .wpsbc-prev .wpsbc-arrow { left: 50%; margin-left: -5px; border-width: 5px 8.7px 5px 0; border-color: transparent #fff transparent transparent; }
59
+
60
+ /******************************************************************************************/
61
+ /* Single Calendar Header Select Navigation
62
+ /******************************************************************************************/
63
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-select-container select { width: 100%; height: 26px; line-height: 26px; margin: 0; padding: 0 10px; vertical-align: top; font-size: 13px; box-sizing: border-box !important; }
64
+
65
+ /******************************************************************************************/
66
+ /* Single Calendar Table Week Numbers
67
+ /******************************************************************************************/
68
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar table td .wpsbc-week-number { background: #e8e8e8; height: 32px; }
69
+
70
+ /******************************************************************************************/
71
+ /* Single Calendar Table Dates
72
+ /******************************************************************************************/
73
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar table tr th,
74
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar table tr td { text-align: center; padding: 1px !important; font-size: 13px; }
75
+
76
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar table th { font-weight: bold; color: inherit; }
77
+
78
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar table tr td .wpsbc-date { position: relative; width: 100%; height: 100%; }
79
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar table tr td .wpsbc-date-inner { position: relative; width: 100%; height: 32px; }
80
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar table tr td .wpsbc-date-inner .wpsbc-date-number { position: absolute; z-index: 0; top: 0; left: 0; width: 100%; height: 100%; font-size: 13px; }
81
+
82
+ .wpsbc-container .wpsbc-calendars .wpsbc-calendar table tr td .wpsbc-date.wpsbc-gap { background: #f7f7f7; }
83
+
84
+ /******************************************************************************************/
85
+ /* Legend: Legend Item
86
+ /******************************************************************************************/
87
+ .wpsbc-legend { text-align: left; margin: 1em 0; padding: 8px 8px 0; border: 1px solid #f1f1f1; }
88
+
89
+ .wpsbc-legend .wpsbc-legend-item { display: inline-block; margin-bottom: 8px; margin-right: 8px; }
90
+ .wpsbc-legend .wpsbc-legend-item-icon { position: relative; display: inline-block; width: 24px; height: 24px; vertical-align: middle; margin-right: 5px; box-sizing: border-box; overflow: hidden; }
91
+ .wpsbc-legend .wpsbc-legend-item-name { display: inline-block; vertical-align: middle; font-size: 13px; }
92
+
93
+ /* Legend position: side */
94
+ .wpsbc-calendars-wrapper.wpsbc-legend-position-side { padding-right: 165px; box-sizing: content-box !important; }
95
+ .wpsbc-calendars-wrapper.wpsbc-legend-position-side .wpsbc-legend { position: absolute; top: 0; right: 0; width: 155px; margin-top: 0; background: #fff; box-sizing: border-box !important; }
96
+ .wpsbc-calendars-wrapper.wpsbc-legend-position-side .wpsbc-legend .wpsbc-legend-item { display: block; margin-right: 0; line-height: 18px; }
97
+
98
+ /******************************************************************************************/
99
+ /* Calendar: Legend Item Icon
100
+ /******************************************************************************************/
101
+ .wpsbc-calendars .wpsbc-legend-item-icon { position: absolute; z-index: 0; top: 0; left: 0; display: inline-block; width: 100%; height: 100%; box-sizing: border-box; overflow: hidden; }
102
+
103
+ .wpsbc-legend-item-icon[data-type="single"] div { display: none; }
104
+ .wpsbc-legend-item-icon[data-type="single"] div:first-of-type { position: absolute; top: 0; left: 0; display: block; width: 100%; height: 100%; }
105
+
106
+
107
+ .wpsbc-legend-item-icon[data-type="split"] div svg {position:absolute; width:100%; height:100%; left: 0;top: 0;}
108
+
109
+ .wpsbc-legend-item-icon[data-type="blank"] {background-color: #f3f3f3; height: 100%;}
110
+
111
+
112
+ /******************************************************************************************/
113
+ /* Calendar Columns
114
+ /******************************************************************************************/
115
+ .wpsbc-container[data-columns] .wpsbc-calendars .wpsbc-calendar { float: left; margin-left: 5px; margin-right: 5px; box-sizing: border-box !important; }
116
+ .wpsbc-container[data-columns] .wpsbc-calendars { margin-left: -5px; margin-right: -5px; }
117
+
118
+ .wpsbc-container[data-columns="6"] .wpsbc-calendars .wpsbc-calendar { width: calc( 16.6666% - 10px ); }
119
+ .wpsbc-container[data-columns="4"] .wpsbc-calendars .wpsbc-calendar { width: calc( 25% - 10px ); }
120
+ .wpsbc-container[data-columns="3"] .wpsbc-calendars .wpsbc-calendar { width: calc( 33.3333% - 10px ); }
121
+ .wpsbc-container[data-columns="2"] .wpsbc-calendars .wpsbc-calendar { width: calc( 50% - 10px ); }
122
+
123
+
124
+ /******************************************************************************************/
125
+ /* Media Queries
126
+ /******************************************************************************************/
127
+ @media screen and (max-width: 420px){
128
+ .wpsbc-calendars-wrapper.wpsbc-legend-position-side {padding-right: 0;}
129
+ .wpsbc-calendars-wrapper.wpsbc-legend-position-side .wpsbc-legend {position: relative; top: auto; right: auto; width: auto;}
130
+ .wpsbc-calendars-wrapper.wpsbc-legend-position-side .wpsbc-legend .wpsbc-legend-item {display: inline-block; margin-right: 8px;}
131
+ }
132
+
133
+ /* Datepicker */
134
+ .ui-datepicker.wpsbc-datepicker {padding: 0; margin: 0; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; border: 1px solid #ccc; border-top: none; -webkit-border-bottom-right-radius: 4px; -webkit-border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -moz-border-radius-bottomleft: 4px; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; -webkit-box-shadow: 0 3px 6px rgba(0, 0, 0, 0.075); box-shadow: 0 3px 6px rgba(0, 0, 0, 0.075); min-width: 17em; width: auto; z-index: 1000 !important; max-width: 360px; overflow: hidden; background:#fff;}
135
+ .ui-datepicker.wpsbc-datepicker * {padding: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0;}
136
+ .ui-datepicker.wpsbc-datepicker table {font-size: 13px; margin: 0; border: none; border-collapse: collapse; width: 100%;}
137
+ .ui-datepicker.wpsbc-datepicker .ui-widget-header,
138
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-header {background: #fff; border: none; color: #333; font-weight: normal;}
139
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-header .ui-state-hover {background: transparent; border-color: transparent; cursor: pointer;}
140
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-title {margin: 0; padding: 10px 0; color: #333; font-size: 14px; line-height: 14px; text-align: center;}
141
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-prev,
142
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-next {position: relative; top: 0; height: 34px; width: 34px;}
143
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-prev.ui-state-disabled,
144
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-next.ui-state-disabled {opacity:0.3;}
145
+ .ui-datepicker.wpsbc-datepicker .ui-state-hover.ui-datepicker-prev,
146
+ .ui-datepicker.wpsbc-datepicker .ui-state-hover.ui-datepicker-next {border: none;}
147
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-prev,
148
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-prev-hover {left: 0;}
149
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-next,
150
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-next-hover {right: 0;}
151
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-next span,
152
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-prev span {display: none;}
153
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-prev {float: left;}
154
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-next {float: right;}
155
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-prev:before,
156
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-next:before {font: normal 20px/34px 'dashicons'; padding-left: 7px; color: #333; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; width: 34px; height: 34px; position: relative; top: 8px;}
157
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-prev:before {content: '\f341';}
158
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-next:before {content: '\f345';}
159
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-prev-hover:before,
160
+ .ui-datepicker.wpsbc-datepicker .ui-datepicker-next-hover:before {opacity: 0.7;}
161
+ .ui-datepicker.wpsbc-datepicker select.ui-datepicker-month,
162
+ .ui-datepicker.wpsbc-datepicker select.ui-datepicker-year {width: 33%; border: 1px solid #ccc; padding: 5px;}
163
+ .ui-datepicker.wpsbc-datepicker select.ui-datepicker-month {border-right: none;}
164
+ .ui-datepicker.wpsbc-datepicker thead {color: #333; font-weight: 600;}
165
+ .ui-datepicker.wpsbc-datepicker th {padding: 12px 13px; border: 1px solid #f7f7f7; background: #f7f7f7;}
166
+ .ui-datepicker.wpsbc-datepicker td {padding: 0; border: 1px solid #f4f4f4;}
167
+ .ui-datepicker.wpsbc-datepicker td.ui-datepicker-other-month {background: #f9f9f9; opacity: 1;}
168
+ .ui-datepicker.wpsbc-datepicker tr:first-of-type td {border-top: 1px solid #f0f0f0;}
169
+ .ui-datepicker.wpsbc-datepicker td.ui-datepicker-current-day {background: #7f7f7f; }
170
+ .ui-datepicker.wpsbc-datepicker td.ui-datepicker-current-day a {color:#fff;}
171
+ .ui-datepicker.wpsbc-datepicker td .ui-state-default {background: transparent; border: none; text-align: center; text-decoration: none; width: auto; display: block; padding: 12px 13px; color: #333;}
172
+ .ui-datepicker.wpsbc-datepicker td .ui-state-default.ui-state-hover {box-shadow: 0 3px 6px rgba(0, 0, 0, 0.075); background:#aaa; color:#fff;}
173
+ .ui-datepicker.wpsbc-datepicker td.ui-state-disabled .ui-state-default {opacity: 0.3;}
assets/css/style-front-end.min.css ADDED
@@ -0,0 +1 @@
 
1
+ .wpsbc-container{position:relative;visibility:hidden}.wpsbc-container .wpsbc-calendars-wrapper{position:relative}.wpsbc-container .wpsbc-calendars:after{content:'';display:block;clear:both}.wpsbc-container .wpsbc-calendars .wpsbc-calendar table{table-layout:fixed;width:100%;border-collapse:collapse;margin:0!important;padding:0!important;border:0!important}.wpsbc-container .wpsbc-calendars .wpsbc-calendar table tbody,.wpsbc-container .wpsbc-calendars .wpsbc-calendar table thead,.wpsbc-container .wpsbc-calendars .wpsbc-calendar table tr{border:0!important;padding:0!important;margin:0!important;background:0 0}.wpsbc-container .wpsbc-calendars .wpsbc-calendar table tr td,.wpsbc-container .wpsbc-calendars .wpsbc-calendar table tr th{position:relative;border:0!important;padding:0!important;margin:0!important;vertical-align:middle;background:0 0;text-shadow:none;box-shadow:none;box-sizing:border-box}.wpsbc-container .wpsbc-calendars .wpsbc-calendar{position:relative;border:1px solid #f1f1f1;background:#fff;margin-bottom:1em}.wpsbc-container .wpsbc-calendars .wpsbc-calendar table thead tr th{padding-bottom:8px!important;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.wpsbc-container .wpsbc-calendars .wpsbc-calendar-wrapper{padding:8px 7px 7px 7px;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.wpsbc-container .wpsbc-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(255,255,255,.6)}.wpsbc-overlay-spinner{position:absolute;top:50%;left:50%;margin-top:-6px;margin-left:-35px;width:70px;text-align:center}.wpsbc-overlay-spinner>div{width:12px;height:12px;background-color:#333;border-radius:100%;display:inline-block;-webkit-animation:sk-bouncedelay 1.4s infinite ease-in-out both;animation:sk-bouncedelay 1.4s infinite ease-in-out both}.wpsbc-overlay-spinner .wpsbc-overlay-bounce1{-webkit-animation-delay:-.32s;animation-delay:-.32s}.wpsbc-overlay-spinner .wpsbc-overlay-bounce2{-webkit-animation-delay:-.16s;animation-delay:-.16s}@-webkit-keyframes sk-bouncedelay{0%,100%,80%{-webkit-transform:scale(0)}40%{-webkit-transform:scale(1)}}@keyframes sk-bouncedelay{0%,100%,80%{-webkit-transform:scale(0);transform:scale(0)}40%{-webkit-transform:scale(1);transform:scale(1)}}.wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header{text-align:center;padding:8px;background:#f5f5f5}.wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-calendar-header-navigation{position:relative;padding:0 30px;min-height:26px;font-size:13px;line-height:26px}.wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-calendar-header-navigation .wpsbc-next,.wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-calendar-header-navigation .wpsbc-prev{position:absolute;display:block;top:50%;height:26px;width:26px;line-height:26px;margin-top:-13px;text-decoration:none;border:0;box-shadow:none;color:#fff;background:#bdc3c7;border-radius:2px}.wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-calendar-header-navigation .wpsbc-prev{left:0}.wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-calendar-header-navigation .wpsbc-next{right:0}.wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-calendar-header-navigation .wpsbc-next .wpsbc-arrow,.wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-calendar-header-navigation .wpsbc-prev .wpsbc-arrow{position:absolute;top:50%;margin-top:-5px;display:inline-block;width:0;height:0;border-style:solid}.wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-calendar-header-navigation .wpsbc-next .wpsbc-arrow{right:50%;margin-right:-5px;border-width:5px 0 5px 8.7px;border-color:transparent transparent transparent #fff}.wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-calendar-header-navigation .wpsbc-prev .wpsbc-arrow{left:50%;margin-left:-5px;border-width:5px 8.7px 5px 0;border-color:transparent #fff transparent transparent}.wpsbc-container .wpsbc-calendars .wpsbc-calendar .wpsbc-calendar-header .wpsbc-select-container select{width:100%;height:26px;line-height:26px;margin:0;padding:0 10px;vertical-align:top;font-size:13px;box-sizing:border-box!important}.wpsbc-container .wpsbc-calendars .wpsbc-calendar table td .wpsbc-week-number{background:#e8e8e8;height:32px}.wpsbc-container .wpsbc-calendars .wpsbc-calendar table tr td,.wpsbc-container .wpsbc-calendars .wpsbc-calendar table tr th{text-align:center;padding:1px!important;font-size:13px}.wpsbc-container .wpsbc-calendars .wpsbc-calendar table th{font-weight:700;color:inherit}.wpsbc-container .wpsbc-calendars .wpsbc-calendar table tr td .wpsbc-date{position:relative;width:100%;height:100%}.wpsbc-container .wpsbc-calendars .wpsbc-calendar table tr td .wpsbc-date-inner{position:relative;width:100%;height:32px}.wpsbc-container .wpsbc-calendars .wpsbc-calendar table tr td .wpsbc-date-inner .wpsbc-date-number{position:absolute;z-index:0;top:0;left:0;width:100%;height:100%;font-size:13px}.wpsbc-container .wpsbc-calendars .wpsbc-calendar table tr td .wpsbc-date.wpsbc-gap{background:#f7f7f7}.wpsbc-legend{text-align:left;margin:1em 0;padding:8px 8px 0;border:1px solid #f1f1f1}.wpsbc-legend .wpsbc-legend-item{display:inline-block;margin-bottom:8px;margin-right:8px}.wpsbc-legend .wpsbc-legend-item-icon{position:relative;display:inline-block;width:24px;height:24px;vertical-align:middle;margin-right:5px;box-sizing:border-box;overflow:hidden}.wpsbc-legend .wpsbc-legend-item-name{display:inline-block;vertical-align:middle;font-size:13px}.wpsbc-calendars-wrapper.wpsbc-legend-position-side{padding-right:165px;box-sizing:content-box!important}.wpsbc-calendars-wrapper.wpsbc-legend-position-side .wpsbc-legend{position:absolute;top:0;right:0;width:155px;margin-top:0;background:#fff;box-sizing:border-box!important}.wpsbc-calendars-wrapper.wpsbc-legend-position-side .wpsbc-legend .wpsbc-legend-item{display:block;margin-right:0;line-height:18px}.wpsbc-calendars .wpsbc-legend-item-icon{position:absolute;z-index:0;top:0;left:0;display:inline-block;width:100%;height:100%;box-sizing:border-box;overflow:hidden}.wpsbc-legend-item-icon[data-type=single] div{display:none}.wpsbc-legend-item-icon[data-type=single] div:first-of-type{position:absolute;top:0;left:0;display:block;width:100%;height:100%}.wpsbc-legend-item-icon[data-type=split] div svg{position:absolute;width:100%;height:100%;left:0;top:0}.wpsbc-legend-item-icon[data-type=blank]{background-color:#f3f3f3;height:100%}.wpsbc-container[data-columns] .wpsbc-calendars .wpsbc-calendar{float:left;margin-left:5px;margin-right:5px;box-sizing:border-box!important}.wpsbc-container[data-columns] .wpsbc-calendars{margin-left:-5px;margin-right:-5px}.wpsbc-container[data-columns="6"] .wpsbc-calendars .wpsbc-calendar{width:calc(16.6666% - 10px)}.wpsbc-container[data-columns="4"] .wpsbc-calendars .wpsbc-calendar{width:calc(25% - 10px)}.wpsbc-container[data-columns="3"] .wpsbc-calendars .wpsbc-calendar{width:calc(33.3333% - 10px)}.wpsbc-container[data-columns="2"] .wpsbc-calendars .wpsbc-calendar{width:calc(50% - 10px)}@media screen and (max-width:420px){.wpsbc-calendars-wrapper.wpsbc-legend-position-side{padding-right:0}.wpsbc-calendars-wrapper.wpsbc-legend-position-side .wpsbc-legend{position:relative;top:auto;right:auto;width:auto}.wpsbc-calendars-wrapper.wpsbc-legend-position-side .wpsbc-legend .wpsbc-legend-item{display:inline-block;margin-right:8px}}.ui-datepicker.wpsbc-datepicker{padding:0;margin:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;border:1px solid #ccc;border-top:none;-webkit-border-bottom-right-radius:4px;-webkit-border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px;border-bottom-right-radius:4px;border-bottom-left-radius:4px;-webkit-box-shadow:0 3px 6px rgba(0,0,0,.075);box-shadow:0 3px 6px rgba(0,0,0,.075);min-width:17em;width:auto;z-index:1000!important;max-width:360px;overflow:hidden;background:#fff}.ui-datepicker.wpsbc-datepicker *{padding:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.ui-datepicker.wpsbc-datepicker table{font-size:13px;margin:0;border:none;border-collapse:collapse;width:100%}.ui-datepicker.wpsbc-datepicker .ui-datepicker-header,.ui-datepicker.wpsbc-datepicker .ui-widget-header{background:#fff;border:none;color:#333;font-weight:400}.ui-datepicker.wpsbc-datepicker .ui-datepicker-header .ui-state-hover{background:0 0;border-color:transparent;cursor:pointer}.ui-datepicker.wpsbc-datepicker .ui-datepicker-title{margin:0;padding:10px 0;color:#333;font-size:14px;line-height:14px;text-align:center}.ui-datepicker.wpsbc-datepicker .ui-datepicker-next,.ui-datepicker.wpsbc-datepicker .ui-datepicker-prev{position:relative;top:0;height:34px;width:34px}.ui-datepicker.wpsbc-datepicker .ui-datepicker-next.ui-state-disabled,.ui-datepicker.wpsbc-datepicker .ui-datepicker-prev.ui-state-disabled{opacity:.3}.ui-datepicker.wpsbc-datepicker .ui-state-hover.ui-datepicker-next,.ui-datepicker.wpsbc-datepicker .ui-state-hover.ui-datepicker-prev{border:none}.ui-datepicker.wpsbc-datepicker .ui-datepicker-prev,.ui-datepicker.wpsbc-datepicker .ui-datepicker-prev-hover{left:0}.ui-datepicker.wpsbc-datepicker .ui-datepicker-next,.ui-datepicker.wpsbc-datepicker .ui-datepicker-next-hover{right:0}.ui-datepicker.wpsbc-datepicker .ui-datepicker-next span,.ui-datepicker.wpsbc-datepicker .ui-datepicker-prev span{display:none}.ui-datepicker.wpsbc-datepicker .ui-datepicker-prev{float:left}.ui-datepicker.wpsbc-datepicker .ui-datepicker-next{float:right}.ui-datepicker.wpsbc-datepicker .ui-datepicker-next:before,.ui-datepicker.wpsbc-datepicker .ui-datepicker-prev:before{font:normal 20px/34px dashicons;padding-left:7px;color:#333;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;width:34px;height:34px;position:relative;top:8px}.ui-datepicker.wpsbc-datepicker .ui-datepicker-prev:before{content:'\f341'}.ui-datepicker.wpsbc-datepicker .ui-datepicker-next:before{content:'\f345'}.ui-datepicker.wpsbc-datepicker .ui-datepicker-next-hover:before,.ui-datepicker.wpsbc-datepicker .ui-datepicker-prev-hover:before{opacity:.7}.ui-datepicker.wpsbc-datepicker select.ui-datepicker-month,.ui-datepicker.wpsbc-datepicker select.ui-datepicker-year{width:33%;border:1px solid #ccc;padding:5px}.ui-datepicker.wpsbc-datepicker select.ui-datepicker-month{border-right:none}.ui-datepicker.wpsbc-datepicker thead{color:#333;font-weight:600}.ui-datepicker.wpsbc-datepicker th{padding:12px 13px;border:1px solid #f7f7f7;background:#f7f7f7}.ui-datepicker.wpsbc-datepicker td{padding:0;border:1px solid #f4f4f4}.ui-datepicker.wpsbc-datepicker td.ui-datepicker-other-month{background:#f9f9f9;opacity:1}.ui-datepicker.wpsbc-datepicker tr:first-of-type td{border-top:1px solid #f0f0f0}.ui-datepicker.wpsbc-datepicker td.ui-datepicker-current-day{background:#7f7f7f}.ui-datepicker.wpsbc-datepicker td.ui-datepicker-current-day a{color:#fff}.ui-datepicker.wpsbc-datepicker td .ui-state-default{background:0 0;border:none;text-align:center;text-decoration:none;width:auto;display:block;padding:12px 13px;color:#333}.ui-datepicker.wpsbc-datepicker td .ui-state-default.ui-state-hover{box-shadow:0 3px 6px rgba(0,0,0,.075);background:#aaa;color:#fff}.ui-datepicker.wpsbc-datepicker td.ui-state-disabled .ui-state-default{opacity:.3}
assets/img/date-icon.svg ADDED
@@ -0,0 +1 @@
 
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20" height="20" preserveAspectRatio="xMidYMid meet" viewBox="0 0 20 20" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);"><path d="M15 4h3v15H2V4h3V3c0-.41.15-.76.44-1.06.29-.29.65-.44 1.06-.44s.77.15 1.06.44c.29.3.44.65.44 1.06v1h4V3c0-.41.15-.76.44-1.06.29-.29.65-.44 1.06-.44s.77.15 1.06.44c.29.3.44.65.44 1.06v1zM6 3v2.5a.491.491 0 0 0 .5.5.491.491 0 0 0 .5-.5V3c0-.14-.05-.26-.15-.35-.09-.1-.21-.15-.35-.15s-.26.05-.35.15c-.1.09-.15.21-.15.35zm7 0v2.5c0 .14.05.26.14.36.1.09.22.14.36.14s.26-.05.36-.14c.09-.1.14-.22.14-.36V3a.491.491 0 0 0-.5-.5.491.491 0 0 0-.5.5zm4 15V8H3v10h14zM7 9v2H5V9h2zm2 0h2v2H9V9zm4 2V9h2v2h-2zm-6 1v2H5v-2h2zm2 0h2v2H9v-2zm4 2v-2h2v2h-2zm-6 1v2H5v-2h2zm4 2H9v-2h2v2zm4 0h-2v-2h2v2z" fill="#7f7f7f"/><rect x="0" y="0" width="20" height="20" fill="rgba(0, 0, 0, 0)" /></svg>
assets/img/flags/ad.png ADDED
Binary file
assets/img/flags/ae.png ADDED
Binary file
assets/img/flags/af.png ADDED
Binary file
assets/img/flags/ag.png ADDED
Binary file
assets/img/flags/ai.png ADDED
Binary file
assets/img/flags/al.png ADDED
Binary file
assets/img/flags/am.png ADDED
Binary file
assets/img/flags/an.png ADDED
Binary file
assets/img/flags/ao.png ADDED
Binary file
assets/img/flags/ar.png ADDED
Binary file
assets/img/flags/as.png ADDED
Binary file
assets/img/flags/at.png ADDED
Binary file
assets/img/flags/au.png ADDED
Binary file
assets/img/flags/aw.png ADDED
Binary file
assets/img/flags/ax.png ADDED
Binary file
assets/img/flags/az.png ADDED
Binary file
assets/img/flags/ba.png ADDED
Binary file
assets/img/flags/bb.png ADDED
Binary file
assets/img/flags/bd.png ADDED
Binary file
assets/img/flags/be.png ADDED
Binary file
assets/img/flags/bf.png ADDED
Binary file
assets/img/flags/bg.png ADDED
Binary file
assets/img/flags/bh.png ADDED
Binary file
assets/img/flags/bi.png ADDED
Binary file
assets/img/flags/bj.png ADDED
Binary file
assets/img/flags/bm.png ADDED
Binary file
assets/img/flags/bn.png ADDED
Binary file
assets/img/flags/bo.png ADDED
Binary file
assets/img/flags/br.png ADDED
Binary file
assets/img/flags/bs.png ADDED
Binary file
assets/img/flags/bt.png ADDED
Binary file
assets/img/flags/bv.png ADDED
Binary file
assets/img/flags/bw.png ADDED
Binary file
assets/img/flags/by.png ADDED
Binary file
assets/img/flags/bz.png ADDED
Binary file
assets/img/flags/ca.png ADDED
Binary file
assets/img/flags/cc.png ADDED
Binary file
assets/img/flags/cd.png ADDED
Binary file
assets/img/flags/cf.png ADDED
Binary file
assets/img/flags/cg.png ADDED
Binary file
assets/img/flags/ch.png ADDED
Binary file
assets/img/flags/ci.png ADDED
Binary file
assets/img/flags/ck.png ADDED
Binary file
assets/img/flags/cl.png ADDED
Binary file
assets/img/flags/cm.png ADDED
Binary file
assets/img/flags/cn.png ADDED
Binary file
assets/img/flags/co.png ADDED
Binary file
assets/img/flags/cr.png ADDED
Binary file
assets/img/flags/cs.png ADDED
Binary file
assets/img/flags/cu.png ADDED
Binary file
assets/img/flags/cv.png ADDED
Binary file
assets/img/flags/cx.png ADDED
Binary file
assets/img/flags/cy.png ADDED
Binary file
assets/img/flags/cz.png ADDED
Binary file
assets/img/flags/da.png ADDED
Binary file
assets/img/flags/de.png ADDED
Binary file
assets/img/flags/dj.png ADDED
Binary file
assets/img/flags/dm.png ADDED
Binary file
assets/img/flags/do.png ADDED
Binary file
assets/img/flags/dz.png ADDED
Binary file
assets/img/flags/ec.png ADDED
Binary file
assets/img/flags/ee.png ADDED
Binary file
assets/img/flags/eg.png ADDED
Binary file
assets/img/flags/eh.png ADDED
Binary file
assets/img/flags/el.png ADDED
Binary file
assets/img/flags/en.png ADDED
Binary file
assets/img/flags/england.png ADDED
Binary file
assets/img/flags/er.png ADDED
Binary file
assets/img/flags/es.png ADDED
Binary file
assets/img/flags/et.png ADDED
Binary file
assets/img/flags/eu.png ADDED
Binary file
assets/img/flags/europeanunion.png ADDED
Binary file
assets/img/flags/fam.png ADDED
Binary file
assets/img/flags/fi.png ADDED
Binary file
assets/img/flags/fj.png ADDED
Binary file
assets/img/flags/fk.png ADDED
Binary file
assets/img/flags/fm.png ADDED
Binary file
assets/img/flags/fo.png ADDED
Binary file
assets/img/flags/fr.png ADDED
Binary file
assets/img/flags/ga.png ADDED
Binary file
assets/img/flags/gb.png ADDED
Binary file
assets/img/flags/gd.png ADDED
Binary file
assets/img/flags/ge.png ADDED
Binary file
assets/img/flags/gf.png ADDED
Binary file
assets/img/flags/gh.png ADDED
Binary file
assets/img/flags/gi.png ADDED
Binary file
assets/img/flags/gl.png ADDED
Binary file
assets/img/flags/gm.png ADDED
Binary file
assets/img/flags/gn.png ADDED
Binary file
assets/img/flags/gp.png ADDED
Binary file
assets/img/flags/gq.png ADDED
Binary file
assets/img/flags/gs.png ADDED
Binary file
assets/img/flags/gt.png ADDED
Binary file
assets/img/flags/gu.png ADDED
Binary file
assets/img/flags/gw.png ADDED
Binary file
assets/img/flags/gy.png ADDED
Binary file
assets/img/flags/hk.png ADDED
Binary file
assets/img/flags/hm.png ADDED
Binary file
assets/img/flags/hn.png ADDED
Binary file
assets/img/flags/hr.png ADDED
Binary file
assets/img/flags/ht.png ADDED
Binary file
assets/img/flags/hu.png ADDED
Binary file
assets/img/flags/id.png ADDED
Binary file
assets/img/flags/ie.png ADDED
Binary file
assets/img/flags/il.png ADDED
Binary file
assets/img/flags/in.png ADDED
Binary file
assets/img/flags/io.png ADDED
Binary file
assets/img/flags/iq.png ADDED
Binary file
assets/img/flags/ir.png ADDED
Binary file
assets/img/flags/is.png ADDED
Binary file
assets/img/flags/it.png ADDED
Binary file
assets/img/flags/jm.png ADDED
Binary file
assets/img/flags/jo.png ADDED
Binary file
assets/img/flags/jp.png ADDED
Binary file
assets/img/flags/ke.png ADDED
Binary file
assets/img/flags/kg.png ADDED
Binary file
assets/img/flags/kh.png ADDED
Binary file
assets/img/flags/ki.png ADDED
Binary file
assets/img/flags/km.png ADDED
Binary file
assets/img/flags/kn.png ADDED
Binary file
assets/img/flags/kp.png ADDED
Binary file
assets/img/flags/kr.png ADDED
Binary file
assets/img/flags/kw.png ADDED
Binary file
assets/img/flags/ky.png ADDED
Binary file
assets/img/flags/kz.png ADDED
Binary file
assets/img/flags/la.png ADDED
Binary file
assets/img/flags/lb.png ADDED
Binary file
assets/img/flags/lc.png ADDED
Binary file
assets/img/flags/li.png ADDED
Binary file
assets/img/flags/lk.png ADDED
Binary file
assets/img/flags/lr.png ADDED
Binary file
assets/img/flags/ls.png ADDED
Binary file
assets/img/flags/lt.png ADDED
Binary file
assets/img/flags/lu.png ADDED
Binary file
assets/img/flags/lv.png ADDED
Binary file
assets/img/flags/ly.png ADDED
Binary file
assets/img/flags/ma.png ADDED
Binary file
assets/img/flags/mc.png ADDED
Binary file
assets/img/flags/md.png ADDED
Binary file
assets/img/flags/me.png ADDED
Binary file
assets/img/flags/mg.png ADDED
Binary file
assets/img/flags/mh.png ADDED
Binary file
assets/img/flags/mk.png ADDED
Binary file
assets/img/flags/ml.png ADDED
Binary file
assets/img/flags/mm.png ADDED
Binary file
assets/img/flags/mn.png ADDED
Binary file
assets/img/flags/mo.png ADDED
Binary file
assets/img/flags/mp.png ADDED
Binary file
assets/img/flags/mq.png ADDED
Binary file
assets/img/flags/mr.png ADDED
Binary file
assets/img/flags/ms.png ADDED
Binary file
assets/img/flags/mt.png ADDED
Binary file
assets/img/flags/mu.png ADDED
Binary file
assets/img/flags/mv.png ADDED
Binary file
assets/img/flags/mw.png ADDED
Binary file
assets/img/flags/mx.png ADDED
Binary file
assets/img/flags/my.png ADDED
Binary file
assets/img/flags/mz.png ADDED
Binary file
assets/img/flags/na.png ADDED
Binary file
assets/img/flags/nc.png ADDED
Binary file
assets/img/flags/ne.png ADDED
Binary file
assets/img/flags/nf.png ADDED
Binary file
assets/img/flags/ng.png ADDED
Binary file
assets/img/flags/ni.png ADDED
Binary file
assets/img/flags/nl.png ADDED
Binary file
assets/img/flags/no.png ADDED
Binary file
assets/img/flags/np.png ADDED
Binary file
assets/img/flags/nr.png ADDED
Binary file
assets/img/flags/nu.png ADDED
Binary file
assets/img/flags/nz.png ADDED
Binary file
assets/img/flags/om.png ADDED
Binary file
assets/img/flags/pa.png ADDED
Binary file
assets/img/flags/pe.png ADDED
Binary file
assets/img/flags/pf.png ADDED
Binary file
assets/img/flags/pg.png ADDED
Binary file
assets/img/flags/ph.png ADDED
Binary file
assets/img/flags/pk.png ADDED
Binary file
assets/img/flags/pl.png ADDED
Binary file
assets/img/flags/pm.png ADDED
Binary file
assets/img/flags/pn.png ADDED
Binary file
assets/img/flags/pr.png ADDED
Binary file
assets/img/flags/ps.png ADDED
Binary file
assets/img/flags/pt.png ADDED
Binary file
assets/img/flags/pw.png ADDED
Binary file
assets/img/flags/py.png ADDED
Binary file
assets/img/flags/qa.png ADDED
Binary file
assets/img/flags/re.png ADDED
Binary file
assets/img/flags/ro.png ADDED
Binary file
assets/img/flags/ru.png ADDED
Binary file
assets/img/flags/rw.png ADDED
Binary file
assets/img/flags/sa.png ADDED
Binary file
assets/img/flags/sb.png ADDED
Binary file
assets/img/flags/sc.png ADDED
Binary file
assets/img/flags/scotland.png ADDED
Binary file
assets/img/flags/sd.png ADDED
Binary file
assets/img/flags/se.png ADDED
Binary file
assets/img/flags/sg.png ADDED
Binary file
assets/img/flags/sh.png ADDED
Binary file
assets/img/flags/si.png ADDED
Binary file
assets/img/flags/sj.png ADDED
Binary file
assets/img/flags/sk.png ADDED
Binary file
assets/img/flags/sl.png ADDED
Binary file
assets/img/flags/sm.png ADDED
Binary file
assets/img/flags/sn.png ADDED
Binary file
assets/img/flags/so.png ADDED
Binary file
assets/img/flags/sr.png ADDED
Binary file
assets/img/flags/srn.png ADDED
Binary file
assets/img/flags/st.png ADDED
Binary file
assets/img/flags/sv.png ADDED
Binary file
assets/img/flags/sy.png ADDED
Binary file
assets/img/flags/sz.png ADDED
Binary file
assets/img/flags/tc.png ADDED
Binary file
assets/img/flags/td.png ADDED
Binary file
assets/img/flags/tf.png ADDED
Binary file
assets/img/flags/tg.png ADDED
Binary file
assets/img/flags/th.png ADDED
Binary file
assets/img/flags/tj.png ADDED
Binary file
assets/img/flags/tk.png ADDED
Binary file
assets/img/flags/tl.png ADDED
Binary file
assets/img/flags/tm.png ADDED
Binary file
assets/img/flags/tn.png ADDED
Binary file
assets/img/flags/to.png ADDED
Binary file
assets/img/flags/tr.png ADDED
Binary file
assets/img/flags/tt.png ADDED
Binary file
assets/img/flags/tv.png ADDED
Binary file
assets/img/flags/tw.png ADDED
Binary file
assets/img/flags/tz.png ADDED
Binary file
assets/img/flags/ua.png ADDED
Binary file
assets/img/flags/ug.png ADDED
Binary file
assets/img/flags/um.png ADDED
Binary file
assets/img/flags/us.png ADDED
Binary file
assets/img/flags/uy.png ADDED
Binary file
assets/img/flags/uz.png ADDED
Binary file
assets/img/flags/va.png ADDED
Binary file
assets/img/flags/vc.png ADDED
Binary file
assets/img/flags/ve.png ADDED
Binary file
assets/img/flags/vg.png ADDED
Binary file
assets/img/flags/vi.png ADDED
Binary file
assets/img/flags/vn.png ADDED
Binary file
assets/img/flags/vu.png ADDED
Binary file
assets/img/flags/wales.png ADDED
Binary file
assets/img/flags/wf.png ADDED
Binary file
assets/img/flags/ws.png ADDED
Binary file
assets/img/flags/ye.png ADDED
Binary file
assets/img/flags/yt.png ADDED
Binary file
assets/img/flags/za.png ADDED
Binary file
assets/img/flags/zm.png ADDED
Binary file
assets/img/flags/zw.png ADDED
Binary file
assets/js/script-admin-edit-calendar.js ADDED
@@ -0,0 +1,368 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * This is the object that stores all calendar data when editing a calendar
3
+ *
4
+ * Its structure is by year, month, day basis. Eg: wpsbc_calendar_data[2018][1][21]
5
+ *
6
+ */
7
+ var wpsbc_calendar_data = {};
8
+
9
+ jQuery(function ($) {
10
+
11
+ /**
12
+ * Retrieves an array with the query arguments found in a given string
13
+ *
14
+ */
15
+ function get_query_args(string) {
16
+
17
+ var query_arr = string.replace('?', '').split('&');
18
+ var query_params = [];
19
+
20
+ for (var q = 0, q_query_arr = query_arr.length; q < q_query_arr; q++) {
21
+
22
+ var q_arr = query_arr[q].split('=');
23
+ query_params[q_arr[0]] = q_arr[1];
24
+
25
+ }
26
+
27
+ return query_params;
28
+
29
+ }
30
+
31
+ /**
32
+ * Resizes the calendar to always have square dates
33
+ *
34
+ */
35
+ function resize_calendar($calendars_wrapper) {
36
+
37
+ var td_width = $calendars_wrapper.find('td').first().width();
38
+
39
+ $calendars_wrapper.find('td .wpsbc-date-inner, td .wpsbc-week-number').css('height', td_width + 'px');
40
+ $calendars_wrapper.find('td .wpsbc-date-inner, td .wpsbc-week-number').css('line-height', td_width + 'px');
41
+
42
+ $calendars_wrapper.css('visibility', 'visible');
43
+
44
+ }
45
+
46
+
47
+ /**
48
+ * Refreshed the output of the calendar with the given data
49
+ *
50
+ */
51
+ function refresh_calendar($calendar_container, current_year, current_month) {
52
+
53
+ var $calendar_container = $calendar_container;
54
+ var $calendar_editor = $('#wpsbc-calendar-editor');
55
+
56
+ if ($calendar_container.hasClass('wpsbc-is-loading'))
57
+ return false;
58
+
59
+ /**
60
+ * Prepare the calendar data
61
+ *
62
+ */
63
+ var data = $calendar_container.data();
64
+
65
+ data['action'] = 'wpsbc_refresh_calendar_editor';
66
+ data['current_year'] = current_year;
67
+ data['current_month'] = current_month;
68
+ data['calendar_data'] = JSON.stringify(wpsbc_calendar_data);
69
+
70
+ /**
71
+ * Add loading animation
72
+ *
73
+ */
74
+ $calendar_container.find('.wpsbc-calendar').append('<div class="wpsbc-overlay"><div class="wpsbc-overlay-spinner"><div class="wpsbc-overlay-bounce1"></div><div class="wpsbc-overlay-bounce2"></div><div class="wpsbc-overlay-bounce3"></div></div></div>');
75
+ $calendar_container.addClass('wpsbc-is-loading');
76
+ $calendar_container.find('select').attr('disabled', true);
77
+
78
+ $('#wpsbc-calendar-editor').append('<div class="wpsbc-overlay"><div class="wpsbc-overlay-spinner"><div class="wpsbc-overlay-bounce1"></div><div class="wpsbc-overlay-bounce2"></div><div class="wpsbc-overlay-bounce3"></div></div></div>');
79
+
80
+ /**
81
+ * Make the request
82
+ *
83
+ */
84
+ $.post(ajaxurl, data, function (response) {
85
+
86
+ response = JSON.parse(response);
87
+
88
+ $calendar_container.replaceWith(response.calendar);
89
+ $calendar_editor.replaceWith(response.calendar_editor);
90
+
91
+ resize_calendar($('.wpsbc-container[data-id="' + data['id'] + '"]'));
92
+ refresh_calendar_dates();
93
+
94
+ });
95
+
96
+ }
97
+
98
+
99
+ /**
100
+ * Updates the calendar legend items icons of each date from the
101
+ * data found in wpsbc_calendar_data for the legend items
102
+ *
103
+ */
104
+ function refresh_calendar_dates() {
105
+
106
+ var $calendar_container = $('.wpsbc-container');
107
+
108
+ var year = $calendar_container.data('current_year');
109
+ var month = $calendar_container.data('current_month');
110
+
111
+ if (typeof wpsbc_calendar_data[year] == 'undefined')
112
+ return false;
113
+
114
+ if (typeof wpsbc_calendar_data[year][month] == 'undefined')
115
+ return false;
116
+
117
+ for (day in wpsbc_calendar_data[year][month]) {
118
+
119
+ if (typeof wpsbc_calendar_data[year][month][day] == 'undefined')
120
+ continue;
121
+
122
+ if (typeof wpsbc_calendar_data[year][month][day]['legend_item_id'] == 'undefined')
123
+ continue;
124
+
125
+ var $legend_item_selector = $('.wpsbc-calendar-date-legend-item select[data-year="' + year + '"][data-month="' + month + '"][data-day="' + day + '"]');
126
+
127
+ $calendar_container.find('[data-year="' + year + '"][data-month="' + month + '"][data-day="' + day + '"] .wpsbc-legend-item-icon')
128
+ .attr('class', 'wpsbc-legend-item-icon wpsbc-legend-item-icon-' + $legend_item_selector.val())
129
+ .attr('data-type', $legend_item_selector.find('option:selected').data('type'));
130
+
131
+ }
132
+
133
+ }
134
+
135
+
136
+ /**
137
+ * Callback function that is triggered on changes made to input, textarea, select, etc.
138
+ * fields from the calendar editor
139
+ *
140
+ */
141
+ function calendar_editor_field_change($input) {
142
+
143
+ /**
144
+ * Exit if the input does not have the needed data values
145
+ *
146
+ */
147
+ if (typeof $input.data('year') == 'undefined')
148
+ return false;
149
+
150
+ if (typeof $input.data('month') == 'undefined')
151
+ return false;
152
+
153
+ if (typeof $input.data('day') == 'undefined')
154
+ return false;
155
+
156
+ if (typeof $input.data('name') == 'undefined')
157
+ return false;
158
+
159
+ /**
160
+ * Sanitize the data values and set them as variables
161
+ *
162
+ */
163
+ var year = parseInt($input.data('year'));
164
+ var month = parseInt($input.data('month'));
165
+ var day = parseInt($input.data('day'));
166
+ var name = $input.data('name');
167
+
168
+ // Update data
169
+ update_calendar_data(year, month, day, name, $input.val());
170
+
171
+ }
172
+
173
+
174
+ /**
175
+ * Updates the calendar data object with new data from the provided field
176
+ *
177
+ */
178
+ function update_calendar_data(year, month, day, field_name, field_value) {
179
+
180
+ if (typeof field_name == 'undefined')
181
+ return false;
182
+
183
+ if (typeof field_value == 'undefined')
184
+ return false;
185
+
186
+ /**
187
+ * Create the object for each date layer if needed
188
+ *
189
+ */
190
+ if (typeof wpsbc_calendar_data[year] == 'undefined')
191
+ wpsbc_calendar_data[year] = {};
192
+
193
+ if (typeof wpsbc_calendar_data[year][month] == 'undefined')
194
+ wpsbc_calendar_data[year][month] = {};
195
+
196
+ if (typeof wpsbc_calendar_data[year][month][day] == 'undefined')
197
+ wpsbc_calendar_data[year][month][day] = {};
198
+
199
+ /**
200
+ * Set the value for the current date
201
+ *
202
+ */
203
+ wpsbc_calendar_data[year][month][day][field_name] = field_value;
204
+
205
+ }
206
+
207
+
208
+ /**
209
+ * Resize the calendars on page load
210
+ *
211
+ */
212
+ $('.wpsbc-container').each(function () {
213
+ resize_calendar($(this));
214
+ });
215
+
216
+ /**
217
+ * Resize the calendars on page resize
218
+ *
219
+ */
220
+ $(window).on('resize', function () {
221
+ $('.wpsbc-container').each(function () {
222
+ resize_calendar($(this));
223
+ });
224
+ });
225
+
226
+
227
+ /**
228
+ * Handles the navigation of the Previous button
229
+ *
230
+ */
231
+ $(document).on('click', '.wpsbc-container .wpsbc-prev', function (e) {
232
+
233
+ e.preventDefault();
234
+
235
+ // Set container
236
+ var $container = $(this).closest('.wpsbc-container');
237
+
238
+ // Set the current year and month that are displayed in the calendar
239
+ var current_month = $container.data('current_month');
240
+ var current_year = $container.data('current_year');
241
+
242
+
243
+ current_month -= 1;
244
+
245
+ if (current_month < 1) {
246
+ current_month = 12;
247
+ current_year -= 1;
248
+ }
249
+
250
+ refresh_calendar($container, current_year, current_month);
251
+
252
+ });
253
+
254
+ /**
255
+ * Handles the navigation of the Next button
256
+ *
257
+ */
258
+ $(document).on('click', '.wpsbc-container .wpsbc-next', function (e) {
259
+
260
+ e.preventDefault();
261
+
262
+ // Set container
263
+ var $container = $(this).closest('.wpsbc-container');
264
+
265
+ // Set the current year and month that are displayed in the calendar
266
+ var current_month = $container.data('current_month');
267
+ var current_year = $container.data('current_year');
268
+
269
+ current_month += 1;
270
+
271
+ if (current_month > 12) {
272
+ current_month = 1;
273
+ current_year += 1;
274
+ }
275
+
276
+ refresh_calendar($container, current_year, current_month);
277
+
278
+ });
279
+
280
+ /**
281
+ * Handles the navigation of the Month Selector
282
+ *
283
+ */
284
+ $(document).on('change', '.wpsbc-container .wpsbc-select-container select', function () {
285
+
286
+ // Set container
287
+ var $container = $(this).closest('.wpsbc-container');
288
+
289
+ var date = new Date($(this).val() * 1000);
290
+
291
+ var year = date.getFullYear();
292
+ var month = date.getMonth() + 1;
293
+
294
+ refresh_calendar($container, year, month);
295
+
296
+ });
297
+
298
+
299
+ /**
300
+ * Updates the calendar wpsbc_calendar_data object when doing changes in the
301
+ * calendar editor
302
+ *
303
+ */
304
+ $(document).on('change', '#wpsbc-calendar-editor select', function () {
305
+
306
+ calendar_editor_field_change($(this));
307
+
308
+ });
309
+
310
+ $(document).on('keyup', '#wpsbc-calendar-editor input, #wpsbc-calendar-editor textarea', function () {
311
+
312
+ calendar_editor_field_change($(this));
313
+
314
+ });
315
+
316
+
317
+ /**
318
+ * Handle legend item select change in edit caledndar screen
319
+ *
320
+ */
321
+ $(document).on('change', '.wpsbc-calendar-date-legend-item select', function () {
322
+
323
+ $(this).siblings('div').find('.wpsbc-legend-item-icon')
324
+ .attr('class', 'wpsbc-legend-item-icon wpsbc-legend-item-icon-' + $(this).val())
325
+ .attr('data-type', $(this).find('option:selected').data('type'));
326
+
327
+ $(this).closest('.wpsbc-calendar-date-legend-item')
328
+ .attr('class', 'wpsbc-calendar-date-legend-item wpsbc-calendar-date-legend-item-' + $(this).val());
329
+
330
+ refresh_calendar_dates();
331
+
332
+ });
333
+
334
+ /**
335
+ * Handles the saving of the calendar by making an AJAX call to the server
336
+ * with the wpsbc_calendar_data.
337
+ *
338
+ * Upon success refreshes the page and adds a success message
339
+ *
340
+ */
341
+ $(document).on('click', '.wpsbc-save-calendar', function (e) {
342
+
343
+ e.preventDefault();
344
+
345
+ var form_data = $(this).closest('form').serialize();
346
+
347
+ var data = {
348
+ action: 'wpsbc_save_calendar_data',
349
+ form_data: form_data,
350
+ calendar_data: JSON.stringify(wpsbc_calendar_data),
351
+ current_year: $('.wpsbc-container').data('current_year'),
352
+ current_month: $('.wpsbc-container').data('current_month')
353
+ }
354
+
355
+ // Disable all buttons and show loading spinner
356
+ $('.wpsbc-wrap-edit-calendar input, .wpsbc-wrap-edit-calendar select, .wpsbc-wrap-edit-calendar textarea').attr('disabled', true);
357
+ $(this).siblings('.wpsbc-save-calendar-spinner').css('visibility', 'visible');
358
+
359
+ $.post(ajaxurl, data, function (response) {
360
+
361
+ if (typeof response != 'undefined')
362
+ window.location.replace(response);
363
+
364
+ });
365
+
366
+ });
367
+
368
+ });
assets/js/script-admin-uninstaller.js ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( function($) {
2
+
3
+ /**
4
+ * Handle the Uninstall Plugin button
5
+ *
6
+ */
7
+ $(document).on( 'click', '#wpsbc-uninstaller-button', function(e) {
8
+
9
+ if( ! $('#wpsbc-uninstaller-confirmation').is(':visible') ) {
10
+
11
+ e.preventDefault();
12
+
13
+ $('#wpsbc-uninstaller-confirmation').fadeIn( 300 );
14
+ $(this).attr( 'disabled', true );
15
+
16
+ return false;
17
+
18
+ } else {
19
+
20
+ if( ! confirm( 'Are you sure you wish to remove all WP Simple Booking Calendar related data?' ) )
21
+ return false;
22
+
23
+ }
24
+
25
+ });
26
+
27
+ /**
28
+ * Track the value of the confirmation field to match the word REMOVE
29
+ * before letting the user click the Uninstall button
30
+ *
31
+ */
32
+ $(document).on( 'keyup', function() {
33
+
34
+ if( ! $('#wpsbc-uninstaller-confirmation').is(':visible') )
35
+ return false;
36
+
37
+ if( $('#wpsbc-uninstaller-confirmation-field').val() == 'REMOVE' )
38
+ $('#wpsbc-uninstaller-button').attr( 'disabled', false );
39
+ else
40
+ $('#wpsbc-uninstaller-button').attr( 'disabled', true );
41
+
42
+ });
43
+
44
+ });
assets/js/script-admin-upgrader.js ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( function($) {
2
+
3
+ var loading_step = 0;
4
+ var total_steps = 4;
5
+
6
+ /**
7
+ * Handles the incrementation of the loading step and
8
+ *
9
+ */
10
+ function increment_loading_step() {
11
+
12
+ loading_step++;
13
+
14
+ // Increment the data-step
15
+ $('#wpsbc-upgrader-loading-bar-wrapper').attr( 'data-step', loading_step );
16
+
17
+ $('#wpsbc-upgrader-message-step-' + loading_step ).fadeOut( 200, function() {
18
+ $('#wpsbc-upgrader-message-doing-step-' + loading_step ).fadeIn( 200 );
19
+ } );
20
+
21
+
22
+ $('#wpsbc-upgrader-message-doing-step-' + ( loading_step - 1 ) ).fadeOut( 200, function() {
23
+ $('#wpsbc-upgrader-message-step-' + ( loading_step - 1 ) ).fadeIn( 200 );
24
+ } );
25
+
26
+ }
27
+
28
+
29
+ /**
30
+ * Makes consecutive AJAX calls to migrate the old plugin data into the new format
31
+ *
32
+ */
33
+ function migrate_data( token ) {
34
+
35
+ if( typeof token == 'undefined' )
36
+ return false;
37
+
38
+ increment_loading_step();
39
+
40
+ /**
41
+ * Finish migration if loading steps have been done
42
+ *
43
+ */
44
+ if( loading_step - 1 == total_steps ) {
45
+
46
+ $('#wpsbc-upgrader-button-start-upgrade').remove();
47
+ $('#wpsbc-upgrader-button-continue').css( 'display', 'block' );
48
+
49
+ return false;
50
+
51
+ }
52
+
53
+ /**
54
+ * Process migration step
55
+ *
56
+ */
57
+ var action = '';
58
+
59
+ if( loading_step == 1 )
60
+ action = 'wpsbc_action_ajax_migrate_calendars';
61
+
62
+ if( loading_step == 2 )
63
+ action = 'wpsbc_action_ajax_migrate_bookings';
64
+
65
+ if( loading_step == 3 )
66
+ action = 'wpsbc_action_ajax_migrate_general_settings';
67
+
68
+ if( loading_step == 4 )
69
+ action = 'wpsbc_action_ajax_migrate_finishing_up';
70
+
71
+ if( action == '' )
72
+ return false;
73
+
74
+ var data = {
75
+ action : action,
76
+ token : token
77
+ }
78
+
79
+ $.post( ajaxurl, data, function( response ) {
80
+
81
+ response = JSON.parse( response );
82
+
83
+ if( response.success ) {
84
+
85
+ setTimeout( function() {
86
+
87
+ migrate_data( token );
88
+
89
+ }, 1000 );
90
+
91
+ }
92
+
93
+ });
94
+
95
+ }
96
+
97
+
98
+ /**
99
+ * Starts the upgrade procedure
100
+ *
101
+ */
102
+ $(document).on( 'click', '#wpsbc-upgrader-button-start-upgrade', function(e) {
103
+
104
+ e.preventDefault();
105
+
106
+ $this = $(this);
107
+
108
+ if( $this.hasClass('disabled') )
109
+ return false;
110
+
111
+ // Disable the button
112
+ $this.addClass('disabled');
113
+
114
+ // Change the text of the button
115
+ $this.find('span:first-of-type').hide();
116
+ $this.find('span:nth-of-type(2)').show();
117
+
118
+ // Add and remove the spinner next to the button
119
+ $this.siblings( '.spinner' ).css( 'visibility', 'visible' );
120
+
121
+ setTimeout( function() {
122
+ $this.siblings( '.spinner' ).css( 'visibility', 'hidden' );
123
+ }, 1200 );
124
+
125
+
126
+ // Remove the copy text and show the loading bar
127
+ $('#wpsbc-upgrader-content-inner').delay( 1200 ).fadeOut( 300, function() {
128
+
129
+ $('#wpsbc-upgrader-loading-bar-wrapper').fadeIn( 300 );
130
+
131
+ });
132
+
133
+ // Remove the skip upgrader message
134
+ $('#wpsbc-upgrader-skip-wrapper').fadeOut( 300 );
135
+
136
+ // Get the token
137
+ var token = $('#wpsbc_token').val();
138
+
139
+ // Run the data migrator
140
+ setTimeout( function() {
141
+
142
+ migrate_data( token );
143
+
144
+ }, 1200 );
145
+
146
+ });
147
+
148
+ });
assets/js/script-admin.js ADDED
@@ -0,0 +1,363 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function ($) {
2
+
3
+ /*
4
+ * Strips one query argument from a given URL string
5
+ *
6
+ */
7
+ function remove_query_arg(key, sourceURL) {
8
+
9
+ var rtn = sourceURL.split("?")[0],
10
+ param,
11
+ params_arr = [],
12
+ queryString = (sourceURL.indexOf("?") !== -1) ? sourceURL.split("?")[1] : "";
13
+
14
+ if (queryString !== "") {
15
+ params_arr = queryString.split("&");
16
+ for (var i = params_arr.length - 1; i >= 0; i -= 1) {
17
+ param = params_arr[i].split("=")[0];
18
+ if (param === key) {
19
+ params_arr.splice(i, 1);
20
+ }
21
+ }
22
+
23
+ rtn = rtn + "?" + params_arr.join("&");
24
+
25
+ }
26
+
27
+ if (rtn.split("?")[1] == "") {
28
+ rtn = rtn.split("?")[0];
29
+ }
30
+
31
+ return rtn;
32
+ }
33
+
34
+
35
+ /*
36
+ * Adds an argument name, value pair to a given URL string
37
+ *
38
+ */
39
+ function add_query_arg(key, value, sourceURL) {
40
+
41
+ return sourceURL + '&' + key + '=' + value;
42
+
43
+ }
44
+
45
+
46
+ /**
47
+ * Initialize colorpicker
48
+ *
49
+ */
50
+ $('.wpsbc-colorpicker').wpColorPicker();
51
+
52
+ /**
53
+ * Initialize Chosen
54
+ *
55
+ */
56
+ if (typeof $.fn.chosen != 'undefined') {
57
+
58
+ $('.wpsbc-chosen').chosen();
59
+
60
+ }
61
+
62
+ /**
63
+ * Links that have the inactive class should do nothing
64
+ *
65
+ */
66
+ $(document).on('click', 'a.wpsbc-inactive, input[type=submit].wpsbc-inactive', function () {
67
+
68
+ return false;
69
+
70
+ });
71
+
72
+ /**
73
+ * Initialize the sortable function on the Calendar Legend List Table
74
+ *
75
+ */
76
+ $('table.wpsbc_legend_items tbody').sortable({
77
+ handle: '.wpsbc-move-legend-item',
78
+ containment: '#wpcontent',
79
+ placeholder: 'wpsbc-list-table-sort-placeholder',
80
+ helper: function (e, tr) {
81
+ var $originals = tr.children();
82
+ var $helper = tr.clone();
83
+
84
+ $helper.children().each(function (index) {
85
+ // Set helper cell sizes to match the original sizes
86
+ $(this).width($originals.eq(index).width());
87
+ });
88
+
89
+ return $helper;
90
+ },
91
+ update: function (e, ui) {
92
+
93
+ var legend_item_ids = [];
94
+
95
+ $('table.wpsbc_legend_items tbody tr .wpsbc-move-legend-item').each(function () {
96
+ legend_item_ids.push($(this).data('id'));
97
+ })
98
+
99
+ var data = {
100
+ action: 'wpsbc_sort_legend_items',
101
+ token: $('[name="wpsbc_token"]').val(),
102
+ calendar_id: $('[name="calendar_id"]').val(),
103
+ legend_item_ids: legend_item_ids
104
+ }
105
+
106
+ // Add table wrapper and overlay
107
+ $('table.wpsbc_legend_items').wrap('<div class="wpsbc-wp-list-table-wrapper"></div>');
108
+ $('table.wpsbc_legend_items').closest('.wpsbc-wp-list-table-wrapper').append('<div class="wpsbc-overlay"><div class="spinner"></div></div>');
109
+
110
+ // Make sort ajax call
111
+ $.post(ajaxurl, data, function (response) {
112
+
113
+ response = JSON.parse(response);
114
+
115
+ if (!response.success) {
116
+
117
+ window.location.replace(response.redirect_url_error);
118
+
119
+ } else {
120
+
121
+ // Remove table wrapper and overlay
122
+ $('table.wpsbc_legend_items').siblings('.wpsbc-overlay').remove();
123
+ $('table.wpsbc_legend_items').unwrap('.wpsbc-wp-list-table-wrapper');
124
+
125
+ }
126
+
127
+
128
+ });
129
+
130
+ }
131
+ });
132
+
133
+ $('table.wpsbc_legend_items tbody').disableSelection();
134
+
135
+
136
+ /**
137
+ * Handle show/hide of the second color option for Legend Item add/edit screen
138
+ *
139
+ */
140
+ $(document).on('change', 'select[name="legend_item_type"]', function () {
141
+
142
+ if ($(this).val() == 'single')
143
+ $('#wpsbc-legend-item-color-2').closest('.wp-picker-container').hide();
144
+ else
145
+ $('#wpsbc-legend-item-color-2').closest('.wp-picker-container').show();
146
+
147
+ });
148
+
149
+ $(document).ready(function () {
150
+
151
+ if ($('select[name="legend_item_type"]').length > 0)
152
+ $('select[name="legend_item_type"]').trigger('change');
153
+
154
+ });
155
+
156
+ /**
157
+ * Tab Navigation
158
+ *
159
+ */
160
+ $('.wpsbc-nav-tab').on('click', function (e) {
161
+ e.preventDefault();
162
+
163
+ // Nav Tab activation
164
+ $('.wpsbc-nav-tab').removeClass('wpsbc-active').removeClass('nav-tab-active');
165
+ $(this).addClass('wpsbc-active').addClass('nav-tab-active');
166
+
167
+ // Show tab
168
+ $('.wpsbc-tab').removeClass('wpsbc-active');
169
+
170
+ var nav_tab = $(this).attr('data-tab');
171
+ $('.wpsbc-tab[data-tab="' + nav_tab + '"]').addClass('wpsbc-active');
172
+ $('input[name=active_tab]').val(nav_tab);
173
+
174
+ // Change http referrer
175
+ $_wp_http_referer = $('input[name=_wp_http_referer]');
176
+
177
+ var _wp_http_referer = $_wp_http_referer.val();
178
+ _wp_http_referer = remove_query_arg('tab', _wp_http_referer);
179
+ $_wp_http_referer.val(add_query_arg('tab', $(this).attr('data-tab'), _wp_http_referer));
180
+
181
+ });
182
+
183
+ /**
184
+ * Calendar Title Translations Toggle
185
+ */
186
+ $(".wrap.wpsbc-wrap-edit-calendar #titlediv .titlewrap-toggle").click(function (e) {
187
+ e.preventDefault();
188
+ $(this).toggleClass('open');
189
+ $(".titlewrap-translations").slideToggle();
190
+
191
+ });
192
+
193
+ /**
194
+ * Toggle settings translations
195
+ *
196
+ */
197
+ $(".wpsbc-wrap").on('click', '.wpsbc-settings-field-show-translations', function (e) {
198
+ e.preventDefault();
199
+ $(this).parents('.wpsbc-settings-field-translation-wrapper').find(".wpsbc-settings-field-translations").slideToggle();
200
+ $(this).toggleClass('open');
201
+ })
202
+
203
+
204
+ /**
205
+ * Modifies the modal inner height to permit the scrollbar to function properly
206
+ *
207
+ */
208
+ $(window).resize(function () {
209
+
210
+ $('.wpsbc-modal-inner').outerHeight($('.wpsbc-modal.wpsbc-active').outerHeight() - $('.wpsbc-modal.wpsbc-active .wpsbc-modal-header').outerHeight() - $('.wpsbc-modal.wpsbc-active .wpsbc-modal-nav-tab-wrapper').outerHeight());
211
+
212
+ });
213
+
214
+ /**
215
+ * Close modal window
216
+ *
217
+ */
218
+ $(document).on('click', '.wpsbc-modal-close', function (e) {
219
+
220
+ e.preventDefault();
221
+
222
+ $(this).closest('.wpsbc-modal').find('.wpsbc-modal-inner').scrollTop(0);
223
+
224
+ $(this).closest('.wpsbc-modal').removeClass('wpsbc-active');
225
+ $(this).closest('.wpsbc-modal').siblings('.wpsbc-modal-overlay').removeClass('wpsbc-active');
226
+
227
+ $(window).resize();
228
+
229
+ });
230
+
231
+ /**
232
+ * Close modal on clicking the modal overlay
233
+ *
234
+ */
235
+ $(document).on('click', '.wpsbc-modal-overlay.wpsbc-active', function (e) {
236
+
237
+ $('.wpsbc-modal.wpsbc-active').find('.wpsbc-modal-close').click();
238
+
239
+ });
240
+
241
+ /**
242
+ * Open Shortcode Generator modal
243
+ *
244
+ */
245
+ $(document).on('click', '#wpsbc-shortcode-generator-button', function (e) {
246
+
247
+ e.preventDefault();
248
+
249
+ $('#wpsbc-modal-add-calendar-shortcode, #wpsbc-modal-add-calendar-shortcode-overlay').addClass('wpsbc-active');
250
+
251
+ $(window).resize();
252
+
253
+ $('.wpsbc-modal.wpsbc-active').click();
254
+
255
+ });
256
+
257
+ /**
258
+ * Builds the shortcode for the Single Calendar and inserts it in the WordPress text editor
259
+ *
260
+ */
261
+ $(document).on('click', '#wpsbc-insert-shortcode-single-calendar', function (e) {
262
+
263
+ e.preventDefault();
264
+
265
+ // Begin shortcode
266
+ var shortcode = '[wpsbc ';
267
+
268
+ $('#wpsbc-modal-add-calendar-shortcode.wpsbc-active .wpsbc-shortcode-generator-field-calendar').each(function () {
269
+
270
+ shortcode += $(this).data('attribute') + '="' + $(this).val() + '" ';
271
+
272
+ });
273
+
274
+ // End shortcode
275
+ shortcode = shortcode.trim();
276
+ shortcode += ']';
277
+
278
+ window.send_to_editor(shortcode);
279
+
280
+ $(this).closest('.wpsbc-modal').find('.wpsbc-modal-close').first().trigger('click');
281
+
282
+ });
283
+
284
+
285
+ /**
286
+ * Register and deregister website functionality
287
+ *
288
+ */
289
+ $(document).on('click', '#wpsbc-register-website-button, #wpsbc-deregister-website-button', function (e) {
290
+
291
+ e.preventDefault();
292
+
293
+ window.location = add_query_arg('serial_key', $('[name="serial_key"]').val(), $(this).attr('href'));
294
+
295
+ });
296
+
297
+ $(document).on('click', '#wpsbc-check-for-updates-button', function (e) {
298
+
299
+ if ($(this).attr('disabled') == 'disabled')
300
+ e.preventDefault();
301
+
302
+ });
303
+
304
+ /**
305
+ * Move the calendar from the sidebar to the main content and back in the calendar edit screen
306
+ * when resizing the window
307
+ *
308
+ */
309
+ $(window).on('resize', function () {
310
+
311
+ // Move the calendar from the sidebar to the main content
312
+ if ($(window).innerWidth() < 850) {
313
+
314
+ $('.wpsbc-container').closest('.postbox').detach().prependTo('#post-body-content');
315
+
316
+ } else {
317
+
318
+ $('.wpsbc-container').closest('.postbox').detach().prependTo('#postbox-container-1');
319
+
320
+ }
321
+
322
+ });
323
+
324
+ $(window).trigger('resize');
325
+
326
+
327
+ /**
328
+ * iCalendar Export warning message
329
+ *
330
+ */
331
+ $("#ical-export-legend-items").change(function () {
332
+ if ($(this).find('option:selected').length > 1) {
333
+ $(this).siblings('.wpsbc-warning').show();
334
+ } else {
335
+ $(this).siblings('.wpsbc-warning').hide();
336
+ }
337
+ }).trigger('change');
338
+
339
+ jQuery(window).on('load resize', function () {
340
+ jQuery(".wpsbc-wrap-upgrade-to-premium ul li").wpsbc_adjust_height();
341
+ })
342
+
343
+ });
344
+
345
+ /***** Adjust Height Function *****/
346
+ jQuery.fn.wpsbc_adjust_height = function () {
347
+ var maxHeightFound = 0;
348
+ this.css('min-height', '1px');
349
+
350
+ if (this.is('a')) {
351
+ this.removeClass('loaded');
352
+ };
353
+
354
+ this.each(function () {
355
+ if (jQuery(this).outerHeight() > maxHeightFound) {
356
+ maxHeightFound = jQuery(this).outerHeight();
357
+ }
358
+ });
359
+ this.css('min-height', maxHeightFound);
360
+ if (this.is('a')) {
361
+ this.addClass('loaded');
362
+ };
363
+ };
assets/js/script-front-end.js ADDED
@@ -0,0 +1,283 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function ($) {
2
+
3
+ /**
4
+ * Resizes the calendar to always have square dates
5
+ *
6
+ */
7
+ function resize_calendar($calendars_wrapper) {
8
+
9
+ /**
10
+ * Set variables
11
+ *
12
+ */
13
+ var $months_wrapper = $calendars_wrapper.find('.wpsbc-calendars-wrapper');
14
+ var $months_wrapper_width = $calendars_wrapper.find('.wpsbc-calendars');
15
+ var calendar_min_width = $calendars_wrapper.data('min_width');
16
+ var calendar_max_width = $calendars_wrapper.data('max_width');
17
+
18
+ var $month_inner = $calendars_wrapper.find('.wpsbc-calendar-wrapper');
19
+
20
+ /**
21
+ * Set the calendar months min and max width from the data attributes
22
+ *
23
+ */
24
+ if ($calendars_wrapper.data('min_width') > 0)
25
+ $calendars_wrapper.find('.wpsbc-calendar').css('min-width', calendar_min_width);
26
+
27
+ if ($calendars_wrapper.data('max_width') > 0)
28
+ $calendars_wrapper.find('.wpsbc-calendar').css('max-width', calendar_max_width)
29
+
30
+
31
+ /**
32
+ * Set the column count
33
+ *
34
+ */
35
+ var column_count = 0;
36
+
37
+ if ($months_wrapper_width.width() < calendar_min_width * 2)
38
+ column_count = 1;
39
+
40
+ else if ($months_wrapper_width.width() < calendar_min_width * 3)
41
+ column_count = 2;
42
+
43
+ else if ($months_wrapper_width.width() < calendar_min_width * 4)
44
+ column_count = 3;
45
+
46
+ else if ($months_wrapper_width.width() < calendar_min_width * 6)
47
+ column_count = 4;
48
+
49
+ else
50
+ column_count = 6;
51
+
52
+
53
+ // Adjust for when there are fewer months in a calendar than columns
54
+ if ($calendars_wrapper.find('.wpsbc-calendar').length <= column_count)
55
+ column_count = $calendars_wrapper.find('.wpsbc-calendar').length;
56
+
57
+ // Set column count
58
+ $calendars_wrapper.attr('data-columns', column_count);
59
+
60
+
61
+ /**
62
+ * Set the max-width of the calendars container that has a side legend
63
+ *
64
+ */
65
+ if ($months_wrapper.hasClass('wpsbc-legend-position-side')) {
66
+
67
+ $months_wrapper.css('max-width', 'none');
68
+ $months_wrapper.css('max-width', $calendars_wrapper.find('.wpsbc-calendar').first().outerWidth(true) * column_count);
69
+
70
+ }
71
+
72
+
73
+ /**
74
+ * Handle the height of each date
75
+ *
76
+ */
77
+ var td_width = $calendars_wrapper.find('td').first().width();
78
+
79
+ $calendars_wrapper.find('td .wpsbc-date-inner, td .wpsbc-week-number').css('height', Math.ceil(td_width) + 1 + 'px');
80
+ $calendars_wrapper.find('td .wpsbc-date-inner, td .wpsbc-week-number').css('line-height', Math.ceil(td_width) + 1 + 'px');
81
+
82
+ var th_height = $calendars_wrapper.find('th').css('height', 'auto').first().height();
83
+ $calendars_wrapper.find('th').css('height', Math.ceil(th_height) + 1 + 'px');
84
+
85
+ /**
86
+ * Set calendar month height
87
+ *
88
+ */
89
+ var calendar_month_height = 0;
90
+
91
+ $month_inner.css('min-height', '1px');
92
+
93
+ $month_inner.each(function () {
94
+
95
+ if ($(this).height() >= calendar_month_height)
96
+ calendar_month_height = $(this).height();
97
+
98
+ });
99
+
100
+ $month_inner.css('min-height', Math.ceil(calendar_month_height) + 'px');
101
+
102
+ /**
103
+ * Show the calendars
104
+ *
105
+ */
106
+ $calendars_wrapper.css('visibility', 'visible');
107
+
108
+ }
109
+
110
+
111
+ /**
112
+ * Refreshed the output of the calendar with the given data
113
+ *
114
+ */
115
+ function refresh_calendar($calendar_container, current_year, current_month) {
116
+
117
+ var $calendar_container = $calendar_container;
118
+
119
+ if ($calendar_container.hasClass('wpsbc-is-loading'))
120
+ return false;
121
+
122
+ /**
123
+ * Prepare the calendar data
124
+ *
125
+ */
126
+ var data = $calendar_container.data();
127
+
128
+ data['action'] = 'wpsbc_refresh_calendar';
129
+ data['current_year'] = current_year;
130
+ data['current_month'] = current_month;
131
+
132
+ /**
133
+ * Add loading animation
134
+ *
135
+ */
136
+ $calendar_container.find('.wpsbc-calendar').append('<div class="wpsbc-overlay"><div class="wpsbc-overlay-spinner"><div class="wpsbc-overlay-bounce1"></div><div class="wpsbc-overlay-bounce2"></div><div class="wpsbc-overlay-bounce3"></div></div></div>');
137
+ $calendar_container.addClass('wpsbc-is-loading');
138
+ $calendar_container.find('select').attr('disabled', true);
139
+
140
+
141
+ /**
142
+ * Make the request
143
+ *
144
+ */
145
+ $.post(wpsbc.ajax_url, data, function (response) {
146
+
147
+ $calendar_container.replaceWith(response);
148
+
149
+ $('.wpsbc-container').each(function () {
150
+ resize_calendar($(this));
151
+ });
152
+
153
+ });
154
+
155
+ }
156
+
157
+ /**
158
+ * Resize the calendars on page load
159
+ *
160
+ */
161
+ $('.wpsbc-container').each(function () {
162
+ resize_calendar($(this));
163
+ });
164
+
165
+ /**
166
+ * Resize the calendars on page resize
167
+ *
168
+ */
169
+ $(window).on('resize', function () {
170
+
171
+ $('.wpsbc-container').each(function () {
172
+ resize_calendar($(this));
173
+ });
174
+
175
+ });
176
+
177
+
178
+ /**
179
+ * Handles the navigation of the Previous button
180
+ *
181
+ */
182
+ $(document).on('click', '.wpsbc-container .wpsbc-prev', function (e) {
183
+
184
+ e.preventDefault();
185
+
186
+ // Set container
187
+ var $container = $(this).closest('.wpsbc-container');
188
+
189
+ // Set the current year and month that are displayed in the calendar
190
+ var current_month = $container.data('current_month');
191
+ var current_year = $container.data('current_year');
192
+
193
+ current_month -= 1;
194
+
195
+ if (current_month < 1) {
196
+ current_month = 12;
197
+ current_year -= 1;
198
+ }
199
+
200
+
201
+ refresh_calendar($container, current_year, current_month);
202
+
203
+ });
204
+
205
+ /**
206
+ * Handles the navigation of the Next button
207
+ *
208
+ */
209
+ $(document).on('click', '.wpsbc-container .wpsbc-next', function (e) {
210
+
211
+ e.preventDefault();
212
+
213
+ // Set container
214
+ var $container = $(this).closest('.wpsbc-container');
215
+
216
+ // Set the current year and month that are displayed in the calendar
217
+ var current_month = $container.data('current_month');
218
+ var current_year = $container.data('current_year');
219
+
220
+ current_month += 1;
221
+
222
+ if (current_month > 12) {
223
+ current_month = 1;
224
+ current_year += 1;
225
+ }
226
+
227
+ refresh_calendar($container, current_year, current_month);
228
+
229
+ });
230
+
231
+ /**
232
+ * Handles the navigation of the Month Selector for the Single Calendar
233
+ *
234
+ */
235
+ $(document).on('change', '.wpsbc-container .wpsbc-select-container select', function () {
236
+
237
+ // Set container
238
+ var $container = $(this).closest('.wpsbc-container');
239
+
240
+ var date = new Date($(this).val() * 1000);
241
+
242
+ var year = date.getFullYear();
243
+ var month = date.getMonth() + 1;
244
+
245
+ refresh_calendar($container, year, month);
246
+
247
+ });
248
+
249
+
250
+
251
+ /**
252
+ * Check if a calendar is hidden and wait for it to become visible.
253
+ * When it does, trigger a window resize to properly display the calendar.
254
+ *
255
+ */
256
+ var wpsbc_frontend_visible_calendars = $('.wpsbc-container:visible').length;
257
+ function wpsbc_check_if_calendar_is_visible() {
258
+
259
+ // If no calendars are embedded, exit
260
+ if (!$('.wpsbc-container').length)
261
+ return false;
262
+
263
+ // If a calendar just became visible, trigger a resize
264
+ if (wpsbc_frontend_visible_calendars != $('.wpsbc-container:visible').length) {
265
+ $(window).trigger('resize');
266
+ wpsbc_frontend_visible_calendars = $('.wpsbc-container:visible').length;
267
+ }
268
+
269
+ // If all calendars are visible, exit
270
+ if ($('.wpsbc-container:visible').length == $('.wpsbc-container').length) {
271
+ return false;
272
+ }
273
+
274
+ // Keep checking every 250ms
275
+ setTimeout(wpsbc_check_if_calendar_is_visible, 250);
276
+
277
+ }
278
+ // Manually start the first check
279
+ wpsbc_check_if_calendar_is_visible();
280
+
281
+ });
282
+
283
+
assets/js/script-front-end.min.js ADDED
@@ -0,0 +1 @@
 
1
+ jQuery((function($){function resize_calendar($calendars_wrapper){var $months_wrapper=$calendars_wrapper.find(".wpsbc-calendars-wrapper"),$months_wrapper_width=$calendars_wrapper.find(".wpsbc-calendars"),calendar_min_width=$calendars_wrapper.data("min_width"),calendar_max_width=$calendars_wrapper.data("max_width"),$month_inner=$calendars_wrapper.find(".wpsbc-calendar-wrapper");$calendars_wrapper.data("min_width")>0&&$calendars_wrapper.find(".wpsbc-calendar").css("min-width",calendar_min_width),$calendars_wrapper.data("max_width")>0&&$calendars_wrapper.find(".wpsbc-calendar").css("max-width",calendar_max_width);var column_count=0;column_count=$months_wrapper_width.width()<2*calendar_min_width?1:$months_wrapper_width.width()<3*calendar_min_width?2:$months_wrapper_width.width()<4*calendar_min_width?3:$months_wrapper_width.width()<6*calendar_min_width?4:6,$calendars_wrapper.find(".wpsbc-calendar").length<=column_count&&(column_count=$calendars_wrapper.find(".wpsbc-calendar").length),$calendars_wrapper.attr("data-columns",column_count),$months_wrapper.hasClass("wpsbc-legend-position-side")&&($months_wrapper.css("max-width","none"),$months_wrapper.css("max-width",$calendars_wrapper.find(".wpsbc-calendar").first().outerWidth(!0)*column_count));var td_width=$calendars_wrapper.find("td").first().width();$calendars_wrapper.find("td .wpsbc-date-inner, td .wpsbc-week-number").css("height",Math.ceil(td_width)+1+"px"),$calendars_wrapper.find("td .wpsbc-date-inner, td .wpsbc-week-number").css("line-height",Math.ceil(td_width)+1+"px");var th_height=$calendars_wrapper.find("th").css("height","auto").first().height();$calendars_wrapper.find("th").css("height",Math.ceil(th_height)+1+"px");var calendar_month_height=0;$month_inner.css("min-height","1px"),$month_inner.each((function(){$(this).height()>=calendar_month_height&&(calendar_month_height=$(this).height())})),$month_inner.css("min-height",Math.ceil(calendar_month_height)+"px"),$calendars_wrapper.css("visibility","visible")}function refresh_calendar($calendar_container,current_year,current_month){var $calendar_container;if(($calendar_container=$calendar_container).hasClass("wpsbc-is-loading"))return!1;var data=$calendar_container.data();data.action="wpsbc_refresh_calendar",data.current_year=current_year,data.current_month=current_month,$calendar_container.find(".wpsbc-calendar").append('<div class="wpsbc-overlay"><div class="wpsbc-overlay-spinner"><div class="wpsbc-overlay-bounce1"></div><div class="wpsbc-overlay-bounce2"></div><div class="wpsbc-overlay-bounce3"></div></div></div>'),$calendar_container.addClass("wpsbc-is-loading"),$calendar_container.find("select").attr("disabled",!0),$.post(wpsbc.ajax_url,data,(function(response){$calendar_container.replaceWith(response),$(".wpsbc-container").each((function(){resize_calendar($(this))}))}))}$(".wpsbc-container").each((function(){resize_calendar($(this))})),$(window).on("resize",(function(){$(".wpsbc-container").each((function(){resize_calendar($(this))}))})),$(document).on("click",".wpsbc-container .wpsbc-prev",(function(e){e.preventDefault();var $container=$(this).closest(".wpsbc-container"),current_month=$container.data("current_month"),current_year=$container.data("current_year");(current_month-=1)<1&&(current_month=12,current_year-=1),refresh_calendar($container,current_year,current_month)})),$(document).on("click",".wpsbc-container .wpsbc-next",(function(e){e.preventDefault();var $container=$(this).closest(".wpsbc-container"),current_month=$container.data("current_month"),current_year=$container.data("current_year");(current_month+=1)>12&&(current_month=1,current_year+=1),refresh_calendar($container,current_year,current_month)})),$(document).on("change",".wpsbc-container .wpsbc-select-container select",(function(){var $container=$(this).closest(".wpsbc-container"),date=new Date(1e3*$(this).val()),year,month;refresh_calendar($container,date.getFullYear(),date.getMonth()+1)}));var wpsbc_frontend_visible_calendars=$(".wpsbc-container:visible").length;function wpsbc_check_if_calendar_is_visible(){return!!$(".wpsbc-container").length&&(wpsbc_frontend_visible_calendars!=$(".wpsbc-container:visible").length&&($(window).trigger("resize"),wpsbc_frontend_visible_calendars=$(".wpsbc-container:visible").length),$(".wpsbc-container:visible").length!=$(".wpsbc-container").length&&void setTimeout(wpsbc_check_if_calendar_is_visible,250))}wpsbc_check_if_calendar_is_visible()}));
assets/libs/chosen/chosen-sprite.png ADDED
Binary file
assets/libs/chosen/chosen-sprite@2x.png ADDED
Binary file
assets/libs/chosen/chosen.css ADDED
@@ -0,0 +1,448 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ Chosen, a Select Box Enhancer for jQuery and Prototype
3
+ by Patrick Filler for Harvest, http://getharvest.com
4
+
5
+ Version 1.5.1
6
+ Full source at https://github.com/harvesthq/chosen
7
+ Copyright (c) 2011-2016 Harvest http://getharvest.com
8
+
9
+ MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
10
+ This file is generated by `grunt build`, do not edit it by hand.
11
+ */
12
+
13
+ /* @group Base */
14
+ .chosen-container {
15
+ position: relative;
16
+ display: inline-block;
17
+ vertical-align: middle;
18
+ font-size: 13px;
19
+ -webkit-user-select: none;
20
+ -moz-user-select: none;
21
+ user-select: none;
22
+ }
23
+ .chosen-container * {
24
+ -webkit-box-sizing: border-box;
25
+ -moz-box-sizing: border-box;
26
+ box-sizing: border-box;
27
+ }
28
+ .chosen-container .chosen-drop {
29
+ position: absolute;
30
+ top: 100%;
31
+ left: -9999px;
32
+ z-index: 1010;
33
+ width: 100%;
34
+ border: 1px solid #aaa;
35
+ border-top: 0;
36
+ background: #fff;
37
+ box-shadow: 0 4px 5px rgba(0, 0, 0, 0.15);
38
+ }
39
+ .chosen-container.chosen-with-drop .chosen-drop {
40
+ left: 0;
41
+ }
42
+ .chosen-container a {
43
+ cursor: pointer;
44
+ }
45
+ .chosen-container .search-choice .group-name, .chosen-container .chosen-single .group-name {
46
+ margin-right: 4px;
47
+ overflow: hidden;
48
+ white-space: nowrap;
49
+ text-overflow: ellipsis;
50
+ font-weight: normal;
51
+ color: #999999;
52
+ }
53
+ .chosen-container .search-choice .group-name:after, .chosen-container .chosen-single .group-name:after {
54
+ content: ":";
55
+ padding-left: 2px;
56
+ vertical-align: top;
57
+ }
58
+
59
+ /* @end */
60
+ /* @group Single Chosen */
61
+ .chosen-container-single .chosen-single {
62
+ position: relative;
63
+ display: block;
64
+ overflow: hidden;
65
+ padding: 0 0 0 8px;
66
+ height: 25px;
67
+ border: 1px solid #aaa;
68
+ border-radius: 5px;
69
+ background-color: #fff;
70
+ background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #ffffff), color-stop(50%, #f6f6f6), color-stop(52%, #eeeeee), color-stop(100%, #f4f4f4));
71
+ background: -webkit-linear-gradient(#ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
72
+ background: -moz-linear-gradient(#ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
73
+ background: -o-linear-gradient(#ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
74
+ background: linear-gradient(#ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%);
75
+ background-clip: padding-box;
76
+ box-shadow: 0 0 3px white inset, 0 1px 1px rgba(0, 0, 0, 0.1);
77
+ color: #444;
78
+ text-decoration: none;
79
+ white-space: nowrap;
80
+ line-height: 24px;
81
+ }
82
+ .chosen-container-single .chosen-default {
83
+ color: #999;
84
+ }
85
+ .chosen-container-single .chosen-single span {
86
+ display: block;
87
+ overflow: hidden;
88
+ margin-right: 26px;
89
+ text-overflow: ellipsis;
90
+ white-space: nowrap;
91
+ }
92
+ .chosen-container-single .chosen-single-with-deselect span {
93
+ margin-right: 38px;
94
+ }
95
+ .chosen-container-single .chosen-single abbr {
96
+ position: absolute;
97
+ top: 6px;
98
+ right: 26px;
99
+ display: block;
100
+ width: 12px;
101
+ height: 12px;
102
+ background: url('chosen-sprite.png') -42px 1px no-repeat;
103
+ font-size: 1px;
104
+ }
105
+ .chosen-container-single .chosen-single abbr:hover {
106
+ background-position: -42px -10px;
107
+ }
108
+ .chosen-container-single.chosen-disabled .chosen-single abbr:hover {
109
+ background-position: -42px -10px;
110
+ }
111
+ .chosen-container-single .chosen-single div {
112
+ position: absolute;
113
+ top: 0;
114
+ right: 0;
115
+ display: block;
116
+ width: 18px;
117
+ height: 100%;
118
+ }
119
+ .chosen-container-single .chosen-single div b {
120
+ display: block;
121
+ width: 100%;
122
+ height: 100%;
123
+ background: url('chosen-sprite.png') no-repeat 0px 2px;
124
+ }
125
+ .chosen-container-single .chosen-search {
126
+ position: relative;
127
+ z-index: 1010;
128
+ margin: 0;
129
+ padding: 3px 4px;
130
+ white-space: nowrap;
131
+ }
132
+ .chosen-container-single .chosen-search input[type="text"] {
133
+ margin: 1px 0;
134
+ padding: 4px 20px 4px 5px;
135
+ width: 100%;
136
+ height: auto;
137
+ outline: 0;
138
+ border: 1px solid #aaa;
139
+ background: white url('chosen-sprite.png') no-repeat 100% -20px;
140
+ background: url('chosen-sprite.png') no-repeat 100% -20px;
141
+ font-size: 1em;
142
+ font-family: sans-serif;
143
+ line-height: normal;
144
+ border-radius: 0;
145
+ }
146
+ .chosen-container-single .chosen-drop {
147
+ margin-top: -1px;
148
+ border-radius: 0 0 4px 4px;
149
+ background-clip: padding-box;
150
+ }
151
+ .chosen-container-single.chosen-container-single-nosearch .chosen-search {
152
+ position: absolute;
153
+ left: -9999px;
154
+ }
155
+
156
+ /* @end */
157
+ /* @group Results */
158
+ .chosen-container .chosen-results {
159
+ color: #444;
160
+ position: relative;
161
+ overflow-x: hidden;
162
+ overflow-y: auto;
163
+ margin: 0 4px 4px 0;
164
+ padding: 0 0 0 4px;
165
+ max-height: 240px;
166
+ -webkit-overflow-scrolling: touch;
167
+ }
168
+ .chosen-container .chosen-results li {
169
+ display: none;
170
+ margin: 0;
171
+ padding: 5px 6px;
172
+ list-style: none;
173
+ line-height: 15px;
174
+ word-wrap: break-word;
175
+ -webkit-touch-callout: none;
176
+ }
177
+ .chosen-container .chosen-results li.active-result {
178
+ display: list-item;
179
+ cursor: pointer;
180
+ }
181
+ .chosen-container .chosen-results li.disabled-result {
182
+ display: list-item;
183
+ color: #ccc;
184
+ cursor: default;
185
+ }
186
+ .chosen-container .chosen-results li.highlighted {
187
+ background-color: #3875d7;
188
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #3875d7), color-stop(90%, #2a62bc));
189
+ background-image: -webkit-linear-gradient(#3875d7 20%, #2a62bc 90%);
190
+ background-image: -moz-linear-gradient(#3875d7 20%, #2a62bc 90%);
191
+ background-image: -o-linear-gradient(#3875d7 20%, #2a62bc 90%);
192
+ background-image: linear-gradient(#3875d7 20%, #2a62bc 90%);
193
+ color: #fff;
194
+ }
195
+ .chosen-container .chosen-results li.no-results {
196
+ color: #777;
197
+ display: list-item;
198
+ background: #f4f4f4;
199
+ }
200
+ .chosen-container .chosen-results li.group-result {
201
+ display: list-item;
202
+ font-weight: bold;
203
+ cursor: default;
204
+ }
205
+ .chosen-container .chosen-results li.group-option {
206
+ padding-left: 15px;
207
+ }
208
+ .chosen-container .chosen-results li em {
209
+ font-style: normal;
210
+ text-decoration: underline;
211
+ }
212
+
213
+ /* @end */
214
+ /* @group Multi Chosen */
215
+ .chosen-container-multi .chosen-choices {
216
+ position: relative;
217
+ overflow: hidden;
218
+ margin: 0;
219
+ padding: 0 5px;
220
+ width: 100%;
221
+ height: auto !important;
222
+ height: 1%;
223
+ border: 1px solid #aaa;
224
+ background-color: #fff;
225
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff));
226
+ background-image: -webkit-linear-gradient(#eeeeee 1%, #ffffff 15%);
227
+ background-image: -moz-linear-gradient(#eeeeee 1%, #ffffff 15%);
228
+ background-image: -o-linear-gradient(#eeeeee 1%, #ffffff 15%);
229
+ background-image: linear-gradient(#eeeeee 1%, #ffffff 15%);
230
+ cursor: text;
231
+ }
232
+ .chosen-container-multi .chosen-choices li {
233
+ float: left;
234
+ list-style: none;
235
+ }
236
+ .chosen-container-multi .chosen-choices li.search-field {
237
+ margin: 0;
238
+ padding: 0;
239
+ white-space: nowrap;
240
+ }
241
+ .chosen-container-multi .chosen-choices li.search-field input[type="text"] {
242
+ margin: 1px 0;
243
+ padding: 0;
244
+ height: 25px;
245
+ outline: 0;
246
+ border: 0 !important;
247
+ background: transparent !important;
248
+ box-shadow: none;
249
+ color: #999;
250
+ font-size: 100%;
251
+ font-family: sans-serif;
252
+ line-height: normal;
253
+ border-radius: 0;
254
+ }
255
+ .chosen-container-multi .chosen-choices li.search-choice {
256
+ position: relative;
257
+ margin: 3px 5px 3px 0;
258
+ padding: 3px 20px 3px 5px;
259
+ border: 1px solid #aaa;
260
+ max-width: 100%;
261
+ border-radius: 3px;
262
+ background-color: #eeeeee;
263
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
264
+ background-image: -webkit-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
265
+ background-image: -moz-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
266
+ background-image: -o-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
267
+ background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
268
+ background-size: 100% 19px;
269
+ background-repeat: repeat-x;
270
+ background-clip: padding-box;
271
+ box-shadow: 0 0 2px white inset, 0 1px 0 rgba(0, 0, 0, 0.05);
272
+ color: #333;
273
+ line-height: 13px;
274
+ cursor: default;
275
+ }
276
+ .chosen-container-multi .chosen-choices li.search-choice span {
277
+ word-wrap: break-word;
278
+ }
279
+ .chosen-container-multi .chosen-choices li.search-choice .search-choice-close {
280
+ position: absolute;
281
+ top: 4px;
282
+ right: 3px;
283
+ display: block;
284
+ width: 12px;
285
+ height: 12px;
286
+ background: url('chosen-sprite.png') -42px 1px no-repeat;
287
+ font-size: 1px;
288
+ }
289
+ .chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover {
290
+ background-position: -42px -10px;
291
+ }
292
+ .chosen-container-multi .chosen-choices li.search-choice-disabled {
293
+ padding-right: 5px;
294
+ border: 1px solid #ccc;
295
+ background-color: #e4e4e4;
296
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee));
297
+ background-image: -webkit-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
298
+ background-image: -moz-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
299
+ background-image: -o-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
300
+ background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%);
301
+ color: #666;
302
+ }
303
+ .chosen-container-multi .chosen-choices li.search-choice-focus {
304
+ background: #d4d4d4;
305
+ }
306
+ .chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close {
307
+ background-position: -42px -10px;
308
+ }
309
+ .chosen-container-multi .chosen-results {
310
+ margin: 0;
311
+ padding: 0;
312
+ }
313
+ .chosen-container-multi .chosen-drop .result-selected {
314
+ display: list-item;
315
+ color: #ccc;
316
+ cursor: default;
317
+ }
318
+
319
+ /* @end */
320
+ /* @group Active */
321
+ .chosen-container-active .chosen-single {
322
+ border: 1px solid #5897fb;
323
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
324
+ }
325
+ .chosen-container-active.chosen-with-drop .chosen-single {
326
+ border: 1px solid #aaa;
327
+ -moz-border-radius-bottomright: 0;
328
+ border-bottom-right-radius: 0;
329
+ -moz-border-radius-bottomleft: 0;
330
+ border-bottom-left-radius: 0;
331
+ background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #eeeeee), color-stop(80%, #ffffff));
332
+ background-image: -webkit-linear-gradient(#eeeeee 20%, #ffffff 80%);
333
+ background-image: -moz-linear-gradient(#eeeeee 20%, #ffffff 80%);
334
+ background-image: -o-linear-gradient(#eeeeee 20%, #ffffff 80%);
335
+ background-image: linear-gradient(#eeeeee 20%, #ffffff 80%);
336
+ box-shadow: 0 1px 0 #fff inset;
337
+ }
338
+ .chosen-container-active.chosen-with-drop .chosen-single div {
339
+ border-left: none;
340
+ background: transparent;
341
+ }
342
+ .chosen-container-active.chosen-with-drop .chosen-single div b {
343
+ background-position: -18px 2px;
344
+ }
345
+ .chosen-container-active .chosen-choices {
346
+ border: 1px solid #5897fb;
347
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
348
+ }
349
+ .chosen-container-active .chosen-choices li.search-field input[type="text"] {
350
+ color: #222 !important;
351
+ }
352
+
353
+ /* @end */
354
+ /* @group Disabled Support */
355
+ .chosen-disabled {
356
+ opacity: 0.5 !important;
357
+ cursor: default;
358
+ }
359
+ .chosen-disabled .chosen-single {
360
+ cursor: default;
361
+ }
362
+ .chosen-disabled .chosen-choices .search-choice .search-choice-close {
363
+ cursor: default;
364
+ }
365
+
366
+ /* @end */
367
+ /* @group Right to Left */
368
+ .chosen-rtl {
369
+ text-align: right;
370
+ }
371
+ .chosen-rtl .chosen-single {
372
+ overflow: visible;
373
+ padding: 0 8px 0 0;
374
+ }
375
+ .chosen-rtl .chosen-single span {
376
+ margin-right: 0;
377
+ margin-left: 26px;
378
+ direction: rtl;
379
+ }
380
+ .chosen-rtl .chosen-single-with-deselect span {
381
+ margin-left: 38px;
382
+ }
383
+ .chosen-rtl .chosen-single div {
384
+ right: auto;
385
+ left: 3px;
386
+ }
387
+ .chosen-rtl .chosen-single abbr {
388
+ right: auto;
389
+ left: 26px;
390
+ }
391
+ .chosen-rtl .chosen-choices li {
392
+ float: right;
393
+ }
394
+ .chosen-rtl .chosen-choices li.search-field input[type="text"] {
395
+ direction: rtl;
396
+ }
397
+ .chosen-rtl .chosen-choices li.search-choice {
398
+ margin: 3px 5px 3px 0;
399
+ padding: 3px 5px 3px 19px;
400
+ }
401
+ .chosen-rtl .chosen-choices li.search-choice .search-choice-close {
402
+ right: auto;
403
+ left: 4px;
404
+ }
405
+ .chosen-rtl.chosen-container-single-nosearch .chosen-search,
406
+ .chosen-rtl .chosen-drop {
407
+ left: 9999px;
408
+ }
409
+ .chosen-rtl.chosen-container-single .chosen-results {
410
+ margin: 0 0 4px 4px;
411
+ padding: 0 4px 0 0;
412
+ }
413
+ .chosen-rtl .chosen-results li.group-option {
414
+ padding-right: 15px;
415
+ padding-left: 0;
416
+ }
417
+ .chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div {
418
+ border-right: none;
419
+ }
420
+ .chosen-rtl .chosen-search input[type="text"] {
421
+ padding: 4px 5px 4px 20px;
422
+ background: white url('chosen-sprite.png') no-repeat -30px -20px;
423
+ background: url('chosen-sprite.png') no-repeat -30px -20px;
424
+ direction: rtl;
425
+ }
426
+ .chosen-rtl.chosen-container-single .chosen-single div b {
427
+ background-position: 6px 2px;
428
+ }
429
+ .chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b {
430
+ background-position: -12px 2px;
431
+ }
432
+
433
+ /* @end */
434
+ /* @group Retina compatibility */
435
+ @media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 144dpi), only screen and (min-resolution: 1.5dppx) {
436
+ .chosen-rtl .chosen-search input[type="text"],
437
+ .chosen-container-single .chosen-single abbr,
438
+ .chosen-container-single .chosen-single div b,
439
+ .chosen-container-single .chosen-search input[type="text"],
440
+ .chosen-container-multi .chosen-choices .search-choice .search-choice-close,
441
+ .chosen-container .chosen-results-scroll-down span,
442
+ .chosen-container .chosen-results-scroll-up span {
443
+ background-image: url('chosen-sprite@2x.png') !important;
444
+ background-size: 52px 37px !important;
445
+ background-repeat: no-repeat !important;
446
+ }
447
+ }
448
+ /* @end */
assets/libs/chosen/chosen.jquery.min.js ADDED
@@ -0,0 +1,2 @@
 
 
1
+ /* Chosen v1.5.1 | (c) 2011-2016 by Harvest | MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md */
2
+ (function(){var a,AbstractChosen,Chosen,SelectParser,b,c={}.hasOwnProperty,d=function(a,b){function d(){this.constructor=a}for(var e in b)c.call(b,e)&&(a[e]=b[e]);return d.prototype=b.prototype,a.prototype=new d,a.__super__=b.prototype,a};SelectParser=function(){function SelectParser(){this.options_index=0,this.parsed=[]}return SelectParser.prototype.add_node=function(a){return"OPTGROUP"===a.nodeName.toUpperCase()?this.add_group(a):this.add_option(a)},SelectParser.prototype.add_group=function(a){var b,c,d,e,f,g;for(b=this.parsed.length,this.parsed.push({array_index:b,group:!0,label:this.escapeExpression(a.label),title:a.title?a.title:void 0,children:0,disabled:a.disabled,classes:a.className}),f=a.childNodes,g=[],d=0,e=f.length;e>d;d++)c=f[d],g.push(this.add_option(c,b,a.disabled));return g},SelectParser.prototype.add_option=function(a,b,c){return"OPTION"===a.nodeName.toUpperCase()?(""!==a.text?(null!=b&&(this.parsed[b].children+=1),this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:a.value,text:a.text,html:a.innerHTML,title:a.title?a.title:void 0,selected:a.selected,disabled:c===!0?c:a.disabled,group_array_index:b,group_label:null!=b?this.parsed[b].label:null,classes:a.className,style:a.style.cssText})):this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:!0}),this.options_index+=1):void 0},SelectParser.prototype.escapeExpression=function(a){var b,c;return null==a||a===!1?"":/[\&\<\>\"\'\`]/.test(a)?(b={"<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","`":"&#x60;"},c=/&(?!\w+;)|[\<\>\"\'\`]/g,a.replace(c,function(a){return b[a]||"&amp;"})):a},SelectParser}(),SelectParser.select_to_array=function(a){var b,c,d,e,f;for(c=new SelectParser,f=a.childNodes,d=0,e=f.length;e>d;d++)b=f[d],c.add_node(b);return c.parsed},AbstractChosen=function(){function AbstractChosen(a,b){this.form_field=a,this.options=null!=b?b:{},AbstractChosen.browser_is_supported()&&(this.is_multiple=this.form_field.multiple,this.set_default_text(),this.set_default_values(),this.setup(),this.set_up_html(),this.register_observers(),this.on_ready())}return AbstractChosen.prototype.set_default_values=function(){var a=this;return this.click_test_action=function(b){return a.test_active_click(b)},this.activate_action=function(b){return a.activate_field(b)},this.active_field=!1,this.mouse_on_container=!1,this.results_showing=!1,this.result_highlighted=null,this.allow_single_deselect=null!=this.options.allow_single_deselect&&null!=this.form_field.options[0]&&""===this.form_field.options[0].text?this.options.allow_single_deselect:!1,this.disable_search_threshold=this.options.disable_search_threshold||0,this.disable_search=this.options.disable_search||!1,this.enable_split_word_search=null!=this.options.enable_split_word_search?this.options.enable_split_word_search:!0,this.group_search=null!=this.options.group_search?this.options.group_search:!0,this.search_contains=this.options.search_contains||!1,this.single_backstroke_delete=null!=this.options.single_backstroke_delete?this.options.single_backstroke_delete:!0,this.max_selected_options=this.options.max_selected_options||1/0,this.inherit_select_classes=this.options.inherit_select_classes||!1,this.display_selected_options=null!=this.options.display_selected_options?this.options.display_selected_options:!0,this.display_disabled_options=null!=this.options.display_disabled_options?this.options.display_disabled_options:!0,this.include_group_label_in_selected=this.options.include_group_label_in_selected||!1,this.max_shown_results=this.options.max_shown_results||Number.POSITIVE_INFINITY},AbstractChosen.prototype.set_default_text=function(){return this.form_field.getAttribute("data-placeholder")?this.default_text=this.form_field.getAttribute("data-placeholder"):this.is_multiple?this.default_text=this.options.placeholder_text_multiple||this.options.placeholder_text||AbstractChosen.default_multiple_text:this.default_text=this.options.placeholder_text_single||this.options.placeholder_text||AbstractChosen.default_single_text,this.results_none_found=this.form_field.getAttribute("data-no_results_text")||this.options.no_results_text||AbstractChosen.default_no_result_text},AbstractChosen.prototype.choice_label=function(a){return this.include_group_label_in_selected&&null!=a.group_label?"<b class='group-name'>"+a.group_label+"</b>"+a.html:a.html},AbstractChosen.prototype.mouse_enter=function(){return this.mouse_on_container=!0},AbstractChosen.prototype.mouse_leave=function(){return this.mouse_on_container=!1},AbstractChosen.prototype.input_focus=function(a){var b=this;if(this.is_multiple){if(!this.active_field)return setTimeout(function(){return b.container_mousedown()},50)}else if(!this.active_field)return this.activate_field()},AbstractChosen.prototype.input_blur=function(a){var b=this;return this.mouse_on_container?void 0:(this.active_field=!1,setTimeout(function(){return b.blur_test()},100))},AbstractChosen.prototype.results_option_build=function(a){var b,c,d,e,f,g,h;for(b="",e=0,h=this.results_data,f=0,g=h.length;g>f&&(c=h[f],d="",d=c.group?this.result_add_group(c):this.result_add_option(c),""!==d&&(e++,b+=d),(null!=a?a.first:void 0)&&(c.selected&&this.is_multiple?this.choice_build(c):c.selected&&!this.is_multiple&&this.single_set_selected_text(this.choice_label(c))),!(e>=this.max_shown_results));f++);return b},AbstractChosen.prototype.result_add_option=function(a){var b,c;return a.search_match&&this.include_option_in_results(a)?(b=[],a.disabled||a.selected&&this.is_multiple||b.push("active-result"),!a.disabled||a.selected&&this.is_multiple||b.push("disabled-result"),a.selected&&b.push("result-selected"),null!=a.group_array_index&&b.push("group-option"),""!==a.classes&&b.push(a.classes),c=document.createElement("li"),c.className=b.join(" "),c.style.cssText=a.style,c.setAttribute("data-option-array-index",a.array_index),c.innerHTML=a.search_text,a.title&&(c.title=a.title),this.outerHTML(c)):""},AbstractChosen.prototype.result_add_group=function(a){var b,c;return(a.search_match||a.group_match)&&a.active_options>0?(b=[],b.push("group-result"),a.classes&&b.push(a.classes),c=document.createElement("li"),c.className=b.join(" "),c.innerHTML=a.search_text,a.title&&(c.title=a.title),this.outerHTML(c)):""},AbstractChosen.prototype.results_update_field=function(){return this.set_default_text(),this.is_multiple||this.results_reset_cleanup(),this.result_clear_highlight(),this.results_build(),this.results_showing?this.winnow_results():void 0},AbstractChosen.prototype.reset_single_select_options=function(){var a,b,c,d,e;for(d=this.results_data,e=[],b=0,c=d.length;c>b;b++)a=d[b],a.selected?e.push(a.selected=!1):e.push(void 0);return e},AbstractChosen.prototype.results_toggle=function(){return this.results_showing?this.results_hide():this.results_show()},AbstractChosen.prototype.results_search=function(a){return this.results_showing?this.winnow_results():this.results_show()},AbstractChosen.prototype.winnow_results=function(){var a,b,c,d,e,f,g,h,i,j,k,l;for(this.no_results_clear(),d=0,f=this.get_search_text(),a=f.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),i=new RegExp(a,"i"),c=this.get_search_regex(a),l=this.results_data,j=0,k=l.length;k>j;j++)b=l[j],b.search_match=!1,e=null,this.include_option_in_results(b)&&(b.group&&(b.group_match=!1,b.active_options=0),null!=b.group_array_index&&this.results_data[b.group_array_index]&&(e=this.results_data[b.group_array_index],0===e.active_options&&e.search_match&&(d+=1),e.active_options+=1),b.search_text=b.group?b.label:b.html,(!b.group||this.group_search)&&(b.search_match=this.search_string_match(b.search_text,c),b.search_match&&!b.group&&(d+=1),b.search_match?(f.length&&(g=b.search_text.search(i),h=b.search_text.substr(0,g+f.length)+"</em>"+b.search_text.substr(g+f.length),b.search_text=h.substr(0,g)+"<em>"+h.substr(g)),null!=e&&(e.group_match=!0)):null!=b.group_array_index&&this.results_data[b.group_array_index].search_match&&(b.search_match=!0)));return this.result_clear_highlight(),1>d&&f.length?(this.update_results_content(""),this.no_results(f)):(this.update_results_content(this.results_option_build()),this.winnow_results_set_highlight())},AbstractChosen.prototype.get_search_regex=function(a){var b;return b=this.search_contains?"":"^",new RegExp(b+a,"i")},AbstractChosen.prototype.search_string_match=function(a,b){var c,d,e,f;if(b.test(a))return!0;if(this.enable_split_word_search&&(a.indexOf(" ")>=0||0===a.indexOf("["))&&(d=a.replace(/\[|\]/g,"").split(" "),d.length))for(e=0,f=d.length;f>e;e++)if(c=d[e],b.test(c))return!0},AbstractChosen.prototype.choices_count=function(){var a,b,c,d;if(null!=this.selected_option_count)return this.selected_option_count;for(this.selected_option_count=0,d=this.form_field.options,b=0,c=d.length;c>b;b++)a=d[b],a.selected&&(this.selected_option_count+=1);return this.selected_option_count},AbstractChosen.prototype.choices_click=function(a){return a.preventDefault(),this.results_showing||this.is_disabled?void 0:this.results_show()},AbstractChosen.prototype.keyup_checker=function(a){var b,c;switch(b=null!=(c=a.which)?c:a.keyCode,this.search_field_scale(),b){case 8:if(this.is_multiple&&this.backstroke_length<1&&this.choices_count()>0)return this.keydown_backstroke();if(!this.pending_backstroke)return this.result_clear_highlight(),this.results_search();break;case 13:if(a.preventDefault(),this.results_showing)return this.result_select(a);break;case 27:return this.results_showing&&this.results_hide(),!0;case 9:case 38:case 40:case 16:case 91:case 17:case 18:break;default:return this.results_search()}},AbstractChosen.prototype.clipboard_event_checker=function(a){var b=this;return setTimeout(function(){return b.results_search()},50)},AbstractChosen.prototype.container_width=function(){return null!=this.options.width?this.options.width:""+this.form_field.offsetWidth+"px"},AbstractChosen.prototype.include_option_in_results=function(a){return this.is_multiple&&!this.display_selected_options&&a.selected?!1:!this.display_disabled_options&&a.disabled?!1:a.empty?!1:!0},AbstractChosen.prototype.search_results_touchstart=function(a){return this.touch_started=!0,this.search_results_mouseover(a)},AbstractChosen.prototype.search_results_touchmove=function(a){return this.touch_started=!1,this.search_results_mouseout(a)},AbstractChosen.prototype.search_results_touchend=function(a){return this.touch_started?this.search_results_mouseup(a):void 0},AbstractChosen.prototype.outerHTML=function(a){var b;return a.outerHTML?a.outerHTML:(b=document.createElement("div"),b.appendChild(a),b.innerHTML)},AbstractChosen.browser_is_supported=function(){return/iP(od|hone)/i.test(window.navigator.userAgent)?!1:/Android/i.test(window.navigator.userAgent)&&/Mobile/i.test(window.navigator.userAgent)?!1:/IEMobile/i.test(window.navigator.userAgent)?!1:/Windows Phone/i.test(window.navigator.userAgent)?!1:/BlackBerry/i.test(window.navigator.userAgent)?!1:/BB10/i.test(window.navigator.userAgent)?!1:"Microsoft Internet Explorer"===window.navigator.appName?document.documentMode>=8:!0},AbstractChosen.default_multiple_text="Select Some Options",AbstractChosen.default_single_text="Select an Option",AbstractChosen.default_no_result_text="No results match",AbstractChosen}(),a=jQuery,a.fn.extend({chosen:function(b){return AbstractChosen.browser_is_supported()?this.each(function(c){var d,e;return d=a(this),e=d.data("chosen"),"destroy"===b?void(e instanceof Chosen&&e.destroy()):void(e instanceof Chosen||d.data("chosen",new Chosen(this,b)))}):this}}),Chosen=function(c){function Chosen(){return b=Chosen.__super__.constructor.apply(this,arguments)}return d(Chosen,c),Chosen.prototype.setup=function(){return this.form_field_jq=a(this.form_field),this.current_selectedIndex=this.form_field.selectedIndex,this.is_rtl=this.form_field_jq.hasClass("chosen-rtl")},Chosen.prototype.set_up_html=function(){var b,c;return b=["chosen-container"],b.push("chosen-container-"+(this.is_multiple?"multi":"single")),this.inherit_select_classes&&this.form_field.className&&b.push(this.form_field.className),this.is_rtl&&b.push("chosen-rtl"),c={"class":b.join(" "),style:"width: "+this.container_width()+";",title:this.form_field.title},this.form_field.id.length&&(c.id=this.form_field.id.replace(/[^\w]/g,"_")+"_chosen"),this.container=a("<div />",c),this.is_multiple?this.container.html('<ul class="chosen-choices"><li class="search-field"><input type="text" value="'+this.default_text+'" class="default" autocomplete="off" style="width:25px;" /></li></ul><div class="chosen-drop"><ul class="chosen-results"></ul></div>'):this.container.html('<a class="chosen-single chosen-default"><span>'+this.default_text+'</span><div><b></b></div></a><div class="chosen-drop"><div class="chosen-search"><input type="text" autocomplete="off" /></div><ul class="chosen-results"></ul></div>'),this.form_field_jq.hide().after(this.container),this.dropdown=this.container.find("div.chosen-drop").first(),this.search_field=this.container.find("input").first(),this.search_results=this.container.find("ul.chosen-results").first(),this.search_field_scale(),this.search_no_results=this.container.find("li.no-results").first(),this.is_multiple?(this.search_choices=this.container.find("ul.chosen-choices").first(),this.search_container=this.container.find("li.search-field").first()):(this.search_container=this.container.find("div.chosen-search").first(),this.selected_item=this.container.find(".chosen-single").first()),this.results_build(),this.set_tab_index(),this.set_label_behavior()},Chosen.prototype.on_ready=function(){return this.form_field_jq.trigger("chosen:ready",{chosen:this})},Chosen.prototype.register_observers=function(){var a=this;return this.container.bind("touchstart.chosen",function(b){return a.container_mousedown(b),b.preventDefault()}),this.container.bind("touchend.chosen",function(b){return a.container_mouseup(b),b.preventDefault()}),this.container.bind("mousedown.chosen",function(b){a.container_mousedown(b)}),this.container.bind("mouseup.chosen",function(b){a.container_mouseup(b)}),this.container.bind("mouseenter.chosen",function(b){a.mouse_enter(b)}),this.container.bind("mouseleave.chosen",function(b){a.mouse_leave(b)}),this.search_results.bind("mouseup.chosen",function(b){a.search_results_mouseup(b)}),this.search_results.bind("mouseover.chosen",function(b){a.search_results_mouseover(b)}),this.search_results.bind("mouseout.chosen",function(b){a.search_results_mouseout(b)}),this.search_results.bind("mousewheel.chosen DOMMouseScroll.chosen",function(b){a.search_results_mousewheel(b)}),this.search_results.bind("touchstart.chosen",function(b){a.search_results_touchstart(b)}),this.search_results.bind("touchmove.chosen",function(b){a.search_results_touchmove(b)}),this.search_results.bind("touchend.chosen",function(b){a.search_results_touchend(b)}),this.form_field_jq.bind("chosen:updated.chosen",function(b){a.results_update_field(b)}),this.form_field_jq.bind("chosen:activate.chosen",function(b){a.activate_field(b)}),this.form_field_jq.bind("chosen:open.chosen",function(b){a.container_mousedown(b)}),this.form_field_jq.bind("chosen:close.chosen",function(b){a.input_blur(b)}),this.search_field.bind("blur.chosen",function(b){a.input_blur(b)}),this.search_field.bind("keyup.chosen",function(b){a.keyup_checker(b)}),this.search_field.bind("keydown.chosen",function(b){a.keydown_checker(b)}),this.search_field.bind("focus.chosen",function(b){a.input_focus(b)}),this.search_field.bind("cut.chosen",function(b){a.clipboard_event_checker(b)}),this.search_field.bind("paste.chosen",function(b){a.clipboard_event_checker(b)}),this.is_multiple?this.search_choices.bind("click.chosen",function(b){a.choices_click(b)}):this.container.bind("click.chosen",function(a){a.preventDefault()})},Chosen.prototype.destroy=function(){return a(this.container[0].ownerDocument).unbind("click.chosen",this.click_test_action),this.search_field[0].tabIndex&&(this.form_field_jq[0].tabIndex=this.search_field[0].tabIndex),this.container.remove(),this.form_field_jq.removeData("chosen"),this.form_field_jq.show()},Chosen.prototype.search_field_disabled=function(){return this.is_disabled=this.form_field_jq[0].disabled,this.is_disabled?(this.container.addClass("chosen-disabled"),this.search_field[0].disabled=!0,this.is_multiple||this.selected_item.unbind("focus.chosen",this.activate_action),this.close_field()):(this.container.removeClass("chosen-disabled"),this.search_field[0].disabled=!1,this.is_multiple?void 0:this.selected_item.bind("focus.chosen",this.activate_action))},Chosen.prototype.container_mousedown=function(b){return this.is_disabled||(b&&"mousedown"===b.type&&!this.results_showing&&b.preventDefault(),null!=b&&a(b.target).hasClass("search-choice-close"))?void 0:(this.active_field?this.is_multiple||!b||a(b.target)[0]!==this.selected_item[0]&&!a(b.target).parents("a.chosen-single").length||(b.preventDefault(),this.results_toggle()):(this.is_multiple&&this.search_field.val(""),a(this.container[0].ownerDocument).bind("click.chosen",this.click_test_action),this.results_show()),this.activate_field())},Chosen.prototype.container_mouseup=function(a){return"ABBR"!==a.target.nodeName||this.is_disabled?void 0:this.results_reset(a)},Chosen.prototype.search_results_mousewheel=function(a){var b;return a.originalEvent&&(b=a.originalEvent.deltaY||-a.originalEvent.wheelDelta||a.originalEvent.detail),null!=b?(a.preventDefault(),"DOMMouseScroll"===a.type&&(b=40*b),this.search_results.scrollTop(b+this.search_results.scrollTop())):void 0},Chosen.prototype.blur_test=function(a){return!this.active_field&&this.container.hasClass("chosen-container-active")?this.close_field():void 0},Chosen.prototype.close_field=function(){return a(this.container[0].ownerDocument).unbind("click.chosen",this.click_test_action),this.active_field=!1,this.results_hide(),this.container.removeClass("chosen-container-active"),this.clear_backstroke(),this.show_search_field_default(),this.search_field_scale()},Chosen.prototype.activate_field=function(){return this.container.addClass("chosen-container-active"),this.active_field=!0,this.search_field.val(this.search_field.val()),this.search_field.focus()},Chosen.prototype.test_active_click=function(b){var c;return c=a(b.target).closest(".chosen-container"),c.length&&this.container[0]===c[0]?this.active_field=!0:this.close_field()},Chosen.prototype.results_build=function(){return this.parsing=!0,this.selected_option_count=null,this.results_data=SelectParser.select_to_array(this.form_field),this.is_multiple?this.search_choices.find("li.search-choice").remove():this.is_multiple||(this.single_set_selected_text(),this.disable_search||this.form_field.options.length<=this.disable_search_threshold?(this.search_field[0].readOnly=!0,this.container.addClass("chosen-container-single-nosearch")):(this.search_field[0].readOnly=!1,this.container.removeClass("chosen-container-single-nosearch"))),this.update_results_content(this.results_option_build({first:!0})),this.search_field_disabled(),this.show_search_field_default(),this.search_field_scale(),this.parsing=!1},Chosen.prototype.result_do_highlight=function(a){var b,c,d,e,f;if(a.length){if(this.result_clear_highlight(),this.result_highlight=a,this.result_highlight.addClass("highlighted"),d=parseInt(this.search_results.css("maxHeight"),10),f=this.search_results.scrollTop(),e=d+f,c=this.result_highlight.position().top+this.search_results.scrollTop(),b=c+this.result_highlight.outerHeight(),b>=e)return this.search_results.scrollTop(b-d>0?b-d:0);if(f>c)return this.search_results.scrollTop(c)}},Chosen.prototype.result_clear_highlight=function(){return this.result_highlight&&this.result_highlight.removeClass("highlighted"),this.result_highlight=null},Chosen.prototype.results_show=function(){return this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field_jq.trigger("chosen:maxselected",{chosen:this}),!1):(this.container.addClass("chosen-with-drop"),this.results_showing=!0,this.search_field.focus(),this.search_field.val(this.search_field.val()),this.winnow_results(),this.form_field_jq.trigger("chosen:showing_dropdown",{chosen:this}))},Chosen.prototype.update_results_content=function(a){return this.search_results.html(a)},Chosen.prototype.results_hide=function(){return this.results_showing&&(this.result_clear_highlight(),this.container.removeClass("chosen-with-drop"),this.form_field_jq.trigger("chosen:hiding_dropdown",{chosen:this})),this.results_showing=!1},Chosen.prototype.set_tab_index=function(a){var b;return this.form_field.tabIndex?(b=this.form_field.tabIndex,this.form_field.tabIndex=-1,this.search_field[0].tabIndex=b):void 0},Chosen.prototype.set_label_behavior=function(){var b=this;return this.form_field_label=this.form_field_jq.parents("label"),!this.form_field_label.length&&this.form_field.id.length&&(this.form_field_label=a("label[for='"+this.form_field.id+"']")),this.form_field_label.length>0?this.form_field_label.bind("click.chosen",function(a){return b.is_multiple?b.container_mousedown(a):b.activate_field()}):void 0},Chosen.prototype.show_search_field_default=function(){return this.is_multiple&&this.choices_count()<1&&!this.active_field?(this.search_field.val(this.default_text),this.search_field.addClass("default")):(this.search_field.val(""),this.search_field.removeClass("default"))},Chosen.prototype.search_results_mouseup=function(b){var c;return c=a(b.target).hasClass("active-result")?a(b.target):a(b.target).parents(".active-result").first(),c.length?(this.result_highlight=c,this.result_select(b),this.search_field.focus()):void 0},Chosen.prototype.search_results_mouseover=function(b){var c;return c=a(b.target).hasClass("active-result")?a(b.target):a(b.target).parents(".active-result").first(),c?this.result_do_highlight(c):void 0},Chosen.prototype.search_results_mouseout=function(b){return a(b.target).hasClass("active-result")?this.result_clear_highlight():void 0},Chosen.prototype.choice_build=function(b){var c,d,e=this;return c=a("<li />",{"class":"search-choice"}).html("<span>"+this.choice_label(b)+"</span>"),b.disabled?c.addClass("search-choice-disabled"):(d=a("<a />",{"class":"search-choice-close","data-option-array-index":b.array_index}),d.bind("click.chosen",function(a){return e.choice_destroy_link_click(a)}),c.append(d)),this.search_container.before(c)},Chosen.prototype.choice_destroy_link_click=function(b){return b.preventDefault(),b.stopPropagation(),this.is_disabled?void 0:this.choice_destroy(a(b.target))},Chosen.prototype.choice_destroy=function(a){return this.result_deselect(a[0].getAttribute("data-option-array-index"))?(this.show_search_field_default(),this.is_multiple&&this.choices_count()>0&&this.search_field.val().length<1&&this.results_hide(),a.parents("li").first().remove(),this.search_field_scale()):void 0},Chosen.prototype.results_reset=function(){return this.reset_single_select_options(),this.form_field.options[0].selected=!0,this.single_set_selected_text(),this.show_search_field_default(),this.results_reset_cleanup(),this.form_field_jq.trigger("change"),this.active_field?this.results_hide():void 0},Chosen.prototype.results_reset_cleanup=function(){return this.current_selectedIndex=this.form_field.selectedIndex,this.selected_item.find("abbr").remove()},Chosen.prototype.result_select=function(a){var b,c;return this.result_highlight?(b=this.result_highlight,this.result_clear_highlight(),this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field_jq.trigger("chosen:maxselected",{chosen:this}),!1):(this.is_multiple?b.removeClass("active-result"):this.reset_single_select_options(),b.addClass("result-selected"),c=this.results_data[b[0].getAttribute("data-option-array-index")],c.selected=!0,this.form_field.options[c.options_index].selected=!0,this.selected_option_count=null,this.is_multiple?this.choice_build(c):this.single_set_selected_text(this.choice_label(c)),(a.metaKey||a.ctrlKey)&&this.is_multiple||this.results_hide(),this.show_search_field_default(),(this.is_multiple||this.form_field.selectedIndex!==this.current_selectedIndex)&&this.form_field_jq.trigger("change",{selected:this.form_field.options[c.options_index].value}),this.current_selectedIndex=this.form_field.selectedIndex,a.preventDefault(),this.search_field_scale())):void 0},Chosen.prototype.single_set_selected_text=function(a){return null==a&&(a=this.default_text),a===this.default_text?this.selected_item.addClass("chosen-default"):(this.single_deselect_control_build(),this.selected_item.removeClass("chosen-default")),this.selected_item.find("span").html(a)},Chosen.prototype.result_deselect=function(a){var b;return b=this.results_data[a],this.form_field.options[b.options_index].disabled?!1:(b.selected=!1,this.form_field.options[b.options_index].selected=!1,this.selected_option_count=null,this.result_clear_highlight(),this.results_showing&&this.winnow_results(),this.form_field_jq.trigger("change",{deselected:this.form_field.options[b.options_index].value}),this.search_field_scale(),!0)},Chosen.prototype.single_deselect_control_build=function(){return this.allow_single_deselect?(this.selected_item.find("abbr").length||this.selected_item.find("span").first().after('<abbr class="search-choice-close"></abbr>'),this.selected_item.addClass("chosen-single-with-deselect")):void 0},Chosen.prototype.get_search_text=function(){return a("<div/>").text(a.trim(this.search_field.val())).html()},Chosen.prototype.winnow_results_set_highlight=function(){var a,b;return b=this.is_multiple?[]:this.search_results.find(".result-selected.active-result"),a=b.length?b.first():this.search_results.find(".active-result").first(),null!=a?this.result_do_highlight(a):void 0},Chosen.prototype.no_results=function(b){var c;return c=a('<li class="no-results">'+this.results_none_found+' "<span></span>"</li>'),c.find("span").first().html(b),this.search_results.append(c),this.form_field_jq.trigger("chosen:no_results",{chosen:this})},Chosen.prototype.no_results_clear=function(){return this.search_results.find(".no-results").remove()},Chosen.prototype.keydown_arrow=function(){var a;return this.results_showing&&this.result_highlight?(a=this.result_highlight.nextAll("li.active-result").first())?this.result_do_highlight(a):void 0:this.results_show()},Chosen.prototype.keyup_arrow=function(){var a;return this.results_showing||this.is_multiple?this.result_highlight?(a=this.result_highlight.prevAll("li.active-result"),a.length?this.result_do_highlight(a.first()):(this.choices_count()>0&&this.results_hide(),this.result_clear_highlight())):void 0:this.results_show()},Chosen.prototype.keydown_backstroke=function(){var a;return this.pending_backstroke?(this.choice_destroy(this.pending_backstroke.find("a").first()),this.clear_backstroke()):(a=this.search_container.siblings("li.search-choice").last(),a.length&&!a.hasClass("search-choice-disabled")?(this.pending_backstroke=a,this.single_backstroke_delete?this.keydown_backstroke():this.pending_backstroke.addClass("search-choice-focus")):void 0)},Chosen.prototype.clear_backstroke=function(){return this.pending_backstroke&&this.pending_backstroke.removeClass("search-choice-focus"),this.pending_backstroke=null},Chosen.prototype.keydown_checker=function(a){var b,c;switch(b=null!=(c=a.which)?c:a.keyCode,this.search_field_scale(),8!==b&&this.pending_backstroke&&this.clear_backstroke(),b){case 8:this.backstroke_length=this.search_field.val().length;break;case 9:this.results_showing&&!this.is_multiple&&this.result_select(a),this.mouse_on_container=!1;break;case 13:this.results_showing&&a.preventDefault();break;case 32:this.disable_search&&a.preventDefault();break;case 38:a.preventDefault(),this.keyup_arrow();break;case 40:a.preventDefault(),this.keydown_arrow()}},Chosen.prototype.search_field_scale=function(){var b,c,d,e,f,g,h,i,j;if(this.is_multiple){for(d=0,h=0,f="position:absolute; left: -1000px; top: -1000px; display:none;",g=["font-size","font-style","font-weight","font-family","line-height","text-transform","letter-spacing"],i=0,j=g.length;j>i;i++)e=g[i],f+=e+":"+this.search_field.css(e)+";";return b=a("<div />",{style:f}),b.text(this.search_field.val()),a("body").append(b),h=b.width()+25,b.remove(),c=this.container.outerWidth(),h>c-10&&(h=c-10),this.search_field.css({width:h+"px"})}},Chosen}(AbstractChosen)}).call(this);
blocks/sbc/assets/js/script-block-sbc.js DELETED
@@ -1,71 +0,0 @@
1
- var el = wp.element.createElement;
2
- var ServerSideRender = wp.components.ServerSideRender;
3
- var PanelBody = wp.components.PanelBody;
4
- var SelectControl = wp.components.SelectControl;
5
- var registerBlockType = wp.blocks.registerBlockType;
6
- var InspectorControls = wp.editor.InspectorControls;
7
- var __ = wp.i18n.__;
8
-
9
- // Register the block
10
- registerBlockType( 'wp-simple-booking-calendar/sbc', {
11
-
12
- // The block's title
13
- title : 'Single Calendar',
14
-
15
- // The block's icon
16
- icon : 'calendar-alt',
17
-
18
- // The block category the block should be added to
19
- category : 'wp-simple-booking-calendar',
20
-
21
- // The block's attributes, needed to save the data
22
- attributes : {
23
-
24
- title : {
25
- type : 'string'
26
- }
27
-
28
- },
29
-
30
- edit : function( props ) {
31
-
32
- return [
33
-
34
- el( ServerSideRender, {
35
- block : 'wp-simple-booking-calendar/sbc',
36
- attributes : props.attributes
37
- }),
38
-
39
- el( InspectorControls, { key : 'inspector' },
40
-
41
- el( PanelBody, {
42
- title : __( 'Calendar Options', 'wp-simple-booking-calendar' ),
43
- initialOpen : true
44
- },
45
-
46
- el( SelectControl, {
47
-
48
- label : __( 'Display Calendar Title', 'wp-simple-booking-calendar' ),
49
- value : props.attributes.title,
50
- options : [
51
- { value : 'yes', label : __( 'Yes', 'wp-simple-booking-calendar' ) },
52
- { value : 'no', label : __( 'No', 'wp-simple-booking-calendar' ) }
53
- ],
54
- onChange : function( new_value ) {
55
- props.setAttributes( { title : new_value } );
56
- }
57
- })
58
-
59
- )
60
-
61
- )
62
-
63
- ];
64
-
65
- },
66
-
67
- save : function() {
68
- return null;
69
- }
70
-
71
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
blocks/sbc/functions.php DELETED
@@ -1,152 +0,0 @@
1
- <?php
2
-
3
- // Exit if accessed directly
4
- if ( ! defined( 'ABSPATH' ) ) exit;
5
-
6
- /**
7
- * Function that register the needed categories for the different block
8
- * available in the plugin
9
- *
10
- */
11
- if( ! function_exists( 'wpsbc_register_block_categories' ) ) {
12
-
13
- function wpsbc_register_block_categories( $categories, $post ) {
14
-
15
- /**
16
- * Filter the post types where the blocks are available
17
- *
18
- * @param array
19
- *
20
- */
21
- $post_types = apply_filters( 'wpsbc_register_block_categories_post_types', array( 'post', 'page' ) );
22
-
23
- if( ! in_array( $post->post_type, $post_types ) )
24
- return $categories;
25
-
26
- $categories[] = array(
27
- 'slug' => 'wp-simple-booking-calendar',
28
- 'title' => 'WP Simple Booking Calendar',
29
- 'icon' => ''
30
- );
31
-
32
- return $categories;
33
-
34
- }
35
- add_filter( 'block_categories', 'wpsbc_register_block_categories', 10, 2 );
36
-
37
- }
38
-
39
-
40
- /**
41
- * Adds the front-end files to the admin editor screen
42
- *
43
- */
44
- function wpsbc_add_front_end_scripts() {
45
-
46
- if( ! function_exists( 'get_current_screen' ) )
47
- return;
48
-
49
- $screen = get_current_screen();
50
-
51
- if( is_null( $screen ) )
52
- return;
53
-
54
- /**
55
- * Filter the post types where the calendar media button should appear
56
- *
57
- * @param array
58
- *
59
- */
60
- $post_types = apply_filters( 'wpsbc_register_block_categories_post_types', array( 'post', 'page' ) );
61
-
62
- if( ! in_array( $screen->post_type, $post_types ) )
63
- return;
64
-
65
-
66
- // Enqueue front-end scripts on the admin part
67
- wp_register_script( 'sbc-front-end-script', SBC_DIR_URL . 'js/sbc.js', array( 'jquery' ), SBC_VERSION, true );
68
- wp_enqueue_script( 'sbc-front-end-script' );
69
-
70
- // Enqueue front-end styles on the admin part
71
- wp_register_style( 'sbc-front-end-style', SBC_DIR_URL . 'css/sbc.css', array(), SBC_VERSION );
72
- wp_enqueue_style( 'sbc-front-end-style' );
73
-
74
- }
75
- add_action( 'admin_enqueue_scripts', 'wpsbc_add_front_end_scripts', 10 );
76
-
77
-
78
- /**
79
- * Registers the Single Calendar block
80
- *
81
- */
82
- if( function_exists( 'register_block_type' ) ) {
83
-
84
- function wpsbc_register_block_type_sbc() {
85
-
86
- wp_register_script( 'sbc-script-block-sbc', SBC_DIR_URL . 'blocks/sbc/assets/js/script-block-sbc.js', array( 'wp-blocks', 'wp-element', 'wp-editor', 'wp-i18n' ) );
87
-
88
- register_block_type(
89
- 'wp-simple-booking-calendar/sbc',
90
- array(
91
- 'attributes' => array(
92
- 'title' => array(
93
- 'type' => 'string'
94
- )
95
- ),
96
- 'editor_script' => 'sbc-script-block-sbc',
97
- 'render_callback' => 'wpsbc_block_to_shortcode_sbc'
98
- )
99
- );
100
-
101
- }
102
- add_action( 'init', 'wpsbc_register_block_type_sbc' );
103
-
104
- }
105
-
106
-
107
- /**
108
- * Render callback for the server render block
109
- * Transforms the attributes from the blocks into the needed shortcode arguments
110
- *
111
- * @param array $args
112
- *
113
- * @return string
114
- *
115
- */
116
- function wpsbc_block_to_shortcode_sbc( $args ) {
117
-
118
- $atts = (array) $args;
119
-
120
- // Validate booleans
121
- $booleans = array('title');
122
- foreach ($booleans as $key)
123
- {
124
- if (isset($atts[$key]))
125
- {
126
- // Replace string values: Yes = true, No = false
127
- if (is_bool($atts[$key]) !== true)
128
- {
129
- $value = (strcasecmp($atts[$key], 'yes') == 0 || $atts[$key] == '1');
130
- }
131
- else
132
- {
133
- $value = $atts[$key];
134
- }
135
-
136
- $atts[$key] = $value;
137
- }
138
- }
139
-
140
- // Process attributes
141
- $defaults = array('id' => null, 'title' => true);
142
- $values = shortcode_atts($defaults, $atts);
143
-
144
- $model = new WpSimpleBookingCalendar_Model();
145
- $view = new WpSimpleBookingCalendar_View();
146
-
147
- return $view->setTemplate('shortcode/sbc')
148
- ->assign('showTitle', $values['title'])
149
- ->assign('calendar', $model->getCalendar())
150
- ->fetch();
151
-
152
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
css/sbc-controller.css DELETED
@@ -1,98 +0,0 @@
1
- @CHARSET "UTF-8";
2
-
3
- #sbc-wrapper div.postbox-container {
4
- float: none;
5
- padding: 0;
6
-
7
- /*width: 440px;*/
8
- position:relative;
9
- }
10
-
11
- #sbc-calendar {
12
- float: left;
13
- margin: 0 3px 30px 0;
14
- }
15
-
16
- #sbc-wrapper div.postbox div.inside {
17
- margin: 14px 10px;
18
- position: relative;
19
- }
20
-
21
- #sbc-wrapper .wpsbc-buttons-container {
22
- position: absolute; top: -30px; right: 0;
23
- }
24
-
25
- #sbc-wrapper div.postbox-container p {
26
- margin: 20px 0;
27
- padding-top:0px;
28
- }
29
-
30
- #sbc-wrapper div.postbox-container label {
31
- display: block;
32
- /*font-size: 12px;*/
33
- font-weight: bold;
34
- margin: 0 0 3px;
35
- }
36
-
37
- #sbc-wrapper div.postbox-container label em {
38
- color: #62625A;
39
- font-weight: normal;
40
- }
41
-
42
- #sbc-wrapper div.postbox div.inside textarea {
43
- width: 100%;
44
- }
45
-
46
- #sbc-wrapper div.postbox div.inside input, #sbc-wrapper div.postbox div.inside textarea {
47
- margin: 1px;
48
- padding: 3px;
49
- }
50
-
51
- #sbc-wrapper div.postbox div.inside input.error, #sbc-wrapper div.postbox div.inside textarea.error {
52
- margin: 1px 1px 3px 1px;
53
- padding: 3px;
54
- }
55
-
56
- #sbc-wrapper .tablenav {
57
- line-height: 30px;
58
- }
59
-
60
- #sbc-wrapper p.search-box {
61
- height: 30px;
62
- line-height: 30px;
63
- margin: 0;
64
- }
65
-
66
- #sbc-wrapper div.loader {
67
- display: none;
68
- height: 55px;
69
- }
70
-
71
-
72
-
73
- div.buy-full h1 {font-weight:bold; font-size:20px; line-height:30px; margin-top:30px;}
74
- div.buy-full .box {float:left; padding:15px 15px 7px 15px; width:255px; border:1px solid #cccccc; margin-right:15px; background-color:#fff;}
75
- div.buy-full .box h1 {padding:0px; margin:0px; border-bottom:1px solid #cccccc; color:#0084ff; font-weight:normal; font-size:26px; display:block; text-align:center; padding:11px 0 26px 0;}
76
- div.buy-full .box ul {list-style:none; padding-top:9px; display:block; height:523px;}
77
-
78
- div.buy-full .box ul li {background: transparent url(../images/checked-icon.jpg) no-repeat left 6px; color:#666666; font-size:14px; padding:6px 0 5px 21px;}
79
- div.buy-full .box h2 {font-weight:normal; display:block; text-align:center; color:#0084ff; font-size:30px; float:left; margin:0 0 0 25px; padding:19px 0 0 0;}
80
- div.buy-full .box small {color:#0084ff; font-size:14px; display:block; margin:8px 0 0 6px; float:left; padding-top: 10px; }
81
- div.buy-full .box span {display:block; height:50px; line-height:50px; text-align:center;}
82
- div.buy-full .box p {font-size:14px; color:#666666; padding:13px 20px 6px 20px;}
83
- div.buy-full .box strong {font-weight:normal; color:#0084ff;}
84
- div.buy-full .box a.button {-webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px;font-size:18px; color:#ffffff; text-shadow:0px 2px 2px rgba(0, 0, 0, 0.4); display:block; height:45px; line-height:45px; text-align:center; background: transparent url(../images/button-bg.png) repeat-x left top; border:none; text-decoration:none; clear:both;}
85
- div.buy-full .box a.button:hover {background-position:left bottom;}
86
- div.buy-full .box div.bottom {background-color:#ececec; padding:15px; width:255px; margin:19px 0 0 -15px;}
87
-
88
- div.buy-full .box.first div.bottom {height:75px; line-height:75px; text-align:center; padding-top:0px; padding-bottom:0px;}
89
- div.buy-full .box.first div.bottom p {line-height:75px; text-align:center; padding:0px; margin:0px;}
90
- div.buy-full .box small.bottom {color:#666666; font-size:12px; display:block; text-align:center; float:none;}
91
-
92
-
93
-
94
-
95
-
96
- @media screen and (-webkit-min-device-pixel-ratio:0) {
97
- div.buy-full .box p {padding:13px 20px 10px 20px;}
98
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
css/sbc-shortcode.css DELETED
@@ -1,17 +0,0 @@
1
- @CHARSET "UTF-8";
2
-
3
- /* ----- Token Metabox ----- */
4
- #sbc-metabox h4 {
5
- border-bottom: 1px dashed #dfdfdf;
6
- font-size: 14px;
7
- margin: 0;
8
- padding: 10px 0;
9
- }
10
-
11
- #sbc-metabox .form-table th {
12
- vertical-align: middle;
13
- }
14
-
15
- #sbc-metabox .form-table th.top {
16
- vertical-align: top;
17
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
css/sbc.css DELETED
@@ -1,171 +0,0 @@
1
- @CHARSET "UTF-8";
2
-
3
- #sbc-calendar-wrapper {
4
- font-family: Arial, Helvetica, sans-serif;
5
- font-size: 12px;
6
- clear: both;
7
- overflow: hidden;
8
- }
9
-
10
- #sbc-calendar-wrapper, #sbc-calendar-wrapper * {
11
- -webkit-box-sizing: content-box; /* Safari/Chrome, other WebKit */
12
- -moz-box-sizing: content-box; /* Firefox, other Gecko */
13
- box-sizing: content-box;
14
- }
15
-
16
- #sbc-calendar-wrapper .sbc-status-free {
17
- background-color: #dfc;
18
- }
19
-
20
- #sbc-calendar-wrapper .sbc-status-booked {
21
- background-color: #ffc0bd;
22
- }
23
-
24
- #sbc-calendar-wrapper .sbc-status-changeover {
25
- background-color: #fee2a0;
26
- }
27
-
28
- #sbc-calendar-wrapper table {
29
- border: 0;
30
- border-collapse: separate;
31
- border-spacing: 2px;
32
- table-layout: fixed;
33
- margin: 0;
34
- -webkit-border-radius: 0px;
35
- -moz-border-radius: 0px;
36
- border-radius: 0px;
37
- padding: 0;
38
- }
39
- #sbc-calendar-wrapper table tr, #sbc-calendar-wrapper table th {
40
- background: none !important;
41
- }
42
-
43
- #sbc-calendar-wrapper table tr td {
44
- border: 0;
45
- padding: 0;
46
- line-height: 12px;
47
- vertical-align: middle;
48
- }
49
-
50
- #sbc-calendar {
51
- float: left;
52
- margin: 0 5px 30px 0;
53
- }
54
-
55
- #sbc-calendar table thead th
56
- {
57
- text-transform: uppercase;
58
- }
59
-
60
- .sbc-calendar-month {
61
- float: left;
62
- padding: 10px;
63
- width: 172px;
64
- border: 1px solid #ccc;
65
- background-color: #fff;
66
- margin-right: 10px;
67
- }
68
-
69
- #sbc-calendar .sbc-navigation {
70
- padding-bottom: 10px;
71
- line-height: 21px;
72
- }
73
-
74
- #sbc-calendar .sbc-navigation select {
75
- margin: 0; padding: 0 !important; width: auto; height: auto; font-size: 12px;
76
- }
77
-
78
- #sbc-calendar form {margin: 0 !important;}
79
-
80
- #sbc-calendar .sbc-loader {
81
- background-color: #ffc0bd;
82
- font-weight: bold;
83
- margin: 4px 0;
84
- padding: 1px 0;
85
- text-align: center;
86
- visibility: hidden;
87
- }
88
-
89
- #sbc-calendar .sbc-loader-visible {
90
- visibility: visible;
91
- }
92
-
93
- #sbc-calendar table {
94
- border-top: 1px solid #000;
95
- }
96
-
97
- #sbc-calendar table thead th,#sbc-calendar tr td {
98
- color: #333;
99
- font-family: Arial, Helvetica, sans-serif;
100
- font-size: 12px;
101
- text-align: center;
102
- line-height: 16px;
103
- padding: 8px 0 4px;
104
- width: 22px;
105
- height: 22px;
106
- border:none;
107
- }
108
-
109
- #sbc-calendar table tfoot td {
110
- padding-top: 5px;
111
- text-align: left;
112
- }
113
-
114
- #sbc-calendar tr td {
115
- border: 1px solid #fff;
116
- padding: 2px 0;
117
- }
118
-
119
- #sbc-calendar tr td.pad {
120
- width: auto;
121
- }
122
-
123
- #sbc-calendar td.sbc-status-free {
124
- border-color: #dfc;
125
- }
126
-
127
- #sbc-calendar td.sbc-status-booked {
128
- border-color: #ffc0bd;
129
- }
130
-
131
- #sbc-calendar td.sbc-status-changeover {
132
- border-color: #fee2a0;
133
- }
134
-
135
- #sbc-calendar td.sbc-today {
136
- border-color: #aeaeae
137
- }
138
-
139
- #sbc-editor {
140
- border: 1px solid #ccc;
141
- float: left;
142
- padding: 5px;
143
- background-color:#ffffff;
144
- }
145
-
146
- #sbc-editor td.sbc-editor-day {
147
- font-family: Arial, Helvetica, sans-serif;
148
- font-size: 12px;
149
- text-align: center;
150
- width: 25px;
151
- }
152
-
153
- #sbc-editor td.sbc-editor-status select {
154
- width: 120px;
155
- height:auto !important;
156
- }
157
-
158
-
159
- .widget-sbc #sbc-calendar {
160
- float: none;
161
- margin: 0 0 10px 0;
162
- }
163
-
164
- .alignRight {
165
- text-align: right !important;
166
- }
167
-
168
- span.powered-by {width:192px; text-align:center; padding-top:3px; clear:both; }
169
-
170
- div#sbc-calendar-wrapper p { padding:0 !important;}
171
- div#sbc-calendar-wrapper br {display:none !important;}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
images/button-bg.png DELETED
Binary file
images/checked-icon.jpg DELETED
Binary file
images/checked.png DELETED
Binary file
includes/abstracts/abstract-class-db.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Base class for all core database objects.
9
+ *
10
+ */
11
+ abstract class WPSBC_DB {
12
+
13
+ /**
14
+ * Database table name
15
+ *
16
+ * @access public
17
+ * @var string
18
+ *
19
+ */
20
+ public $table_name;
21
+
22
+ /**
23
+ * Primary key of the table
24
+ *
25
+ * @access public
26
+ * @var string
27
+ *
28
+ */
29
+ public $primary_key;
30
+
31
+ /**
32
+ * The context of the object affected
33
+ *
34
+ * @access public
35
+ * @var string
36
+ *
37
+ */
38
+ public $context = '';
39
+
40
+
41
+ /**
42
+ * Constructor
43
+ *
44
+ * Subclasses should set the $table_name, $primary_key, $context
45
+ *
46
+ * @access public
47
+ *
48
+ */
49
+ public function __construct() {}
50
+
51
+
52
+ /**
53
+ * Retrieves the list of columns for the database table
54
+ *
55
+ * @access public
56
+ *
57
+ * @return array
58
+ *
59
+ */
60
+ public function get_columns() {
61
+
62
+ return array();
63
+
64
+ }
65
+
66
+ }
includes/abstracts/abstract-class-list-table.php ADDED
@@ -0,0 +1,1373 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ /**
7
+ * Copy of WordPress's WP_List_Table class
8
+ *
9
+ * Base class for displaying a list of items in an ajaxified HTML table.
10
+ *
11
+ */
12
+ abstract class WPSBC_WP_List_Table {
13
+
14
+ /**
15
+ * The current list of items.
16
+ *
17
+ * @since 3.1.0
18
+ * @access public
19
+ * @var array
20
+ */
21
+ public $items;
22
+
23
+ /**
24
+ * Various information about the current table.
25
+ *
26
+ * @since 3.1.0
27
+ * @access protected
28
+ * @var array
29
+ */
30
+ protected $_args;
31
+
32
+ /**
33
+ * Various information needed for displaying the pagination.
34
+ *
35
+ * @since 3.1.0
36
+ * @access protected
37
+ * @var array
38
+ */
39
+ protected $_pagination_args = array();
40
+
41
+ /**
42
+ * The current screen.
43
+ *
44
+ * @since 3.1.0
45
+ * @access protected
46
+ * @var object
47
+ */
48
+ protected $screen;
49
+
50
+ /**
51
+ * Cached bulk actions.
52
+ *
53
+ * @since 3.1.0
54
+ * @access private
55
+ * @var array
56
+ */
57
+ private $_actions;
58
+
59
+ /**
60
+ * Cached pagination output.
61
+ *
62
+ * @since 3.1.0
63
+ * @access private
64
+ * @var string
65
+ */
66
+ private $_pagination;
67
+
68
+ /**
69
+ * The view switcher modes.
70
+ *
71
+ * @since 4.1.0
72
+ * @access protected
73
+ * @var array
74
+ */
75
+ protected $modes = array();
76
+
77
+ /**
78
+ * Stores the value returned by ->get_column_info().
79
+ *
80
+ * @since 4.1.0
81
+ * @access protected
82
+ * @var array
83
+ */
84
+ protected $_column_headers;
85
+
86
+ /**
87
+ * {@internal Missing Summary}
88
+ *
89
+ * @access protected
90
+ * @var array
91
+ */
92
+ protected $compat_fields = array( '_args', '_pagination_args', 'screen', '_actions', '_pagination' );
93
+
94
+ /**
95
+ * {@internal Missing Summary}
96
+ *
97
+ * @access protected
98
+ * @var array
99
+ */
100
+ protected $compat_methods = array( 'set_pagination_args', 'get_views', 'get_bulk_actions', 'bulk_actions',
101
+ 'row_actions', 'months_dropdown', 'view_switcher', 'comments_bubble', 'get_items_per_page', 'pagination',
102
+ 'get_sortable_columns', 'get_column_info', 'get_table_classes', 'display_tablenav', 'extra_tablenav',
103
+ 'single_row_columns' );
104
+
105
+ /**
106
+ * Constructor.
107
+ *
108
+ * The child class should call this constructor from its own constructor to override
109
+ * the default $args.
110
+ *
111
+ * @since 3.1.0
112
+ * @access public
113
+ *
114
+ * @param array|string $args {
115
+ * Array or string of arguments.
116
+ *
117
+ * @type string $plural Plural value used for labels and the objects being listed.
118
+ * This affects things such as CSS class-names and nonces used
119
+ * in the list table, e.g. 'posts'. Default empty.
120
+ * @type string $singular Singular label for an object being listed, e.g. 'post'.
121
+ * Default empty
122
+ * @type bool $ajax Whether the list table supports Ajax. This includes loading
123
+ * and sorting data, for example. If true, the class will call
124
+ * the _js_vars() method in the footer to provide variables
125
+ * to any scripts handling Ajax events. Default false.
126
+ * @type string $screen String containing the hook name used to determine the current
127
+ * screen. If left null, the current screen will be automatically set.
128
+ * Default null.
129
+ * }
130
+ */
131
+ public function __construct( $args = array() ) {
132
+ $args = wp_parse_args( $args, array(
133
+ 'plural' => '',
134
+ 'singular' => '',
135
+ 'ajax' => false,
136
+ 'screen' => null,
137
+ ) );
138
+
139
+ $this->screen = convert_to_screen( $args['screen'] );
140
+
141
+ add_filter( "manage_{$this->screen->id}_columns", array( $this, 'get_columns' ), 0 );
142
+
143
+ if ( !$args['plural'] )
144
+ $args['plural'] = $this->screen->base;
145
+
146
+ $args['plural'] = sanitize_key( $args['plural'] );
147
+ $args['singular'] = sanitize_key( $args['singular'] );
148
+
149
+ $this->_args = $args;
150
+
151
+ if ( $args['ajax'] ) {
152
+ // wp_enqueue_script( 'list-table' );
153
+ add_action( 'admin_footer', array( $this, '_js_vars' ) );
154
+ }
155
+
156
+ if ( empty( $this->modes ) ) {
157
+ $this->modes = array(
158
+ 'list' => __( 'List View' ),
159
+ 'excerpt' => __( 'Excerpt View' )
160
+ );
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Make private properties readable for backward compatibility.
166
+ *
167
+ * @since 4.0.0
168
+ * @access public
169
+ *
170
+ * @param string $name Property to get.
171
+ * @return mixed Property.
172
+ */
173
+ public function __get( $name ) {
174
+ if ( in_array( $name, $this->compat_fields ) ) {
175
+ return $this->$name;
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Make private properties settable for backward compatibility.
181
+ *
182
+ * @since 4.0.0
183
+ * @access public
184
+ *
185
+ * @param string $name Property to check if set.
186
+ * @param mixed $value Property value.
187
+ * @return mixed Newly-set property.
188
+ */
189
+ public function __set( $name, $value ) {
190
+ if ( in_array( $name, $this->compat_fields ) ) {
191
+ return $this->$name = $value;
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Make private properties checkable for backward compatibility.
197
+ *
198
+ * @since 4.0.0
199
+ * @access public
200
+ *
201
+ * @param string $name Property to check if set.
202
+ * @return bool Whether the property is set.
203
+ */
204
+ public function __isset( $name ) {
205
+ if ( in_array( $name, $this->compat_fields ) ) {
206
+ return isset( $this->$name );
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Make private properties un-settable for backward compatibility.
212
+ *
213
+ * @since 4.0.0
214
+ * @access public
215
+ *
216
+ * @param string $name Property to unset.
217
+ */
218
+ public function __unset( $name ) {
219
+ if ( in_array( $name, $this->compat_fields ) ) {
220
+ unset( $this->$name );
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Make private/protected methods readable for backward compatibility.
226
+ *
227
+ * @since 4.0.0
228
+ * @access public
229
+ *
230
+ * @param callable $name Method to call.
231
+ * @param array $arguments Arguments to pass when calling.
232
+ * @return mixed|bool Return value of the callback, false otherwise.
233
+ */
234
+ public function __call( $name, $arguments ) {
235
+ if ( in_array( $name, $this->compat_methods ) ) {
236
+ return call_user_func_array( array( $this, $name ), $arguments );
237
+ }
238
+ return false;
239
+ }
240
+
241
+ /**
242
+ * Checks the current user's permissions
243
+ *
244
+ * @since 3.1.0
245
+ * @access public
246
+ * @abstract
247
+ */
248
+ public function ajax_user_can() {
249
+ die( 'function WP_List_Table::ajax_user_can() must be over-ridden in a sub-class.' );
250
+ }
251
+
252
+ /**
253
+ * Prepares the list of items for displaying.
254
+ * @uses WP_List_Table::set_pagination_args()
255
+ *
256
+ * @since 3.1.0
257
+ * @access public
258
+ * @abstract
259
+ */
260
+ public function prepare_items() {
261
+ die( 'function WP_List_Table::prepare_items() must be over-ridden in a sub-class.' );
262
+ }
263
+
264
+ /**
265
+ * An internal method that sets all the necessary pagination arguments
266
+ *
267
+ * @since 3.1.0
268
+ * @access protected
269
+ *
270
+ * @param array|string $args Array or string of arguments with information about the pagination.
271
+ */
272
+ protected function set_pagination_args( $args ) {
273
+ $args = wp_parse_args( $args, array(
274
+ 'total_items' => 0,
275
+ 'total_pages' => 0,
276
+ 'per_page' => 0,
277
+ ) );
278
+
279
+ if ( !$args['total_pages'] && $args['per_page'] > 0 )
280
+ $args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] );
281
+
282
+ // Redirect if page number is invalid and headers are not already sent.
283
+ if ( ! headers_sent() && ! wp_doing_ajax() && $args['total_pages'] > 0 && $this->get_pagenum() > $args['total_pages'] ) {
284
+ wp_redirect( add_query_arg( 'paged', $args['total_pages'] ) );
285
+ exit;
286
+ }
287
+
288
+ $this->_pagination_args = $args;
289
+ }
290
+
291
+ /**
292
+ * Access the pagination args.
293
+ *
294
+ * @since 3.1.0
295
+ * @access public
296
+ *
297
+ * @param string $key Pagination argument to retrieve. Common values include 'total_items',
298
+ * 'total_pages', 'per_page', or 'infinite_scroll'.
299
+ * @return int Number of items that correspond to the given pagination argument.
300
+ */
301
+ public function get_pagination_arg( $key ) {
302
+ if ( 'page' === $key ) {
303
+ return $this->get_pagenum();
304
+ }
305
+
306
+ if ( isset( $this->_pagination_args[$key] ) ) {
307
+ return $this->_pagination_args[$key];
308
+ }
309
+ }
310
+
311
+ /**
312
+ * Whether the table has items to display or not
313
+ *
314
+ * @since 3.1.0
315
+ * @access public
316
+ *
317
+ * @return bool
318
+ */
319
+ public function has_items() {
320
+ return !empty( $this->items );
321
+ }
322
+
323
+ /**
324
+ * Message to be displayed when there are no items
325
+ *
326
+ * @since 3.1.0
327
+ * @access public
328
+ */
329
+ public function no_items() {
330
+ _e( 'No items found.' );
331
+ }
332
+
333
+ /**
334
+ * Displays the search box.
335
+ *
336
+ * @since 3.1.0
337
+ * @access public
338
+ *
339
+ * @param string $text The 'submit' button label.
340
+ * @param string $input_id ID attribute value for the search input field.
341
+ */
342
+ public function search_box( $text, $input_id ) {
343
+ if ( empty( $_REQUEST['s'] ) && !$this->has_items() )
344
+ return;
345
+
346
+ $input_id = $input_id . '-search-input';
347
+
348
+ if ( ! empty( $_REQUEST['orderby'] ) )
349
+ echo '<input type="hidden" name="orderby" value="' . esc_attr( $_REQUEST['orderby'] ) . '" />';
350
+ if ( ! empty( $_REQUEST['order'] ) )
351
+ echo '<input type="hidden" name="order" value="' . esc_attr( $_REQUEST['order'] ) . '" />';
352
+ if ( ! empty( $_REQUEST['post_mime_type'] ) )
353
+ echo '<input type="hidden" name="post_mime_type" value="' . esc_attr( $_REQUEST['post_mime_type'] ) . '" />';
354
+ if ( ! empty( $_REQUEST['detached'] ) )
355
+ echo '<input type="hidden" name="detached" value="' . esc_attr( $_REQUEST['detached'] ) . '" />';
356
+ ?>
357
+ <p class="search-box">
358
+ <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"><?php echo $text; ?>:</label>
359
+ <input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="s" value="<?php _admin_search_query(); ?>" />
360
+ <?php submit_button( $text, '', '', false, array( 'id' => 'search-submit' ) ); ?>
361
+ </p>
362
+ <?php
363
+ }
364
+
365
+ /**
366
+ * Get an associative array ( id => link ) with the list
367
+ * of views available on this table.
368
+ *
369
+ * @since 3.1.0
370
+ * @access protected
371
+ *
372
+ * @return array
373
+ */
374
+ protected function get_views() {
375
+ return array();
376
+ }
377
+
378
+ /**
379
+ * Display the list of views available on this table.
380
+ *
381
+ * @since 3.1.0
382
+ * @access public
383
+ */
384
+ public function views() {
385
+ $views = $this->get_views();
386
+ /**
387
+ * Filters the list of available list table views.
388
+ *
389
+ * The dynamic portion of the hook name, `$this->screen->id`, refers
390
+ * to the ID of the current screen, usually a string.
391
+ *
392
+ * @since 3.5.0
393
+ *
394
+ * @param array $views An array of available list table views.
395
+ */
396
+ $views = apply_filters( "views_{$this->screen->id}", $views );
397
+
398
+ if ( empty( $views ) )
399
+ return;
400
+
401
+ $this->screen->render_screen_reader_content( 'heading_views' );
402
+
403
+ echo "<ul class='subsubsub'>\n";
404
+ foreach ( $views as $class => $view ) {
405
+ $views[ $class ] = "\t<li class='$class'>$view";
406
+ }
407
+ echo implode( " |</li>\n", $views ) . "</li>\n";
408
+ echo "</ul>";
409
+ }
410
+
411
+ /**
412
+ * Get an associative array ( option_name => option_title ) with the list
413
+ * of bulk actions available on this table.
414
+ *
415
+ * @since 3.1.0
416
+ * @access protected
417
+ *
418
+ * @return array
419
+ */
420
+ protected function get_bulk_actions() {
421
+ return array();
422
+ }
423
+
424
+ /**
425
+ * Display the bulk actions dropdown.
426
+ *
427
+ * @since 3.1.0
428
+ * @access protected
429
+ *
430
+ * @param string $which The location of the bulk actions: 'top' or 'bottom'.
431
+ * This is designated as optional for backward compatibility.
432
+ */
433
+ protected function bulk_actions( $which = '' ) {
434
+ if ( is_null( $this->_actions ) ) {
435
+ $this->_actions = $this->get_bulk_actions();
436
+ /**
437
+ * Filters the list table Bulk Actions drop-down.
438
+ *
439
+ * The dynamic portion of the hook name, `$this->screen->id`, refers
440
+ * to the ID of the current screen, usually a string.
441
+ *
442
+ * This filter can currently only be used to remove bulk actions.
443
+ *
444
+ * @since 3.5.0
445
+ *
446
+ * @param array $actions An array of the available bulk actions.
447
+ */
448
+ $this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions );
449
+ $two = '';
450
+ } else {
451
+ $two = '2';
452
+ }
453
+
454
+ if ( empty( $this->_actions ) )
455
+ return;
456
+
457
+ echo '<label for="bulk-action-selector-' . esc_attr( $which ) . '" class="screen-reader-text">' . __( 'Select bulk action' ) . '</label>';
458
+ echo '<select name="action' . $two . '" id="bulk-action-selector-' . esc_attr( $which ) . "\">\n";
459
+ echo '<option value="-1">' . __( 'Bulk Actions' ) . "</option>\n";
460
+
461
+ foreach ( $this->_actions as $name => $title ) {
462
+ $class = 'edit' === $name ? ' class="hide-if-no-js"' : '';
463
+
464
+ echo "\t" . '<option value="' . $name . '"' . $class . '>' . $title . "</option>\n";
465
+ }
466
+
467
+ echo "</select>\n";
468
+
469
+ submit_button( __( 'Apply' ), 'action', '', false, array( 'id' => "doaction$two" ) );
470
+ echo "\n";
471
+ }
472
+
473
+ /**
474
+ * Get the current action selected from the bulk actions dropdown.
475
+ *
476
+ * @since 3.1.0
477
+ * @access public
478
+ *
479
+ * @return string|false The action name or False if no action was selected
480
+ */
481
+ public function current_action() {
482
+ if ( isset( $_REQUEST['filter_action'] ) && ! empty( $_REQUEST['filter_action'] ) )
483
+ return false;
484
+
485
+ if ( isset( $_REQUEST['action'] ) && -1 != $_REQUEST['action'] )
486
+ return $_REQUEST['action'];
487
+
488
+ if ( isset( $_REQUEST['action2'] ) && -1 != $_REQUEST['action2'] )
489
+ return $_REQUEST['action2'];
490
+
491
+ return false;
492
+ }
493
+
494
+ /**
495
+ * Generate row actions div
496
+ *
497
+ * @since 3.1.0
498
+ * @access protected
499
+ *
500
+ * @param array $actions The list of actions
501
+ * @param bool $always_visible Whether the actions should be always visible
502
+ * @return string
503
+ */
504
+ protected function row_actions( $actions, $always_visible = false ) {
505
+ $action_count = count( $actions );
506
+ $i = 0;
507
+
508
+ if ( !$action_count )
509
+ return '';
510
+
511
+ $out = '<div class="' . ( $always_visible ? 'row-actions visible' : 'row-actions' ) . '">';
512
+ foreach ( $actions as $action => $link ) {
513
+ ++$i;
514
+ ( $i == $action_count ) ? $sep = '' : $sep = ' | ';
515
+ $out .= "<span class='$action'>$link$sep</span>";
516
+ }
517
+ $out .= '</div>';
518
+
519
+ $out .= '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>';
520
+
521
+ return $out;
522
+ }
523
+
524
+ /**
525
+ * Display a monthly dropdown for filtering items
526
+ *
527
+ * @since 3.1.0
528
+ * @access protected
529
+ *
530
+ * @global wpdb $wpdb
531
+ * @global WP_Locale $wp_locale
532
+ *
533
+ * @param string $post_type
534
+ */
535
+ protected function months_dropdown( $post_type ) {
536
+ global $wpdb, $wp_locale;
537
+
538
+ /**
539
+ * Filters whether to remove the 'Months' drop-down from the post list table.
540
+ *
541
+ * @since 4.2.0
542
+ *
543
+ * @param bool $disable Whether to disable the drop-down. Default false.
544
+ * @param string $post_type The post type.
545
+ */
546
+ if ( apply_filters( 'disable_months_dropdown', false, $post_type ) ) {
547
+ return;
548
+ }
549
+
550
+ $extra_checks = "AND post_status != 'auto-draft'";
551
+ if ( ! isset( $_GET['post_status'] ) || 'trash' !== $_GET['post_status'] ) {
552
+ $extra_checks .= " AND post_status != 'trash'";
553
+ } elseif ( isset( $_GET['post_status'] ) ) {
554
+ $extra_checks = $wpdb->prepare( ' AND post_status = %s', $_GET['post_status'] );
555
+ }
556
+
557
+ $months = $wpdb->get_results( $wpdb->prepare( "
558
+ SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
559
+ FROM $wpdb->posts
560
+ WHERE post_type = %s
561
+ $extra_checks
562
+ ORDER BY post_date DESC
563
+ ", $post_type ) );
564
+
565
+ /**
566
+ * Filters the 'Months' drop-down results.
567
+ *
568
+ * @since 3.7.0
569
+ *
570
+ * @param object $months The months drop-down query results.
571
+ * @param string $post_type The post type.
572
+ */
573
+ $months = apply_filters( 'months_dropdown_results', $months, $post_type );
574
+
575
+ $month_count = count( $months );
576
+
577
+ if ( !$month_count || ( 1 == $month_count && 0 == $months[0]->month ) )
578
+ return;
579
+
580
+ $m = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0;
581
+ ?>
582
+ <label for="filter-by-date" class="screen-reader-text"><?php _e( 'Filter by date' ); ?></label>
583
+ <select name="m" id="filter-by-date">
584
+ <option<?php selected( $m, 0 ); ?> value="0"><?php _e( 'All dates' ); ?></option>
585
+ <?php
586
+ foreach ( $months as $arc_row ) {
587
+ if ( 0 == $arc_row->year )
588
+ continue;
589
+
590
+ $month = zeroise( $arc_row->month, 2 );
591
+ $year = $arc_row->year;
592
+
593
+ printf( "<option %s value='%s'>%s</option>\n",
594
+ selected( $m, $year . $month, false ),
595
+ esc_attr( $arc_row->year . $month ),
596
+ /* translators: 1: month name, 2: 4-digit year */
597
+ sprintf( __( '%1$s %2$d' ), $wp_locale->get_month( $month ), $year )
598
+ );
599
+ }
600
+ ?>
601
+ </select>
602
+ <?php
603
+ }
604
+
605
+ /**
606
+ * Display a view switcher
607
+ *
608
+ * @since 3.1.0
609
+ * @access protected
610
+ *
611
+ * @param string $current_mode
612
+ */
613
+ protected function view_switcher( $current_mode ) {
614
+ ?>
615
+ <input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>" />
616
+ <div class="view-switch">
617
+ <?php
618
+ foreach ( $this->modes as $mode => $title ) {
619
+ $classes = array( 'view-' . $mode );
620
+ if ( $current_mode === $mode )
621
+ $classes[] = 'current';
622
+ printf(
623
+ "<a href='%s' class='%s' id='view-switch-$mode'><span class='screen-reader-text'>%s</span></a>\n",
624
+ esc_url( add_query_arg( 'mode', $mode ) ),
625
+ implode( ' ', $classes ),
626
+ $title
627
+ );
628
+ }
629
+ ?>
630
+ </div>
631
+ <?php
632
+ }
633
+
634
+ /**
635
+ * Display a comment count bubble
636
+ *
637
+ * @since 3.1.0
638
+ * @access protected
639
+ *
640
+ * @param int $post_id The post ID.
641
+ * @param int $pending_comments Number of pending comments.
642
+ */
643
+ protected function comments_bubble( $post_id, $pending_comments ) {
644
+ $approved_comments = get_comments_number();
645
+
646
+ $approved_comments_number = number_format_i18n( $approved_comments );
647
+ $pending_comments_number = number_format_i18n( $pending_comments );
648
+
649
+ $approved_only_phrase = sprintf( _n( '%s comment', '%s comments', $approved_comments ), $approved_comments_number );
650
+ $approved_phrase = sprintf( _n( '%s approved comment', '%s approved comments', $approved_comments ), $approved_comments_number );
651
+ $pending_phrase = sprintf( _n( '%s pending comment', '%s pending comments', $pending_comments ), $pending_comments_number );
652
+
653
+ // No comments at all.
654
+ if ( ! $approved_comments && ! $pending_comments ) {
655
+ printf( '<span aria-hidden="true">—</span><span class="screen-reader-text">%s</span>',
656
+ __( 'No comments' )
657
+ );
658
+ // Approved comments have different display depending on some conditions.
659
+ } elseif ( $approved_comments ) {
660
+ printf( '<a href="%s" class="post-com-count post-com-count-approved"><span class="comment-count-approved" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></a>',
661
+ esc_url( add_query_arg( array( 'p' => $post_id, 'comment_status' => 'approved' ), admin_url( 'edit-comments.php' ) ) ),
662
+ $approved_comments_number,
663
+ $pending_comments ? $approved_phrase : $approved_only_phrase
664
+ );
665
+ } else {
666
+ printf( '<span class="post-com-count post-com-count-no-comments"><span class="comment-count comment-count-no-comments" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></span>',
667
+ $approved_comments_number,
668
+ $pending_comments ? __( 'No approved comments' ) : __( 'No comments' )
669
+ );
670
+ }
671
+
672
+ if ( $pending_comments ) {
673
+ printf( '<a href="%s" class="post-com-count post-com-count-pending"><span class="comment-count-pending" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></a>',
674
+ esc_url( add_query_arg( array( 'p' => $post_id, 'comment_status' => 'moderated' ), admin_url( 'edit-comments.php' ) ) ),
675
+ $pending_comments_number,
676
+ $pending_phrase
677
+ );
678
+ } else {
679
+ printf( '<span class="post-com-count post-com-count-pending post-com-count-no-pending"><span class="comment-count comment-count-no-pending" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></span>',
680
+ $pending_comments_number,
681
+ $approved_comments ? __( 'No pending comments' ) : __( 'No comments' )
682
+ );
683
+ }
684
+ }
685
+
686
+ /**
687
+ * Get the current page number
688
+ *
689
+ * @since 3.1.0
690
+ * @access public
691
+ *
692
+ * @return int
693
+ */
694
+ public function get_pagenum() {
695
+ $pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0;
696
+
697
+ if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] )
698
+ $pagenum = $this->_pagination_args['total_pages'];
699
+
700
+ return max( 1, $pagenum );
701
+ }
702
+
703
+ /**
704
+ * Get number of items to display on a single page
705
+ *
706
+ * @since 3.1.0
707
+ * @access protected
708
+ *
709
+ * @param string $option
710
+ * @param int $default
711
+ * @return int
712
+ */
713
+ protected function get_items_per_page( $option, $default = 20 ) {
714
+ $per_page = (int) get_user_option( $option );
715
+ if ( empty( $per_page ) || $per_page < 1 )
716
+ $per_page = $default;
717
+
718
+ /**
719
+ * Filters the number of items to be displayed on each page of the list table.
720
+ *
721
+ * The dynamic hook name, $option, refers to the `per_page` option depending
722
+ * on the type of list table in use. Possible values include: 'edit_comments_per_page',
723
+ * 'sites_network_per_page', 'site_themes_network_per_page', 'themes_network_per_page',
724
+ * 'users_network_per_page', 'edit_post_per_page', 'edit_page_per_page',
725
+ * 'edit_{$post_type}_per_page', etc.
726
+ *
727
+ * @since 2.9.0
728
+ *
729
+ * @param int $per_page Number of items to be displayed. Default 20.
730
+ */
731
+ return (int) apply_filters( $option, $per_page );
732
+ }
733
+
734
+ /**
735
+ * Display the pagination.
736
+ *
737
+ * @since 3.1.0
738
+ * @access protected
739
+ *
740
+ * @param string $which
741
+ */
742
+ protected function pagination( $which ) {
743
+ if ( empty( $this->_pagination_args ) ) {
744
+ return;
745
+ }
746
+
747
+ $total_items = $this->_pagination_args['total_items'];
748
+ $total_pages = $this->_pagination_args['total_pages'];
749
+ $infinite_scroll = false;
750
+ if ( isset( $this->_pagination_args['infinite_scroll'] ) ) {
751
+ $infinite_scroll = $this->_pagination_args['infinite_scroll'];
752
+ }
753
+
754
+ if ( 'top' === $which && $total_pages > 1 ) {
755
+ $this->screen->render_screen_reader_content( 'heading_pagination' );
756
+ }
757
+
758
+ $output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';
759
+
760
+ $current = $this->get_pagenum();
761
+ $removable_query_args = wp_removable_query_args();
762
+
763
+ $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
764
+
765
+ $current_url = remove_query_arg( $removable_query_args, $current_url );
766
+
767
+ $page_links = array();
768
+
769
+ $total_pages_before = '<span class="paging-input">';
770
+ $total_pages_after = '</span></span>';
771
+
772
+ $disable_first = $disable_last = $disable_prev = $disable_next = false;
773
+
774
+ if ( $current == 1 ) {
775
+ $disable_first = true;
776
+ $disable_prev = true;
777
+ }
778
+ if ( $current == 2 ) {
779
+ $disable_first = true;
780
+ }
781
+ if ( $current == $total_pages ) {
782
+ $disable_last = true;
783
+ $disable_next = true;
784
+ }
785
+ if ( $current == $total_pages - 1 ) {
786
+ $disable_last = true;
787
+ }
788
+
789
+ if ( $disable_first ) {
790
+ $page_links[] = '<span class="tablenav-pages-navspan button" aria-hidden="true">&laquo;</span>';
791
+ } else {
792
+ $page_links[] = sprintf( "<a class='first-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
793
+ esc_url( remove_query_arg( 'paged', $current_url ) ),
794
+ __( 'First page' ),
795
+ '&laquo;'
796
+ );
797
+ }
798
+
799
+ if ( $disable_prev ) {
800
+ $page_links[] = '<span class="tablenav-pages-navspan button" aria-hidden="true">&lsaquo;</span>';
801
+ } else {
802
+ $page_links[] = sprintf( "<a class='prev-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
803
+ esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ),
804
+ __( 'Previous page' ),
805
+ '&lsaquo;'
806
+ );
807
+ }
808
+
809
+ if ( 'bottom' === $which ) {
810
+ $html_current_page = $current;
811
+ $total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page' ) . '</span><span id="table-paging" class="paging-input"><span class="tablenav-paging-text">';
812
+ } else {
813
+ $html_current_page = sprintf( "%s<input class='current-page' id='current-page-selector' type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' /><span class='tablenav-paging-text'>",
814
+ '<label for="current-page-selector" class="screen-reader-text">' . __( 'Current Page' ) . '</label>',
815
+ $current,
816
+ strlen( $total_pages )
817
+ );
818
+ }
819
+ $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
820
+ $page_links[] = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . $total_pages_after;
821
+
822
+ if ( $disable_next ) {
823
+ $page_links[] = '<span class="tablenav-pages-navspan button" aria-hidden="true">&rsaquo;</span>';
824
+ } else {
825
+ $page_links[] = sprintf( "<a class='next-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
826
+ esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ),
827
+ __( 'Next page' ),
828
+ '&rsaquo;'
829
+ );
830
+ }
831
+
832
+ if ( $disable_last ) {
833
+ $page_links[] = '<span class="tablenav-pages-navspan button" aria-hidden="true">&raquo;</span>';
834
+ } else {
835
+ $page_links[] = sprintf( "<a class='last-page button' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
836
+ esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
837
+ __( 'Last page' ),
838
+ '&raquo;'
839
+ );
840
+ }
841
+
842
+ $pagination_links_class = 'pagination-links';
843
+ if ( ! empty( $infinite_scroll ) ) {
844
+ $pagination_links_class = ' hide-if-js';
845
+ }
846
+ $output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';
847
+
848
+ if ( $total_pages ) {
849
+ $page_class = $total_pages < 2 ? ' one-page' : '';
850
+ } else {
851
+ $page_class = ' no-pages';
852
+ }
853
+ $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";
854
+
855
+ echo $this->_pagination;
856
+ }
857
+
858
+ /**
859
+ * Get a list of columns. The format is:
860
+ * 'internal-name' => 'Title'
861
+ *
862
+ * @since 3.1.0
863
+ * @access public
864
+ * @abstract
865
+ *
866
+ * @return array
867
+ */
868
+ public function get_columns() {
869
+ die( 'function WP_List_Table::get_columns() must be over-ridden in a sub-class.' );
870
+ }
871
+
872
+ /**
873
+ * Get a list of sortable columns. The format is:
874
+ * 'internal-name' => 'orderby'
875
+ * or
876
+ * 'internal-name' => array( 'orderby', true )
877
+ *
878
+ * The second format will make the initial sorting order be descending
879
+ *
880
+ * @since 3.1.0
881
+ * @access protected
882
+ *
883
+ * @return array
884
+ */
885
+ protected function get_sortable_columns() {
886
+ return array();
887
+ }
888
+
889
+ /**
890
+ * Gets the name of the default primary column.
891
+ *
892
+ * @since 4.3.0
893
+ * @access protected
894
+ *
895
+ * @return string Name of the default primary column, in this case, an empty string.
896
+ */
897
+ protected function get_default_primary_column_name() {
898
+ $columns = $this->get_columns();
899
+ $column = '';
900
+
901
+ if ( empty( $columns ) ) {
902
+ return $column;
903
+ }
904
+
905
+ // We need a primary defined so responsive views show something,
906
+ // so let's fall back to the first non-checkbox column.
907
+ foreach ( $columns as $col => $column_name ) {
908
+ if ( 'cb' === $col ) {
909
+ continue;
910
+ }
911
+
912
+ $column = $col;
913
+ break;
914
+ }
915
+
916
+ return $column;
917
+ }
918
+
919
+ /**
920
+ * Public wrapper for WP_List_Table::get_default_primary_column_name().
921
+ *
922
+ * @since 4.4.0
923
+ * @access public
924
+ *
925
+ * @return string Name of the default primary column.
926
+ */
927
+ public function get_primary_column() {
928
+ return $this->get_primary_column_name();
929
+ }
930
+
931
+ /**
932
+ * Gets the name of the primary column.
933
+ *
934
+ * @since 4.3.0
935
+ * @access protected
936
+ *
937
+ * @return string The name of the primary column.
938
+ */
939
+ protected function get_primary_column_name() {
940
+ $columns = get_column_headers( $this->screen );
941
+ $default = $this->get_default_primary_column_name();
942
+
943
+ // If the primary column doesn't exist fall back to the
944
+ // first non-checkbox column.
945
+ if ( ! isset( $columns[ $default ] ) ) {
946
+ $default = WPSBC_WP_List_Table::get_default_primary_column_name();
947
+ }
948
+
949
+ /**
950
+ * Filters the name of the primary column for the current list table.
951
+ *
952
+ * @since 4.3.0
953
+ *
954
+ * @param string $default Column name default for the specific list table, e.g. 'name'.
955
+ * @param string $context Screen ID for specific list table, e.g. 'plugins'.
956
+ */
957
+ $column = apply_filters( 'list_table_primary_column', $default, $this->screen->id );
958
+
959
+ if ( empty( $column ) || ! isset( $columns[ $column ] ) ) {
960
+ $column = $default;
961
+ }
962
+
963
+ return $column;
964
+ }
965
+
966
+ /**
967
+ * Get a list of all, hidden and sortable columns, with filter applied
968
+ *
969
+ * @since 3.1.0
970
+ * @access protected
971
+ *
972
+ * @return array
973
+ */
974
+ protected function get_column_info() {
975
+ // $_column_headers is already set / cached
976
+ if ( isset( $this->_column_headers ) && is_array( $this->_column_headers ) ) {
977
+ // Back-compat for list tables that have been manually setting $_column_headers for horse reasons.
978
+ // In 4.3, we added a fourth argument for primary column.
979
+ $column_headers = array( array(), array(), array(), $this->get_primary_column_name() );
980
+ foreach ( $this->_column_headers as $key => $value ) {
981
+ $column_headers[ $key ] = $value;
982
+ }
983
+
984
+ return $column_headers;
985
+ }
986
+
987
+ $columns = get_column_headers( $this->screen );
988
+ $hidden = get_hidden_columns( $this->screen );
989
+
990
+ $sortable_columns = $this->get_sortable_columns();
991
+ /**
992
+ * Filters the list table sortable columns for a specific screen.
993
+ *
994
+ * The dynamic portion of the hook name, `$this->screen->id`, refers
995
+ * to the ID of the current screen, usually a string.
996
+ *
997
+ * @since 3.5.0
998
+ *
999
+ * @param array $sortable_columns An array of sortable columns.
1000
+ */
1001
+ $_sortable = apply_filters( "manage_{$this->screen->id}_sortable_columns", $sortable_columns );
1002
+
1003
+ $sortable = array();
1004
+ foreach ( $_sortable as $id => $data ) {
1005
+ if ( empty( $data ) )
1006
+ continue;
1007
+
1008
+ $data = (array) $data;
1009
+ if ( !isset( $data[1] ) )
1010
+ $data[1] = false;
1011
+
1012
+ $sortable[$id] = $data;
1013
+ }
1014
+
1015
+ $primary = $this->get_primary_column_name();
1016
+ $this->_column_headers = array( $columns, $hidden, $sortable, $primary );
1017
+
1018
+ return $this->_column_headers;
1019
+ }
1020
+
1021
+ /**
1022
+ * Return number of visible columns
1023
+ *
1024
+ * @since 3.1.0
1025
+ * @access public
1026
+ *
1027
+ * @return int
1028
+ */
1029
+ public function get_column_count() {
1030
+ list ( $columns, $hidden ) = $this->get_column_info();
1031
+ $hidden = array_intersect( array_keys( $columns ), array_filter( $hidden ) );
1032
+ return count( $columns ) - count( $hidden );
1033
+ }
1034
+
1035
+ /**
1036
+ * Print column headers, accounting for hidden and sortable columns.
1037
+ *
1038
+ * @since 3.1.0
1039
+ * @access public
1040
+ *
1041
+ * @staticvar int $cb_counter
1042
+ *
1043
+ * @param bool $with_id Whether to set the id attribute or not
1044
+ */
1045
+ public function print_column_headers( $with_id = true ) {
1046
+ list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
1047
+
1048
+ $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
1049
+ $current_url = remove_query_arg( 'paged', $current_url );
1050
+
1051
+ if ( isset( $_GET['orderby'] ) ) {
1052
+ $current_orderby = $_GET['orderby'];
1053
+ } else {
1054
+ $current_orderby = '';
1055
+ }
1056
+
1057
+ if ( isset( $_GET['order'] ) && 'desc' === $_GET['order'] ) {
1058
+ $current_order = 'desc';
1059
+ } else {
1060
+ $current_order = 'asc';
1061
+ }
1062
+
1063
+ if ( ! empty( $columns['cb'] ) ) {
1064
+ static $cb_counter = 1;
1065
+ $columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All' ) . '</label>'
1066
+ . '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />';
1067
+ $cb_counter++;
1068
+ }
1069
+
1070
+ foreach ( $columns as $column_key => $column_display_name ) {
1071
+ $class = array( 'manage-column', "column-$column_key" );
1072
+
1073
+ if ( in_array( $column_key, $hidden ) ) {
1074
+ $class[] = 'hidden';
1075
+ }
1076
+
1077
+ if ( 'cb' === $column_key )
1078
+ $class[] = 'check-column';
1079
+ elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ) ) )
1080
+ $class[] = 'num';
1081
+
1082
+ if ( $column_key === $primary ) {
1083
+ $class[] = 'column-primary';
1084
+ }
1085
+
1086
+ if ( isset( $sortable[$column_key] ) ) {
1087
+ list( $orderby, $desc_first ) = $sortable[$column_key];
1088
+
1089
+ if ( $current_orderby === $orderby ) {
1090
+ $order = 'asc' === $current_order ? 'desc' : 'asc';
1091
+ $class[] = 'sorted';
1092
+ $class[] = $current_order;
1093
+ } else {
1094
+ $order = $desc_first ? 'desc' : 'asc';
1095
+ $class[] = 'sortable';
1096
+ $class[] = $desc_first ? 'asc' : 'desc';
1097
+ }
1098
+
1099
+ $column_display_name = '<a href="' . esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ) . '"><span>' . $column_display_name . '</span><span class="sorting-indicator"></span></a>';
1100
+ }
1101
+
1102
+ $tag = ( 'cb' === $column_key ) ? 'td' : 'th';
1103
+ $scope = ( 'th' === $tag ) ? 'scope="col"' : '';
1104
+ $id = $with_id ? "id='$column_key'" : '';
1105
+
1106
+ if ( !empty( $class ) )
1107
+ $class = "class='" . join( ' ', $class ) . "'";
1108
+
1109
+ echo "<$tag $scope $id $class>$column_display_name</$tag>";
1110
+ }
1111
+ }
1112
+
1113
+ /**
1114
+ * Display the table
1115
+ *
1116
+ * @since 3.1.0
1117
+ * @access public
1118
+ */
1119
+ public function display() {
1120
+ $singular = $this->_args['singular'];
1121
+
1122
+ $this->display_tablenav( 'top' );
1123
+
1124
+ $this->screen->render_screen_reader_content( 'heading_list' );
1125
+ ?>
1126
+ <table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>">
1127
+ <thead>
1128
+ <tr>
1129
+ <?php $this->print_column_headers(); ?>
1130
+ </tr>
1131
+ </thead>
1132
+
1133
+ <tbody id="the-list"<?php
1134
+ if ( $singular ) {
1135
+ echo " data-wp-lists='list:$singular'";
1136
+ } ?>>
1137
+ <?php $this->display_rows_or_placeholder(); ?>
1138
+ </tbody>
1139
+
1140
+ <tfoot>
1141
+ <tr>
1142
+ <?php $this->print_column_headers( false ); ?>
1143
+ </tr>
1144
+ </tfoot>
1145
+
1146
+ </table>
1147
+ <?php
1148
+ $this->display_tablenav( 'bottom' );
1149
+ }
1150
+
1151
+ /**
1152
+ * Get a list of CSS classes for the WP_List_Table table tag.
1153
+ *
1154
+ * @since 3.1.0
1155
+ * @access protected
1156
+ *
1157
+ * @return array List of CSS classes for the table tag.
1158
+ */
1159
+ protected function get_table_classes() {
1160
+ return array( 'widefat', 'fixed', 'striped', $this->_args['plural'] );
1161
+ }
1162
+
1163
+ /**
1164
+ * Generate the table navigation above or below the table
1165
+ *
1166
+ * @since 3.1.0
1167
+ * @access protected
1168
+ * @param string $which
1169
+ */
1170
+ protected function display_tablenav( $which ) {
1171
+ if ( 'top' === $which ) {
1172
+ wp_nonce_field( 'bulk-' . $this->_args['plural'], '_wpnonce', false );
1173
+ }
1174
+ ?>
1175
+ <div class="tablenav <?php echo esc_attr( $which ); ?>">
1176
+
1177
+ <?php if ( $this->has_items() ): ?>
1178
+ <div class="alignleft actions bulkactions">
1179
+ <?php $this->bulk_actions( $which ); ?>
1180
+ </div>
1181
+ <?php endif;
1182
+ $this->extra_tablenav( $which );
1183
+ $this->pagination( $which );
1184
+ ?>
1185
+
1186
+ <br class="clear" />
1187
+ </div>
1188
+ <?php
1189
+ }
1190
+
1191
+ /**
1192
+ * Extra controls to be displayed between bulk actions and pagination
1193
+ *
1194
+ * @since 3.1.0
1195
+ * @access protected
1196
+ *
1197
+ * @param string $which
1198
+ */
1199
+ protected function extra_tablenav( $which ) {}
1200
+
1201
+ /**
1202
+ * Generate the tbody element for the list table.
1203
+ *
1204
+ * @since 3.1.0
1205
+ * @access public
1206
+ */
1207
+ public function display_rows_or_placeholder() {
1208
+ if ( $this->has_items() ) {
1209
+ $this->display_rows();
1210
+ } else {
1211
+ echo '<tr class="no-items"><td class="colspanchange" colspan="' . $this->get_column_count() . '">';
1212
+ $this->no_items();
1213
+ echo '</td></tr>';
1214
+ }
1215
+ }
1216
+
1217
+ /**
1218
+ * Generate the table rows
1219
+ *
1220
+ * @since 3.1.0
1221
+ * @access public
1222
+ */
1223
+ public function display_rows() {
1224
+ foreach ( $this->items as $item )
1225
+ $this->single_row( $item );
1226
+ }
1227
+
1228
+ /**
1229
+ * Generates content for a single row of the table
1230
+ *
1231
+ * @since 3.1.0
1232
+ * @access public
1233
+ *
1234
+ * @param object $item The current item
1235
+ */
1236
+ public function single_row( $item ) {
1237
+ echo '<tr>';
1238
+ $this->single_row_columns( $item );
1239
+ echo '</tr>';
1240
+ }
1241
+
1242
+ /**
1243
+ *
1244
+ * @param object $item
1245
+ * @param string $column_name
1246
+ */
1247
+ protected function column_default( $item, $column_name ) {}
1248
+
1249
+ /**
1250
+ *
1251
+ * @param object $item
1252
+ */
1253
+ protected function column_cb( $item ) {}
1254
+
1255
+ /**
1256
+ * Generates the columns for a single row of the table
1257
+ *
1258
+ * @since 3.1.0
1259
+ * @access protected
1260
+ *
1261
+ * @param object $item The current item
1262
+ */
1263
+ protected function single_row_columns( $item ) {
1264
+ list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
1265
+
1266
+ foreach ( $columns as $column_name => $column_display_name ) {
1267
+ $classes = "$column_name column-$column_name";
1268
+ if ( $primary === $column_name ) {
1269
+ $classes .= ' has-row-actions column-primary';
1270
+ }
1271
+
1272
+ if ( in_array( $column_name, $hidden ) ) {
1273
+ $classes .= ' hidden';
1274
+ }
1275
+
1276
+ // Comments column uses HTML in the display name with screen reader text.
1277
+ // Instead of using esc_attr(), we strip tags to get closer to a user-friendly string.
1278
+ $data = 'data-colname="' . wp_strip_all_tags( $column_display_name ) . '"';
1279
+
1280
+ $attributes = "class='$classes' $data";
1281
+
1282
+ if ( 'cb' === $column_name ) {
1283
+ echo '<th scope="row" class="check-column">';
1284
+ echo $this->column_cb( $item );
1285
+ echo '</th>';
1286
+ } elseif ( method_exists( $this, '_column_' . $column_name ) ) {
1287
+ echo call_user_func(
1288
+ array( $this, '_column_' . $column_name ),
1289
+ $item,
1290
+ $classes,
1291
+ $data,
1292
+ $primary
1293
+ );
1294
+ } elseif ( method_exists( $this, 'column_' . $column_name ) ) {
1295
+ echo "<td $attributes>";
1296
+ echo call_user_func( array( $this, 'column_' . $column_name ), $item );
1297
+ echo $this->handle_row_actions( $item, $column_name, $primary );
1298
+ echo "</td>";
1299
+ } else {
1300
+ echo "<td $attributes>";
1301
+ echo $this->column_default( $item, $column_name );
1302
+ echo $this->handle_row_actions( $item, $column_name, $primary );
1303
+ echo "</td>";
1304
+ }
1305
+ }
1306
+ }
1307
+
1308
+ /**
1309
+ * Generates and display row actions links for the list table.
1310
+ *
1311
+ * @since 4.3.0
1312
+ * @access protected
1313
+ *
1314
+ * @param object $item The item being acted upon.
1315
+ * @param string $column_name Current column name.
1316
+ * @param string $primary Primary column name.
1317
+ * @return string The row actions HTML, or an empty string if the current column is the primary column.
1318
+ */
1319
+ protected function handle_row_actions( $item, $column_name, $primary ) {
1320
+ return $column_name === $primary ? '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>' : '';
1321
+ }
1322
+
1323
+ /**
1324
+ * Handle an incoming ajax request (called from admin-ajax.php)
1325
+ *
1326
+ * @since 3.1.0
1327
+ * @access public
1328
+ */
1329
+ public function ajax_response() {
1330
+ $this->prepare_items();
1331
+
1332
+ ob_start();
1333
+ if ( ! empty( $_REQUEST['no_placeholder'] ) ) {
1334
+ $this->display_rows();
1335
+ } else {
1336
+ $this->display_rows_or_placeholder();
1337
+ }
1338
+
1339
+ $rows = ob_get_clean();
1340
+
1341
+ $response = array( 'rows' => $rows );
1342
+
1343
+ if ( isset( $this->_pagination_args['total_items'] ) ) {
1344
+ $response['total_items_i18n'] = sprintf(
1345
+ _n( '%s item', '%s items', $this->_pagination_args['total_items'] ),
1346
+ number_format_i18n( $this->_pagination_args['total_items'] )
1347
+ );
1348
+ }
1349
+ if ( isset( $this->_pagination_args['total_pages'] ) ) {
1350
+ $response['total_pages'] = $this->_pagination_args['total_pages'];
1351
+ $response['total_pages_i18n'] = number_format_i18n( $this->_pagination_args['total_pages'] );
1352
+ }
1353
+
1354
+ die( wp_json_encode( $response ) );
1355
+ }
1356
+
1357
+ /**
1358
+ * Send required variables to JavaScript land
1359
+ *
1360
+ * @access public
1361
+ */
1362
+ public function _js_vars() {
1363
+ $args = array(
1364
+ 'class' => get_class( $this ),
1365
+ 'screen' => array(
1366
+ 'id' => $this->screen->id,
1367
+ 'base' => $this->screen->base,
1368
+ )
1369
+ );
1370
+
1371
+ printf( "<script type='text/javascript'>list_args = %s;</script>\n", wp_json_encode( $args ) );
1372
+ }
1373
+ }
includes/abstracts/abstract-class-object-db.php ADDED
@@ -0,0 +1,351 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Base class for all core database objects
9
+ *
10
+ */
11
+ abstract class WPSBC_Object_DB extends WPSBC_DB {
12
+
13
+ /**
14
+ * Object type to query for
15
+ *
16
+ * @access public
17
+ * @var string
18
+ *
19
+ */
20
+ public $query_object_type = 'stdClass';
21
+
22
+
23
+ /**
24
+ * Constructor
25
+ *
26
+ * Subclasses should set the $table_name, $primary_key, $context and $query_object_type
27
+ *
28
+ * @access public
29
+ *
30
+ */
31
+ public function __construct() {}
32
+
33
+
34
+ /**
35
+ * Returns a table row for the given row id
36
+ *
37
+ * @param int $row_id
38
+ *
39
+ * @return mixed object|null
40
+ *
41
+ */
42
+ public function get( $row_id ) {
43
+
44
+ global $wpdb;
45
+
46
+ $row = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$this->table_name} WHERE {$this->primary_key} = %s LIMIT 1", $row_id ) );
47
+
48
+ // json decode
49
+ if( is_object( $row ) ) {
50
+
51
+ foreach( array_keys( get_object_vars( $row ) ) as $key )
52
+ $row->$key = $this->json_decode( $row->$key );
53
+
54
+ }
55
+
56
+ return $row;
57
+
58
+ }
59
+
60
+
61
+ /**
62
+ * Retrieves results for the given query clauses
63
+ *
64
+ * @param array $clauses - an array with SQL ready clauses
65
+ * @param array $args - the query args
66
+ * @param string $callback - a callback to be run against every returned result
67
+ *
68
+ * @return mixed array|int|null
69
+ *
70
+ */
71
+ protected function get_results( $clauses, $args, $callback = '' ) {
72
+
73
+ global $wpdb;
74
+
75
+ if( true === $clauses['count'] ) {
76
+
77
+ $results = $wpdb->get_var( "SELECT COUNT({$this->primary_key}) FROM {$this->table_name} {$clauses['where']}" );
78
+
79
+ return absint( $results );
80
+
81
+ } else {
82
+
83
+ $results = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$this->table_name} {$clauses['where']} ORDER BY {$clauses['orderby']} {$clauses['order']} LIMIT %d, %d", absint( $args['offset'] ), absint( $args['number'] ) ) );
84
+
85
+ }
86
+
87
+ // json decode
88
+ if( ! empty( $results ) ) {
89
+
90
+ foreach( $results as $result_key => $row ) {
91
+
92
+ if( is_object( $row ) ) {
93
+
94
+ foreach( array_keys( get_object_vars( $row ) ) as $key )
95
+ $row->$key = $this->json_decode( $row->$key );
96
+
97
+ }
98
+
99
+ $results[$result_key] = $row;
100
+
101
+ }
102
+
103
+ }
104
+
105
+
106
+ if( ! empty( $callback ) && is_callable( $callback ) )
107
+ $results = array_map( $callback, $results );
108
+
109
+ return $results;
110
+
111
+ }
112
+
113
+
114
+ /**
115
+ * Inserts a new row into the database table
116
+ *
117
+ * @param array $data
118
+ *
119
+ * @return mixed int|false
120
+ *
121
+ */
122
+ public function insert( $data ) {
123
+
124
+ global $wpdb;
125
+
126
+ /**
127
+ * Modify the data to be added into a new row just before
128
+ * the insert procedure
129
+ *
130
+ * @param array $data
131
+ *
132
+ */
133
+ $data = apply_filters( "wpsbc_pre_insert_{$this->context}_data", $data );
134
+
135
+ if( empty( $data ) )
136
+ return false;
137
+
138
+ $column_formats = $this->get_columns();
139
+
140
+ // Encodes the values of the array that are arrays
141
+ foreach( $data as $key => $val ) {
142
+ $data[$key] = $this->json_encode( $val );
143
+ }
144
+
145
+ // Make array keys lowercase
146
+ $data = array_change_key_case( $data );
147
+
148
+ // Filter out unwanted keys
149
+ $data = array_intersect_key( $data, $column_formats );
150
+
151
+ // Strip slashes
152
+ $data = wp_unslash( $data );
153
+
154
+ /**
155
+ * Fires just before a new row is to be inserted into the table
156
+ *
157
+ * @param array $data
158
+ *
159
+ */
160
+ do_action( 'wpsbc_pre_insert_' . $this->context, $data );
161
+
162
+ // Arrange column formats to match data elements
163
+ $data_keys = array_keys( $data );
164
+ $column_formats = array_merge( array_flip( $data_keys ), $column_formats );
165
+
166
+ // Insert the new row
167
+ $inserted = $wpdb->insert( $this->table_name, $data, $column_formats );
168
+
169
+ if( ! $inserted )
170
+ return false;
171
+
172
+ $insert_id = $wpdb->insert_id;
173
+
174
+ /**
175
+ * Fires right after a new row has been inserted
176
+ *
177
+ * @param int $insert_id
178
+ * @param array $data
179
+ *
180
+ */
181
+ do_action( 'wpsbc_insert_' . $this->context, $insert_id, $data );
182
+
183
+ return $insert_id;
184
+
185
+ }
186
+
187
+
188
+ /**
189
+ * Updates a row from the database table
190
+ *
191
+ * @param int $row_id
192
+ *
193
+ * @return bool
194
+ *
195
+ */
196
+ public function update( $row_id, $data ) {
197
+
198
+ global $wpdb;
199
+
200
+ $row_id = absint( $row_id );
201
+
202
+ $column_formats = $this->get_columns();
203
+
204
+ // Encodes the values of the array that are arrays
205
+ foreach( $data as $key => $val ) {
206
+ $data[$key] = $this->json_encode( $val );
207
+ }
208
+
209
+ // Make array keys lowercase
210
+ $data = array_change_key_case( $data );
211
+
212
+ // Filter out unwanted keys
213
+ $data = array_intersect_key( $data, $column_formats );
214
+
215
+ // Strip slashes
216
+ $data = wp_unslash( $data );
217
+
218
+ // Make sure the primary key is not included
219
+ if( isset( $data[ $this->primary_key ] ) )
220
+ unset( $data[ $this->primary_key ] );
221
+
222
+ $data_keys = array_keys( $data );
223
+ $column_formats = array_merge( array_flip( $data_keys ), $column_formats );
224
+
225
+ // Update
226
+ $updated = $wpdb->update( $this->table_name, $data, array( $this->primary_key => $row_id ), $column_formats );
227
+
228
+ if( false === $updated )
229
+ return false;
230
+
231
+ /**
232
+ * Fires right after a row has been updated
233
+ *
234
+ * @param int $inserted
235
+ * @param array $data
236
+ *
237
+ */
238
+ do_action( 'wpsbc_update_' . $this->context, $row_id, $data );
239
+
240
+ return true;
241
+
242
+ }
243
+
244
+
245
+ /**
246
+ * Removes a row from the database table
247
+ *
248
+ * @param int $row_id
249
+ *
250
+ * @return bool
251
+ *
252
+ */
253
+ public function delete( $row_id ) {
254
+
255
+ global $wpdb;
256
+
257
+ $row_id = absint( $row_id );
258
+
259
+ /**
260
+ * Fires right before a row is removed from the database table
261
+ *
262
+ * @param int $row_id
263
+ *
264
+ */
265
+ do_action( 'wpsbc_pre_delete_' . $this->context, $row_id );
266
+
267
+ // Delete the row
268
+ $deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM {$this->table_name} WHERE {$this->primary_key} = %d", $row_id ) );
269
+
270
+ if( false === $deleted )
271
+ return false;
272
+
273
+ /**
274
+ * Fires right after the row is removed from the database table
275
+ *
276
+ * @param int $row_id
277
+ *
278
+ */
279
+ do_action( 'wpsbc_delete_' . $this->context, $row_id );
280
+
281
+ return true;
282
+
283
+ }
284
+
285
+
286
+ /**
287
+ * Returns an instance of the $query_object_type given an object or an id
288
+ *
289
+ * @param mixed int|object
290
+ *
291
+ * @return mixed object|null
292
+ *
293
+ */
294
+ public function get_object( $object ) {
295
+
296
+ if( ! class_exists( $this->query_object_type ) )
297
+ return null;
298
+
299
+ if( $object instanceof $this->query_object_type )
300
+ $_object = $object;
301
+
302
+ elseif( is_object( $object ) )
303
+ $_object = new $this->query_object_type( $object );
304
+
305
+ else {
306
+
307
+ $object = $this->get( $object );
308
+
309
+ if( is_null( $object ) )
310
+ $_object = null;
311
+ else
312
+ $_object = new $this->query_object_type( $object );
313
+
314
+ }
315
+
316
+ return $_object;
317
+
318
+ }
319
+
320
+
321
+ /**
322
+ * Encodes each value of a given array to json
323
+ *
324
+ * @param array $val
325
+ *
326
+ * @return string
327
+ *
328
+ */
329
+ protected function json_encode( $val ) {
330
+
331
+ if( is_array( $val ) || is_object( $val ) )
332
+ $val = json_encode( $val );
333
+
334
+ return $val;
335
+
336
+ }
337
+
338
+
339
+ /**
340
+ * Decodes a given json string to array
341
+ *
342
+ */
343
+ protected function json_decode( $str ) {
344
+
345
+ $arr = json_decode( $str, true );
346
+
347
+ return ( json_last_error() == JSON_ERROR_NONE ? $arr : $str );
348
+
349
+ }
350
+
351
+ }
includes/abstracts/abstract-class-object-meta-db.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Base class for all core database objects.
9
+ *
10
+ */
11
+ abstract class WPSBC_Object_Meta_DB extends WPSBC_DB {
12
+
13
+
14
+ /**
15
+ * Constructor
16
+ *
17
+ * Subclasses should set the $table_name, $primary_key, $context
18
+ *
19
+ * @access public
20
+ *
21
+ */
22
+ public function __construct() {}
23
+
24
+
25
+ /**
26
+ * Inserts a new meta entry for the object
27
+ *
28
+ * @param int $object_id
29
+ * @param string $meta_key
30
+ * @param string $meta_value
31
+ * @param bool $unique
32
+ *
33
+ * @return mixed int|false
34
+ *
35
+ */
36
+ public function add( $object_id, $meta_key, $meta_value, $unique = false ) {
37
+
38
+ return add_metadata( $this->context, $object_id, $meta_key, $meta_value, $unique );
39
+
40
+ }
41
+
42
+
43
+ /**
44
+ * Updates a meta entry for the object
45
+ *
46
+ * @param int $object_id
47
+ * @param string $meta_key
48
+ * @param string $meta_value
49
+ * @param bool $prev_value
50
+ *
51
+ * @return bool
52
+ *
53
+ */
54
+ public function update( $object_id, $meta_key, $meta_value, $prev_value = '' ) {
55
+
56
+ return update_metadata( $this->context, $object_id, $meta_key, $meta_value, $prev_value );
57
+
58
+ }
59
+
60
+
61
+ /**
62
+ * Returns a meta entry for the object
63
+ *
64
+ * @param int $object_id
65
+ * @param string $meta_key
66
+ * @param bool $single
67
+ *
68
+ * @return mixed
69
+ *
70
+ */
71
+ public function get( $object_id, $meta_key = '', $single = false ) {
72
+
73
+ return get_metadata( $this->context, $object_id, $meta_key, $single );
74
+
75
+ }
76
+
77
+
78
+ /**
79
+ * Removes a meta entry for the object
80
+ *
81
+ * @param int $object_id
82
+ * @param string $meta_key
83
+ * @param string $meta_value
84
+ * @param bool $delete_all
85
+ *
86
+ * @return bool
87
+ *
88
+ */
89
+ public function delete( $object_id, $meta_key, $meta_value = '', $delete_all = '' ) {
90
+
91
+ return delete_metadata( $this->context, $object_id, $meta_key, $meta_value, $delete_all );
92
+
93
+ }
94
+
95
+ }
includes/abstracts/abstract-class-object.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Base class for all object
9
+ *
10
+ */
11
+ abstract class WPSBC_Base_Object {
12
+
13
+
14
+ /**
15
+ * Constructor
16
+ *
17
+ */
18
+ public function __construct( $object ) {
19
+
20
+ foreach( get_object_vars( $object ) as $key => $value ) {
21
+
22
+ if( ! property_exists( $object, $key ) )
23
+ continue;
24
+
25
+ $this->$key = $value;
26
+
27
+ }
28
+
29
+ }
30
+
31
+
32
+ /**
33
+ * Getter
34
+ *
35
+ * @param string $property
36
+ *
37
+ */
38
+ public function get( $property = '' ) {
39
+
40
+ if( method_exists( $this, 'get_' . $property ) )
41
+ return $this->{'get_' . $property}();
42
+ else
43
+ return $this->$property;
44
+
45
+ }
46
+
47
+
48
+ /**
49
+ * Returns the object attributes and their values as an array
50
+ *
51
+ */
52
+ public function to_array() {
53
+
54
+ return get_object_vars( $this );
55
+
56
+ }
57
+
58
+ }
includes/abstracts/abstract-class-submenu-page.php ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Base class to add custom submenu pages
9
+ *
10
+ */
11
+ abstract class WPSBC_Submenu_Page {
12
+
13
+ /**
14
+ * The menu page under which the submenu page should be added
15
+ *
16
+ * @access protected
17
+ * @var string
18
+ *
19
+ */
20
+ protected $parent_slug;
21
+
22
+
23
+ /**
24
+ * The title of the submenu page
25
+ *
26
+ * @access protected
27
+ * @var string
28
+ *
29
+ */
30
+ protected $page_title;
31
+
32
+
33
+ /**
34
+ * The title that should appear in the menu
35
+ *
36
+ * @access protected
37
+ * @var string
38
+ *
39
+ */
40
+ protected $menu_title;
41
+
42
+
43
+ /**
44
+ * The user capability required to view this page
45
+ *
46
+ * @access protected
47
+ * @var string
48
+ *
49
+ */
50
+ protected $capability;
51
+
52
+
53
+ /**
54
+ * The menu
55
+ *
56
+ * @access protected
57
+ * @var string
58
+ *
59
+ */
60
+ protected $menu_slug;
61
+
62
+ /**
63
+ * The current subpage url query arg
64
+ *
65
+ * @access protected
66
+ * @var string
67
+ *
68
+ */
69
+ protected $current_subpage;
70
+
71
+ /**
72
+ * The current tab url query arg
73
+ *
74
+ * @access protected
75
+ * @var string
76
+ *
77
+ */
78
+ protected $current_tab;
79
+
80
+ /**
81
+ * The admin path to the page, used in admin_url
82
+ *
83
+ * @access protected
84
+ * @var string
85
+ *
86
+ */
87
+ protected $admin_url;
88
+
89
+ /**
90
+ * A list with admin notices to be printed on the page
91
+ *
92
+ * @access protected
93
+ * @var string
94
+ *
95
+ */
96
+ protected $admin_notices = array();
97
+
98
+
99
+ /**
100
+ * Constructor
101
+ *
102
+ */
103
+ public function __construct( $page_title = '', $menu_title = '', $capability = '', $menu_slug = '' ) {
104
+
105
+ $this->parent_slug = 'wp-simple-booking-calendar';
106
+ $this->page_title = $page_title;
107
+ $this->menu_title = $menu_title;
108
+ $this->capability = $capability;
109
+ $this->menu_slug = $menu_slug;
110
+
111
+ $this->current_subpage = ( ! empty( $_GET['subpage'] ) ? $_GET['subpage'] : '' );
112
+ $this->current_tab = ( ! empty( $_GET['tab'] ) ? $_GET['tab'] : '' );
113
+
114
+ add_action( 'admin_menu', array( $this, 'add_submenu_page' ) );
115
+
116
+ $this->init();
117
+
118
+ }
119
+
120
+
121
+ /**
122
+ * Helper init method to avoid rewriting of the __construct method by subclasses
123
+ *
124
+ */
125
+ protected function init() {}
126
+
127
+
128
+ /**
129
+ * Getter
130
+ *
131
+ * @param string $property
132
+ *
133
+ */
134
+ public function get( $property = '' ) {
135
+
136
+ if( method_exists( $this, 'get_' . $property ) )
137
+ return $this->{'get_' . $property}();
138
+ else
139
+ return $this->$property;
140
+
141
+ }
142
+
143
+
144
+ /**
145
+ * Callback to add the submenu page
146
+ *
147
+ */
148
+ public function add_submenu_page() {
149
+
150
+ $hook_sufix = add_submenu_page( $this->parent_slug, $this->page_title, $this->menu_title, $this->capability, $this->menu_slug, array( $this, 'output' ) );
151
+
152
+ if( $hook_sufix ) {
153
+
154
+ $this->admin_url = add_query_arg( array( 'page' => $this->menu_slug ), 'admin.php' );
155
+
156
+ }
157
+
158
+ }
159
+
160
+
161
+ /**
162
+ * Callback for the HTML output for the page
163
+ *
164
+ */
165
+ public function output() {}
166
+
167
+ }
includes/base/admin/backup/class-submenu-page-backup.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ Class WPSBC_Submenu_Page_Backup extends WPSBC_Submenu_Page {
8
+
9
+ /**
10
+ * Helper init method that runs on parent __construct
11
+ *
12
+ */
13
+ protected function init() {
14
+
15
+ add_action( 'admin_init', array( $this, 'register_admin_notices' ), 10 );
16
+
17
+ }
18
+
19
+
20
+ /**
21
+ * Callback method to register admin notices that are sent via URL parameters
22
+ *
23
+ */
24
+ public function register_admin_notices() {
25
+
26
+ if( empty( $_GET['wpsbc_message'] ) )
27
+ return;
28
+
29
+ // Calendar insert success
30
+ wpsbc_admin_notices()->register_notice( 'import_file_success', '<p>' . __( 'Data from the file has been imported successfully.', 'wp-simple-booking-calendar' ) . '</p>' );
31
+
32
+ }
33
+
34
+
35
+ /**
36
+ * Callback for the HTML output for the Calendar page
37
+ *
38
+ */
39
+ public function output() {
40
+
41
+ include 'views/view-backup.php';
42
+
43
+ }
44
+
45
+ }
includes/base/admin/backup/functions-actions-backup.php ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Handles the export of the backup file
9
+ *
10
+ */
11
+ function wpsbc_action_backup_export() {
12
+
13
+ // Let only admins do this procedure
14
+ if( ! current_user_can( 'manage_options' ) )
15
+ return;
16
+
17
+ // Verify for nonce
18
+ if( empty( $_POST['wpsbc_token'] ) || ! wp_verify_nonce( $_POST['wpsbc_token'], 'wpsbc_backup_export' ) )
19
+ return;
20
+
21
+ // Set export data variable
22
+ $export_data = array();
23
+
24
+ // Get wpdb global
25
+ global $wpdb;
26
+
27
+ /**
28
+ * Go through each custom db table and save the values
29
+ *
30
+ */
31
+ foreach( wp_simple_booking_calendar()->db as $table_slug => $db_object ) {
32
+
33
+ $results = $wpdb->get_results( "SELECT * FROM {$db_object->table_name}" );
34
+
35
+ $export_data[$table_slug] = $results;
36
+
37
+ }
38
+
39
+ /**
40
+ * General settings and plugin version number
41
+ *
42
+ */
43
+ $export_data['settings'] = get_option( 'wpsbc_settings', array() );
44
+ $export_data['plugin_version'] = WPSBC_VERSION;
45
+
46
+ header( 'Content-disposition: attachment; filename=wpsbc-export-' . current_time('Y') . '-' . current_time('m') . '-' . current_time('d') . '-' . current_time('H') . '-' . current_time('i') . '-' . current_time('s') . '.wpsbc' );
47
+ header( 'Content-type: application/json' );
48
+
49
+ echo( json_encode( $export_data ) );
50
+ exit;
51
+
52
+ }
53
+ add_action( 'wpsbc_action_backup_export', 'wpsbc_action_backup_export' );
54
+
55
+
56
+ /**
57
+ * Handles the export of the backup file
58
+ *
59
+ */
60
+ function wpsbc_action_backup_import() {
61
+
62
+ // Let only admins do this procedure
63
+ if( ! current_user_can( 'manage_options' ) )
64
+ return;
65
+
66
+ // Verify for nonce
67
+ if( empty( $_POST['wpsbc_token'] ) || ! wp_verify_nonce( $_POST['wpsbc_token'], 'wpsbc_backup_import' ) )
68
+ return;
69
+
70
+ // Check if file has been selected
71
+ if( empty( $_FILES['import_file']['tmp_name'] ) ) {
72
+
73
+ wpsbc_admin_notices()->register_notice( 'import_file_missing', '<p>' . __( 'Please select a file.', 'wp-simple-booking-calendar' ) . '</p>', 'error' );
74
+ wpsbc_admin_notices()->display_notice( 'import_file_missing' );
75
+
76
+ return;
77
+
78
+ }
79
+
80
+ // Check for proper extension
81
+ if( false === strpos( $_FILES['import_file']['name'], '.wpsbc' ) ) {
82
+
83
+ wpsbc_admin_notices()->register_notice( 'import_file_extension_fail', '<p>' . __( 'The file you are trying to import does not have a valid extension. The extension of the file should be ".wpsbc".', 'wp-simple-booking-calendar' ) . '</p>', 'error' );
84
+ wpsbc_admin_notices()->display_notice( 'import_file_extension_fail' );
85
+
86
+ return;
87
+
88
+ }
89
+
90
+ // Decode the file
91
+ $file_contents = json_decode( file_get_contents( $_FILES['import_file']['tmp_name'] ), true );
92
+
93
+ // Check for proper plugin version
94
+ if( empty( $file_contents['plugin_version'] ) ) {
95
+
96
+ wpsbc_admin_notices()->register_notice( 'import_file_version_fail', '<p>' . __( 'The file you are trying to import is not compatible with this version of the plugin.', 'wp-simple-booking-calendar' ) . '</p>', 'error' );
97
+ wpsbc_admin_notices()->display_notice( 'import_file_version_fail' );
98
+
99
+ return;
100
+
101
+ }
102
+
103
+
104
+ // Get wpdb global
105
+ global $wpdb;
106
+
107
+
108
+ /**
109
+ * Empty all registered tables
110
+ *
111
+ */
112
+ foreach( wp_simple_booking_calendar()->db as $table_slug => $db_object ) {
113
+
114
+ $wpdb->query( "TRUNCATE {$db_object->table_name}" );
115
+
116
+ }
117
+
118
+
119
+ /**
120
+ * Go through each custom db table and insert the values from the file
121
+ *
122
+ */
123
+ foreach( wp_simple_booking_calendar()->db as $table_slug => $db_object ) {
124
+
125
+ if( empty( $file_contents[$table_slug] ) || ! is_array( $file_contents[$table_slug] ) )
126
+ continue;
127
+
128
+ foreach( $file_contents[$table_slug] as $key => $values ) {
129
+
130
+ $wpdb->insert( $db_object->table_name, $values );
131
+
132
+ }
133
+
134
+ }
135
+
136
+ /**
137
+ * Add general settings
138
+ *
139
+ */
140
+ if( ! empty( $file_contents['settings'] ) )
141
+ update_option( 'wpsbc_settings', $file_contents['settings'] );
142
+
143
+ // Redirect with a success message
144
+ wp_redirect( add_query_arg( array( 'page' => 'wpsbc-backup', 'wpsbc_message' => 'import_file_success' ), admin_url( 'admin.php' ) ) );
145
+ exit;
146
+
147
+ }
148
+ add_action( 'wpsbc_action_backup_import', 'wpsbc_action_backup_import' );
includes/base/admin/backup/functions.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Includes the files needed for the Backup/Restore admin area
9
+ *
10
+ */
11
+ function wpsbc_include_files_admin_backup() {
12
+
13
+ // Get legend admin dir path
14
+ $dir_path = plugin_dir_path( __FILE__ );
15
+
16
+ // Include actions
17
+ if( file_exists( $dir_path . 'functions-actions-backup.php' ) )
18
+ include $dir_path . 'functions-actions-backup.php';
19
+
20
+ // Include submenu page
21
+ if( file_exists( $dir_path . 'class-submenu-page-backup.php' ) )
22
+ include $dir_path . 'class-submenu-page-backup.php';
23
+
24
+ }
25
+ add_action( 'wpsbc_include_files', 'wpsbc_include_files_admin_backup' );
26
+
27
+
28
+ /**
29
+ * Register the Backup/Restore admin submenu page
30
+ *
31
+ */
32
+ function wpsbc_register_submenu_page_backup( $submenu_pages ) {
33
+
34
+ if( ! is_array( $submenu_pages ) )
35
+ return $submenu_pages;
36
+
37
+ $submenu_pages['backup'] = array(
38
+ 'class_name' => 'WPSBC_Submenu_Page_Backup',
39
+ 'data' => array(
40
+ 'page_title' => __( 'Backup/Restore', 'wp-simple-booking-calendar' ),
41
+ 'menu_title' => __( 'Backup/Restore', 'wp-simple-booking-calendar' ),
42
+ 'capability' => apply_filters( 'wpsbc_submenu_page_capability_backup', 'manage_options' ),
43
+ 'menu_slug' => 'wpsbc-backup'
44
+ )
45
+ );
46
+
47
+ return $submenu_pages;
48
+
49
+ }
50
+ add_filter( 'wpsbc_register_submenu_page', 'wpsbc_register_submenu_page_backup', 75 );
includes/base/admin/backup/views/view-backup.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ ?>
7
+
8
+ <div class="wrap wpsbc-wrap">
9
+
10
+ <!-- Page Heading -->
11
+ <h1 class="wp-heading-inline"><?php echo __( 'Backup/Restore', 'wp-simple-booking-calendar' ); ?></h1>
12
+ <hr class="wp-header-end" />
13
+
14
+ <div id="poststuff" class="postbox-container">
15
+
16
+ <!-- Postbox Export -->
17
+ <div class="postbox">
18
+
19
+ <form method="POST" action="">
20
+
21
+ <h2 class="hndle"><?php echo __( 'Create export file', 'wp-simple-booking-calendar' ); ?></h2>
22
+
23
+ <!-- Form Fields -->
24
+ <div class="inside">
25
+
26
+ <p><?php echo __( 'To export a backup file for WP Simple Booking Calendar, please click the Download Export File button.', 'wp-simple-booking-calendar' ); ?></p>
27
+
28
+ <p><?php echo __( 'The export file will contain all information saved by the plugin, including calendars, bookings and general settings.', 'wp-simple-booking-calendar' ); ?></p>
29
+
30
+ </div>
31
+
32
+ <!-- Card Bottom -->
33
+ <div class="wpsbc-plugin-card-bottom plugin-card-bottom">
34
+ <input type="submit" class="button-primary" value="<?php echo __( 'Download Export File', 'wp-simple-booking-calendar' ); ?>" />
35
+ </div>
36
+
37
+ <!-- Nonce -->
38
+ <?php wp_nonce_field( 'wpsbc_backup_export', 'wpsbc_token', false ); ?>
39
+ <input type="hidden" name="wpsbc_action" value="backup_export" />
40
+
41
+ </form>
42
+
43
+ </div>
44
+
45
+ <!-- Postbox Import -->
46
+ <div class="postbox">
47
+
48
+ <form enctype="multipart/form-data" method="POST" action="">
49
+
50
+ <h2 class="hndle"><?php echo __( 'Import data from file', 'wp-simple-booking-calendar' ); ?></h2>
51
+
52
+ <!-- Form Fields -->
53
+ <div class="inside">
54
+
55
+ <p><?php echo __( 'To import all data from a WP Simple Booking Calendar export file, browse for that file and click the Import Data button.', 'wp-simple-booking-calendar' ); ?></p>
56
+
57
+ <p><?php echo __( 'Please take into account that this action will firstly remove all data that you currently have in the plugin, then import the data from the export file.', 'wp-simple-booking-calendar' ); ?></p>
58
+
59
+ <input type="file" name="import_file" />
60
+
61
+ </div>
62
+
63
+ <!-- Card Bottom -->
64
+ <div class="wpsbc-plugin-card-bottom plugin-card-bottom">
65
+ <input onclick="return confirm('<?php echo __( 'Are you sure you want to import data from the file?', 'wp-simple-booking-calendar' ); ?>' )" type="submit" class="button-primary" value="<?php echo __( 'Import Data', 'wp-simple-booking-calendar' ); ?>" />
66
+ </div>
67
+
68
+ <!-- Nonce -->
69
+ <?php wp_nonce_field( 'wpsbc_backup_import', 'wpsbc_token', false ); ?>
70
+ <input type="hidden" name="wpsbc_action" value="backup_import" />
71
+
72
+ </form>
73
+
74
+ </div>
75
+
76
+ </div>
77
+
78
+ </div>
includes/base/admin/calendar/class-calendar-editor-outputter.php ADDED
@@ -0,0 +1,454 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if (!defined('ABSPATH')) {
5
+ exit;
6
+ }
7
+
8
+ class WPSBC_Calendar_Editor_Outputter
9
+ {
10
+
11
+ /**
12
+ * The arguments for the calendar outputter
13
+ *
14
+ * @access protected
15
+ * @var array
16
+ *
17
+ */
18
+ protected $args;
19
+
20
+ /**
21
+ * The WPSBC_Calendar
22
+ *
23
+ * @access protected
24
+ * @var WPSBC_Calendar
25
+ *
26
+ */
27
+ protected $calendar = null;
28
+
29
+ /**
30
+ * The list of legend items associated with the calendar
31
+ *
32
+ * @access protected
33
+ * @var array
34
+ *
35
+ */
36
+ protected $legend_items = array();
37
+
38
+ /**
39
+ * The default legend item of the calendar
40
+ *
41
+ * @access protected
42
+ * @var WPSBC_Legend_Item
43
+ *
44
+ */
45
+ protected $default_legend_item = null;
46
+
47
+ /**
48
+ * The list of events for the calendar for the given displayed range
49
+ *
50
+ * @access protected
51
+ * @var array
52
+ *
53
+ */
54
+ protected $events = array();
55
+
56
+ /**
57
+ * Custom calendar data arranged by date
58
+ *
59
+ * @access protected
60
+ * @var array
61
+ *
62
+ */
63
+ protected $data = array();
64
+
65
+ /**
66
+ * Constructor
67
+ *
68
+ * @param int $calendar - the calendar for which to print the editable calendar fields
69
+ * @param array $args - arguments from which to build the calendar fields
70
+ * @param array $data - extra data to be populated in the calendar fields on top of the
71
+ * already saved events
72
+ *
73
+ */
74
+ public function __construct($calendar, $args, $data = array())
75
+ {
76
+
77
+ $defaults = array(
78
+ 'current_year' => (!empty($args['current_year']) ? $args['current_year'] : current_time('Y')),
79
+ 'current_month' => (!empty($args['current_month']) ? $args['current_month'] : current_time('n')),
80
+ );
81
+
82
+ /**
83
+ * Set arguments
84
+ *
85
+ */
86
+ $this->args = wp_parse_args($args, $defaults);
87
+
88
+ /**
89
+ * Set the calendar
90
+ *
91
+ */
92
+ $this->calendar = $calendar;
93
+
94
+ /**
95
+ * Set the calendar legend items
96
+ *
97
+ */
98
+ $this->legend_items = wpsbc_get_legend_items(array('calendar_id' => $calendar->get('id')));
99
+
100
+ /**
101
+ * Set the default legend item
102
+ *
103
+ */
104
+ foreach ($this->legend_items as $legend_item) {
105
+
106
+ if ($legend_item->get('is_default') == 1) {
107
+ $this->default_legend_item = $legend_item;
108
+ }
109
+
110
+ }
111
+
112
+ /**
113
+ * Set the calendar events
114
+ *
115
+ */
116
+ $this->events = wpsbc_get_events(array('calendar_id' => $calendar->get('id'), 'date_year' => $this->args['current_year'], 'date_month' => $this->args['current_month']));
117
+
118
+ /**
119
+ * Set the calendar data
120
+ *
121
+ */
122
+ $this->data = $data;
123
+
124
+ }
125
+
126
+ /**
127
+ * Constructs and returns the HTML for the entire calendar month editor
128
+ *
129
+ * @return string
130
+ *
131
+ */
132
+ public function get_display()
133
+ {
134
+
135
+ $total_days = date('t', mktime(0, 0, 0, $this->args['current_month'], 1, $this->args['current_year']));
136
+
137
+ $output = '<div id="wpsbc-calendar-editor">';
138
+
139
+ $output .= $this->get_display_date_header();
140
+
141
+ for ($i = 1; $i <= $total_days; $i++) {
142
+
143
+ $output .= $this->get_display_date($this->args['current_year'], $this->args['current_month'], $i);
144
+
145
+ }
146
+
147
+ /**
148
+ * Calendar Editor Custom CSS
149
+ *
150
+ */
151
+ $output .= $this->get_custom_css();
152
+
153
+ $output .= '</div>';
154
+
155
+ return $output;
156
+
157
+ }
158
+
159
+ /**
160
+ * Constructs and returns the HTML for the dates columns header
161
+ *
162
+ * @return string
163
+ *
164
+ */
165
+ protected function get_display_date_header()
166
+ {
167
+
168
+ $output = '<div class="wpsbc-calendar-date">';
169
+
170
+ $output .= '<div class="wpsbc-calendar-date-legend-item-header">' . __('Availability', 'wp-simple-booking-calendar') . '</div>';
171
+ $output .= '<div class="wpsbc-calendar-date-description-header">' . __('Description', 'wp-simple-booking-calendar') . wpsbc_get_output_tooltip(__('You can use the description field of the date to add private information regarding your booking. This information will not be displayed anywhere else, but here.', 'wp-simple-booking-calendar')) . '</div>';
172
+
173
+ $output .= '</div>';
174
+
175
+ return $output;
176
+
177
+ }
178
+
179
+ /**
180
+ * Constructs and returns the HTML for a single calendar given date
181
+ *
182
+ * @param int $year
183
+ * @param int $month
184
+ * @param int $day
185
+ *
186
+ * @return string
187
+ *
188
+ */
189
+ protected function get_display_date($year, $month, $day)
190
+ {
191
+
192
+ /**
193
+ * Prepare data
194
+ *
195
+ */
196
+ $data = $this->get_data_by_date($year, $month, $day);
197
+ $event = $this->get_event_by_date($year, $month, $day);
198
+
199
+ /**
200
+ * Prepare output
201
+ *
202
+ */
203
+ $output = '<div class="wpsbc-calendar-date">';
204
+
205
+ // Get the date legend item
206
+ $output .= $this->get_display_date_legend_item($year, $month, $day, $event, $data);
207
+
208
+ // Get the date description
209
+ $output .= $this->get_display_date_description($year, $month, $day, $event, $data);
210
+
211
+ // Set-up extra data that can be added by others
212
+ $output .= '<div class="wpsbc-calendar-date-meta">';
213
+
214
+ /**
215
+ * Filter to add custom calendar date meta
216
+ *
217
+ * @param int $year
218
+ * @param int $month
219
+ * @param int $day
220
+ * @param mixed WPSBC_Event|null
221
+ * @param mixed array|null
222
+ *
223
+ */
224
+ $output .= apply_filters('wpsbc_calendar_editor_display_date', '', $year, $month, $day, $event, $data);
225
+
226
+ $output .= '</div>';
227
+
228
+ $output .= '</div>';
229
+
230
+ return $output;
231
+
232
+ }
233
+
234
+ /**
235
+ * Constructs and returns the HTML for the date legend item selector
236
+ *
237
+ * @param int $year
238
+ * @param int $month
239
+ * @param int $day
240
+ * @param mixed WPSBC_Event|null
241
+ * @param mixed array|null
242
+ *
243
+ * @return string
244
+ *
245
+ */
246
+ protected function get_display_date_legend_item($year, $month, $day, $event, $data)
247
+ {
248
+
249
+ /**
250
+ * Set selected value
251
+ *
252
+ */
253
+ $selected_id = '';
254
+ $selected = null;
255
+
256
+ if (!is_null($data)) {
257
+
258
+ $selected_id = $data['legend_item_id'];
259
+
260
+ } elseif (!is_null($event)) {
261
+
262
+ $selected_id = $event->get('legend_item_id');
263
+
264
+ }
265
+
266
+ foreach ($this->legend_items as $legend_item) {
267
+
268
+ if ($selected_id == $legend_item->get('id')) {
269
+ $selected = $legend_item;
270
+ break;
271
+ }
272
+
273
+ }
274
+
275
+ // If none of the existing legend items is the selected one,
276
+ // set the default legend item as the selected one
277
+ if (is_null($selected)) {
278
+ $selected = $this->default_legend_item;
279
+ $selected_id = $selected->get('id');
280
+ }
281
+
282
+ /**
283
+ * Prepare output
284
+ *
285
+ */
286
+ $output = '<div class="wpsbc-calendar-date-legend-item wpsbc-calendar-date-legend-item-' . esc_attr($selected->get('id')) . '">';
287
+
288
+ $output .= '<div class="wpsbc-legend-item-icon-wrapper">';
289
+ $output .= '<div class="wpsbc-date-inner">' . esc_attr($day) . '</div>';
290
+ $output .= wpsbc_get_legend_item_icon($selected->get('id'), $selected->get('type'));
291
+ $output .= '</div>';
292
+
293
+ $output .= '<select data-name="legend_item_id" data-year="' . esc_attr($year) . '" data-month="' . esc_attr($month) . '" data-day="' . esc_attr($day) . '" ' . (!is_null($event) && is_null($event->get('id')) ? 'disabled' : '') . '>';
294
+
295
+ foreach ($this->legend_items as $legend_item) {
296
+
297
+ $output .= '<option value="' . esc_attr($legend_item->get('id')) . '" ' . selected($selected_id, $legend_item->get('id'), false) . ' data-type="' . esc_attr($legend_item->get('type')) . '">' . $legend_item->get('name') . '</option>';
298
+
299
+ }
300
+
301
+ $output .= '</select>';
302
+ $output .= '</div>';
303
+
304
+ return $output;
305
+
306
+ }
307
+
308
+ /**
309
+ * Constructs and returns the HTML for the date description field
310
+ *
311
+ * @param int $year
312
+ * @param int $month
313
+ * @param int $day
314
+ * @param mixed WPSBC_Event|null
315
+ * @param mixed array|null
316
+ *
317
+ * @return string
318
+ *
319
+ */
320
+ protected function get_display_date_description($year, $month, $day, $event, $data)
321
+ {
322
+
323
+ /**
324
+ * Set selected value
325
+ *
326
+ */
327
+ $value = '';
328
+
329
+ if (!is_null($data) && isset($data['description'])) {
330
+
331
+ $value = $data['description'];
332
+
333
+ } elseif (!is_null($event)) {
334
+
335
+ $value = $event->get('description');
336
+
337
+ }
338
+
339
+ /**
340
+ * Prepare output
341
+ *
342
+ */
343
+ $output = '<div class="wpsbc-calendar-date-description">';
344
+
345
+ $output .= '<span class="dashicons dashicons-edit"></span>';
346
+ $output .= '<input type="text" value="' . esc_attr($value) . '" data-name="description" data-year="' . esc_attr($year) . '" data-month="' . esc_attr($month) . '" data-day="' . esc_attr($day) . '" />';
347
+
348
+ $output .= '</div>';
349
+
350
+ return $output;
351
+
352
+ }
353
+
354
+ /**
355
+ * Passes through all stored events and searches for the event that matches the given date
356
+ * If an event is found it is returned, else null is returned
357
+ *
358
+ * @param int $year
359
+ * @param int $month
360
+ * @param int $day
361
+ *
362
+ * @return mixed WPSBC_Event|null
363
+ *
364
+ */
365
+ protected function get_event_by_date($year, $month, $day)
366
+ {
367
+
368
+ foreach ($this->events as $event) {
369
+
370
+ if ($event->get('date_year') == $year && $event->get('date_month') == $month && $event->get('date_day') == $day) {
371
+ return $event;
372
+ }
373
+
374
+ }
375
+
376
+ return null;
377
+
378
+ }
379
+
380
+ /**
381
+ * Passes through all stored calendar data and searches for the data that matches the given date
382
+ * If data is found it is returned, else null is returned
383
+ *
384
+ * @param int $year
385
+ * @param int $month
386
+ * @param int $day
387
+ *
388
+ * @return mixed array|null
389
+ *
390
+ */
391
+ protected function get_data_by_date($year, $month, $day)
392
+ {
393
+
394
+ if (isset($this->data[$year][$month][$day])) {
395
+ return $this->data[$year][$month][$day];
396
+ }
397
+
398
+ return null;
399
+
400
+ }
401
+
402
+ /**
403
+ * Constructs and returns the calendar's custom CSS
404
+ *
405
+ * @return string
406
+ *
407
+ */
408
+ protected function get_custom_css()
409
+ {
410
+
411
+ $output = '<style>';
412
+
413
+ // Set the parent calendar class
414
+ $calendar_parent_class = '#wpsbc-calendar-editor';
415
+
416
+ /**
417
+ * Legend Items CSS
418
+ *
419
+ */
420
+ foreach ($this->legend_items as $legend_item) {
421
+
422
+ // Background colors
423
+ $colors = $legend_item->get('color');
424
+
425
+ $output .= $calendar_parent_class . ' .wpsbc-legend-item-icon-' . esc_attr($legend_item->get('id')) . ' div:first-of-type { background-color: ' . (!empty($colors[0]) ? esc_attr($colors[0]) : 'transparent') . '; }';
426
+ $output .= $calendar_parent_class . ' .wpsbc-legend-item-icon-' . esc_attr($legend_item->get('id')) . ' div:nth-of-type(2) { background-color: ' . (!empty($colors[1]) ? esc_attr($colors[1]) : 'transparent') . '; }';
427
+
428
+ // Text color
429
+ $color_text = $legend_item->get('color_text');
430
+
431
+ if (!empty($color_text)) {
432
+ $output .= $calendar_parent_class . ' .wpsbc-calendar-date-legend-item-' . esc_attr($legend_item->get('id')) . ' .wpsbc-date-inner { color: ' . esc_attr($color_text) . '; }';
433
+ }
434
+
435
+ }
436
+
437
+ $output .= '</style>';
438
+
439
+ return $output;
440
+
441
+ }
442
+
443
+ /**
444
+ * Helper function that prints the calendar editor
445
+ *
446
+ */
447
+ public function display()
448
+ {
449
+
450
+ echo $this->get_display();
451
+
452
+ }
453
+
454
+ }
includes/base/admin/calendar/class-list-table-calendars.php ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * List table class outputter for Calendars
9
+ *
10
+ */
11
+ Class WPSBC_WP_List_Table_Calendars extends WPSBC_WP_List_Table {
12
+
13
+ /**
14
+ * The number of calendars that should appear in the table
15
+ *
16
+ * @access private
17
+ * @var int
18
+ *
19
+ */
20
+ private $items_per_page;
21
+
22
+ /**
23
+ * The number of the page being displayed by the pagination
24
+ *
25
+ * @access private
26
+ * @var int
27
+ *
28
+ */
29
+ private $paged;
30
+
31
+ /**
32
+ * The data of the table
33
+ *
34
+ * @access public
35
+ * @var array
36
+ *
37
+ */
38
+ public $data = array();
39
+
40
+
41
+ /**
42
+ * Constructor
43
+ *
44
+ */
45
+ public function __construct() {
46
+
47
+ parent::__construct( array(
48
+ 'plural' => 'wpsbc_calendars',
49
+ 'singular' => 'wpsbc_calendar',
50
+ 'ajax' => false
51
+ ));
52
+
53
+ /**
54
+ * Filter the number of calendars shown in the table
55
+ *
56
+ * @param int
57
+ *
58
+ */
59
+ $this->items_per_page = apply_filters( 'wpsbc_list_table_calendars_items_per_page', 20 );
60
+
61
+ $this->paged = ( ! empty( $_GET['paged'] ) ? (int)$_GET['paged'] : 1 );
62
+
63
+ $this->set_pagination_args( array(
64
+ 'total_items' => wpsbc_get_calendars( array( 'number' => -1, 'search' => ( ! empty( $_GET['s'] ) ? sanitize_text_field( $_GET['s'] ) : '' ) ), true ),
65
+ 'per_page' => $this->items_per_page
66
+ ));
67
+
68
+ // Get and set table data
69
+ $this->set_table_data();
70
+
71
+ // Add column headers and table items
72
+ $this->_column_headers = array( $this->get_columns(), array(), $this->get_sortable_columns() );
73
+ $this->items = $this->data;
74
+
75
+ }
76
+
77
+
78
+ /**
79
+ * Returns all the columns for the table
80
+ *
81
+ */
82
+ public function get_columns() {
83
+
84
+ $columns = array(
85
+ 'name' => __( 'Name', 'wp-simple-booking-calendar' ),
86
+ 'id' => __( 'ID', 'wp-simple-booking-calendar' ),
87
+ 'date_created' => __( 'Date Created', 'wp-simple-booking-calendar' ),
88
+ 'date_modified' => __( 'Date Modified', 'wp-simple-booking-calendar' )
89
+ );
90
+
91
+ /**
92
+ * Filter the columns of the calendars table
93
+ *
94
+ * @param array $columns
95
+ *
96
+ */
97
+ return apply_filters( 'wpsbc_list_table_calendars_columns', $columns );
98
+
99
+ }
100
+
101
+
102
+ /**
103
+ * Overwrites the parent class.
104
+ * Define which columns are sortable
105
+ *
106
+ * @return array
107
+ *
108
+ */
109
+ public function get_sortable_columns() {
110
+
111
+ return array(
112
+ 'name' => array( 'name', false ),
113
+ 'id' => array( 'id', false ),
114
+ 'date_created' => array( 'date_created', false ),
115
+ 'date_modified' => array( 'date_modified', false )
116
+ );
117
+
118
+ }
119
+
120
+
121
+ /**
122
+ * Returns the possible views for the calendar list table
123
+ *
124
+ */
125
+ protected function get_views() {
126
+
127
+ $calendar_status = ( ! empty( $_GET['calendar_status'] ) ? sanitize_text_field( $_GET['calendar_status'] ) : 'active' );
128
+
129
+ $views = array(
130
+ 'active' => '<a href="' . add_query_arg( array( 'page' => 'wpsbc-calendars', 'calendar_status' => 'active', 'paged' => 1, 's' => ( ! empty( $_GET['s'] ) ? sanitize_text_field( $_GET['s'] ) : '' ) ), admin_url( 'admin.php' ) ) . '" ' . ( $calendar_status == 'active' ? 'class="current"' : '' ) . '>' . __( 'Active', 'wp-simple-booking-calendar' ) . ' <span class="count">(' . wpsbc_get_calendars( array( 'status' => 'active', 'search' => ( ! empty( $_GET['s'] ) ? sanitize_text_field( $_GET['s'] ) : '' ) ), true ) . ')</span></a>',
131
+ 'trash' => '<a href="' . add_query_arg( array( 'page' => 'wpsbc-calendars', 'calendar_status' => 'trash', 'paged' => 1, 's' => ( ! empty( $_GET['s'] ) ? sanitize_text_field( $_GET['s'] ) : '' ) ), admin_url( 'admin.php' ) ) . '" ' . ( $calendar_status == 'trash' ? 'class="current"' : '' ) . '>' . __( 'Trash', 'wp-simple-booking-calendar' ) . ' <span class="count">(' . wpsbc_get_calendars( array( 'status' => 'trash', 'search' => ( ! empty( $_GET['s'] ) ? sanitize_text_field( $_GET['s'] ) : '' ) ), true ) . ')</span></a>',
132
+ );
133
+
134
+ /**
135
+ * Filter the views of the calendars table
136
+ *
137
+ * @param array $views
138
+ *
139
+ */
140
+ return apply_filters( 'wpsbc_list_table_calendars_views', $views );
141
+
142
+ }
143
+
144
+
145
+ /**
146
+ * Gets the calendars data and sets it
147
+ *
148
+ */
149
+ private function set_table_data() {
150
+
151
+ $calendar_args = array(
152
+ 'number' => $this->items_per_page,
153
+ 'offset' => ( $this->paged - 1 ) * $this->items_per_page,
154
+ 'status' => ( ! empty( $_GET['calendar_status'] ) ? sanitize_text_field( $_GET['calendar_status'] ) : 'active' ),
155
+ 'orderby' => ( ! empty( $_GET['orderby'] ) ? sanitize_text_field( $_GET['orderby'] ) : 'id' ),
156
+ 'order' => ( ! empty( $_GET['order'] ) ? sanitize_text_field( strtoupper( $_GET['order'] ) ) : 'DESC' ),
157
+ 'search' => ( ! empty( $_GET['s'] ) ? sanitize_text_field( $_GET['s'] ) : '' )
158
+ );
159
+
160
+ $calendars = wpsbc_get_calendars( $calendar_args );
161
+
162
+ if( empty( $calendars ) )
163
+ return;
164
+
165
+ foreach( $calendars as $calendar ) {
166
+
167
+ $row_data = $calendar->to_array();
168
+
169
+ /**
170
+ * Filter the calendar row data
171
+ *
172
+ * @param array $row_data
173
+ * @param WPSBC_Calendar $calendar
174
+ *
175
+ */
176
+ $row_data = apply_filters( 'wpsbc_list_table_calendars_row_data', $row_data, $calendar );
177
+
178
+ $this->data[] = $row_data;
179
+
180
+ }
181
+
182
+ }
183
+
184
+
185
+ /**
186
+ * Returns the HTML that will be displayed in each columns
187
+ *
188
+ * @param array $item - data for the current row
189
+ * @param string $column_name - name of the current column
190
+ *
191
+ * @return string
192
+ *
193
+ */
194
+ public function column_default( $item, $column_name ) {
195
+
196
+ return isset( $item[ $column_name ] ) ? $item[ $column_name ] : '-';
197
+
198
+ }
199
+
200
+
201
+ /**
202
+ * Returns the HTML that will be displayed in the "name" column
203
+ *
204
+ * @param array $item - data for the current row
205
+ *
206
+ * @return string
207
+ *
208
+ */
209
+ public function column_name( $item ) {
210
+
211
+ if( $item['status'] == 'active' ) {
212
+
213
+ $output = '<strong><a class="row-title" href="' . add_query_arg( array( 'page' => 'wpsbc-calendars', 'subpage' => 'edit-calendar', 'calendar_id' => $item['id'] ) , admin_url( 'admin.php' ) ) . '">' . ( !empty( $item['name'] ) ? $item['name'] : '' ) . '</a></strong>';
214
+
215
+ $actions = array(
216
+ 'edit_calendar' => '<a href="' . add_query_arg( array( 'page' => 'wpsbc-calendars', 'subpage' => 'edit-calendar', 'calendar_id' => $item['id'] ) , admin_url( 'admin.php' ) ) . '">' . __( 'Edit Calendar', 'wp-simple-booking-calendar' ) . '</a>',
217
+ 'view_legend' => '<a href="' . add_query_arg( array( 'page' => 'wpsbc-calendars', 'subpage' => 'view-legend', 'calendar_id' => $item['id'] ) , admin_url( 'admin.php' ) ) . '">' . __( 'Edit Calendar Legend', 'wp-simple-booking-calendar' ) . '</a>',
218
+ 'trash' => '<span class="trash"><a onclick="return confirm( \'' . __( "Are you sure you want to send this calendar to the trash?", "wp-simple-booking-calendar" ) . ' \' )" href="' . wp_nonce_url( add_query_arg( array( 'page' => 'wpsbc-calendars', 'wpsbc_action' => 'trash_calendar', 'calendar_id' => $item['id'] ) , admin_url( 'admin.php' ) ), 'wpsbc_trash_calendar', 'wpsbc_token' ) . '" class="submitdelete">' . __( 'Trash', 'wp-simple-booking-calendar' ) . '</a></span>'
219
+ );
220
+
221
+ }
222
+
223
+ if( $item['status'] == 'trash' ) {
224
+
225
+ $output = '<strong>' . ( !empty( $item['name'] ) ? $item['name'] : '' ) . '</strong>';
226
+
227
+ $actions = array(
228
+ 'restore_calendar' => '<a href="' . wp_nonce_url( add_query_arg( array( 'page' => 'wpsbc-calendars', 'wpsbc_action' => 'restore_calendar', 'calendar_id' => $item['id'] ) , admin_url( 'admin.php' ) ), 'wpsbc_restore_calendar', 'wpsbc_token' ) . '">' . __( 'Restore Calendar', 'wp-simple-booking-calendar' ) . '</a>',
229
+ 'delete' => '<span class="trash"><a onclick="return confirm( \'' . __( "Are you sure you want to delete this calendar?", "wp-simple-booking-calendar" ) . ' \' )" href="' . wp_nonce_url( add_query_arg( array( 'page' => 'wpsbc-calendars', 'wpsbc_action' => 'delete_calendar', 'calendar_id' => $item['id'] ) , admin_url( 'admin.php' ) ), 'wpsbc_delete_calendar', 'wpsbc_token' ) . '" class="submitdelete">' . __( 'Delete Permanently', 'wp-simple-booking-calendar' ) . '</a></span>'
230
+ );
231
+
232
+ }
233
+
234
+ /**
235
+ * Filter the row actions before adding them to the table
236
+ *
237
+ * @param array $actions
238
+ * @param array $item
239
+ *
240
+ */
241
+ $actions = apply_filters( 'wpsbc_list_table_calendars_row_actions', $actions, $item );
242
+
243
+ $output .= $this->row_actions( $actions );
244
+
245
+ return $output;
246
+
247
+ }
248
+
249
+
250
+ /**
251
+ * Returns the HTML that will be displayed in the "date_created" column
252
+ *
253
+ * @param array $item - data for the current row
254
+ *
255
+ * @return string
256
+ *
257
+ */
258
+ public function column_date_created( $item ) {
259
+
260
+ $output = date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), strtotime( $item['date_created'] ) );
261
+
262
+ return $output;
263
+
264
+ }
265
+
266
+
267
+ /**
268
+ * Returns the HTML that will be displayed in the "date_created" column
269
+ *
270
+ * @param array $item - data for the current row
271
+ *
272
+ * @return string
273
+ *
274
+ */
275
+ public function column_date_modified( $item ) {
276
+
277
+ $output = date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), strtotime( $item['date_modified'] ) );
278
+
279
+ return $output;
280
+
281
+ }
282
+
283
+
284
+ /**
285
+ * HTML display when there are no items in the table
286
+ *
287
+ */
288
+ public function no_items() {
289
+
290
+ echo __( 'No calendars found.', 'wp-simple-booking-calendar' );
291
+
292
+ }
293
+
294
+ }
includes/base/admin/calendar/class-submenu-page-calendar.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if (!defined('ABSPATH')) {
5
+ exit;
6
+ }
7
+
8
+ class WPSBC_Submenu_Page_Calendars extends WPSBC_Submenu_Page
9
+ {
10
+
11
+ /**
12
+ * Helper init method that runs on parent __construct
13
+ *
14
+ */
15
+ protected function init()
16
+ {
17
+
18
+ add_action('admin_init', array($this, 'register_admin_notices'), 10);
19
+
20
+ }
21
+
22
+ /**
23
+ * Callback method to register admin notices that are sent via URL parameters
24
+ *
25
+ */
26
+ public function register_admin_notices()
27
+ {
28
+
29
+ if (empty($_GET['wpsbc_message'])) {
30
+ return;
31
+ }
32
+
33
+ // Calendar insert success
34
+ wpsbc_admin_notices()->register_notice('calendar_insert_success', '<p>' . __('Calendar created successfully.', 'wp-simple-booking-calendar') . '</p>');
35
+
36
+ // Calendar updated successfully
37
+ wpsbc_admin_notices()->register_notice('calendar_update_success', '<p>' . __('Calendar updated successfully.', 'wp-simple-booking-calendar') . '</p>');
38
+
39
+ // Calendar updated fail
40
+ wpsbc_admin_notices()->register_notice('calendar_update_fail', '<p>' . __('Something went wrong. Could not update the calendar.', 'wp-simple-booking-calendar') . '</p>', 'error');
41
+
42
+ // Calendar trash success
43
+ wpsbc_admin_notices()->register_notice('calendar_trash_success', '<p>' . __('Calendar successfully moved to Trash.', 'wp-simple-booking-calendar') . '</p>');
44
+
45
+ // Calendar restore success
46
+ wpsbc_admin_notices()->register_notice('calendar_restore_success', '<p>' . __('Calendar has been successfully restored.', 'wp-simple-booking-calendar') . '</p>');
47
+
48
+ // Calendar delete success
49
+ wpsbc_admin_notices()->register_notice('calendar_delete_success', '<p>' . __('Calendar has been successfully deleted.', 'wp-simple-booking-calendar') . '</p>');
50
+
51
+ // Legend item insert success
52
+ wpsbc_admin_notices()->register_notice('legend_item_insert_success', '<p>' . __('Legend item created successfully.', 'wp-simple-booking-calendar') . '</p>');
53
+
54
+ // Legend item update success
55
+ wpsbc_admin_notices()->register_notice('legend_item_update_success', '<p>' . __('Legend item updated successfully.', 'wp-simple-booking-calendar') . '</p>');
56
+
57
+ // Legend item delete success
58
+ wpsbc_admin_notices()->register_notice('legend_item_delete_success', '<p>' . __('Legend item deleted successfully.', 'wp-simple-booking-calendar') . '</p>');
59
+
60
+ // Legend items sort fail
61
+ wpsbc_admin_notices()->register_notice('sort_legend_items_fail', '<p>' . __('Something went wrong. Could not sort the legend items.', 'wp-simple-booking-calendar') . '</p>', 'error');
62
+
63
+ // Legend item make visible/invisible
64
+ if ($_GET['wpsbc_message'] == 'legend_item_make_visible_success' || $_GET['wpsbc_message'] == 'legend_item_make_invisible_success') {
65
+
66
+ $legend_item_id = absint($_GET['legend_item_id']);
67
+ $legend_item = wpsbc_get_legend_item($legend_item_id);
68
+
69
+ wpsbc_admin_notices()->register_notice('legend_item_make_visible_success', '<p>' . sprintf(__('Legend item %s is now visible.', 'wp-simple-booking-calendar'), '<strong>' . $legend_item->get('name') . '</strong>') . '</p>');
70
+ wpsbc_admin_notices()->register_notice('legend_item_make_invisible_success', '<p>' . sprintf(__('Legend item %s is now hidden.', 'wp-simple-booking-calendar'), '<strong>' . $legend_item->get('name') . '</strong>') . '</p>');
71
+
72
+ }
73
+
74
+ // Legend item make default
75
+ if ($_GET['wpsbc_message'] == 'legend_item_make_default_success') {
76
+
77
+ $legend_item_id = absint($_GET['legend_item_id']);
78
+ $legend_item = wpsbc_get_legend_item($legend_item_id);
79
+
80
+ wpsbc_admin_notices()->register_notice('legend_item_make_default_success', '<p>' . sprintf(__('%s legend item is now the default one.', 'wp-simple-booking-calendar'), '<strong>' . $legend_item->get('name') . '</strong>') . '</p>');
81
+
82
+ }
83
+
84
+ }
85
+
86
+ /**
87
+ * Callback for the HTML output for the Calendar page
88
+ *
89
+ */
90
+ public function output()
91
+ {
92
+
93
+ if (empty($this->current_subpage)) {
94
+ include 'views/view-calendars.php';
95
+ } else {
96
+
97
+ if ($this->current_subpage == 'add-calendar') {
98
+ if (count(wpsbc_get_calendars()) > 0) {
99
+ include WPSBC_PLUGIN_DIR . 'includes/base/admin/upgrade-to-premium.php';
100
+ } else {
101
+ include 'views/view-add-calendar.php';
102
+ }
103
+
104
+ }
105
+
106
+ if ($this->current_subpage == 'edit-calendar') {
107
+ include 'views/view-edit-calendar.php';
108
+ }
109
+
110
+ if ($this->current_subpage == 'view-legend') {
111
+ include WPSBC_PLUGIN_DIR . 'includes/base/admin/upgrade-to-premium.php';
112
+ }
113
+
114
+ if ($this->current_subpage == 'ical-import-export') {
115
+ include WPSBC_PLUGIN_DIR . 'includes/base/admin/upgrade-to-premium.php';
116
+ }
117
+
118
+ if ($this->current_subpage == 'upgrade-to-premium') {
119
+ include WPSBC_PLUGIN_DIR . 'includes/base/admin/upgrade-to-premium.php';
120
+ }
121
+
122
+ if ($this->current_subpage == 'csv-export') {
123
+ include WPSBC_PLUGIN_DIR . 'includes/base/admin/upgrade-to-premium.php';
124
+ }
125
+
126
+ }
127
+
128
+ }
129
+
130
+ }
includes/base/admin/calendar/functions-actions-ajax-calendar.php ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Ajax callback to refresh the calendar with new given data from JS
9
+ *
10
+ */
11
+ function wpsbc_refresh_calendar_editor() {
12
+
13
+ if( empty( $_POST['action'] ) || $_POST['action'] != 'wpsbc_refresh_calendar_editor' ) {
14
+ echo __( '', 'wp-simple-booking-calendar' );
15
+ wp_die();
16
+ }
17
+
18
+ if( empty( $_POST['id'] ) ) {
19
+ wp_die();
20
+ }
21
+
22
+ $_POST = stripslashes_deep( $_POST );
23
+
24
+ /**
25
+ * The calendar data had to be JSON encoded due to the php input limit which limited
26
+ * the number of arrays that could be sent.
27
+ *
28
+ * If the calendar data would contain data for a number of years this would be chopped
29
+ *
30
+ */
31
+ $_POST['calendar_data'] = ( ! empty( $_POST['calendar_data'] ) ? json_decode( $_POST['calendar_data'], true ) : array() );
32
+
33
+ $calendar_id = absint( $_POST['id'] );
34
+ $calendar = wpsbc_get_calendar( $calendar_id );
35
+
36
+ $calendar_args = array();
37
+ $calendar_data = ( ! empty( $_POST['calendar_data'] ) ? $_POST['calendar_data'] : array() );
38
+
39
+ foreach( $_POST as $key => $val ) {
40
+
41
+ if( in_array( $key, array_keys( wpsbc_get_calendar_output_default_args() ) ) )
42
+ $calendar_args[$key] = sanitize_text_field( $val );
43
+
44
+ }
45
+
46
+ $calendar_outputter = new WPSBC_Calendar_Outputter( $calendar, $calendar_args );
47
+ $calendar_editor_outputter = new WPSBC_Calendar_Editor_Outputter( $calendar, $calendar_args, $calendar_data );
48
+
49
+ $output = array(
50
+ 'calendar' => $calendar_outputter->get_display(),
51
+ 'calendar_editor' => $calendar_editor_outputter->get_display()
52
+ );
53
+
54
+ echo json_encode( $output );
55
+ wp_die();
56
+
57
+ }
58
+ add_action( 'wp_ajax_wpsbc_refresh_calendar_editor', 'wpsbc_refresh_calendar_editor' );
59
+
60
+
61
+ /**
62
+ * Ajax callback to save the calendar events data in the database
63
+ *
64
+ */
65
+ function wpsbc_save_calendar_data() {
66
+
67
+ $_POST = stripslashes_deep( $_POST );
68
+
69
+ /**
70
+ * Parse the form data as name value pair
71
+ *
72
+ */
73
+ if( empty( $_POST['form_data'] ) ) {
74
+
75
+ echo add_query_arg( array( 'page' => 'wpsbc-calendars', 'wpsbc_message' => 'calendar_update_fail' ), admin_url( 'admin.php' ) );
76
+ wp_die();
77
+
78
+ }
79
+
80
+ parse_str( $_POST['form_data'], $_POST['form_data'] );
81
+
82
+
83
+ /**
84
+ * The calendar data had to be JSON encoded due to the php input limit which limited
85
+ * the number of arrays that could be sent.
86
+ *
87
+ * If the calendar data would contain data for a number of years this would be chopped
88
+ *
89
+ */
90
+ $_POST['calendar_data'] = ( ! empty( $_POST['calendar_data'] ) ? json_decode( $_POST['calendar_data'], true ) : array() );
91
+
92
+ if( empty( $_POST['form_data']['calendar_id'] ) ) {
93
+
94
+ echo add_query_arg( array( 'page' => 'wpsbc-calendars', 'wpsbc_message' => 'calendar_update_fail' ), admin_url( 'admin.php' ) );
95
+ wp_die();
96
+
97
+ }
98
+
99
+ /**
100
+ * Prepare variables
101
+ *
102
+ */
103
+ $calendar_id = absint( $_POST['form_data']['calendar_id'] );
104
+ $calendar_name = sanitize_text_field( $_POST['form_data']['calendar_name'] );
105
+ $calendar_data = ( ! empty( $_POST['calendar_data'] ) ? _wpsbc_array_wp_kses_post( $_POST['calendar_data'] ) : array() );
106
+
107
+ /**
108
+ * Get default legend item
109
+ *
110
+ */
111
+ $legend_items = wpsbc_get_legend_items( array( 'calendar_id' => $calendar_id, 'is_default' => 1 ) );
112
+ $default_legend_item = ( ! empty( $legend_items ) && is_array( $legend_items ) ? $legend_items[0] : null );
113
+
114
+ // If there is no default legend item return with an error
115
+ if( is_null( $default_legend_item ) ) {
116
+
117
+ echo add_query_arg( array( 'page' => 'wpsbc-calendars', 'subpage' => 'edit-calendar', 'calendar_id' => $calendar_id, 'wpsbc_message' => 'calendar_update_fail' ), admin_url( 'admin.php' ) );
118
+ wp_die();
119
+
120
+ }
121
+
122
+
123
+ /**
124
+ * Action hook to save extra calendar form data
125
+ *
126
+ * @param array $_POST
127
+ *
128
+ */
129
+ do_action( 'wpsbc_save_calendar_data', $_POST );
130
+
131
+
132
+ /**
133
+ * Handle calendar object data
134
+ *
135
+ */
136
+
137
+ // Get calendar
138
+ $calendar = wpsbc_get_calendar( $calendar_id );
139
+
140
+ // Update calendar
141
+ $update_data = array(
142
+ 'name' => ( ! empty( $calendar_name ) ? $calendar_name : $calendar->get('name') ),
143
+ 'date_modified' => current_time( 'Y-m-d H:i:s' )
144
+ );
145
+
146
+ wpsbc_update_calendar( $calendar_id, $update_data );
147
+
148
+
149
+ // Update Calendar Meta
150
+ $settings = get_option( 'wpsbc_settings', array() );
151
+
152
+ if(isset($settings['active_languages']) && count($settings['active_languages']) > 0){
153
+ foreach($settings['active_languages'] as $language){
154
+ wpsbc_update_calendar_meta($calendar_id, 'calendar_name_translation_' . $language, sanitize_text_field($_POST['form_data']['calendar_name_translation_' . $language]));
155
+ }
156
+ }
157
+
158
+
159
+ /**
160
+ * Handle event objects data
161
+ *
162
+ */
163
+ $events_args = array(
164
+ 'calendar_id' => $calendar_id
165
+ );
166
+
167
+ $events = wpsbc_get_events( $events_args );
168
+
169
+ $events_data = array();
170
+
171
+ // Prepare events data for saving
172
+ foreach( $calendar_data as $year => $months ) {
173
+
174
+ foreach( $months as $month => $days ) {
175
+
176
+ foreach( $days as $day => $event_data ) {
177
+
178
+ // Set event calendar id
179
+ $event_data['calendar_id'] = $calendar_id;
180
+
181
+ // Set event date data
182
+ $event_data['date_year'] = $year;
183
+ $event_data['date_month'] = $month;
184
+ $event_data['date_day'] = $day;
185
+
186
+ // Add event to events array
187
+ if(checkdate($month, $day, $year)){
188
+ $events_data[] = $event_data;
189
+ }
190
+ }
191
+ }
192
+ }
193
+
194
+
195
+ // Save events data
196
+ foreach( $events_data as $event_data ) {
197
+
198
+ // Check to see if event exists
199
+ $event = null;
200
+
201
+ foreach( $events as $_event ) {
202
+
203
+ if( $_event->get('date_year') == $event_data['date_year'] && $_event->get('date_month') == $event_data['date_month'] && $_event->get('date_day') == $event_data['date_day'] ) {
204
+ $event = $_event;
205
+ break;
206
+ }
207
+
208
+ }
209
+
210
+ // Handle insert
211
+ if( is_null( $event ) ) {
212
+
213
+ if( $default_legend_item->get('id') == $event_data['legend_item_id'] && empty( $event_data['description'] ) && empty( $event_data['tooltip'] ) )
214
+ continue;
215
+
216
+ wpsbc_insert_event( $event_data );
217
+
218
+ // Handle update
219
+ } else {
220
+
221
+ // For update we don't need the entire data set
222
+ unset( $event_data['calendar_id'] );
223
+ unset( $event_data['date_year'] );
224
+ unset( $event_data['date_month'] );
225
+ unset( $event_data['date_day'] );
226
+
227
+ wpsbc_update_event( $event->get('id'), $event_data );
228
+
229
+ }
230
+
231
+ }
232
+
233
+ /**
234
+ * Success redirect
235
+ *
236
+ */
237
+ echo add_query_arg( array( 'page' => 'wpsbc-calendars', 'subpage' => 'edit-calendar', 'calendar_id' => $calendar_id, 'year' => (int)$_POST['current_year'], 'month' => (int)$_POST['current_month'], 'wpsbc_message' => 'calendar_update_success' ), admin_url( 'admin.php' ) );
238
+ wp_die();
239
+
240
+ }
241
+ add_action( 'wp_ajax_wpsbc_save_calendar_data', 'wpsbc_save_calendar_data' );
includes/base/admin/calendar/functions-actions-calendar.php ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if (!defined('ABSPATH')) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Validates and handles the adding of the new calendar in the database
10
+ *
11
+ */
12
+ function wpsbc_action_add_calendar()
13
+ {
14
+
15
+ // Verify for nonce
16
+ if (empty($_POST['wpsbc_token']) || !wp_verify_nonce($_POST['wpsbc_token'], 'wpsbc_add_calendar')) {
17
+ return;
18
+ }
19
+
20
+ // Verify for calendar name
21
+ if (empty($_POST['calendar_name'])) {
22
+
23
+ wpsbc_admin_notices()->register_notice('calendar_name_missing', '<p>' . __('Please add a name for your new calendar.', 'wp-simple-booking-calendar') . '</p>', 'error');
24
+ wpsbc_admin_notices()->display_notice('calendar_name_missing');
25
+
26
+ return;
27
+
28
+ }
29
+
30
+ // Prepare calendar data to be inserted
31
+ $calendar_data = array(
32
+ 'name' => sanitize_text_field($_POST['calendar_name']),
33
+ 'date_created' => current_time('Y-m-d H:i:s'),
34
+ 'date_modified' => current_time('Y-m-d H:i:s'),
35
+ 'status' => 'active',
36
+ 'ical_hash' => wpsbc_generate_ical_hash(),
37
+ );
38
+
39
+ // Insert calendar into the database
40
+ $calendar_id = wpsbc_insert_calendar($calendar_data);
41
+
42
+ // If the calendar could not be inserted show a message to the user
43
+ if (!$calendar_id) {
44
+
45
+ wpsbc_admin_notices()->register_notice('calendar_insert_false', '<p>' . __('Something went wrong. Could not create the calendar. Please try again.', 'wp-simple-booking-calendar') . '</p>', 'error');
46
+ wpsbc_admin_notices()->display_notice('calendar_insert_false');
47
+
48
+ return;
49
+
50
+ }
51
+
52
+ /**
53
+ * Add default legend items
54
+ *
55
+ */
56
+
57
+ $legend_items_data = wpsbc_get_default_legend_items_data();
58
+
59
+ foreach ($legend_items_data as $legend_item_data) {
60
+
61
+ // Set the calendar id for the legend items data
62
+ $legend_item_data['calendar_id'] = $calendar_id;
63
+
64
+ // Insert legend item
65
+ $legend_id = wpsbc_insert_legend_item($legend_item_data);
66
+
67
+ // Add default translations
68
+ foreach ($legend_item_data['translations'] as $language_code => $legend_translation) {
69
+ wpsbc_add_legend_item_meta($legend_id, 'translation_' . $language_code, $legend_translation);
70
+ }
71
+
72
+ }
73
+
74
+ /**
75
+ * Add legend items from another calendar
76
+ *
77
+ */
78
+ if (!empty($_POST['calendar_legend'])) {
79
+
80
+ $copy_calendar_id = absint($_POST['calendar_legend']);
81
+ $copy_calendar_legend_items = wpsbc_get_legend_items(array('calendar_id' => $copy_calendar_id));
82
+
83
+ if (!empty($copy_calendar_legend_items)) {
84
+
85
+ foreach ($copy_calendar_legend_items as $legend_item) {
86
+
87
+ // Prepare data
88
+ $copy_legend_item_data = $legend_item->to_array();
89
+ $copy_legend_item_data['calendar_id'] = $calendar_id;
90
+
91
+ // Unset the legend item id from the array
92
+ unset($copy_legend_item_data['id']);
93
+
94
+ $copy_legend_item_id = $legend_item->get('id');
95
+
96
+ // Insert the new legend item
97
+ $legend_item_id = wpsbc_insert_legend_item($copy_legend_item_data);
98
+
99
+ if (!$legend_item_id) {
100
+ continue;
101
+ }
102
+
103
+ // Get all meta from the copy calendar legend items
104
+ $copy_legend_item_meta = wpsbc_get_legend_item_meta($copy_legend_item_id);
105
+
106
+ if (empty($copy_legend_item_meta)) {
107
+ continue;
108
+ }
109
+
110
+ foreach ($copy_legend_item_meta as $meta_key => $meta_values) {
111
+
112
+ foreach ($meta_values as $meta_value) {
113
+ wpsbc_add_legend_item_meta($legend_item_id, $meta_key, $meta_value);
114
+ }
115
+
116
+ }
117
+
118
+ }
119
+
120
+ }
121
+
122
+ }
123
+
124
+ // Redirect to the edit page of the calendar with a success message
125
+ wp_redirect(add_query_arg(array('page' => 'wpsbc-calendars', 'subpage' => 'edit-calendar', 'calendar_id' => $calendar_id, 'wpsbc_message' => 'calendar_insert_success'), admin_url('admin.php')));
126
+ exit;
127
+
128
+ }
129
+ add_action('wpsbc_action_add_calendar', 'wpsbc_action_add_calendar', 50);
130
+
131
+ /**
132
+ * Handles the trash calendar action, which changes the status of the calendar from active to trash
133
+ *
134
+ */
135
+ function wpsbc_action_trash_calendar()
136
+ {
137
+
138
+ // Verify for nonce
139
+ if (empty($_GET['wpsbc_token']) || !wp_verify_nonce($_GET['wpsbc_token'], 'wpsbc_trash_calendar')) {
140
+ return;
141
+ }
142
+
143
+ if (empty($_GET['calendar_id'])) {
144
+ return;
145
+ }
146
+
147
+ $calendar_id = absint($_GET['calendar_id']);
148
+
149
+ $calendar_data = array(
150
+ 'status' => 'trash',
151
+ );
152
+
153
+ $updated = wpsbc_update_calendar($calendar_id, $calendar_data);
154
+
155
+ if (!$updated) {
156
+ return;
157
+ }
158
+
159
+ // Redirect to the current page
160
+ wp_redirect(add_query_arg(array('page' => 'wpsbc-calendars', 'calendar_status' => 'active', 'wpsbc_message' => 'calendar_trash_success'), admin_url('admin.php')));
161
+ exit;
162
+
163
+ }
164
+ add_action('wpsbc_action_trash_calendar', 'wpsbc_action_trash_calendar', 50);
165
+
166
+ /**
167
+ * Handles the restore calendar action, which changes the status of the calendar from trash to active
168
+ *
169
+ */
170
+ function wpsbc_action_restore_calendar()
171
+ {
172
+
173
+ // Verify for nonce
174
+ if (empty($_GET['wpsbc_token']) || !wp_verify_nonce($_GET['wpsbc_token'], 'wpsbc_restore_calendar')) {
175
+ return;
176
+ }
177
+
178
+ if (empty($_GET['calendar_id'])) {
179
+ return;
180
+ }
181
+
182
+ $calendar_id = absint($_GET['calendar_id']);
183
+
184
+ $calendar_data = array(
185
+ 'status' => 'active',
186
+ );
187
+
188
+ $updated = wpsbc_update_calendar($calendar_id, $calendar_data);
189
+
190
+ if (!$updated) {
191
+ return;
192
+ }
193
+
194
+ // Redirect to the current page
195
+ wp_redirect(add_query_arg(array('page' => 'wpsbc-calendars', 'calendar_status' => 'trash', 'wpsbc_message' => 'calendar_restore_success'), admin_url('admin.php')));
196
+ exit;
197
+
198
+ }
199
+ add_action('wpsbc_action_restore_calendar', 'wpsbc_action_restore_calendar', 50);
200
+
201
+ /**
202
+ * Handles the delete calendar action, which removes all calendar data, legend items and events data
203
+ * associated with the calendar
204
+ *
205
+ */
206
+ function wpsbc_action_delete_calendar()
207
+ {
208
+
209
+ // Verify for nonce
210
+ if (empty($_GET['wpsbc_token']) || !wp_verify_nonce($_GET['wpsbc_token'], 'wpsbc_delete_calendar')) {
211
+ return;
212
+ }
213
+
214
+ if (empty($_GET['calendar_id'])) {
215
+ return;
216
+ }
217
+
218
+ $calendar_id = absint($_GET['calendar_id']);
219
+
220
+ /**
221
+ * Delete the calendar
222
+ *
223
+ */
224
+ $deleted = wpsbc_delete_calendar($calendar_id);
225
+
226
+ if (!$deleted) {
227
+ return;
228
+ }
229
+
230
+ /**
231
+ * Delete legend items
232
+ *
233
+ */
234
+ $legend_items = wpsbc_get_legend_items(array('calendar_id' => $calendar_id));
235
+
236
+ foreach ($legend_items as $legend_item) {
237
+
238
+ wpsbc_delete_legend_item($legend_item->get('id'));
239
+
240
+ }
241
+
242
+ /**
243
+ * Delete legend items meta
244
+ *
245
+ */
246
+ foreach ($legend_items as $legend_item) {
247
+
248
+ $legend_item_meta = wpsbc_get_legend_item_meta($legend_item->get('id'));
249
+
250
+ if (!empty($legend_item_meta)) {
251
+
252
+ foreach ($legend_item_meta as $key => $value) {
253
+
254
+ wpsbc_delete_legend_item_meta($legend_item->get('id'), $key);
255
+
256
+ }
257
+
258
+ }
259
+
260
+ }
261
+
262
+ /**
263
+ * Delete events
264
+ *
265
+ */
266
+ $events = wpsbc_get_events(array('calendar_id' => $calendar_id));
267
+
268
+ foreach ($events as $event) {
269
+
270
+ wpsbc_delete_event($event->get('id'));
271
+
272
+ }
273
+
274
+ /**
275
+ * Delete events meta
276
+ *
277
+ */
278
+ foreach ($events as $event) {
279
+
280
+ $event_meta = wpsbc_get_legend_item_meta($event->get('id'));
281
+
282
+ if (!empty($event_meta)) {
283
+
284
+ foreach ($event_meta as $key => $value) {
285
+
286
+ wpsbc_delete_event_meta($event->get('id'), $key);
287
+
288
+ }
289
+
290
+ }
291
+
292
+ }
293
+
294
+ // Redirect to the current page
295
+ wp_redirect(add_query_arg(array('page' => 'wpsbc-calendars', 'calendar_status' => 'trash', 'wpsbc_message' => 'calendar_delete_success'), admin_url('admin.php')));
296
+ exit;
297
+
298
+ }
299
+ add_action('wpsbc_action_delete_calendar', 'wpsbc_action_delete_calendar', 50);
includes/base/admin/calendar/functions-actions-legend-item.php ADDED
@@ -0,0 +1,396 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ /**
7
+ * Handles "add new legend item" action
8
+ *
9
+ */
10
+ function wpsbc_action_add_legend_item() {
11
+
12
+ // Verify for nonce
13
+ if( empty( $_POST['wpsbc_token'] ) || ! wp_verify_nonce( $_POST['wpsbc_token'], 'wpsbc_add_legend_item' ) )
14
+ return;
15
+
16
+ // Verify for the calendar id
17
+ if( empty( $_GET['calendar_id'] ) ) {
18
+
19
+ wpsbc_admin_notices()->register_notice( 'legend_item_calendar_id_missing', '<p>' . __( 'Something went wrong. Could not add the legend item.', 'wp-simple-booking-calendar' ) . '</p>', 'error' );
20
+ wpsbc_admin_notices()->display_notice( 'legend_item_calendar_id_missing' );
21
+
22
+ return;
23
+
24
+ }
25
+
26
+ // Verify for legend item name
27
+ if( empty( $_POST['legend_item_name'] ) ) {
28
+
29
+ wpsbc_admin_notices()->register_notice( 'legend_item_name_missing', '<p>' . __( 'Please add a name for your legend item.', 'wp-simple-booking-calendar' ) . '</p>', 'error' );
30
+ wpsbc_admin_notices()->display_notice( 'legend_item_name_missing' );
31
+
32
+ return;
33
+
34
+ }
35
+
36
+ // Verify for legend item type
37
+ if( empty( $_POST['legend_item_type'] ) ) {
38
+
39
+ wpsbc_admin_notices()->register_notice( 'legend_item_type_missing', '<p>' . __( 'Please select a type for your legend item.', 'wp-simple-booking-calendar' ) . '</p>', 'error' );
40
+ wpsbc_admin_notices()->display_notice( 'legend_item_type_missing' );
41
+
42
+ return;
43
+
44
+ }
45
+
46
+ // Remove empty values from the colors array
47
+ if( isset( $_POST['legend_item_color'] ) && is_array( $_POST['legend_item_color'] ) )
48
+ $_POST['legend_item_color'] = array_filter( $_POST['legend_item_color'] );
49
+
50
+ // Verify for legend item type
51
+ if( empty( $_POST['legend_item_color'] ) || ( $_POST['legend_item_type'] != 'single' && count( $_POST['legend_item_color'] ) <= 1 ) ) {
52
+
53
+ wpsbc_admin_notices()->register_notice( 'legend_item_color_missing', '<p>' . __( 'Please select the color for your legend item.', 'wp-simple-booking-calendar' ) . '</p>', 'error' );
54
+ wpsbc_admin_notices()->display_notice( 'legend_item_color_missing' );
55
+
56
+ return;
57
+
58
+ }
59
+
60
+ // Get legend items to see if there are any added, if not we set this one to be the default one
61
+ $legend_items = wpsbc_get_legend_items( array( 'calendar_id' => absint( $_GET['calendar_id'] ), 'number' => 1 ) );
62
+
63
+ // Prepare legend item data to be inserted into the database
64
+ $legend_item_data = array(
65
+ 'name' => sanitize_text_field( $_POST['legend_item_name'] ),
66
+ 'type' => sanitize_text_field( $_POST['legend_item_type'] ),
67
+ 'color' => $_POST['legend_item_color'],
68
+ 'color_text' => ( ! empty( $_POST['legend_item_color_text'] ) ? sanitize_text_field( $_POST['legend_item_color_text'] ) : '' ),
69
+ 'is_visible' => 1,
70
+ 'is_default' => ( empty( $legend_items ) ? 1 : 0 ),
71
+ 'calendar_id' => absint( $_GET['calendar_id'] )
72
+ );
73
+
74
+
75
+ // Insert the legend item
76
+ $inserted = wpsbc_insert_legend_item( $legend_item_data );
77
+
78
+ // If the legend item could not be inserted show a message to the user
79
+ if( ! $inserted ) {
80
+
81
+ wpsbc_admin_notices()->register_notice( 'legend_item_insert_fail', '<p>' . __( 'Something went wrong. Could not add the legend item. Please try again.', 'wp-simple-booking-calendar' ) . '</p>', 'error' );
82
+ wpsbc_admin_notices()->display_notice( 'legend_item_insert_fail' );
83
+
84
+ return;
85
+
86
+ }
87
+
88
+ /**
89
+ * Handle translations
90
+ *
91
+ */
92
+ $settings = get_option( 'wpsbc_settings', array() );
93
+ $active_languages = ( ! empty( $settings['active_languages'] ) ? $settings['active_languages'] : array() );
94
+
95
+ foreach( $active_languages as $code ) {
96
+
97
+ // Add translation
98
+ if( ! empty( $_POST['legend_item_translation_' . $code] ) )
99
+ wpsbc_add_legend_item_meta( $inserted, 'translation_' . $code, sanitize_text_field( $_POST['legend_item_translation_' . $code] ) );
100
+
101
+ }
102
+
103
+ // Redirect to the edit page of the calendar with a success message
104
+ wp_redirect( add_query_arg( array( 'page' => 'wpsbc-calendars', 'subpage' => 'view-legend', 'calendar_id' => absint( $_GET['calendar_id'] ), 'wpsbc_message' => 'legend_item_insert_success' ), admin_url( 'admin.php' ) ) );
105
+ exit;
106
+
107
+ }
108
+ add_action( 'wpsbc_action_add_legend_item', 'wpsbc_action_add_legend_item' );
109
+
110
+
111
+ /**
112
+ * Handles "add new legend item" action
113
+ *
114
+ */
115
+ function wpsbc_action_edit_legend_item() {
116
+
117
+ // Verify for nonce
118
+ if( empty( $_POST['wpsbc_token'] ) || ! wp_verify_nonce( $_POST['wpsbc_token'], 'wpsbc_edit_legend_item' ) )
119
+ return;
120
+
121
+ // Verify for legend item name
122
+ if( empty( $_POST['legend_item_name'] ) ) {
123
+
124
+ wpsbc_admin_notices()->register_notice( 'legend_item_name_missing', '<p>' . __( 'Please add a name for your legend item.', 'wp-simple-booking-calendar' ) . '</p>', 'error' );
125
+ wpsbc_admin_notices()->display_notice( 'legend_item_name_missing' );
126
+
127
+ return;
128
+
129
+ }
130
+
131
+ // Verify for legend item type
132
+ if( empty( $_POST['legend_item_type'] ) ) {
133
+
134
+ wpsbc_admin_notices()->register_notice( 'legend_item_type_missing', '<p>' . __( 'Please select a type for your legend item.', 'wp-simple-booking-calendar' ) . '</p>', 'error' );
135
+ wpsbc_admin_notices()->display_notice( 'legend_item_type_missing' );
136
+
137
+ return;
138
+
139
+ }
140
+
141
+ // Remove empty values from the colors array
142
+ if( isset( $_POST['legend_item_color'] ) && is_array( $_POST['legend_item_color'] ) )
143
+ $_POST['legend_item_color'] = array_filter( $_POST['legend_item_color'] );
144
+
145
+ // Verify for legend item type
146
+ if( empty( $_POST['legend_item_color'] ) || ( $_POST['legend_item_type'] != 'single' && count( $_POST['legend_item_color'] ) <= 1 ) ) {
147
+
148
+ wpsbc_admin_notices()->register_notice( 'legend_item_color_missing', '<p>' . __( 'Please select the color for your legend item.', 'wp-simple-booking-calendar' ) . '</p>', 'error' );
149
+ wpsbc_admin_notices()->display_notice( 'legend_item_color_missing' );
150
+
151
+ return;
152
+
153
+ }
154
+
155
+ // Prepare legend item data to be inserted into the database
156
+ $legend_item_data = array(
157
+ 'name' => sanitize_text_field( $_POST['legend_item_name'] ),
158
+ 'type' => sanitize_text_field( $_POST['legend_item_type'] ),
159
+ 'color' => $_POST['legend_item_color'],
160
+ 'color_text' => ( ! empty( $_POST['legend_item_color_text'] ) ? sanitize_text_field( $_POST['legend_item_color_text'] ) : '' ),
161
+ );
162
+
163
+ // Insert the legend item
164
+ $updated = wpsbc_update_legend_item( absint( $_GET['legend_item_id'] ), $legend_item_data );
165
+
166
+ // If the legend item could not be updated show a message to the user
167
+ if( ! $updated ) {
168
+
169
+ wpsbc_admin_notices()->register_notice( 'legend_item_update_fail', '<p>' . __( 'Something went wrong. Could not updated the legend item. Please try again.', 'wp-simple-booking-calendar' ) . '</p>', 'error' );
170
+ wpsbc_admin_notices()->display_notice( 'legend_item_update_fail' );
171
+
172
+ return;
173
+
174
+ }
175
+
176
+ /**
177
+ * Handle translations
178
+ *
179
+ */
180
+ $settings = get_option( 'wpsbc_settings', array() );
181
+ $active_languages = ( ! empty( $settings['active_languages'] ) ? $settings['active_languages'] : array() );
182
+
183
+ foreach( $active_languages as $code ) {
184
+
185
+ // Firstly remove the translation
186
+ wpsbc_delete_legend_item_meta( absint( $_GET['legend_item_id'] ), 'translation_' . $code );
187
+
188
+ // Add translation
189
+ if( ! empty( $_POST['legend_item_translation_' . $code] ) )
190
+ wpsbc_add_legend_item_meta( absint( $_GET['legend_item_id'] ), 'translation_' . $code, sanitize_text_field( $_POST['legend_item_translation_' . $code] ) );
191
+
192
+ }
193
+
194
+ // Redirect to the edit page of the calendar with a success message
195
+ wp_redirect( add_query_arg( array( 'page' => 'wpsbc-calendars', 'subpage' => 'edit-legend-item', 'legend_item_id' => absint( $_GET['legend_item_id'] ), 'calendar_id' => absint( $_GET['calendar_id'] ), 'wpsbc_message' => 'legend_item_update_success' ), admin_url( 'admin.php' ) ) );
196
+ exit;
197
+
198
+ }
199
+ add_action( 'wpsbc_action_edit_legend_item', 'wpsbc_action_edit_legend_item' );
200
+
201
+
202
+ /**
203
+ * Handles the "delete legend item" action
204
+ *
205
+ */
206
+ function wpsbc_action_delete_legend_item() {
207
+
208
+ // Verify for nonce
209
+ if( empty( $_GET['wpsbc_token'] ) || ! wp_verify_nonce( $_GET['wpsbc_token'], 'wpsbc_delete_legend_item' ) )
210
+ return;
211
+
212
+ // Verify for legend item id
213
+ if( empty( $_GET['legend_item_id'] ) ) {
214
+
215
+ wpsbc_admin_notices()->register_notice( 'legend_item_id_missing', '<p>' . __( 'Something went wrong. Could not delete the legend item. Please try again.', 'wp-simple-booking-calendar' ) . '</p>', 'error' );
216
+ wpsbc_admin_notices()->display_notice( 'legend_item_id_missing' );
217
+
218
+ return;
219
+
220
+ }
221
+
222
+ // Set legend item id
223
+ $legend_item_id = absint( $_GET['legend_item_id'] );
224
+
225
+ // Get legend item to see if it exists
226
+ $legend_item = wpsbc_get_legend_item( $legend_item_id );
227
+
228
+ if( is_null( $legend_item ) ) {
229
+
230
+ wpsbc_admin_notices()->register_notice( 'legend_item_is_null', '<p>' . __( 'Something went wrong. Could not delete the legend item. Please try again.', 'wp-simple-booking-calendar' ) . '</p>', 'error' );
231
+ wpsbc_admin_notices()->display_notice( 'legend_item_is_null' );
232
+
233
+ return;
234
+
235
+ }
236
+
237
+ // Set calendar_id
238
+ $calendar_id = $legend_item->get('calendar_id');
239
+
240
+ // Delete the legend item
241
+ $deleted = wpsbc_delete_legend_item( $legend_item_id );
242
+
243
+ if( ! $deleted ) {
244
+
245
+ wpsbc_admin_notices()->register_notice( 'legend_item_delete_fail', '<p>' . __( 'Something went wrong. Could not delete the legend item. Please try again.', 'wp-simple-booking-calendar' ) . '</p>', 'error' );
246
+ wpsbc_admin_notices()->display_notice( 'legend_item_delete_fail' );
247
+
248
+ return;
249
+
250
+ }
251
+
252
+ // Delete the legend item meta data
253
+ $legend_item_meta = wpsbc_get_legend_item_meta( $legend_item_id );
254
+
255
+ if( ! empty( $legend_item_meta ) ) {
256
+
257
+ foreach( $legend_item_meta as $meta_key => $meta_value )
258
+ wpsbc_delete_legend_item_meta( $legend_item_id, $meta_key );
259
+
260
+ }
261
+
262
+ // Redirect to the view legend page with a success message
263
+ wp_redirect( add_query_arg( array( 'wpsbc_message' => 'legend_item_delete_success', 'calendar_id' => $calendar_id ), remove_query_arg( array( 'wpsbc_action', 'wpsbc_token' ) ) ) );
264
+ exit;
265
+
266
+ }
267
+ add_action( 'wpsbc_action_delete_legend_item', 'wpsbc_action_delete_legend_item' );
268
+
269
+
270
+ /**
271
+ * Handles the "make legend item default" action
272
+ *
273
+ */
274
+ function wpsbc_action_make_default_legend_item() {
275
+
276
+ // Verify for nonce
277
+ if( empty( $_GET['wpsbc_token'] ) || ! wp_verify_nonce( $_GET['wpsbc_token'], 'wpsbc_make_default_legend_item' ) )
278
+ return;
279
+
280
+ if( empty( $_GET['legend_item_id'] ) )
281
+ return;
282
+
283
+ $legend_item = wpsbc_get_legend_item( absint( $_GET['legend_item_id'] ) );
284
+
285
+ if( is_null( $legend_item ) )
286
+ return;
287
+
288
+ // Get default legend item
289
+ $default_legend_item = wpsbc_get_legend_items( array( 'calendar_id' => $legend_item->get('calendar_id'), 'is_default' => 1 ) );
290
+ $default_legend_item = ( ! empty( $default_legend_item ) && is_array( $default_legend_item ) ? $default_legend_item[0] : null );
291
+
292
+ // Update the default legend item to not be the default one
293
+ if( ! is_null( $default_legend_item ) ) {
294
+
295
+ $legend_item_data = array(
296
+ 'is_default' => 0
297
+ );
298
+
299
+ $updated = wpsbc_update_legend_item( $default_legend_item->get('id'), $legend_item_data );
300
+
301
+ } else
302
+ $updated = true;
303
+
304
+ if( ! $updated )
305
+ return;
306
+
307
+ // Update the current legend item to be the default one
308
+ $legend_item_data = array(
309
+ 'is_default' => 1
310
+ );
311
+
312
+ $updated = wpsbc_update_legend_item( $legend_item->get('id'), $legend_item_data );
313
+
314
+ if( ! $updated )
315
+ return;
316
+
317
+ // Redirect to the edit page of the legend with a success message
318
+ wp_redirect( add_query_arg( array( 'wpsbc_message' => 'legend_item_make_default_success' ), remove_query_arg( array( 'wpsbc_action', 'wpsbc_token' ) ) ) );
319
+ exit;
320
+
321
+ }
322
+ add_action( 'wpsbc_action_make_default_legend_item', 'wpsbc_action_make_default_legend_item' );
323
+
324
+
325
+ /**
326
+ * Handles the "make legend item invisible" action
327
+ *
328
+ */
329
+ function wpsbc_action_make_visible_legend_item() {
330
+
331
+ // Verify for nonce
332
+ if( empty( $_GET['wpsbc_token'] ) || ! wp_verify_nonce( $_GET['wpsbc_token'], 'wpsbc_make_visible_legend_item' ) )
333
+ return;
334
+
335
+ if( empty( $_GET['legend_item_id'] ) )
336
+ return;
337
+
338
+ $legend_item = wpsbc_get_legend_item( absint( $_GET['legend_item_id'] ) );
339
+
340
+ if( is_null( $legend_item ) )
341
+ return;
342
+
343
+ // Prepare legend item data to be updated
344
+ $legend_item_data = array(
345
+ 'is_visible' => 1
346
+ );
347
+
348
+ // Update legend item in the database
349
+ $updated = wpsbc_update_legend_item( $legend_item->get('id'), $legend_item_data );
350
+
351
+ if( ! $updated )
352
+ return;
353
+
354
+ // Redirect to the edit page of the legend with a success message
355
+ wp_redirect( add_query_arg( array( 'wpsbc_message' => 'legend_item_make_visible_success' ), remove_query_arg( array( 'wpsbc_action', 'wpsbc_token' ) ) ) );
356
+ exit;
357
+
358
+ }
359
+ add_action( 'wpsbc_action_make_visible_legend_item', 'wpsbc_action_make_visible_legend_item', 50 );
360
+
361
+
362
+ /**
363
+ * Handles the "make legend item invisible" action
364
+ *
365
+ */
366
+ function wpsbc_action_make_invisible_legend_item() {
367
+
368
+ // Verify for nonce
369
+ if( empty( $_GET['wpsbc_token'] ) || ! wp_verify_nonce( $_GET['wpsbc_token'], 'wpsbc_make_invisible_legend_item' ) )
370
+ return;
371
+
372
+ if( empty( $_GET['legend_item_id'] ) )
373
+ return;
374
+
375
+ $legend_item = wpsbc_get_legend_item( absint( $_GET['legend_item_id'] ) );
376
+
377
+ if( is_null( $legend_item ) )
378
+ return;
379
+
380
+ // Prepare legend item data to be updated
381
+ $legend_item_data = array(
382
+ 'is_visible' => 0
383
+ );
384
+
385
+ // Update legend item in the database
386
+ $updated = wpsbc_update_legend_item( $legend_item->get('id'), $legend_item_data );
387
+
388
+ if( ! $updated )
389
+ return;
390
+
391
+ // Redirect to the edit page of the legend with a success message
392
+ wp_redirect( add_query_arg( array( 'wpsbc_message' => 'legend_item_make_invisible_success' ), remove_query_arg( array( 'wpsbc_action', 'wpsbc_token' ) ) ) );
393
+ exit;
394
+
395
+ }
396
+ add_action( 'wpsbc_action_make_invisible_legend_item', 'wpsbc_action_make_invisible_legend_item', 50 );
includes/base/admin/calendar/functions-shortcode-generator.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ /**
7
+ * Adds the "Add Calendar" button to the text editor
8
+ *
9
+ */
10
+ function wpsbc_add_calendar_shortcode_media_button() {
11
+
12
+ if( ! function_exists( 'get_current_screen' ) )
13
+ return;
14
+
15
+ $screen = get_current_screen();
16
+
17
+ if( is_null( $screen ) )
18
+ return;
19
+
20
+ /**
21
+ * Filter the post types where the shortcode media button should appear
22
+ *
23
+ * @param array
24
+ *
25
+ */
26
+ $post_types = get_post_types(array('public' => true));
27
+
28
+ $post_types = apply_filters( 'wpsbc_add_calendar_shortcode_media_button_post_types', $post_types );
29
+
30
+ if( ! in_array( $screen->post_type, $post_types ) )
31
+ return;
32
+
33
+ echo '<a href="#" id="wpsbc-shortcode-generator-button" class="button"><span class="dashicons dashicons-calendar-alt"></span>' . __( 'Add Calendar', 'wp-simple-booking-calendar' ) . '</a>';
34
+
35
+ }
36
+ add_action( 'media_buttons', 'wpsbc_add_calendar_shortcode_media_button', 20 );
37
+
38
+
39
+ /**
40
+ * Adds the modal window triggered by the "Add Calendar" button
41
+ *
42
+ */
43
+ function wpsbc_add_calendar_shortcode_modal() {
44
+
45
+ if( ! function_exists( 'get_current_screen' ) )
46
+ return;
47
+
48
+ $screen = get_current_screen();
49
+
50
+ if( is_null( $screen ) )
51
+ return;
52
+
53
+ /**
54
+ * Filter the post types where the shortcode modal generated by the shortcode media button should appear
55
+ *
56
+ * @param array
57
+ *
58
+ */
59
+ $post_types = get_post_types(array('public' => true));
60
+
61
+ $post_types = apply_filters( 'wpsbc_add_calendar_shortcode_media_button_post_types', $post_types );
62
+
63
+ if( ! in_array( $screen->post_type, $post_types ) )
64
+ return;
65
+
66
+ include 'views/view-shortcode-generator.php';
67
+
68
+ }
69
+ add_action( 'admin_footer', 'wpsbc_add_calendar_shortcode_modal' );
includes/base/admin/calendar/functions.php ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Includes the files needed for the Legend Items admin area
9
+ *
10
+ */
11
+ function wpsbc_include_files_admin_calendar() {
12
+
13
+ // Get legend admin dir path
14
+ $dir_path = plugin_dir_path( __FILE__ );
15
+
16
+ // Include submenu page
17
+ if( file_exists( $dir_path . 'class-submenu-page-calendar.php' ) )
18
+ include $dir_path . 'class-submenu-page-calendar.php';
19
+
20
+ // Include calendars list table
21
+ if( file_exists( $dir_path . 'class-list-table-calendars.php' ) )
22
+ include $dir_path . 'class-list-table-calendars.php';
23
+
24
+ // Include calendar editor outputter
25
+ if( file_exists( $dir_path . 'class-calendar-editor-outputter.php' ) )
26
+ include $dir_path . 'class-calendar-editor-outputter.php';
27
+
28
+ // Include admin actions
29
+ if( file_exists( $dir_path . 'functions-actions-ical.php' ) )
30
+ include $dir_path . 'functions-actions-ical.php';
31
+
32
+ if( file_exists( $dir_path . 'functions-actions-calendar.php' ) )
33
+ include $dir_path . 'functions-actions-calendar.php';
34
+
35
+ if( file_exists( $dir_path . 'functions-actions-legend-item.php' ) )
36
+ include $dir_path . 'functions-actions-legend-item.php';
37
+
38
+ if( file_exists( $dir_path . 'functions-actions-ajax-calendar.php' ) )
39
+ include $dir_path . 'functions-actions-ajax-calendar.php';
40
+
41
+ if( file_exists( $dir_path . 'functions-actions-ajax-legend-item.php' ) )
42
+ include $dir_path . 'functions-actions-ajax-legend-item.php';
43
+
44
+ if( file_exists( $dir_path . 'functions-shortcode-generator.php' ) )
45
+ include $dir_path . 'functions-shortcode-generator.php';
46
+
47
+ }
48
+ add_action( 'wpsbc_include_files', 'wpsbc_include_files_admin_calendar' );
49
+
50
+
51
+ /**
52
+ * Register the Calendars admin submenu page
53
+ *
54
+ */
55
+ function wpsbc_register_submenu_page_calendars( $submenu_pages ) {
56
+
57
+ if( ! is_array( $submenu_pages ) )
58
+ return $submenu_pages;
59
+
60
+ $submenu_pages['calendars'] = array(
61
+ 'class_name' => 'WPSBC_Submenu_Page_Calendars',
62
+ 'data' => array(
63
+ 'page_title' => __( 'Calendars', 'wp-simple-booking-calendar' ),
64
+ 'menu_title' => __( 'Calendars', 'wp-simple-booking-calendar' ),
65
+ 'capability' => apply_filters( 'wpsbc_submenu_page_capability_calendars', 'manage_options' ),
66
+ 'menu_slug' => 'wpsbc-calendars'
67
+ )
68
+ );
69
+
70
+ return $submenu_pages;
71
+
72
+ }
73
+ add_filter( 'wpsbc_register_submenu_page', 'wpsbc_register_submenu_page_calendars', 20 );
74
+
75
+
76
+ /**
77
+ * Returns the HTML for the legend item icon
78
+ *
79
+ * @param int $legend_item_id
80
+ * @param string $type
81
+ * @param array $color
82
+ *
83
+ * @return string
84
+ *
85
+ */
86
+ function wpsbc_get_legend_item_icon( $legend_item_id, $type, $color = array() ) {
87
+
88
+ $output = '<div class="wpsbc-legend-item-icon wpsbc-legend-item-icon-' . esc_attr( $legend_item_id ) . '" data-type="' . esc_attr( $type ) . '">';
89
+
90
+ for( $i = 0; $i <= 1; $i++ ){
91
+
92
+ $svg = '';
93
+ if($type == "split"){
94
+ $svg = ($i == 0) ? '<svg height="100%" width="100%" viewBox="0 0 50 50" preserveAspectRatio="none"><polygon points="0,0 0,50 50,0" /></svg>' : '<svg height="100%" width="100%" viewBox="0 0 50 50" preserveAspectRatio="none"><polygon points="0,50 50,50 50,0" /></svg>';
95
+ }
96
+
97
+
98
+ $output .= '<div class="wpsbc-legend-item-icon-color" ' . ( ! empty( $color[$i] ) ? 'style="background-color: ' . esc_attr( $color[$i] ) . ';"' : '' ) . '>' . $svg . '</div>';
99
+ }
100
+
101
+ $output .= '</div>';
102
+
103
+ return $output;
104
+
105
+ }
includes/base/admin/calendar/views/view-add-calendar.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ $calendars = wpsbc_get_calendars( array( 'status' => 'active' ) );
7
+
8
+ ?>
9
+
10
+ <div class="wrap wpsbc-wrap wpsbc-wrap-add-calendar">
11
+
12
+ <form action="" method="POST">
13
+
14
+ <!-- Icon -->
15
+ <div id="wpsbc-add-new-calendar-icon">
16
+ <div class="wpsbc-icon-wrap">
17
+ <span class="dashicons dashicons-calendar-alt"></span>
18
+ <span class="dashicons dashicons-plus"></span>
19
+ </div>
20
+ </div>
21
+
22
+ <!-- Heading -->
23
+ <h1 id="wpsbc-add-new-calendar-heading"><?php echo __( 'Add New Calendar', 'wp-simple-booking-calendar' ); ?></h1>
24
+
25
+ <!-- Postbox -->
26
+ <div id="wpsbc-add-new-calendar-postbox" class="postbox">
27
+
28
+ <!-- Form Fields -->
29
+ <div class="inside">
30
+
31
+ <!-- Add Calendar Name -->
32
+ <label for="wpsbc-new-calendar-name"><?php echo __( 'Calendar Name', 'wp-simple-booking-calendar' ); ?> *</label>
33
+ <input id="wpsbc-new-calendar-name" name="calendar_name" type="text" value="<?php echo ( ! empty( $_POST['calendar_name'] ) ? esc_attr( $_POST['calendar_name'] ) : '' ); ?>" />
34
+
35
+ </div>
36
+
37
+ <!-- Form Submit button -->
38
+ <div id="major-publishing-actions">
39
+ <a href="<?php echo admin_url( $this->admin_url ); ?>"><?php echo __( 'Cancel', 'wp-simple-booking-calendar' ); ?></a>
40
+ <input type="submit" class="button-primary wpsbc-button-large" value="<?php echo __( 'Add Calendar', 'wp-simple-booking-calendar' ); ?>" />
41
+ </div>
42
+
43
+ <!-- Action and nonce -->
44
+ <input type="hidden" name="wpsbc_action" value="add_calendar" />
45
+ <?php wp_nonce_field( 'wpsbc_add_calendar', 'wpsbc_token', false ); ?>
46
+
47
+ </div>
48
+
49
+ </form>
50
+
51
+ </div>
includes/base/admin/calendar/views/view-calendars.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ ?>
7
+
8
+ <div class="wrap wpsbc-wrap wpsbc-wrap-calendars">
9
+
10
+ <!-- Page Heading -->
11
+ <h1 class="wp-heading-inline"><?php echo __( 'Calendars', 'wp-simple-booking-calendar' ); ?></h1>
12
+ <a href="<?php echo add_query_arg( array( 'subpage' => 'add-calendar' ), $this->admin_url ); ?>" class="page-title-action"><?php echo __( 'Add New Calendar', 'wp-simple-booking-calendar' ); ?></a>
13
+ <hr class="wp-header-end" />
14
+
15
+ <!-- Calendars List Table -->
16
+ <form method="get">
17
+
18
+ <input type="hidden" name="page" value="wpsbc-calendars" />
19
+ <input type="hidden" name="paged" value="1">
20
+
21
+ <?php
22
+ $table = new WPSBC_WP_List_Table_Calendars();
23
+ $table->views();
24
+ $table->search_box( __( 'Search Calendars' ), 'wpsbc-search-calendars' );
25
+ $table->display();
26
+ ?>
27
+ </form>
28
+
29
+ <a href="<?php echo add_query_arg( array( 'page' => 'wpsbc-calendars', 'subpage' => 'upgrade-to-premium' ), $this->admin_url ); ?>" class="wpsbc-wrap-upgrade-cta">
30
+ <span class="wpsbc-wrap-upgrade-cta-button">I'm interested</span>
31
+ <span class="wpsbc-wrap-upgrade-cta-heading">Missing anything? Discover more powerful features in the premium version now!</span>
32
+ </a>
33
+
34
+ </div>
includes/base/admin/calendar/views/view-edit-calendar.php ADDED
@@ -0,0 +1,219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ $calendar_id = absint( ! empty( $_GET['calendar_id'] ) ? $_GET['calendar_id'] : 0 );
7
+ $calendar = wpsbc_get_calendar( $calendar_id );
8
+
9
+ if( is_null( $calendar ) )
10
+ return;
11
+
12
+ $current_year = ( ! empty( $_GET['year'] ) ? absint( $_GET['year'] ) : current_time('Y') );
13
+ $current_month = ( ! empty( $_GET['month'] ) ? absint( $_GET['month'] ) : current_time('n') );
14
+
15
+ $settings = get_option( 'wpsbc_settings', array() );
16
+
17
+ $removable_query_args = wp_removable_query_args();
18
+
19
+ ?>
20
+
21
+ <div class="wrap wpsbc-wrap wpsbc-wrap-edit-calendar">
22
+
23
+ <form method="POST" action="" autocomplete="off">
24
+
25
+ <!-- Page Heading -->
26
+ <h1 class="wp-heading-inline"><?php echo __( 'Edit Calendar', 'wp-simple-booking-calendar' ); ?><span class="wpsbc-heading-tag"><?php printf( __( 'Calendar ID: %d', 'wp-simple-booking-calendar' ), $calendar_id ); ?></span></h1>
27
+
28
+ <!-- Page Heading Actions -->
29
+ <div class="wpsbc-heading-actions">
30
+
31
+ <!-- Back Button -->
32
+ <a href="<?php echo add_query_arg( array( 'page' => 'wpsbc-calendars'), admin_url( 'admin.php' ) ); ?>" class="button-secondary"><?php echo __('Back to all calendars', 'wp-simple-booking-calendar') ?></a>
33
+
34
+ <!-- Save button -->
35
+ <input type="submit" class="wpsbc-save-calendar button-primary" value="<?php echo __( 'Save Calendar', 'wp-simple-booking-calendar' ); ?>" />
36
+
37
+ </div>
38
+
39
+ <hr class="wp-header-end" />
40
+
41
+ <div id="poststuff">
42
+
43
+ <!-- Calendar Title -->
44
+ <div id="titlediv">
45
+ <div id="titlewrap">
46
+ <input type="text" name="calendar_name" size="30" value="<?php echo esc_attr( $calendar->get('name') ) ?>" id="title">
47
+
48
+ <?php if(isset($settings['active_languages']) && count($settings['active_languages']) > 0): ?>
49
+
50
+ <a href="#" class="titlewrap-toggle"><?php echo __('Translate calendar title','wp-simple-booking-calendar') ?> <svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" ><path fill="currentColor" d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z" class=""></path></svg></a>
51
+ <div class="titlewrap-translations">
52
+ <?php foreach($settings['active_languages'] as $language): ?>
53
+ <div class="titlewrap-translation">
54
+ <div class="titlewrap-translation-flag"><img src="<?php echo WPSBC_PLUGIN_DIR_URL; ?>assets/img/flags/<?php echo $language;?>.png" /></div>
55
+ <input type="text" name="calendar_name_translation_<?php echo $language;?>" size="30" value="<?php echo esc_attr( wpsbc_get_calendar_meta($calendar->get('id'), 'calendar_name_translation_' . $language, true) ) ?>" >
56
+ </div>
57
+ <?php endforeach; ?>
58
+ </div>
59
+
60
+ <?php endif ?>
61
+ </div>
62
+ </div>
63
+
64
+ <div id="post-body" class="metabox-holder columns-2">
65
+
66
+ <!-- Main Post Body Content -->
67
+ <div id="post-body-content">
68
+
69
+ <!-- Availability -->
70
+ <div class="postbox">
71
+
72
+ <h3 class="hndle"><?php echo __( 'Edit Dates', 'wp-simple-booking-calendar' ); ?></h3>
73
+
74
+ <div class="inside">
75
+
76
+ <div id="wpsbc-calendar-events">
77
+ <?php
78
+ $calendar_args = array(
79
+ 'current_year' => $current_year,
80
+ 'current_month' => $current_month
81
+ );
82
+
83
+ $calendar_editor = new WPSBC_Calendar_Editor_Outputter( $calendar, $calendar_args );
84
+ $calendar_editor->display();
85
+ ?>
86
+ </div>
87
+
88
+ </div>
89
+ </div>
90
+
91
+ <?php
92
+
93
+ /**
94
+ * Action hook to add extra form fields to the main calendar edit area
95
+ *
96
+ * @param WPSBC_Calendar $calendar
97
+ *
98
+ */
99
+ do_action( 'wpsbc_view_edit_calendar_main', $calendar );
100
+
101
+ ?>
102
+
103
+ </div><!-- / Main Post Body Content -->
104
+
105
+ <!-- Sidebar Content -->
106
+ <div id="postbox-container-1" class="postbox-container">
107
+
108
+ <!-- Calendar -->
109
+ <div class="postbox">
110
+
111
+ <h3 class="hndle"><?php echo __( 'Calendar', 'wp-simple-booking-calendar' ); ?></h3>
112
+
113
+ <div class="inside">
114
+
115
+ <?php
116
+ $calendar_args = array(
117
+ 'current_year' => $current_year,
118
+ 'current_month' => $current_month,
119
+ 'show_title' => 0,
120
+ 'show_legend' => 0
121
+ );
122
+
123
+ $calendar_outputter = new WPSBC_Calendar_Outputter( $calendar, $calendar_args );
124
+ $calendar_outputter->display();
125
+ ?>
126
+
127
+ </div>
128
+ </div><!-- / Calendar -->
129
+
130
+ <!-- Calendar Legend -->
131
+ <div class="postbox">
132
+
133
+ <h3 class="hndle"><?php echo __( 'Legend', 'wp-simple-booking-calendar' ); ?></h3>
134
+
135
+ <div class="inside">
136
+
137
+ <?php
138
+ $legend_items = wpsbc_get_legend_items( array( 'calendar_id' => $calendar_id ) );
139
+
140
+ foreach( $legend_items as $legend_item ) {
141
+
142
+ echo '<div class="wpsbc-legend-item">';
143
+ echo wpsbc_get_legend_item_icon( $legend_item->get('id'), $legend_item->get('type'), $legend_item->get('color') );
144
+ echo '<span class="wpsbc-legend-item-name">' . $legend_item->get('name') . '</span>';
145
+ echo '</div>';
146
+
147
+ }
148
+ ?>
149
+
150
+ </div>
151
+
152
+ <div class="wpsbc-plugin-card-bottom plugin-card-bottom">
153
+ <a class="button-secondary" href="<?php echo add_query_arg( array( 'subpage' => 'view-legend' ), remove_query_arg( $removable_query_args ) ); ?>"><?php echo __( 'Edit Legend Items', 'wp-simple-booking-calendar' ); ?></a>
154
+ </div>
155
+
156
+ </div><!-- / Calendar Legend -->
157
+
158
+ <!-- iCal Export -->
159
+ <div class="postbox">
160
+
161
+ <h3 class="hndle"><?php echo __( 'iCal Import/Export', 'wp-simple-booking-calendar' ); ?></h3>
162
+
163
+ <div class="inside">
164
+
165
+ <p><?php echo __( 'To configure the iCal import & export settings and have access to the iCal export link please click the button below.', 'wp-simple-booking-calendar' ); ?></p>
166
+
167
+ <a href="<?php echo add_query_arg( array( 'subpage' => 'ical-import-export' ), remove_query_arg( $removable_query_args ) ); ?>" class="button-secondary"><span class="dashicons dashicons-upload"></span> <?php echo __( 'iCal Import/Export', 'wp-simple-booking-calendar' ); ?></a>
168
+
169
+ </div>
170
+
171
+ </div><!-- / iCal Export -->
172
+
173
+
174
+ <!-- CSV Export -->
175
+ <div class="postbox">
176
+
177
+ <h3 class="hndle"><?php echo __( 'CSV Export', 'wp-simple-booking-calendar' ); ?></h3>
178
+
179
+ <div class="inside">
180
+
181
+ <p><?php echo __( 'To configure the CSV export settings please click the button below. ', 'wp-simple-booking-calendar' ); ?></p>
182
+
183
+ <a href="<?php echo add_query_arg( array( 'subpage' => 'csv-export' ), remove_query_arg( $removable_query_args ) ); ?>" class="button-secondary"><span class="dashicons dashicons-media-spreadsheet"></span> <?php echo __( 'CSV Export', 'wp-simple-booking-calendar' ); ?></a>
184
+
185
+ </div>
186
+
187
+ </div><!-- / CSV Export -->
188
+
189
+
190
+ <?php
191
+
192
+ /**
193
+ * Action hook to add extra form fields to the main calendar edit area
194
+ *
195
+ * @param WPSBC_Calendar $calendar
196
+ *
197
+ */
198
+ do_action( 'wpsbc_view_edit_calendar_sidebar', $calendar );
199
+
200
+ ?>
201
+
202
+ </div><!-- / Sidebar Content -->
203
+
204
+ </div><!-- / #post-body -->
205
+
206
+ </div><!-- / #poststuff -->
207
+
208
+ <!-- Hidden fields -->
209
+ <input type="hidden" name="calendar_id" value="<?php echo $calendar_id; ?>" />
210
+
211
+ <!-- Save button -->
212
+ <input type="submit" class="wpsbc-save-calendar button-primary" value="<?php echo __( 'Save Calendar', 'wp-simple-booking-calendar' ); ?>" />
213
+
214
+ <!-- Save Button Spinner -->
215
+ <div class="wpsbc-save-calendar-spinner spinner"><!-- --></div>
216
+
217
+ </form>
218
+
219
+ </div>
includes/base/admin/calendar/views/view-shortcode-generator.php ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ ?>
7
+
8
+ <div id="wpsbc-modal-add-calendar-shortcode" class="wpsbc-modal">
9
+
10
+ <!-- Modal Header -->
11
+ <div class="wpsbc-modal-header">
12
+
13
+ <h2>
14
+ <span class="dashicons dashicons-calendar-alt"><!-- --></span>
15
+ <?php echo __( 'Add Calendar', 'wp-simple-booking-calendar' ); ?>
16
+ </h2>
17
+
18
+ <span class="wpsbc-modal-close dashicons dashicons-no-alt"><!-- --></span>
19
+
20
+ </div>
21
+
22
+ <!-- Modal Body -->
23
+ <div class="wpsbc-modal-body">
24
+
25
+ <!-- Modal Nav Tab -->
26
+ <ul class="wpsbc-modal-nav-tab-wrapper">
27
+ <li class="wpsbc-nav-tab wpsbc-modal-nav-tab wpsbc-active" data-tab="calendar-single"><a href="#"><?php echo __( 'Insert Calendar', 'wp-simple-booking-calendar' ); ?></a></li>
28
+ <li class="wpsbc-nav-tab wpsbc-modal-nav-tab" data-tab="calendar-overview"><a href="#"><?php echo __( 'Insert Overview Calendar', 'wp-simple-booking-calendar' ); ?></a></li>
29
+ <li class="wpsbc-nav-tab wpsbc-modal-nav-tab" data-tab="calendar-search-widget"><a href="#"><?php echo __( 'Insert Search Widget', 'wp-simple-booking-calendar' ); ?></a></li>
30
+ </ul>
31
+
32
+ <!-- Modal Body Inner -->
33
+ <div class="wpsbc-modal-inner">
34
+
35
+ <!-- Modal Tab: Calendar Single -->
36
+ <div class="wpsbc-tab wpsbc-modal-tab wpsbc-active" data-tab="calendar-single">
37
+
38
+ <h3><?php echo __( 'Insert a Calendar', 'wp-simple-booking-calendar' ); ?></h3>
39
+ <p><?php echo __( 'Select which calendar you wish to insert and use the options to customize it to your needs.', 'wp-simple-booking-calendar' ); ?></p>
40
+
41
+ <h4><?php echo __( 'Basic Options', 'wp-simple-booking-calendar' ); ?></h4>
42
+ <hr />
43
+
44
+ <!-- Row -->
45
+ <div class="wpsbc-row">
46
+
47
+ <!-- Column: Calendar -->
48
+ <div class="wpsbc-col-1-4">
49
+
50
+ <?php $calendars = wpsbc_get_calendars(); ?>
51
+
52
+ <label for="modal-add-calendar-shortcode-calendar"><?php echo __( 'Calendar', 'wp-simple-booking-calendar' ); ?></label>
53
+
54
+ <select id="modal-add-calendar-shortcode-calendar" class="wpsbc-shortcode-generator-field-calendar" data-attribute="id">
55
+ <?php
56
+ foreach( $calendars as $calendar )
57
+ echo '<option value="' . $calendar->get('id') . '">' . $calendar->get('name') . '</option>';
58
+ ?>
59
+ </select>
60
+
61
+ </div>
62
+
63
+ <!-- Column: Calendar Title -->
64
+ <div class="wpsbc-col-1-4">
65
+
66
+ <label for="modal-add-calendar-shortcode-calendar-title"><?php echo __( 'Display Calendar Title', 'wp-simple-booking-calendar' ); ?></label>
67
+
68
+ <select id="modal-add-calendar-shortcode-calendar-title" class="wpsbc-shortcode-generator-field-calendar" data-attribute="title">
69
+ <option value="yes"><?php echo __( 'Yes', 'wp-simple-booking-calendar' ); ?></option>
70
+ <option value="no"><?php echo __( 'No', 'wp-simple-booking-calendar' ); ?></option>
71
+ </select>
72
+
73
+ </div>
74
+
75
+ <!-- Column: Legend -->
76
+ <div class="wpsbc-col-1-4">
77
+
78
+ <label for="modal-add-calendar-shortcode-legend"><?php echo __( 'Display Legend', 'wp-simple-booking-calendar' ); ?></label>
79
+
80
+ <select id="modal-add-calendar-shortcode-legend" class="wpsbc-shortcode-generator-field-calendar" data-attribute="legend">
81
+ <option value="yes"><?php echo __( 'Yes', 'wp-simple-booking-calendar' ); ?></option>
82
+ <option value="no"><?php echo __( 'No', 'wp-simple-booking-calendar' ); ?></option>
83
+ </select>
84
+
85
+ </div>
86
+
87
+ <!-- Column: Legend Position -->
88
+ <div class="wpsbc-col-1-4">
89
+
90
+ <label for="modal-add-calendar-shortcode-legend-position"><?php echo __( 'Legend Position', 'wp-simple-booking-calendar' ); ?></label>
91
+
92
+ <select id="modal-add-calendar-shortcode-legend-position" class="wpsbc-shortcode-generator-field-calendar" data-attribute="legend_position">
93
+ <option value="side"><?php echo __( 'Side', 'wp-simple-booking-calendar' ); ?></option>
94
+ <option value="top"><?php echo __( 'Top', 'wp-simple-booking-calendar' ); ?></option>
95
+ <option value="bottom"><?php echo __( 'Bottom', 'wp-simple-booking-calendar' ); ?></option>
96
+ </select>
97
+
98
+ </div>
99
+
100
+ </div><!-- / Row -->
101
+
102
+ <!-- Row -->
103
+ <div class="wpsbc-row">
104
+
105
+ <!-- Column: Language -->
106
+ <div class="wpsbc-col-1-4">
107
+
108
+ <label for="modal-add-calendar-shortcode-language"><?php echo __( 'Language', 'wp-simple-booking-calendar' ); ?></label>
109
+
110
+ <select id="modal-add-calendar-shortcode-language" class="wpsbc-shortcode-generator-field-calendar" data-attribute="language">
111
+
112
+ <option value="auto"><?php echo __( 'Auto (let WP choose)', 'wp-simple-booking-calendar' ); ?></option>
113
+
114
+ <?php
115
+
116
+ $settings = get_option( 'wpsbc_settings', array() );
117
+ $languages = wpsbc_get_languages();
118
+ $active_languages = ( ! empty( $settings['active_languages'] ) ? $settings['active_languages'] : array() );
119
+
120
+ foreach( $active_languages as $code ) {
121
+
122
+ echo '<option value="' . esc_attr( $code ) . '">' . ( ! empty( $languages[$code] ) ? $languages[$code] : '' ) . '</option>';
123
+
124
+ }
125
+
126
+ ?>
127
+
128
+ </select>
129
+
130
+ </div>
131
+
132
+ </div><!-- / Row -->
133
+
134
+ <hr />
135
+
136
+ <!-- Shortcode insert -->
137
+ <a href="#" id="wpsbc-insert-shortcode-single-calendar" class="button button-primary"><?php echo __( 'Insert Calendar', 'wp-simple-booking-calendar' ); ?></a>
138
+ <a href="#" class="button button-secondary wpsbc-modal-close"><?php echo __( 'Cancel', 'wp-simple-booking-calendar' ); ?></a>
139
+
140
+ </div>
141
+
142
+
143
+ <!-- Modal Tab: Calendar Overview -->
144
+ <div class="wpsbc-tab wpsbc-modal-tab" data-tab="calendar-overview">
145
+ <a href="<?php echo add_query_arg( array( 'page' => 'wpsbc-calendars', 'subpage' => 'upgrade-to-premium' ), admin_url('admin.php') ); ?>" class="wpsbc-wrap-upgrade-cta">
146
+ <span class="wpsbc-wrap-upgrade-cta-button">See details</span>
147
+ <span class="wpsbc-wrap-upgrade-cta-heading">The overview calendar is only available in the premium version.</span>
148
+ </a>
149
+ </div>
150
+
151
+ <!-- Modal Tab: Search Widget -->
152
+ <div class="wpsbc-tab wpsbc-modal-tab" data-tab="calendar-search-widget">
153
+
154
+ <a href="<?php echo add_query_arg( array( 'page' => 'wpsbc-calendars', 'subpage' => 'upgrade-to-premium' ), admin_url('admin.php') ); ?>" class="wpsbc-wrap-upgrade-cta">
155
+ <span class="wpsbc-wrap-upgrade-cta-button">See details</span>
156
+ <span class="wpsbc-wrap-upgrade-cta-heading">The search widget is only available in the premium version.</span>
157
+ </a>
158
+
159
+ </div>
160
+
161
+ </div>
162
+
163
+ </div>
164
+
165
+ </div>
166
+
167
+ <div id="wpsbc-modal-add-calendar-shortcode-overlay" class="wpsbc-modal-overlay"><!-- --></div>
includes/base/admin/class-admin-notices.php ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ /**
7
+ * Class that handles admin notice registration and output
8
+ *
9
+ */
10
+ Class WPSBC_Admin_Notices {
11
+
12
+ /**
13
+ * The current instance of the object
14
+ *
15
+ * @access private
16
+ * @var WPSBC_Admin_Notices
17
+ *
18
+ */
19
+ private static $instance;
20
+
21
+ /**
22
+ * List of notices that have been registered
23
+ *
24
+ * @access protected
25
+ * @var array
26
+ *
27
+ */
28
+ protected $notices = array();
29
+
30
+ /**
31
+ * List of notices that will be printed in the page
32
+ *
33
+ * @access protected
34
+ * @var array
35
+ *
36
+ */
37
+ protected $display_notices = array();
38
+
39
+
40
+ /**
41
+ * Constructor
42
+ *
43
+ */
44
+ public function __construct() {
45
+
46
+ add_action( 'admin_init', array( $this, 'catch_url_admin_notice' ), 100 );
47
+ add_action( 'admin_notices', array( $this, 'print_notices' ) );
48
+
49
+ }
50
+
51
+
52
+ /**
53
+ * Returns an instance of the object
54
+ *
55
+ * @return WPSBC_Admin_Notices
56
+ *
57
+ */
58
+ public static function instance() {
59
+
60
+ if( ! isset( self::$instance ) && ! ( self::$instance instanceof WPSBC_Admin_Notices ) )
61
+ self::$instance = new WPSBC_Admin_Notices;
62
+
63
+ return self::$instance;
64
+
65
+ }
66
+
67
+
68
+ /**
69
+ * Adds a new admin notice to the $notices property
70
+ *
71
+ * @param string $slug
72
+ * @param string $message
73
+ * @param string $class
74
+ *
75
+ */
76
+ public function register_notice( $slug, $message, $class = 'updated' ) {
77
+
78
+ $this->notices[$slug] = array(
79
+ 'message' => $message,
80
+ 'class' => $class
81
+ );
82
+
83
+ }
84
+
85
+
86
+ /**
87
+ * Prepares a registered admin notice for printing on admin_notices action
88
+ *
89
+ * @param string $slug
90
+ *
91
+ */
92
+ public function display_notice( $slug ) {
93
+
94
+ $this->display_notices[] = $slug;
95
+
96
+ }
97
+
98
+
99
+ /**
100
+ * Callback function to print admin notices
101
+ *
102
+ */
103
+ public function print_notices() {
104
+
105
+ if( ! isset( $this->display_notices ) )
106
+ return;
107
+
108
+ foreach( $this->display_notices as $notice_slug ) {
109
+
110
+ if( empty( $this->notices[$notice_slug] ) )
111
+ continue;
112
+
113
+ echo '<div class="' . $this->notices[$notice_slug]['class'] . ' notice">';
114
+ echo $this->notices[$notice_slug]['message'];
115
+ echo '</div>';
116
+
117
+ }
118
+
119
+ }
120
+
121
+
122
+ /**
123
+ * Catches messages sent through the URL
124
+ *
125
+ */
126
+ public function catch_url_admin_notice() {
127
+
128
+ if( empty( $_GET['wpsbc_message'] ) )
129
+ return;
130
+
131
+ $message_slug = sanitize_text_field( $_GET['wpsbc_message'] );
132
+
133
+ if( ! empty( $this->notices[$message_slug] ) )
134
+ $this->display_notice( $message_slug );
135
+
136
+ }
137
+
138
+
139
+ }
140
+
141
+
142
+ /**
143
+ * Returns the instance of WPSBC_Admin_Notices
144
+ *
145
+ * @return WPSBC_Admin_Notices
146
+ *
147
+ */
148
+ function wpsbc_admin_notices() {
149
+
150
+ return WPSBC_Admin_Notices::instance();
151
+
152
+ }
153
+
154
+ wpsbc_admin_notices();
includes/base/admin/functions.php ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Includes the files needed for the admin area
9
+ *
10
+ */
11
+ function wpsbc_include_files_admin() {
12
+
13
+ // Get calendar dir path
14
+ $dir_path = plugin_dir_path( __FILE__ );
15
+
16
+ // Include the db layer classes
17
+ if( file_exists( $dir_path . 'class-admin-notices.php' ) )
18
+ include $dir_path . 'class-admin-notices.php';
19
+
20
+ }
21
+ add_action( 'wpsbc_include_files', 'wpsbc_include_files_admin' );
22
+
23
+
24
+ /**
25
+ * Adds a central action hook on the admin_init that the plugin and add-ons
26
+ * can use to do certain actions, like adding a new calendar, editing a calendar, deleting, etc.
27
+ *
28
+ */
29
+ function wpsbc_register_do_actions() {
30
+
31
+ if( empty( $_REQUEST['wpsbc_action'] ) )
32
+ return;
33
+
34
+ $action = sanitize_text_field( $_REQUEST['wpsbc_action'] );
35
+
36
+ /**
37
+ * Hook that should be used by all processes that make a certain action
38
+ * withing the plugin, like adding a new calendar, editing a calendar, deleting, etc.
39
+ *
40
+ */
41
+ do_action( 'wpsbc_action_' . $action );
42
+
43
+ }
44
+ add_action( 'admin_init', 'wpsbc_register_do_actions' );
45
+
46
+
47
+ /**
48
+ * Builds and returns the HTML with a tooltip for the given message
49
+ *
50
+ * @param string $message
51
+ *
52
+ * @return string
53
+ *
54
+ */
55
+ function wpsbc_get_output_tooltip( $message ) {
56
+
57
+ $output = '<span class="wpsbc-tooltip-wrapper">';
58
+
59
+ // Icon
60
+ $output .= '<span class="wpsbc-tooltip-icon">?</span>';
61
+
62
+ // Message
63
+ $output .= '<span class="wpsbc-tooltip-message">';
64
+
65
+ $output .= $message;
66
+
67
+ // Arrow
68
+ $output .= '<span class="wpsbc-tooltip-arrow"></span>';
69
+
70
+ $output .= '</span>';
71
+
72
+ $output .= '</span>';
73
+
74
+ return $output;
75
+
76
+ }
includes/base/admin/settings/class-submenu-page-settings.php ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ Class WPSBC_Submenu_Page_Settings extends WPSBC_Submenu_Page {
8
+
9
+ /**
10
+ * Helper init method that runs on parent __construct
11
+ *
12
+ */
13
+ protected function init() {
14
+
15
+ add_action( 'admin_init', array( $this, 'register_settings' ), 10 );
16
+ add_action( 'admin_init', array( $this, 'register_admin_notices' ), 10 );
17
+
18
+ }
19
+
20
+ /**
21
+ * Callback method to register admin notices that are sent via URL parameters
22
+ *
23
+ */
24
+ public function register_admin_notices() {
25
+
26
+ // Settings save success
27
+ if( ! empty( $_GET['settings-updated'] ) ) {
28
+ wpsbc_admin_notices()->register_notice( 'settings_save_success', '<p>' . __( 'Settings saved successfully.', 'wp-simple-booking-calendar' ) . '</p>' );
29
+ wpsbc_admin_notices()->display_notice( 'settings_save_success' );
30
+ }
31
+
32
+ }
33
+
34
+
35
+ /**
36
+ * Returns an array with the page tabs that should be displayed on the page
37
+ *
38
+ * @return array
39
+ *
40
+ */
41
+ protected function get_tabs() {
42
+
43
+ $tabs = array(
44
+ 'languages' => __( 'Languages', 'wp-simple-booking-calendar' ),
45
+ );
46
+
47
+ /**
48
+ * Filter the tabs before returning
49
+ *
50
+ * @param array $tabs
51
+ *
52
+ */
53
+ return apply_filters( 'wpsbc_submenu_page_settings_tabs', $tabs );
54
+
55
+ }
56
+
57
+
58
+ /**
59
+ * Registers the settings option
60
+ *
61
+ */
62
+ public function register_settings() {
63
+
64
+ register_setting( 'wpsbc_settings', 'wpsbc_settings', array( $this, 'settings_sanitize' ) );
65
+
66
+ }
67
+
68
+
69
+ /**
70
+ * Sanitizes the settings before saving them to the db
71
+ *
72
+ */
73
+ public function settings_sanitize( $settings ) {
74
+
75
+ return $settings;
76
+
77
+ }
78
+
79
+
80
+ /**
81
+ * Callback for the HTML output for the Calendar page
82
+ *
83
+ */
84
+ public function output() {
85
+
86
+ include 'views/view-settings.php';
87
+
88
+ }
89
+
90
+ }
includes/base/admin/settings/functions.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Includes the files needed for the Settings admin area
9
+ *
10
+ */
11
+ function wpsbc_include_files_admin_settings() {
12
+
13
+ // Get legend admin dir path
14
+ $dir_path = plugin_dir_path( __FILE__ );
15
+
16
+ // Include submenu page
17
+ if( file_exists( $dir_path . 'class-submenu-page-settings.php' ) )
18
+ include $dir_path . 'class-submenu-page-settings.php';
19
+
20
+ }
21
+ add_action( 'wpsbc_include_files', 'wpsbc_include_files_admin_settings' );
22
+
23
+
24
+ /**
25
+ * Register the Settings admin submenu page
26
+ *
27
+ */
28
+ function wpsbc_register_submenu_page_settings( $submenu_pages ) {
29
+
30
+ if( ! is_array( $submenu_pages ) )
31
+ return $submenu_pages;
32
+
33
+ $submenu_pages['settings'] = array(
34
+ 'class_name' => 'WPSBC_Submenu_Page_Settings',
35
+ 'data' => array(
36
+ 'page_title' => __( 'Settings', 'wp-simple-booking-calendar' ),
37
+ 'menu_title' => __( 'Settings', 'wp-simple-booking-calendar' ),
38
+ 'capability' => apply_filters( 'wpsbc_submenu_page_capability_settings', 'manage_options' ),
39
+ 'menu_slug' => 'wpsbc-settings'
40
+ )
41
+ );
42
+
43
+ return $submenu_pages;
44
+
45
+ }
46
+ add_filter( 'wpsbc_register_submenu_page', 'wpsbc_register_submenu_page_settings', 50 );
includes/base/admin/settings/views/view-settings-tab-languages.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ ?>
7
+
8
+ <div class="wpsbc-settings-field-wrapper wpsbc-settings-field-inline">
9
+
10
+ <label class="wpsbc-settings-field-label">
11
+ <strong><?php echo __( 'Languages', 'wp-simple-booking-calendar' ); ?></strong>
12
+ <br /><br />
13
+ <?php echo __( 'What languages do you wish to use?', 'wp-simple-booking-calendar' ); ?>
14
+ </label>
15
+
16
+ <div class="wpsbc-settings-field-inner">
17
+
18
+ <?php
19
+
20
+ $languages = wpsbc_get_languages();
21
+
22
+ foreach( $languages as $code => $name ) {
23
+
24
+ $flag_code = ( $code == 'sv' ? 'se' : ( $code == 'sl' ? 'si' : $code ) );
25
+
26
+ echo '<div>';
27
+ echo '<label>';
28
+ echo '<input type="checkbox" name="wpsbc_settings[active_languages][]" value="' . esc_attr( $code ) . '" ' . ( ! empty( $settings['active_languages'] ) && in_array( $code, $settings['active_languages'] ) ? 'checked' : '' ) . ' />';
29
+ echo '<img src="' . WPSBC_PLUGIN_DIR_URL . 'assets/img/flags/' . esc_attr( $flag_code ) . '.png" />';
30
+ echo esc_html( $name );
31
+ echo '</label>';
32
+ echo '</div>';
33
+
34
+ }
35
+
36
+ ?>
37
+
38
+ </div>
39
+
40
+ </div>
41
+
42
+ <!-- Submit button -->
43
+ <input type="submit" class="button-primary" value="<?php echo __( 'Save Settings', 'wp-simple-booking-calendar' ); ?>" />
includes/base/admin/settings/views/view-settings.php ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ $tabs = $this->get_tabs();
7
+ $active_tab = ( ! empty( $_GET['tab'] ) ? sanitize_text_field( $_GET['tab'] ) : 'languages' );
8
+ $settings = get_option( 'wpsbc_settings', array() );
9
+
10
+ ?>
11
+
12
+ <div class="wrap wpsbc-wrap">
13
+
14
+ <form action="options.php" method="POST">
15
+
16
+ <?php settings_fields( 'wpsbc_settings' ); ?>
17
+
18
+ <!-- Page Heading -->
19
+ <h1 class="wp-heading-inline"><?php echo __( 'Settings', 'wp-simple-booking-calendar' ); ?></h1>
20
+ <hr class="wp-header-end" />
21
+
22
+ <!-- Navigation Tabs -->
23
+ <h2 class="wpsbc-nav-tab-wrapper nav-tab-wrapper">
24
+ <?php
25
+
26
+ if( ! empty( $tabs ) ) {
27
+ foreach( $tabs as $tab_slug => $tab_name ) {
28
+
29
+ echo '<a href="' . add_query_arg( array( 'page' => 'wpsbc-settings', 'tab' => $tab_slug ), admin_url('admin.php') ) . '" data-tab="' . $tab_slug . '" class="nav-tab wpsbc-nav-tab ' . ( $active_tab == $tab_slug ? 'nav-tab-active' : '' ) . '">' . $tab_name . '</a>';
30
+
31
+ }
32
+ }
33
+
34
+ ?>
35
+ </h2>
36
+
37
+ <!-- Tabs Contents -->
38
+ <div class="wpsbc-tab-wrapper">
39
+
40
+ <?php
41
+
42
+ if( ! empty( $tabs ) ) {
43
+
44
+ foreach( $tabs as $tab_slug => $tab_name ) {
45
+
46
+ echo '<div class="wpsbc-tab wpsbc-tab-' . $tab_slug . ' ' . ( $active_tab == $tab_slug ? 'wpsbc-active' : '' ) . '" data-tab="' . $tab_slug . '">';
47
+
48
+ // Handle general tab
49
+ if( $tab_slug == 'general' ) {
50
+
51
+ include 'view-settings-tab-general.php';
52
+
53
+ // Handle languages tab
54
+ } else if( $tab_slug == 'languages' ) {
55
+
56
+ include 'view-settings-tab-languages.php';
57
+
58
+ } else if( $tab_slug == 'search-widget' ) {
59
+
60
+ include 'view-settings-tab-search-widget.php';
61
+
62
+ // Handle dynamic tabs
63
+ } else {
64
+
65
+ /**
66
+ * Action to dynamically add content for each tab
67
+ *
68
+ */
69
+ do_action( 'wpsbc_submenu_page_settings_tab_' . $tab_slug );
70
+
71
+ }
72
+
73
+ echo '</div>';
74
+
75
+ }
76
+
77
+ }
78
+
79
+ ?>
80
+ </div>
81
+
82
+ <!-- Always update hidden -->
83
+ <input type="hidden" name="wpsbc_settings[always_update]" value="<?php echo ( isset( $wpsbc_settings['always_update'] ) && $wpsbc_settings['always_update'] == 1 ? 0 : 1 ); ?>" />
84
+
85
+ </form>
86
+
87
+ </div>
includes/base/admin/upgrade-to-premium.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ ?>
7
+
8
+ <div class="wrap wpsbc-wrap wpsbc-wrap-upgrade-to-premium">
9
+
10
+ <h2>Upgrade to Premium</h2>
11
+
12
+ <h3>Finally, a calendar plugin that has it all!</h3>
13
+
14
+ <ul>
15
+ <li>
16
+ <strong><span class="dashicons dashicons-yes"></span> Create unlimited calendars</strong>
17
+ <p>Create unlimited calendars as opposed to just one.</p>
18
+ </li>
19
+ <li>
20
+ <strong><span class="dashicons dashicons-yes"></span> Create your own legend</strong>
21
+ <p>Set custom statuses for your dates, add split days, or just change the colors for the available and booked legends.</p>
22
+ </li>
23
+
24
+ <li>
25
+ <strong><span class="dashicons dashicons-yes"></span> iCalendar Sync, Import and Export</strong>
26
+ <p>Sync the availability of your calendar to any website that can import iCal feeds (including Airbnb, Booking.com or Google Calendar). </p>
27
+ </li>
28
+ <li>
29
+ <strong><span class="dashicons dashicons-yes"></span> Split days</strong>
30
+ <p>Choose between using full days or half(changeover) days when displaying availability.</p>
31
+ </li>
32
+
33
+ <li>
34
+ <strong><span class="dashicons dashicons-yes"></span> User Management</strong>
35
+ <p>Control who has access to your calendars by assigning WordPress users. You can assign general access per user type or assign a specific user to a specific calendar.</p>
36
+ </li>
37
+ <li>
38
+ <strong><span class="dashicons dashicons-yes"></span> Display an overview calendar</strong>
39
+ <p>You can create multiple calendars and combine them into one clear overview.</p>
40
+ </li>
41
+ <li>
42
+ <strong><span class="dashicons dashicons-yes"></span> Fully Translatable</strong>
43
+ <p>Some texts can automatically be translated in other languages, and you can fully translate forms and calendars in any language you want/need manually. </p>
44
+ </li>
45
+ <li>
46
+ <strong><span class="dashicons dashicons-yes"></span> Create a search widget</strong>
47
+ <p>You can create and embed a search widget, allowing your customers to search for available dates in all your calendars.</p>
48
+ </li>
49
+ <li>
50
+ <strong><span class="dashicons dashicons-yes"></span> Bulk Editor</strong>
51
+ <p>Use the powerful bulk editor to edit the availability of multiple days with one click.</p>
52
+ </li>
53
+ <li>
54
+ <strong><span class="dashicons dashicons-yes"></span> Tooltips</strong>
55
+ <p>Show tooltips on the front-end when users hover over calendar dates.</p>
56
+ </li>
57
+ <li>
58
+ <strong><span class="dashicons dashicons-yes"></span> Display multiple months</strong>
59
+ <p>Display multiple months so your customers can easily see the availability without needing to scroll through months.</p>
60
+ </li>
61
+ <li>
62
+ <strong><span class="dashicons dashicons-yes"></span> ...and a lot more</strong>
63
+ <p>Like changing the starting day of the week, set a custom start month or year, hide past bookings, show week numbers, and the list goes on.</p>
64
+ </li>
65
+
66
+ </ul>
67
+
68
+
69
+
70
+
71
+ <div class="cta">
72
+ <h3>Ready to take full advantage of the premium version?</h3>
73
+
74
+
75
+ <a class="wpsbc-button" href="https://www.wpsimplebookingcalendar.com/pricing/" target="_blank">Get Started</a>
76
+
77
+ <p class="prices">Prices starting from $39.</p>
78
+
79
+
80
+ </div>
81
+
82
+
83
+ <div class="cta-2">
84
+ <h4>Use this coupon code for a 15% discount. </h4>
85
+ <p class="discount">
86
+ <strong>FREE-VERSION-UPGRADE</strong>
87
+ </p>
88
+
89
+ </div>
90
+
91
+ <h3 class="wpsbc-clear">Need more convincing? See how the features work for you. <a href="https://www.wpsimplebookingcalendar.com/" target="_blank">Access the demos now</a>.</h3>
92
+
93
+ </div>
includes/base/calendar/class-calendar-outputter.php ADDED
@@ -0,0 +1,820 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if (!defined('ABSPATH')) {
5
+ exit;
6
+ }
7
+
8
+ class WPSBC_Calendar_Outputter
9
+ {
10
+
11
+ /**
12
+ * The arguments for the calendar outputter
13
+ *
14
+ * @access protected
15
+ * @var array
16
+ *
17
+ */
18
+ protected $args;
19
+
20
+ /**
21
+ * The WPSBC_Calendar
22
+ *
23
+ * @access protected
24
+ * @var WPSBC_Calendar
25
+ *
26
+ */
27
+ protected $calendar = null;
28
+
29
+ /**
30
+ * The list of legend items associated with the calendar
31
+ *
32
+ * @access protected
33
+ * @var array
34
+ *
35
+ */
36
+ protected $legend_items = array();
37
+
38
+ /**
39
+ * The default legend item of the calendar
40
+ *
41
+ * @access protected
42
+ * @var WPSBC_Legend_Item
43
+ *
44
+ */
45
+ protected $default_legend_item = null;
46
+
47
+ /**
48
+ * The list of events for the calendar for the given displayed range
49
+ *
50
+ * @access protected
51
+ * @var array
52
+ *
53
+ */
54
+ protected $events = array();
55
+
56
+ /**
57
+ * The plugin general settings
58
+ *
59
+ * @access protected
60
+ * @var array
61
+ *
62
+ */
63
+ protected $plugin_settings = array();
64
+
65
+ /**
66
+ * A counter for the number of months being displayed in the calendar while
67
+ * iterating through them
68
+ *
69
+ * @access private
70
+ * @var int
71
+ *
72
+ */
73
+ private $_months_iterator;
74
+
75
+ /**
76
+ * Constructor
77
+ *
78
+ * @param WPSBC_Calendar $calendar
79
+ * @param array $args
80
+ *
81
+ */
82
+ public function __construct($calendar, $args = array())
83
+ {
84
+
85
+ /**
86
+ * Set arguments
87
+ *
88
+ */
89
+ $this->args = wp_parse_args($args, wpsbc_get_calendar_output_default_args());
90
+
91
+ /**
92
+ * Set the calendar
93
+ *
94
+ */
95
+ $this->calendar = $calendar;
96
+
97
+ /**
98
+ * Set the calendar legend items
99
+ *
100
+ */
101
+ $this->legend_items = wpsbc_get_legend_items(array('calendar_id' => $calendar->get('id')));
102
+
103
+ /**
104
+ * Set the default legend item
105
+ *
106
+ */
107
+ foreach ($this->legend_items as $legend_item) {
108
+
109
+ if ($legend_item->get('is_default') == 1) {
110
+ $this->default_legend_item = $legend_item;
111
+ }
112
+
113
+ }
114
+
115
+ /**
116
+ * Set the calendar events
117
+ *
118
+ */
119
+ $this->events = wpsbc_get_events(array('calendar_id' => $calendar->get('id')));
120
+
121
+ /**
122
+ * Set plugin settings
123
+ *
124
+ */
125
+ $this->plugin_settings = get_option('wpsbc_settings', array());
126
+
127
+
128
+ }
129
+
130
+ /**
131
+ * Constructs and returns the HTML for the entire calendar
132
+ *
133
+ * @return string
134
+ *
135
+ */
136
+ public function get_display()
137
+ {
138
+
139
+ /**
140
+ * Return nothing if calendar is in Trash
141
+ *
142
+ */
143
+ if ($this->calendar->get('status') == 'trash') {
144
+ return '';
145
+ }
146
+
147
+ /**
148
+ * Prepare needed data
149
+ *
150
+ */
151
+ $year_to_show = (int) $this->args['current_year'];
152
+ $month_to_show = (int) $this->args['current_month'];
153
+
154
+ $calendar_html_data = 'data-id="' . $this->calendar->get('id') . '" ';
155
+
156
+ foreach ($this->args as $arg => $val) {
157
+ $calendar_html_data .= 'data-' . $arg . '="' . esc_attr($val) . '" ';
158
+ }
159
+
160
+ /**
161
+ * Handle output for existing calendar
162
+ *
163
+ */
164
+ $output = '<div class="wpsbc-container wpsbc-calendar-' . (int) $this->calendar->get('id') . '" ' . $calendar_html_data . '>';
165
+
166
+ /**
167
+ * Calendar title
168
+ *
169
+ */
170
+ if ($this->args['show_title']) {
171
+
172
+ $calendar_title_wrapper_el = apply_filters('wpsbc_calendar_output_title_wrapper_element', 'h2', $this->calendar->get('id'));
173
+
174
+ if(wpsbc_get_calendar_meta($this->calendar->get('id'), 'calendar_name_translation_' . $this->args['language'],true)){
175
+ $calendar_name = wpsbc_get_calendar_meta($this->calendar->get('id'), 'calendar_name_translation_' . $this->args['language'],true);
176
+ } else {
177
+ $calendar_name = $this->calendar->get('name');
178
+ }
179
+
180
+ $output .= '<' . $calendar_title_wrapper_el . '>' . $calendar_name . '</' . $calendar_title_wrapper_el . '>';
181
+
182
+ }
183
+
184
+ $output .= '<div class="wpsbc-calendars-wrapper ' . ($this->args['show_legend'] ? 'wpsbc-legend-position-' . esc_attr($this->args['legend_position']) : '') . '">';
185
+
186
+ /**
187
+ * Calendar Legend Top
188
+ *
189
+ */
190
+ if ($this->args['show_legend'] && $this->args['legend_position'] != 'bottom') {
191
+ $output .= $this->get_display_legend();
192
+ }
193
+
194
+ /**
195
+ * Iterate through the number of months to show and get the display
196
+ * of the given month and year
197
+ *
198
+ */
199
+ $output .= '<div class="wpsbc-calendars">';
200
+
201
+ $excluded_months = apply_filters('wpsbc_calendar_output_exclude_months', array());
202
+
203
+
204
+ // Set the months iterator
205
+ $this->_months_iterator = 1;
206
+
207
+ // Check if we need to exclude any months
208
+ if (!empty($excluded_months)) {
209
+
210
+ while (in_array($month_to_show, $excluded_months)) {
211
+ // Increment month and year (if needed)
212
+ $year_to_show = ($month_to_show + 1 > 12 ? ($year_to_show + 1) : $year_to_show);
213
+ $month_to_show = ($month_to_show + 1 > 12 ? ($month_to_show + 1 - 12) : ($month_to_show + 1));
214
+
215
+ }
216
+
217
+ }
218
+
219
+ // Get the month
220
+ $output .= $this->get_display_month($year_to_show, $month_to_show);
221
+
222
+ // Increment month and year (if needed)
223
+ $year_to_show = ($month_to_show + 1 > 12 ? ($year_to_show + 1) : $year_to_show);
224
+ $month_to_show = ($month_to_show + 1 > 12 ? ($month_to_show + 1 - 12) : ($month_to_show + 1));
225
+
226
+ $output .= '</div>'; // end of .wpsbc-calendars
227
+
228
+ /**
229
+ * Calendar Legend Bottom
230
+ *
231
+ */
232
+ if ($this->args['show_legend'] && $this->args['legend_position'] == 'bottom') {
233
+ $output .= $this->get_display_legend();
234
+ }
235
+
236
+ $output .= '</div>'; // end of .wpsbc-calendars-wrapper
237
+
238
+ /**
239
+ * Calendar Custom CSS
240
+ *
241
+ */
242
+ $output .= $this->get_custom_css();
243
+
244
+ /**
245
+ * Flag needed for Gutenberg block to properly display the calendar
246
+ * in the editor after the block settings are changed
247
+ *
248
+ */
249
+ $output .= '<div class="wpsbc-container-loaded" data-just-loaded="1"></div>';
250
+
251
+ $output .= '</div>'; // end of .wpsbc-container
252
+
253
+ return $output;
254
+
255
+ }
256
+
257
+ /**
258
+ * Constructs and returns the HTML for the given month of the given year
259
+ *
260
+ * @param int $year
261
+ * @param int $month
262
+ *
263
+ * @return string
264
+ *
265
+ */
266
+ protected function get_display_month($year, $month)
267
+ {
268
+
269
+ $day_names = wpsbc_get_days_first_letters($this->args['language']);
270
+ $first_day = getdate(mktime(0, 0, 0, $month, 1, $year));
271
+ $total_days = date('t', mktime(0, 0, 0, $month, 1, $year));
272
+
273
+ $output = '<div class="wpsbc-calendar">';
274
+
275
+ /**
276
+ * Month header
277
+ *
278
+ */
279
+ $output .= $this->get_display_month_header($year, $month);
280
+
281
+ /**
282
+ * Table with the actual calendar
283
+ *
284
+ */
285
+ $output .= '<div class="wpsbc-calendar-wrapper">';
286
+ $output .= '<table>';
287
+
288
+ /**
289
+ * Table head
290
+ *
291
+ * This will display the name of the week days
292
+ *
293
+ */
294
+ $output .= '<thead>';
295
+ $output .= '<tr>';
296
+
297
+
298
+
299
+ // The name of each day
300
+ for ($i = 0; $i < 7; $i++) {
301
+
302
+ $index = ($i > 6 ? $i - 7 : $i);
303
+
304
+ $output .= '<th>' . $day_names[$index] . '</th>';
305
+ }
306
+
307
+ $output .= '</tr>';
308
+ $output .= '</thead>';
309
+
310
+ /**
311
+ * Table body
312
+ *
313
+ * This will display the actual dates for the current month
314
+ *
315
+ */
316
+ $output .= '<tbody>';
317
+
318
+ // The days array
319
+ $days = array();
320
+
321
+ // The empty days at the begining of the calendar
322
+ $offset = $first_day['wday'];
323
+
324
+ if ($offset < 1) {
325
+ $offset += 7;
326
+ }
327
+
328
+ // Add first empty days
329
+ for ($i = 1; $i < $offset; $i++) {
330
+ $days[$i] = 0;
331
+ }
332
+
333
+ for ($j = $i; $j < $total_days + $i; $j++) {
334
+ $days[$j] = $j - $i + 1;
335
+ }
336
+
337
+ // Add remaining empty days
338
+ for ($i = 1; $i <= 7; $i++) {
339
+
340
+ if (count($days) % 7 == 0) {
341
+ break;
342
+ }
343
+
344
+ $days[] = 0;
345
+
346
+ }
347
+
348
+ $output .= '<tr>';
349
+
350
+ foreach ($days as $key => $day) {
351
+
352
+
353
+ // Get the output for the current day
354
+ $output .= '<td>' . $this->get_display_day($year, $month, $day) . '</td>';
355
+
356
+ if ($key % 7 == 0 && $key != count($days)) {
357
+ $output .= '</tr><tr>';
358
+ }
359
+
360
+ }
361
+
362
+ $output .= '</tr>';
363
+
364
+ $output .= '</tbody>';
365
+
366
+ $output .= '</table>';
367
+ $output .= '</div>'; // end of .wpsbc-calendar-wrapper
368
+ $output .= '</div>'; // end of .wpsbc-calendar
369
+
370
+ return $output;
371
+
372
+ }
373
+
374
+ /**
375
+ * Constructs and returns the HTML of the calendar (month) header
376
+ *
377
+ * @param int $year
378
+ * @param int $month
379
+ *
380
+ * @return string
381
+ *
382
+ */
383
+ protected function get_display_month_header($year, $month)
384
+ {
385
+
386
+ $output = '<div class="wpsbc-calendar-header wpsbc-heading">';
387
+ $output .= '<div class="wpsbc-calendar-header-navigation">';
388
+
389
+ // Add navigate previous
390
+ if ($this->_months_iterator == 1) {
391
+ $output .= '<a href="#" class="wpsbc-prev"><span class="wpsbc-arrow"></span></a>';
392
+ }
393
+
394
+ // Add month selector
395
+ $output .= $this->get_display_month_selector($year, $month);
396
+
397
+
398
+ // Add navigate next
399
+ if ($this->_months_iterator == 1) {
400
+ $output .= '<a href="#" class="wpsbc-next"><span class="wpsbc-arrow"></span></a>';
401
+ }
402
+
403
+ $output .= '</div>'; // end .wpsbc-calendar-header-navigation
404
+ $output .= '</div>'; // end .wpsbc-calendar-header
405
+
406
+ return $output;
407
+
408
+ }
409
+
410
+ /**
411
+ * Constructs and returns the HTML of the calendar month selector from the header
412
+ *
413
+ * @param int $year
414
+ * @param int $month
415
+ *
416
+ * @return string
417
+ *
418
+ */
419
+ protected function get_display_month_selector($year, $month)
420
+ {
421
+
422
+ $output = '<div class="wpsbc-select-container">';
423
+
424
+ /**
425
+ * Hook to modify how many months are being displayed in the select dropdown
426
+ * before the current given month of the year
427
+ *
428
+ * @param int $months_before
429
+ * @param int $calendar_id
430
+ * @param int $year
431
+ * @param int $month
432
+ *
433
+ */
434
+ $months_before = apply_filters('wpsbc_calendar_output_month_selector_months_before', 3, $this->calendar->get('id'), $year, $month);
435
+
436
+ /**
437
+ * Hook to modify how many months are being displayed in the select dropdown
438
+ * after the current given month of the year
439
+ *
440
+ * @param int $months_after
441
+ * @param int $calendar_id
442
+ * @param int $year
443
+ * @param int $month
444
+ *
445
+ */
446
+ $months_after = apply_filters('wpsbc_calendar_output_month_selector_months_after', 12, $this->calendar->get('id'), $year, $month);
447
+
448
+ /**
449
+ * Hook to modify the maximum number of months to display before now()
450
+ *
451
+ * @param int $months_before_max
452
+ * @param int $calendar_id
453
+ * @param int $year
454
+ * @param int $month
455
+ *
456
+ */
457
+ $months_before_max = apply_filters('wpsbc_calendar_output_month_selector_months_before_max', -1, $this->calendar->get('id'), $year, $month);
458
+
459
+ /**
460
+ * Hook to modify the maximum number of months to display after now()
461
+ *
462
+ * @param int $months_after_max
463
+ * @param int $calendar_id
464
+ * @param int $year
465
+ * @param int $month
466
+ *
467
+ */
468
+ $months_after_max = apply_filters('wpsbc_calendar_output_month_selector_months_after_max', -1, $this->calendar->get('id'), $year, $month);
469
+
470
+ /**
471
+ * Hook to exclude months from the date selector
472
+ *
473
+ * @param array $months
474
+ *
475
+ */
476
+ $excluded_months = apply_filters('wpsbc_calendar_output_month_selector_exclude_months', array());
477
+
478
+ /**
479
+ * Hook to exclude past months from the date selector
480
+ *
481
+ * @param bool
482
+ *
483
+ */
484
+ $show_past_months = apply_filters('wpsbc_calendar_output_month_selector_hide_past_months', true);
485
+
486
+ /**
487
+ * Build the months array
488
+ *
489
+ */
490
+
491
+ // The array that will contain all options data
492
+ $select_options = array();
493
+
494
+ // Maximum before time
495
+ $before_max_month = date('n') + (12 * ceil($months_before_max / 12)) - $months_before_max;
496
+ $before_max_year = $year - ceil($months_before_max / 12);
497
+ $time_before_max = mktime(0, 0, 0, $before_max_month, 1, $before_max_year);
498
+
499
+ // Maximum after time
500
+ $after_max_month = (date('n') + $months_after_max - (12 * floor((date('n') + $months_after_max) / 12)));
501
+ $after_max_year = $year + floor((date('n') + $months_after_max) / 12);
502
+ $time_after_max = mktime(0, 0, 0, $after_max_month, 1, $after_max_year);
503
+
504
+ /**
505
+ * Add past months
506
+ *
507
+ */
508
+ $_year = $year;
509
+ $_month = $month;
510
+
511
+ for ($i = 1; $i <= $months_before; $i++) {
512
+
513
+ // Exit loop if the max number of months has been reached
514
+ if ($months_before_max != -1 && mktime(0, 0, 0, $_month, 1, $_year) <= $time_before_max) {
515
+ break;
516
+ }
517
+
518
+ $_month -= 1;
519
+
520
+ if ($_month < 1) {
521
+ $_month += 12;
522
+ $_year -= 1;
523
+ }
524
+
525
+ if (in_array($_month, $excluded_months)) {
526
+ $months_before++;
527
+ continue;
528
+ }
529
+
530
+ if($show_past_months === false && $_month < current_time('m') && $_year <= current_time('Y')){
531
+ break;
532
+ }
533
+
534
+ $select_options[] = array(
535
+ 'value' => mktime(0, 0, 0, $_month, 15, $_year),
536
+ 'option' => apply_filters('wpsbc_calendar_output_month_selector_date_format', wpsbc_get_month_name($_month, $this->args['language']) . ' ' . $_year),
537
+ );
538
+
539
+ }
540
+
541
+ $select_options = array_reverse($select_options);
542
+
543
+ /**
544
+ * Add given current month
545
+ *
546
+ */
547
+ $select_options[] = array(
548
+ 'value' => mktime(0, 0, 0, $month, 15, $year),
549
+ 'option' => apply_filters('wpsbc_calendar_output_month_selector_date_format', wpsbc_get_month_name($month, $this->args['language']) . ' ' . $year),
550
+ );
551
+
552
+ /**
553
+ * Add future months
554
+ *
555
+ */
556
+ $_year = $year;
557
+ $_month = $month;
558
+
559
+ for ($i = 1; $i <= $months_after; $i++) {
560
+
561
+ if ($months_after_max != -1 && mktime(0, 0, 0, $_month, 1, $_year) >= $time_after_max) {
562
+ break;
563
+ }
564
+
565
+ $_month += 1;
566
+
567
+ if ($_month > 12) {
568
+ $_month -= 12;
569
+ $_year += 1;
570
+ }
571
+
572
+ if (in_array($_month, $excluded_months)) {
573
+ $months_after++;
574
+ continue;
575
+ }
576
+
577
+ $select_options[] = array(
578
+ 'value' => mktime(0, 0, 0, $_month, 15, $_year),
579
+ 'option' => apply_filters('wpsbc_calendar_output_month_selector_date_format', wpsbc_get_month_name($_month, $this->args['language']) . ' ' . $_year),
580
+ );
581
+
582
+ }
583
+
584
+ /**
585
+ * Output select
586
+ *
587
+ */
588
+ $output .= '<select>';
589
+
590
+ foreach ($select_options as $select_option) {
591
+ $output .= '<option value="' . esc_attr($select_option['value']) . '" ' . selected($select_option['value'], mktime(0, 0, 0, $month, 15, $year), false) . '>' . $select_option['option'] . '</option>';
592
+ }
593
+
594
+ $output .= '</select>';
595
+
596
+ $output .= '</div>'; // end .wpsbc-select-container
597
+
598
+ return $output;
599
+
600
+ }
601
+
602
+ /**
603
+ * Constructs and returns the HTML for the given day of the given month of the given year
604
+ *
605
+ * @param int $year
606
+ * @param int $month
607
+ * @param int $day
608
+ *
609
+ * @return string
610
+ *
611
+ */
612
+ protected function get_display_day($year, $month, $day)
613
+ {
614
+
615
+ $output = '';
616
+
617
+ /**
618
+ * Get the event for the current day
619
+ *
620
+ */
621
+ $event = $this->get_event_by_date($year, $month, $day);
622
+
623
+
624
+ /**
625
+ * Get the legend item for the current day
626
+ *
627
+ */
628
+ $legend_item = null;
629
+
630
+ if (!is_null($event)) {
631
+
632
+ foreach ($this->legend_items as $li) {
633
+
634
+ if ($event->get('legend_item_id') == $li->get('id')) {
635
+ $legend_item = $li;
636
+ }
637
+
638
+ }
639
+
640
+ }
641
+
642
+ if (is_null($legend_item)) {
643
+ $legend_item = $this->default_legend_item;
644
+ }
645
+
646
+ // Determine if the current day is in the past
647
+ $is_past = $this->is_date_past($year, $month, $day);
648
+
649
+ /**
650
+ * Set the needed variables for the legend item output
651
+ *
652
+ */
653
+ if (!empty($day)) {
654
+
655
+ $legend_item_id_icon = $legend_item->get('id');
656
+ $legend_item_type_icon = $legend_item->get('type');
657
+
658
+ }
659
+
660
+ /**
661
+ * Putting the day output pieces together
662
+ *
663
+ */
664
+ $output .= '<div class="wpsbc-date ' . (!empty($legend_item_id_icon) && is_numeric($legend_item_id_icon) ? 'wpsbc-legend-item-' . $legend_item->get('id') : '') . ' ' . (empty($day) ? 'wpsbc-gap' : '') . '" ' . (!empty($day) ? ('data-year="' . esc_attr($year) . '" data-month="' . esc_attr($month) . '" data-day="' . esc_attr($day) . '"') : '') . '>';
665
+
666
+ if (!empty($day)) {
667
+
668
+ /**
669
+ * Legend item icon output
670
+ *
671
+ */
672
+ $output .= wpsbc_get_legend_item_icon($legend_item_id_icon, $legend_item_type_icon);
673
+
674
+ }
675
+
676
+ $output .= '<div class="wpsbc-date-inner">' . (!empty($day) ? '<span class="wpsbc-date-number">' . $day . '</span>' : '') . '</div>';
677
+
678
+ $output .= '</div>';
679
+
680
+ return $output;
681
+
682
+ }
683
+
684
+
685
+ /**
686
+ * Constructs and returns the HTML for the calendar's legend
687
+ *
688
+ * @return string
689
+ *
690
+ */
691
+ protected function get_display_legend()
692
+ {
693
+
694
+ $output = '<div class="wpsbc-legend">';
695
+
696
+ foreach ($this->legend_items as $legend_item) {
697
+
698
+ if ($legend_item->get('is_visible') == 0) {
699
+ continue;
700
+ }
701
+
702
+ $output .= '<div class="wpsbc-legend-item">';
703
+ $output .= wpsbc_get_legend_item_icon($legend_item->get('id'), $legend_item->get('type'));
704
+ $output .= '<span class=wpsbc-legend-item-name>' . $legend_item->get_name($this->args['language']) . '</span>';
705
+ $output .= '</div>';
706
+
707
+ }
708
+
709
+ $output .= '</div>';
710
+
711
+ return $output;
712
+
713
+ }
714
+
715
+ /**
716
+ * Constructs and returns the calendar's custom CSS
717
+ *
718
+ * @return string
719
+ *
720
+ */
721
+ protected function get_custom_css()
722
+ {
723
+
724
+ $output = '<style>';
725
+
726
+ // Set the parent calendar class
727
+ $calendar_parent_class = '.wpsbc-container.wpsbc-calendar-' . (int) $this->calendar->get('id');
728
+
729
+ /**
730
+ * Legend Items CSS
731
+ *
732
+ */
733
+ foreach ($this->legend_items as $legend_item) {
734
+
735
+ // Background colors
736
+ $colors = $legend_item->get('color');
737
+
738
+ $output .= $calendar_parent_class . ' .wpsbc-legend-item-icon-' . esc_attr($legend_item->get('id')) . ' div:first-of-type { background-color: ' . (!empty($colors[0]) ? esc_attr($colors[0]) : 'transparent') . '; }';
739
+ $output .= $calendar_parent_class . ' .wpsbc-legend-item-icon-' . esc_attr($legend_item->get('id')) . ' div:nth-of-type(2) { background-color: ' . (!empty($colors[1]) ? esc_attr($colors[1]) : 'transparent') . '; }';
740
+
741
+ $output .= $calendar_parent_class . ' .wpsbc-legend-item-icon-' . esc_attr($legend_item->get('id')) . ' div:first-of-type svg { fill: ' . (!empty($colors[0]) ? esc_attr($colors[0]) : 'transparent') . '; }';
742
+ $output .= $calendar_parent_class . ' .wpsbc-legend-item-icon-' . esc_attr($legend_item->get('id')) . ' div:nth-of-type(2) svg { fill: ' . (!empty($colors[1]) ? esc_attr($colors[1]) : 'transparent') . '; }';
743
+
744
+ // Text color
745
+ $color_text = $legend_item->get('color_text');
746
+
747
+ if (!empty($color_text)) {
748
+ $output .= $calendar_parent_class . ' .wpsbc-legend-item-' . esc_attr($legend_item->get('id')) . ' .wpsbc-date-number { color: ' . esc_attr($color_text) . '; }';
749
+ }
750
+
751
+ }
752
+
753
+
754
+ $output .= '</style>';
755
+
756
+ return $output;
757
+
758
+ }
759
+
760
+ /**
761
+ * Passes through all stored events and searches for the event that matches the given date
762
+ * If an event is found it is returned, else null is returned
763
+ *
764
+ * @param int $year
765
+ * @param int $month
766
+ * @param int $day
767
+ *
768
+ * @return mixed WPSBC_Event|null
769
+ *
770
+ */
771
+ protected function get_event_by_date($year, $month, $day)
772
+ {
773
+
774
+ foreach ($this->events as $event) {
775
+
776
+ if ($event->get('date_year') == $year && $event->get('date_month') == $month && $event->get('date_day') == $day) {
777
+ return $event;
778
+ }
779
+
780
+ }
781
+
782
+ return null;
783
+
784
+ }
785
+
786
+
787
+
788
+ /**
789
+ * Determines whether the given date is in the past or not
790
+ *
791
+ * @param int $year
792
+ * @param int $month
793
+ * @param int $day
794
+ *
795
+ * @return bool
796
+ *
797
+ */
798
+ protected function is_date_past($year, $month, $day)
799
+ {
800
+
801
+ $today = mktime(0, 0, 0, current_time('n'), current_time('j'), current_time('Y'));
802
+ $date = mktime(0, 0, 0, $month, $day, $year);
803
+
804
+ return ($today > $date);
805
+
806
+ }
807
+
808
+
809
+ /**
810
+ * Helper function that prints the calendar
811
+ *
812
+ */
813
+ public function display()
814
+ {
815
+
816
+ echo $this->get_display();
817
+
818
+ }
819
+
820
+ }
includes/base/calendar/class-calendar.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * The main class for the Calendar
9
+ *
10
+ */
11
+ class WPSBC_Calendar extends WPSBC_Base_Object {
12
+
13
+ /**
14
+ * The Id of the legend item
15
+ *
16
+ * @access protected
17
+ * @var int
18
+ *
19
+ */
20
+ protected $id;
21
+
22
+ /**
23
+ * The legend item name
24
+ *
25
+ * @access protected
26
+ * @var string
27
+ *
28
+ */
29
+ protected $name;
30
+
31
+ /**
32
+ * The date when the calendar was created
33
+ *
34
+ * @access protected
35
+ * @var string
36
+ *
37
+ */
38
+ protected $date_created;
39
+
40
+ /**
41
+ * The date when the calendar was last modified
42
+ *
43
+ * @access protected
44
+ * @var string
45
+ *
46
+ */
47
+ protected $date_modified;
48
+
49
+ /**
50
+ * The status of the calendar
51
+ *
52
+ * @access protected
53
+ * @var string
54
+ *
55
+ */
56
+ protected $status;
57
+
58
+ /**
59
+ * The random ical hash
60
+ *
61
+ * @access protected
62
+ * @var string
63
+ *
64
+ */
65
+ protected $ical_hash;
66
+
67
+ }
includes/base/calendar/class-object-db-calendars.php ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Class that handles database queries for the Calendars
9
+ *
10
+ */
11
+ Class WPSBC_Object_DB_Calendars extends WPSBC_Object_DB {
12
+
13
+ /**
14
+ * Construct
15
+ *
16
+ */
17
+ public function __construct() {
18
+
19
+ global $wpdb;
20
+
21
+ $this->table_name = $wpdb->prefix . 'wpsbc_calendars';
22
+ $this->primary_key = 'id';
23
+ $this->context = 'calendar';
24
+ $this->query_object_type = 'WPSBC_Calendar';
25
+
26
+ }
27
+
28
+
29
+ /**
30
+ * Return the table columns
31
+ *
32
+ */
33
+ public function get_columns() {
34
+
35
+ return array(
36
+ 'id' => '%d',
37
+ 'name' => '%s',
38
+ 'date_created' => '%s',
39
+ 'date_modified' => '%s',
40
+ 'status' => '%s',
41
+ 'ical_hash' => '%s'
42
+ );
43
+
44
+ }
45
+
46
+
47
+ /**
48
+ * Returns an array of WPSBC_Calendar objects from the database
49
+ *
50
+ * @param array $args
51
+ * @param bool $count - whether to return just the count for the query or not
52
+ *
53
+ * @return mixed array|int
54
+ *
55
+ */
56
+ public function get_calendars( $args = array(), $count = false ) {
57
+
58
+ $defaults = array(
59
+ 'number' => -1,
60
+ 'offset' => 0,
61
+ 'orderby' => 'id',
62
+ 'order' => 'DESC',
63
+ 'include' => array(),
64
+ 'search' => ''
65
+ );
66
+
67
+ $args = wp_parse_args( $args, $defaults );
68
+
69
+ /**
70
+ * Filter the query arguments just before making the db call
71
+ *
72
+ * @param array $args
73
+ *
74
+ */
75
+ $args = apply_filters( 'wpsbc_get_calendars_args', $args );
76
+
77
+ // Number args
78
+ if( $args['number'] < 1 )
79
+ $args['number'] = 999999;
80
+
81
+ // Where clause
82
+ $where = "WHERE 1=1";
83
+
84
+ // Status where clause
85
+ if( ! empty( $args['status'] ) ) {
86
+
87
+ $status = sanitize_text_field( $args['status'] );
88
+ $where .= " AND status = '{$status}'";
89
+
90
+ }
91
+
92
+ // iCalendar hash where clause
93
+ if( ! empty( $args['ical_hash'] ) ) {
94
+
95
+ $ical_hash = sanitize_text_field( $args['ical_hash'] );
96
+ $where .= " AND ical_hash = '{$ical_hash}'";
97
+
98
+ }
99
+
100
+ // Include where clause
101
+ if( ! empty( $args['include'] ) ) {
102
+
103
+ $include = implode( ',', $args['include'] );
104
+ $where .= " AND id IN({$include})";
105
+
106
+ }
107
+
108
+ // Include search
109
+ if( ! empty( $args['search'] ) ) {
110
+
111
+ $search = sanitize_text_field( $args['search'] );
112
+ $where .= " AND name LIKE '%%{$search}%%'";
113
+
114
+ }
115
+
116
+ // Orderby
117
+ $orderby = sanitize_text_field( $args['orderby'] );
118
+
119
+ // Order
120
+ $order = ( 'DESC' === strtoupper( $args['order'] ) ? 'DESC' : 'ASC' );
121
+
122
+ $clauses = compact( 'where', 'orderby', 'order', 'count' );
123
+
124
+ $results = $this->get_results( $clauses, $args, 'wpsbc_get_calendar' );
125
+
126
+ return $results;
127
+
128
+ }
129
+
130
+
131
+ /**
132
+ * Creates and updates the database table for the calendars
133
+ *
134
+ */
135
+ public function create_table() {
136
+
137
+ global $wpdb;
138
+
139
+ $table_name = $this->table_name;
140
+ $charset_collate = $wpdb->get_charset_collate();
141
+
142
+ $query = "CREATE TABLE {$table_name} (
143
+ id bigint(10) NOT NULL AUTO_INCREMENT,
144
+ name text NOT NULL,
145
+ date_created datetime NOT NULL,
146
+ date_modified datetime NOT NULL,
147
+ status text NOT NULL,
148
+ ical_hash text NOT NULL,
149
+ PRIMARY KEY id (id)
150
+ ) {$charset_collate};";
151
+
152
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
153
+ dbDelta( $query );
154
+
155
+ }
156
+
157
+ }
includes/base/calendar/class-object-meta-db-calendars.php ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ /**
7
+ * Class that handles database queries for the Calendars
8
+ *
9
+ */
10
+ Class WPSBC_Object_Meta_DB_Calendars extends WPSBC_Object_Meta_DB {
11
+
12
+ /**
13
+ * Construct
14
+ *
15
+ */
16
+ public function __construct() {
17
+
18
+ global $wpdb;
19
+
20
+ $this->table_name = $wpdb->prefix . 'wpsbc_calendar_meta';
21
+ $this->primary_key = 'calendar_id';
22
+ $this->context = 'calendar';
23
+
24
+ add_action( 'plugins_loaded', array( $this, 'register_wpdb_column' ) );
25
+
26
+ }
27
+
28
+
29
+ /**
30
+ * Register the meta table for the calendars with the $wpdb global
31
+ *
32
+ */
33
+ public function register_wpdb_column() {
34
+
35
+ global $wpdb;
36
+
37
+ $meta_table_name = $this->context . 'meta';
38
+ $wpdb->{$meta_table_name} = $this->table_name;
39
+
40
+ }
41
+
42
+
43
+ /**
44
+ * Return the table columns
45
+ *
46
+ */
47
+ public function get_columns() {
48
+
49
+ return array(
50
+ 'meta_id' => '%d',
51
+ 'calendar_id' => '%d',
52
+ 'meta_key' => '%s',
53
+ 'meta_value' => '%s'
54
+ );
55
+
56
+ }
57
+
58
+
59
+ /**
60
+ * Creates and updates the database table for the calendars
61
+ *
62
+ */
63
+ public function create_table() {
64
+
65
+ global $wpdb;
66
+
67
+ $table_name = $this->table_name;
68
+ $charset_collate = $wpdb->get_charset_collate();
69
+
70
+ $query = "CREATE TABLE {$table_name} (
71
+ meta_id bigint(20) NOT NULL AUTO_INCREMENT,
72
+ calendar_id bigint(20) NOT NULL DEFAULT '0',
73
+ meta_key varchar(255) DEFAULT NULL,
74
+ meta_value longtext,
75
+ PRIMARY KEY (meta_id),
76
+ KEY calendar_id (calendar_id),
77
+ KEY meta_key (meta_key(191))
78
+ ) {$charset_collate};";
79
+
80
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
81
+ dbDelta( $query );
82
+
83
+ }
84
+
85
+ }
includes/base/calendar/functions-ajax.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Ajax callback to refresh the calendar
9
+ *
10
+ */
11
+ function wpsbc_refresh_calendar() {
12
+
13
+ if( empty( $_POST['action'] ) || $_POST['action'] != 'wpsbc_refresh_calendar' ) {
14
+ echo __( '', 'wp-simple-booking-calendar' );
15
+ wp_die();
16
+ }
17
+
18
+ if( empty( $_POST['id'] ) ) {
19
+ wp_die();
20
+ }
21
+
22
+ $calendar_id = absint( $_POST['id'] );
23
+ $calendar = wpsbc_get_calendar( $calendar_id );
24
+ $calendar_args = array();
25
+
26
+ foreach( $_POST as $key => $val ) {
27
+
28
+ if( in_array( $key, array_keys(wpsbc_get_calendar_output_default_args()) ) )
29
+ $calendar_args[$key] = sanitize_text_field( $val );
30
+
31
+ }
32
+
33
+ $calendar_outputter = new WPSBC_Calendar_Outputter( $calendar, $calendar_args );
34
+
35
+ echo $calendar_outputter->get_display();
36
+ wp_die();
37
+
38
+ }
39
+ add_action( 'wp_ajax_nopriv_wpsbc_refresh_calendar', 'wpsbc_refresh_calendar' );
40
+ add_action( 'wp_ajax_wpsbc_refresh_calendar', 'wpsbc_refresh_calendar' );
41
+
includes/base/calendar/functions.php ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if (!defined('ABSPATH')) {
5
+ exit;
6
+ }
7
+
8
+ /**
9
+ * Includes the files needed for the Calendars
10
+ *
11
+ */
12
+ function wpsbc_include_files_calendar()
13
+ {
14
+
15
+ // Get calendar dir path
16
+ $dir_path = plugin_dir_path(__FILE__);
17
+
18
+ // Include other functions files
19
+ if (file_exists($dir_path . 'functions-ajax.php')) {
20
+ include $dir_path . 'functions-ajax.php';
21
+ }
22
+
23
+ // Include main Calendar class
24
+ if (file_exists($dir_path . 'class-calendar.php')) {
25
+ include $dir_path . 'class-calendar.php';
26
+ }
27
+
28
+ // Include the db layer classes
29
+ if (file_exists($dir_path . 'class-object-db-calendars.php')) {
30
+ include $dir_path . 'class-object-db-calendars.php';
31
+ }
32
+
33
+ if (file_exists($dir_path . 'class-object-meta-db-calendars.php')) {
34
+ include $dir_path . 'class-object-meta-db-calendars.php';
35
+ }
36
+
37
+ // Include calendar outputters
38
+ if (file_exists($dir_path . 'class-calendar-outputter.php')) {
39
+ include $dir_path . 'class-calendar-outputter.php';
40
+ }
41
+
42
+
43
+ }
44
+ add_action('wpsbc_include_files', 'wpsbc_include_files_calendar');
45
+
46
+ /**
47
+ * Register the class that handles database queries for the Calendars
48
+ *
49
+ * @param array $classes
50
+ *
51
+ * @return array
52
+ *
53
+ */
54
+ function wpsbc_register_database_classes_calendars($classes)
55
+ {
56
+
57
+ $classes['calendars'] = 'WPSBC_Object_DB_Calendars';
58
+ $classes['calendarmeta'] = 'WPSBC_Object_Meta_DB_Calendars';
59
+
60
+ return $classes;
61
+
62
+ }
63
+ add_filter('wpsbc_register_database_classes', 'wpsbc_register_database_classes_calendars');
64
+
65
+ /**
66
+ * Returns an array with WPSBC_Calendar objects from the database
67
+ *
68
+ * @param array $args
69
+ * @param bool $count
70
+ *
71
+ * @return array
72
+ *
73
+ */
74
+ function wpsbc_get_calendars($args = array(), $count = false)
75
+ {
76
+
77
+ $calendars = wp_simple_booking_calendar()->db['calendars']->get_calendars($args, $count);
78
+
79
+ /**
80
+ * Add a filter hook just before returning
81
+ *
82
+ * @param array $calendars
83
+ * @param array $args
84
+ * @param bool $count
85
+ *
86
+ */
87
+ return apply_filters('wpsbc_get_calendars', $calendars, $args, $count);
88
+
89
+ }
90
+
91
+ /**
92
+ * Gets a calendar from the database
93
+ *
94
+ * @param mixed int|object - calendar id or object representing the calendar
95
+ *
96
+ * @return WPSBC_Calendar|false
97
+ *
98
+ */
99
+ function wpsbc_get_calendar($calendar)
100
+ {
101
+
102
+ return wp_simple_booking_calendar()->db['calendars']->get_object($calendar);
103
+
104
+ }
105
+
106
+ /**
107
+ * Inserts a new calendar into the database
108
+ *
109
+ * @param array $data
110
+ *
111
+ * @return mixed int|false
112
+ *
113
+ */
114
+ function wpsbc_insert_calendar($data)
115
+ {
116
+
117
+ return wp_simple_booking_calendar()->db['calendars']->insert($data);
118
+
119
+ }
120
+
121
+ /**
122
+ * Updates a calendar from the database
123
+ *
124
+ * @param int $calendar_id
125
+ * @param array $data
126
+ *
127
+ * @return bool
128
+ *
129
+ */
130
+ function wpsbc_update_calendar($calendar_id, $data)
131
+ {
132
+
133
+ return wp_simple_booking_calendar()->db['calendars']->update($calendar_id, $data);
134
+
135
+ }
136
+
137
+ /**
138
+ * Deletes a calendar from the database
139
+ *
140
+ * @param int $calendar_id
141
+ *
142
+ * @return bool
143
+ *
144
+ */
145
+ function wpsbc_delete_calendar($calendar_id)
146
+ {
147
+
148
+ return wp_simple_booking_calendar()->db['calendars']->delete($calendar_id);
149
+
150
+ }
151
+
152
+ /**
153
+ * Inserts a new meta entry for the calendar
154
+ *
155
+ * @param int $calendar_id
156
+ * @param string $meta_key
157
+ * @param string $meta_value
158
+ * @param bool $unique
159
+ *
160
+ * @return mixed int|false
161
+ *
162
+ */
163
+ function wpsbc_add_calendar_meta($calendar_id, $meta_key, $meta_value, $unique = false)
164
+ {
165
+
166
+ return wp_simple_booking_calendar()->db['calendarmeta']->add($calendar_id, $meta_key, $meta_value, $unique);
167
+
168
+ }
169
+
170
+ /**
171
+ * Updates a meta entry for the calendar
172
+ *
173
+ * @param int $calendar_id
174
+ * @param string $meta_key
175
+ * @param string $meta_value
176
+ * @param bool $prev_value
177
+ *
178
+ * @return bool
179
+ *
180
+ */
181
+ function wpsbc_update_calendar_meta($calendar_id, $meta_key, $meta_value, $prev_value = '')
182
+ {
183
+
184
+ return wp_simple_booking_calendar()->db['calendarmeta']->update($calendar_id, $meta_key, $meta_value, $prev_value);
185
+
186
+ }
187
+
188
+ /**
189
+ * Returns a meta entry for the calendar
190
+ *
191
+ * @param int $calendar_id
192
+ * @param string $meta_key
193
+ * @param bool $single
194
+ *
195
+ * @return mixed
196
+ *
197
+ */
198
+ function wpsbc_get_calendar_meta($calendar_id, $meta_key = '', $single = false)
199
+ {
200
+
201
+ return wp_simple_booking_calendar()->db['calendarmeta']->get($calendar_id, $meta_key, $single);
202
+
203
+ }
204
+
205
+ /**
206
+ * Removes a meta entry for the calendar
207
+ *
208
+ * @param int $calendar_id
209
+ * @param string $meta_key
210
+ * @param string $meta_value
211
+ * @param bool $delete_all
212
+ *
213
+ * @return bool
214
+ *
215
+ */
216
+ function wpsbc_delete_calendar_meta($calendar_id, $meta_key, $meta_value = '', $delete_all = '')
217
+ {
218
+
219
+ return wp_simple_booking_calendar()->db['calendarmeta']->delete($calendar_id, $meta_key, $meta_value, $delete_all);
220
+
221
+ }
222
+
223
+ /**
224
+ * Returns the default arguments for the calendar outputter
225
+ *
226
+ * @return array
227
+ *
228
+ */
229
+ function wpsbc_get_calendar_output_default_args()
230
+ {
231
+
232
+ $args = array(
233
+ 'show_title' => 1,
234
+ 'show_legend' => 1,
235
+ 'legend_position' => 'side',
236
+ 'show_button_navigation' => 1,
237
+ 'current_year' => current_time('Y'),
238
+ 'current_month' => current_time('n'),
239
+ 'language' => wpsbc_get_locale(),
240
+ 'min_width' => '200',
241
+ 'max_width' => '380',
242
+ );
243
+
244
+ /**
245
+ * Filter the args before returning
246
+ *
247
+ * @param array $args
248
+ *
249
+ */
250
+ $args = apply_filters('wpsbc_get_calendar_output_default_args', $args);
251
+
252
+ return $args;
253
+
254
+ }
255
+
256
+ /**
257
+ * Generates and returns a random 32 character long string
258
+ *
259
+ * @return string
260
+ *
261
+ */
262
+ function wpsbc_generate_ical_hash()
263
+ {
264
+
265
+ $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
266
+ $chars_length = strlen($chars);
267
+ $ical_hash = '';
268
+
269
+ for ($i = 0; $i < 19; $i++) {
270
+
271
+ $ical_hash .= $chars[rand(0, $chars_length - 1)];
272
+
273
+ }
274
+
275
+ return $ical_hash . uniqid();
276
+
277
+ }
278
+
279
+ /**
280
+ * Remove Timezone from Date strings.
281
+ *
282
+ * @param string $date
283
+ *
284
+ * @return string
285
+ *
286
+ */
287
+ function wpsbc_remove_timezone_from_date_string($date)
288
+ {
289
+ return explode('T', $date)[0];
290
+ }
includes/base/class-shortcodes.php ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if (!defined('ABSPATH')) {
5
+ exit;
6
+ }
7
+
8
+ class WPSBC_Shortcodes
9
+ {
10
+
11
+ /**
12
+ * Constructor
13
+ *
14
+ */
15
+ public function __construct()
16
+ {
17
+
18
+ // Register the single calendar shortcode
19
+ add_shortcode('wpsbc', array(__CLASS__, 'single_calendar'));
20
+
21
+ // Register the old shortcode as well
22
+ add_shortcode('sbc', array(__CLASS__, 'single_calendar'));
23
+
24
+ }
25
+
26
+ /**
27
+ * The callback for the WPSBC single calendar shortcode
28
+ *
29
+ * @param array $atts
30
+ *
31
+ */
32
+ public static function single_calendar($atts)
33
+ {
34
+
35
+ // Shortcode default attributes
36
+ $default_atts = array(
37
+ 'id' => 1,
38
+ 'title' => 'yes',
39
+ 'legend' => 'yes',
40
+ 'legend_position' => '',
41
+ 'language' => 'auto',
42
+ );
43
+
44
+ // Shortcode attributes
45
+ $atts = shortcode_atts($default_atts, $atts);
46
+
47
+ // Calendar outputter default arguments
48
+ $default_args = wpsbc_get_calendar_output_default_args();
49
+
50
+ // Translating values from the shortcode attributes to the calendar arguments
51
+ $args = array(
52
+ 'show_title' => (!empty($atts['title']) && $atts['title'] == 'yes' ? 1 : 0),
53
+ 'show_legend' => (!empty($atts['legend']) && $atts['legend'] == 'yes' ? 1 : 0),
54
+ 'legend_position' => $atts['legend_position'],
55
+ 'current_month' => current_time('n'),
56
+ 'current_year' => current_time('Y'),
57
+ 'language' => ($atts['language'] == 'auto' ? wpsbc_get_locale() : $atts['language']),
58
+ );
59
+
60
+ // Remove legend_position if it's empty
61
+ if (empty($args['legend_position'])) {
62
+ unset($args['legend_position']);
63
+ }
64
+
65
+ // Calendar arguments
66
+ $calendar_args = wp_parse_args($args, $default_args);
67
+
68
+ // Calendar id
69
+ $calendar_id = (!empty($atts['id']) ? (int) $atts['id'] : 0);
70
+
71
+ // Calendar
72
+ $calendar = wpsbc_get_calendar($calendar_id);
73
+
74
+ if (is_null($calendar)) {
75
+
76
+ $output = '<p>' . __('Calendar does not exist.', 'wp-simple-booking-calendar') . '</p>';
77
+
78
+ } else {
79
+
80
+ // Initialize the calendar outputter
81
+ $calendar_outputter = new WPSBC_Calendar_Outputter($calendar, $calendar_args);
82
+
83
+ $output = $calendar_outputter->get_display();
84
+
85
+ }
86
+
87
+ return $output;
88
+
89
+ }
90
+
91
+ }
92
+
93
+ // Init shortcodes
94
+ new WPSBC_Shortcodes();
includes/base/class-widget-calendar.php ADDED
@@ -0,0 +1,349 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if (!defined('ABSPATH')) {
5
+ exit;
6
+ }
7
+
8
+ class WPSBC_Widget_Calendar extends WP_Widget
9
+ {
10
+
11
+ /**
12
+ * Constructor
13
+ *
14
+ */
15
+ public function __construct()
16
+ {
17
+
18
+ $widget_ops = array(
19
+ 'classname' => 'wpsbc_calendar',
20
+ 'description' => __('Insert a WP Simple Booking Calendar', 'wp-simple-booking-calendar'),
21
+ );
22
+
23
+ parent::__construct('wpsbc_calendar', 'WP Simple Booking Calendar', $widget_ops);
24
+
25
+ }
26
+
27
+ /**
28
+ * Outputs the content of the widget
29
+ *
30
+ * @param array $args
31
+ * @param array $instance
32
+ *
33
+ */
34
+ public function widget($args, $instance)
35
+ {
36
+
37
+ // Remove the "wpsbc" prefix to have a cleaner code
38
+ $instance = (!empty($instance) && is_array($instance) ? $instance : array());
39
+
40
+ foreach ($instance as $key => $value) {
41
+
42
+ $instance[str_replace('wpsbc_', '', $key)] = $value;
43
+ unset($instance[$key]);
44
+
45
+ }
46
+
47
+ $calendar = wpsbc_get_calendar(absint($instance['select_calendar']));
48
+
49
+ $calendar_args = array(
50
+ 'show_title' => (!empty($instance['show_title']) && $instance['show_title'] == 'yes' ? 1 : 0),
51
+ 'show_legend' => (!empty($instance['show_legend']) && $instance['show_legend'] == 'yes' ? 1 : 0),
52
+ 'legend_position' => (!empty($instance['legend_position']) ? $instance['legend_position'] : 'top'),
53
+ 'language' => (!empty($instance['calendar_language']) ? ($instance['calendar_language'] == 'auto' ? wpsbc_get_locale() : $instance['calendar_language']) : 'en'),
54
+ );
55
+
56
+ echo $args['before_widget'];
57
+
58
+ if (is_null($calendar)) {
59
+
60
+ echo '<p>' . __('Calendar does not exist.', 'wp-simple-booking-calendar') . '</p>';
61
+
62
+ } else {
63
+
64
+ $calendar_outputter = new WPSBC_Calendar_Outputter($calendar, $calendar_args);
65
+
66
+ echo $calendar_outputter->get_display();
67
+ }
68
+
69
+ echo $args['after_widget'];
70
+ }
71
+
72
+ /**
73
+ * Outputs the options form on admin
74
+ *
75
+ * @param array $instance The widget options
76
+ *
77
+ */
78
+ public function form($instance)
79
+ {
80
+
81
+ global $wpdb;
82
+
83
+ $calendar_id = (!empty($instance['wpsbc_select_calendar']) ? $instance['wpsbc_select_calendar'] : 0);
84
+ $show_title = (!empty($instance['wpsbc_show_title']) ? $instance['wpsbc_show_title'] : 'yes');
85
+ $show_legend = (!empty($instance['wpsbc_show_legend']) ? $instance['wpsbc_show_legend'] : 'yes');
86
+ $legend_position = (!empty($instance['wpsbc_legend_position']) ? $instance['wpsbc_legend_position'] : 'top');
87
+ $calendar_language = (!empty($instance['wpsbc_calendar_language']) ? $instance['wpsbc_calendar_language'] : 'en');
88
+
89
+ $calendars = wpsbc_get_calendars();
90
+
91
+ ?>
92
+
93
+ <!-- Calendar -->
94
+ <p>
95
+ <label for="<?php echo $this->get_field_id('wpsbc_select_calendar'); ?>"><?php echo __('Calendar', 'wp-simple-booking-calendar'); ?></label>
96
+
97
+ <select name="<?php echo $this->get_field_name('wpsbc_select_calendar'); ?>" id="<?php echo $this->get_field_id('wpsbc_select_calendar'); ?>" class="widefat">
98
+ <?php foreach ($calendars as $calendar): ?>
99
+ <option <?php echo ($calendar->get('id') == $calendar_id ? 'selected="selected"' : ''); ?> value="<?php echo $calendar->get('id'); ?>"><?php echo $calendar->get('name'); ?></option>
100
+ <?php endforeach;?>
101
+ </select>
102
+ </p>
103
+
104
+ <!-- Show Title -->
105
+ <p>
106
+ <label for="<?php echo $this->get_field_id('wpsbc_show_title'); ?>"><?php echo __('Display title', 'wp-simple-booking-calendar'); ?></label>
107
+
108
+ <select name="<?php echo $this->get_field_name('wpsbc_show_title'); ?>" id="<?php echo $this->get_field_id('wpsbc_show_title'); ?>" class="widefat">
109
+ <option value="yes"><?php echo __('Yes', 'wp-simple-booking-calendar'); ?></option>
110
+ <option value="no" <?php echo ($show_title == 'no' ? 'selected="selected"' : ''); ?>><?php echo __('No', 'wp-simple-booking-calendar'); ?></option>
111
+ </select>
112
+ </p>
113
+
114
+ <!-- Show Legend -->
115
+ <p>
116
+ <label for="<?php echo $this->get_field_id('wpsbc_show_legend'); ?>"><?php echo __('Display legend', 'wp-simple-booking-calendar'); ?></label>
117
+
118
+ <select name="<?php echo $this->get_field_name('wpsbc_show_legend'); ?>" id="<?php echo $this->get_field_id('wpsbc_show_legend'); ?>" class="widefat">
119
+ <option value="yes"><?php echo __('Yes', 'wp-simple-booking-calendar'); ?></option>
120
+ <option value="no" <?php echo ($show_legend == 'no' ? 'selected="selected"' : ''); ?>><?php echo __('No', 'wp-simple-booking-calendar'); ?></option>
121
+ </select>
122
+ </p>
123
+
124
+ <!-- Legend Position -->
125
+ <p>
126
+ <label for="<?php echo $this->get_field_id('wpsbc_legend_position'); ?>"><?php echo __('Legend Position', 'wp-simple-booking-calendar'); ?></label>
127
+
128
+ <select name="<?php echo $this->get_field_name('wpsbc_legend_position'); ?>" id="<?php echo $this->get_field_id('wpsbc_legend_position'); ?>" class="widefat">
129
+ <option <?php echo ($legend_position == 'side' ? 'selected="selected"' : ''); ?> value="side"><?php echo __('Side', 'wp-simple-booking-calendar'); ?></option>
130
+ <option <?php echo ($legend_position == 'top' ? 'selected="selected"' : ''); ?> value="top"><?php echo __('Top', 'wp-simple-booking-calendar'); ?></option>
131
+ <option <?php echo ($legend_position == 'bottom' ? 'selected="selected"' : ''); ?> value="bottom"><?php echo __('Bottom', 'wp-simple-booking-calendar'); ?></option>
132
+ </select>
133
+ </p>
134
+
135
+ <!-- Calendar Language -->
136
+ <p>
137
+ <label for="<?php echo $this->get_field_id('wpsbc_calendar_language'); ?>"><?php echo __('Language', 'wp-simple-booking-calendar'); ?></label>
138
+
139
+ <select name="<?php echo $this->get_field_name('wpsbc_calendar_language'); ?>" id="<?php echo $this->get_field_id('wpsbc_calendar_language'); ?>" class="widefat">
140
+ <?php
141
+ $settings = get_option('wpsbc_settings', array());
142
+ $languages = wpsbc_get_languages();
143
+ $active_languages = (!empty($settings['active_languages']) ? $settings['active_languages'] : array());
144
+ ?>
145
+
146
+ <option value="auto"><?php echo __('Auto (let WP choose)', 'wp-simple-booking-calendar'); ?></option>
147
+
148
+ <?php foreach ($active_languages as $code): ?>
149
+ <option value="<?php echo esc_attr($code); ?>" <?php echo ($calendar_language == $code ? 'selected="selected"' : ''); ?>><?php echo (!empty($languages[$code]) ? $languages[$code] : ''); ?></option>
150
+ <?php endforeach;?>
151
+ </select>
152
+ </p>
153
+
154
+ <?php
155
+
156
+ }
157
+
158
+ /**
159
+ * Processing widget options on save
160
+ *
161
+ * @param array $new_instance The new options
162
+ * @param array $old_instance The previous options
163
+ *
164
+ * @return array
165
+ *
166
+ */
167
+ public function update($new_instance, $old_instance)
168
+ {
169
+
170
+ return $new_instance;
171
+
172
+ }
173
+
174
+ }
175
+
176
+
177
+ // Old widget
178
+ class WpSimpleBookingCalendar_Widget extends WP_Widget
179
+ {
180
+
181
+ /**
182
+ * Constructor
183
+ *
184
+ */
185
+ public function __construct()
186
+ {
187
+
188
+ $widget_ops = array(
189
+ 'classname' => 'widget-sbc',
190
+ 'description' => __('Insert a WP Simple Booking Calendar', 'wp-simple-booking-calendar'),
191
+ );
192
+
193
+ parent::__construct(false, 'WP Simple Booking Calendar - Old Widget', $widget_ops);
194
+
195
+ }
196
+
197
+ /**
198
+ * Outputs the content of the widget
199
+ *
200
+ * @param array $args
201
+ * @param array $instance
202
+ *
203
+ */
204
+ public function widget($args, $instance)
205
+ {
206
+
207
+ // Remove the "wpsbc" prefix to have a cleaner code
208
+ $instance = (!empty($instance) && is_array($instance) ? $instance : array());
209
+
210
+ foreach ($instance as $key => $value) {
211
+
212
+ $instance[str_replace('wpsbc_', '', $key)] = $value;
213
+ unset($instance[$key]);
214
+
215
+ }
216
+
217
+ $calendar = wpsbc_get_calendar(1);
218
+
219
+ $calendar_args = array(
220
+ 'show_title' => (!empty($instance['show_title']) && $instance['show_title'] == 'yes' ? 1 : 0),
221
+ 'show_legend' => (!empty($instance['show_legend']) && $instance['show_legend'] == 'yes' ? 1 : 0),
222
+ 'legend_position' => (!empty($instance['legend_position']) ? $instance['legend_position'] : 'top'),
223
+ 'language' => (!empty($instance['calendar_language']) ? ($instance['calendar_language'] == 'auto' ? wpsbc_get_locale() : $instance['calendar_language']) : 'en'),
224
+ );
225
+
226
+ if (is_null($calendar)) {
227
+
228
+ echo '<p>' . __('Calendar does not exist.', 'wp-simple-booking-calendar') . '</p>';
229
+
230
+ } else {
231
+
232
+ echo $args['before_widget'];
233
+
234
+ $calendar_outputter = new WPSBC_Calendar_Outputter($calendar, $calendar_args);
235
+
236
+ echo $calendar_outputter->get_display();
237
+
238
+ echo $args['after_widget'];
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Outputs the options form on admin
244
+ *
245
+ * @param array $instance The widget options
246
+ *
247
+ */
248
+ public function form($instance)
249
+ {
250
+
251
+ global $wpdb;
252
+
253
+ $calendar_id = (!empty($instance['wpsbc_select_calendar']) ? $instance['wpsbc_select_calendar'] : 1);
254
+ $show_title = (!empty($instance['wpsbc_show_title']) ? $instance['wpsbc_show_title'] : 'yes');
255
+ $show_legend = (!empty($instance['wpsbc_show_legend']) ? $instance['wpsbc_show_legend'] : 'yes');
256
+ $legend_position = (!empty($instance['wpsbc_legend_position']) ? $instance['wpsbc_legend_position'] : 'top');
257
+ $calendar_language = (!empty($instance['wpsbc_calendar_language']) ? $instance['wpsbc_calendar_language'] : 'en');
258
+
259
+ $calendars = wpsbc_get_calendars();
260
+
261
+ ?>
262
+
263
+ <!-- Calendar -->
264
+ <p>
265
+ <label for="<?php echo $this->get_field_id('wpsbc_select_calendar'); ?>"><?php echo __('Calendar', 'wp-simple-booking-calendar'); ?></label>
266
+
267
+ <select name="<?php echo $this->get_field_name('wpsbc_select_calendar'); ?>" id="<?php echo $this->get_field_id('wpsbc_select_calendar'); ?>" class="widefat">
268
+ <?php foreach ($calendars as $calendar): ?>
269
+ <option <?php echo ($calendar->get('id') == $calendar_id ? 'selected="selected"' : ''); ?> value="<?php echo $calendar->get('id'); ?>"><?php echo $calendar->get('name'); ?></option>
270
+ <?php endforeach;?>
271
+ </select>
272
+ </p>
273
+
274
+ <!-- Show Title -->
275
+ <p>
276
+ <label for="<?php echo $this->get_field_id('wpsbc_show_title'); ?>"><?php echo __('Display title', 'wp-simple-booking-calendar'); ?></label>
277
+
278
+ <select name="<?php echo $this->get_field_name('wpsbc_show_title'); ?>" id="<?php echo $this->get_field_id('wpsbc_show_title'); ?>" class="widefat">
279
+ <option value="yes"><?php echo __('Yes', 'wp-simple-booking-calendar'); ?></option>
280
+ <option value="no" <?php echo ($show_title == 'no' ? 'selected="selected"' : ''); ?>><?php echo __('No', 'wp-simple-booking-calendar'); ?></option>
281
+ </select>
282
+ </p>
283
+
284
+ <!-- Show Legend -->
285
+ <p>
286
+ <label for="<?php echo $this->get_field_id('wpsbc_show_legend'); ?>"><?php echo __('Display legend', 'wp-simple-booking-calendar'); ?></label>
287
+
288
+ <select name="<?php echo $this->get_field_name('wpsbc_show_legend'); ?>" id="<?php echo $this->get_field_id('wpsbc_show_legend'); ?>" class="widefat">
289
+ <option value="yes"><?php echo __('Yes', 'wp-simple-booking-calendar'); ?></option>
290
+ <option value="no" <?php echo ($show_legend == 'no' ? 'selected="selected"' : ''); ?>><?php echo __('No', 'wp-simple-booking-calendar'); ?></option>
291
+ </select>
292
+ </p>
293
+
294
+ <!-- Legend Position -->
295
+ <p>
296
+ <label for="<?php echo $this->get_field_id('wpsbc_legend_position'); ?>"><?php echo __('Legend Position', 'wp-simple-booking-calendar'); ?></label>
297
+
298
+ <select name="<?php echo $this->get_field_name('wpsbc_legend_position'); ?>" id="<?php echo $this->get_field_id('wpsbc_legend_position'); ?>" class="widefat">
299
+ <option <?php echo ($legend_position == 'side' ? 'selected="selected"' : ''); ?> value="side"><?php echo __('Side', 'wp-simple-booking-calendar'); ?></option>
300
+ <option <?php echo ($legend_position == 'top' ? 'selected="selected"' : ''); ?> value="top"><?php echo __('Top', 'wp-simple-booking-calendar'); ?></option>
301
+ <option <?php echo ($legend_position == 'bottom' ? 'selected="selected"' : ''); ?> value="bottom"><?php echo __('Bottom', 'wp-simple-booking-calendar'); ?></option>
302
+ </select>
303
+ </p>
304
+
305
+ <!-- Calendar Language -->
306
+ <p>
307
+ <label for="<?php echo $this->get_field_id('wpsbc_calendar_language'); ?>"><?php echo __('Language', 'wp-simple-booking-calendar'); ?></label>
308
+
309
+ <select name="<?php echo $this->get_field_name('wpsbc_calendar_language'); ?>" id="<?php echo $this->get_field_id('wpsbc_calendar_language'); ?>" class="widefat">
310
+ <?php
311
+ $settings = get_option('wpsbc_settings', array());
312
+ $languages = wpsbc_get_languages();
313
+ $active_languages = (!empty($settings['active_languages']) ? $settings['active_languages'] : array());
314
+ ?>
315
+
316
+ <option value="auto"><?php echo __('Auto (let WP choose)', 'wp-simple-booking-calendar'); ?></option>
317
+
318
+ <?php foreach ($active_languages as $code): ?>
319
+ <option value="<?php echo esc_attr($code); ?>" <?php echo ($calendar_language == $code ? 'selected="selected"' : ''); ?>><?php echo (!empty($languages[$code]) ? $languages[$code] : ''); ?></option>
320
+ <?php endforeach;?>
321
+ </select>
322
+ </p>
323
+
324
+ <?php
325
+
326
+ }
327
+
328
+ /**
329
+ * Processing widget options on save
330
+ *
331
+ * @param array $new_instance The new options
332
+ * @param array $old_instance The previous options
333
+ *
334
+ * @return array
335
+ *
336
+ */
337
+ public function update($new_instance, $old_instance)
338
+ {
339
+
340
+ return $new_instance;
341
+
342
+ }
343
+
344
+ }
345
+
346
+ add_action('widgets_init', function () {
347
+ register_widget('WPSBC_Widget_Calendar');
348
+ register_widget('WpSimpleBookingCalendar_Widget');
349
+ });
includes/base/event/class-event.php ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * The main class for the Event
9
+ *
10
+ */
11
+ class WPSBC_Event extends WPSBC_Base_Object {
12
+
13
+ /**
14
+ * The Id of the event
15
+ *
16
+ * @access protected
17
+ * @var int
18
+ *
19
+ */
20
+ protected $id;
21
+
22
+ /**
23
+ * The year
24
+ *
25
+ * @access protected
26
+ * @var int
27
+ *
28
+ */
29
+ protected $date_year;
30
+
31
+ /**
32
+ * The int number representing the month
33
+ *
34
+ * @access protected
35
+ * @var int
36
+ *
37
+ */
38
+ protected $date_month;
39
+
40
+ /**
41
+ * The int number representing the day
42
+ *
43
+ * @access protected
44
+ * @var int
45
+ *
46
+ */
47
+ protected $date_day;
48
+
49
+ /**
50
+ * The ID of the calendar in which the event should appear
51
+ *
52
+ * @access protected
53
+ * @var int
54
+ *
55
+ */
56
+ protected $calendar_id;
57
+
58
+ /**
59
+ * The ID of the legend item attached to the event
60
+ *
61
+ * @access protected
62
+ * @var int
63
+ *
64
+ */
65
+ protected $legend_item_id;
66
+
67
+ /**
68
+ * The admin only description for the event
69
+ *
70
+ * @access protected
71
+ * @var string
72
+ *
73
+ */
74
+ protected $description;
75
+
76
+ /**
77
+ * The optional tooltip to show in the calendar
78
+ *
79
+ * @access protected
80
+ * @var string
81
+ *
82
+ */
83
+ protected $tooltip;
84
+
85
+ }
includes/base/event/class-object-db-events.php ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Class that handles database queries for the Events
9
+ *
10
+ */
11
+ Class WPSBC_Object_DB_Events extends WPSBC_Object_DB {
12
+
13
+ /**
14
+ * Construct
15
+ *
16
+ */
17
+ public function __construct() {
18
+
19
+ global $wpdb;
20
+
21
+ $this->table_name = $wpdb->prefix . 'wpsbc_events';
22
+ $this->primary_key = 'id';
23
+ $this->context = 'event';
24
+ $this->query_object_type = 'WPSBC_Event';
25
+
26
+ }
27
+
28
+
29
+ /**
30
+ * Return the table columns
31
+ *
32
+ */
33
+ public function get_columns() {
34
+
35
+ return array(
36
+ 'id' => '%d',
37
+ 'date_year' => '%d',
38
+ 'date_month' => '%d',
39
+ 'date_day' => '%d',
40
+ 'calendar_id' => '%d',
41
+ 'legend_item_id' => '%d',
42
+ 'description' => '%s',
43
+ 'tooltip' => '%s'
44
+ );
45
+
46
+ }
47
+
48
+
49
+ /**
50
+ * Returns an array of WPSBC_Event objects from the database
51
+ *
52
+ * @param array $args
53
+ * @param bool $count - whether to return just the count for the query or not
54
+ *
55
+ * @return mixed array|int
56
+ *
57
+ */
58
+ public function get_events( $args = array(), $count = false ) {
59
+
60
+ $defaults = array(
61
+ 'number' => -1,
62
+ 'offset' => 0,
63
+ 'orderby' => 'id',
64
+ 'order' => 'ASC',
65
+ 'date_year' => array(),
66
+ 'date_month' => array(),
67
+ 'date_day' => array(),
68
+ 'calendar_id' => 0,
69
+ 'legend_item_id' => 0
70
+ );
71
+
72
+ $args = wp_parse_args( $args, $defaults );
73
+
74
+ // Number args
75
+ if( $args['number'] < 1 )
76
+ $args['number'] = 999999;
77
+
78
+ // Where clause
79
+ $where = '';
80
+
81
+ // Calendar ID where clause
82
+ if( ! empty( $args['calendar_id'] ) ) {
83
+
84
+ $calendar_id = absint( $args['calendar_id'] );
85
+ $where .= "WHERE calendar_id = {$calendar_id}";
86
+
87
+ }
88
+
89
+ // Legend Item ID where clause
90
+ if( ! empty( $args['legend_item_id'] ) ) {
91
+
92
+ $legend_item_id = absint( $args['legend_item_id'] );
93
+ $where .= " AND legend_item_id = {$legend_item_id}";
94
+
95
+ }
96
+
97
+ // Date Year Include
98
+ if( ! empty( $args['date_year'] ) ) {
99
+
100
+ if( is_array( $args['date_year'] ) ) {
101
+
102
+ $date_year = implode( ',', array_map( 'absint', $args['date_year'] ) );
103
+ $where .= " AND date_year IN ( {$date_year} )";
104
+
105
+ } else {
106
+
107
+ $date_year = absint( $args['date_year'] );
108
+ $where .= " AND date_year = {$date_year}";
109
+
110
+ }
111
+
112
+ }
113
+
114
+ // Date Month Include
115
+ if( ! empty( $args['date_month'] ) ) {
116
+
117
+ if( is_array( $args['date_month'] ) ) {
118
+
119
+ $date_month = implode( ',', array_map( 'absint', $args['date_month'] ) );
120
+ $where .= " AND date_month IN ( {$date_month} )";
121
+
122
+ } else {
123
+
124
+ $date_month = absint( $args['date_month'] );
125
+ $where .= " AND date_month = {$date_month}";
126
+
127
+ }
128
+
129
+ }
130
+
131
+ // Date Month Include
132
+ if( ! empty( $args['date_day'] ) ) {
133
+
134
+ if( is_array( $args['date_day'] ) ) {
135
+
136
+ $date_day = implode( ',', array_map( 'absint', $args['date_day'] ) );
137
+ $where .= " AND date_day IN ( {$date_day} )";
138
+
139
+ } else {
140
+
141
+ $date_day = absint( $args['date_day'] );
142
+ $where .= " AND date_day = {$date_day}";
143
+
144
+ }
145
+
146
+ }
147
+
148
+ // Orderby
149
+ $orderby = sanitize_text_field( $args['orderby'] );
150
+
151
+ // Order
152
+ $order = ( 'DESC' === strtoupper( $args['order'] ) ? 'DESC' : 'ASC' );
153
+
154
+ // Merge clauses
155
+ $clauses = compact( 'where', 'orderby', 'order', 'count' );
156
+
157
+ // Get results
158
+ $results = $this->get_results( $clauses, $args, 'wpsbc_get_event' );
159
+
160
+ return $results;
161
+
162
+ }
163
+
164
+
165
+ /**
166
+ * Creates and updates the database table for the legend items
167
+ *
168
+ */
169
+ public function create_table() {
170
+
171
+ global $wpdb;
172
+
173
+ $table_name = $this->table_name;
174
+ $charset_collate = $wpdb->get_charset_collate();
175
+
176
+ $query = "CREATE TABLE {$table_name} (
177
+ id bigint(10) NOT NULL AUTO_INCREMENT,
178
+ date_year smallint(4) NOT NULL,
179
+ date_month smallint(2) NOT NULL,
180
+ date_day smallint(2) NOT NULL,
181
+ calendar_id bigint(10) NOT NULL,
182
+ legend_item_id bigint(10) NOT NULL,
183
+ description longtext NOT NULL,
184
+ tooltip longtext NOT NULL,
185
+ PRIMARY KEY id (id)
186
+ ) {$charset_collate};";
187
+
188
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
189
+ dbDelta( $query );
190
+
191
+ }
192
+
193
+ }
includes/base/event/class-object-meta-db-events.php ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ /**
7
+ * Class that handles database queries for the Events
8
+ *
9
+ */
10
+ Class WPSBC_Object_Meta_DB_Events extends WPSBC_Object_Meta_DB {
11
+
12
+ /**
13
+ * Construct
14
+ *
15
+ */
16
+ public function __construct() {
17
+
18
+ global $wpdb;
19
+
20
+ $this->table_name = $wpdb->prefix . 'wpsbc_event_meta';
21
+ $this->primary_key = 'event_id';
22
+ $this->context = 'event';
23
+
24
+ add_action( 'plugins_loaded', array( $this, 'register_wpdb_column' ) );
25
+
26
+ }
27
+
28
+
29
+ /**
30
+ * Register the meta table for the events with the $wpdb global
31
+ *
32
+ */
33
+ public function register_wpdb_column() {
34
+
35
+ global $wpdb;
36
+
37
+ $meta_table_name = $this->context . 'meta';
38
+ $wpdb->{$meta_table_name} = $this->table_name;
39
+
40
+ }
41
+
42
+
43
+ /**
44
+ * Return the table columns
45
+ *
46
+ */
47
+ public function get_columns() {
48
+
49
+ return array(
50
+ 'meta_id' => '%d',
51
+ 'event_id' => '%d',
52
+ 'meta_key' => '%s',
53
+ 'meta_value' => '%s'
54
+ );
55
+
56
+ }
57
+
58
+
59
+ /**
60
+ * Creates and updates the database table for the events
61
+ *
62
+ */
63
+ public function create_table() {
64
+
65
+ global $wpdb;
66
+
67
+ $table_name = $this->table_name;
68
+ $charset_collate = $wpdb->get_charset_collate();
69
+
70
+ $query = "CREATE TABLE {$table_name} (
71
+ meta_id bigint(20) NOT NULL AUTO_INCREMENT,
72
+ event_id bigint(20) NOT NULL DEFAULT '0',
73
+ meta_key varchar(255) DEFAULT NULL,
74
+ meta_value longtext,
75
+ PRIMARY KEY (meta_id),
76
+ KEY event_id (event_id),
77
+ KEY meta_key (meta_key(191))
78
+ ) {$charset_collate};";
79
+
80
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
81
+ dbDelta( $query );
82
+
83
+ }
84
+
85
+ }
includes/base/event/functions.php ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Includes the files needed for the Legend items
9
+ *
10
+ */
11
+ function wpsbc_include_files_events() {
12
+
13
+ // Get legend dir path
14
+ $dir_path = plugin_dir_path( __FILE__ );
15
+
16
+ // Include main Legend Item class
17
+ if( file_exists( $dir_path . 'class-event.php' ) )
18
+ include $dir_path . 'class-event.php';
19
+
20
+ // Include the db layer classes
21
+ if( file_exists( $dir_path . 'class-object-db-events.php' ) )
22
+ include $dir_path . 'class-object-db-events.php';
23
+
24
+ if( file_exists( $dir_path . 'class-object-meta-db-events.php' ) )
25
+ include $dir_path . 'class-object-meta-db-events.php';
26
+
27
+ }
28
+ add_action( 'wpsbc_include_files', 'wpsbc_include_files_events' );
29
+
30
+
31
+ /**
32
+ * Register the class that handles database queries for the Events
33
+ *
34
+ * @param array $classes
35
+ *
36
+ * @return array
37
+ *
38
+ */
39
+ function wpsbc_register_database_classes_events( $classes ) {
40
+
41
+ $classes['events'] = 'WPSBC_Object_DB_Events';
42
+ $classes['eventmeta'] = 'WPSBC_Object_Meta_DB_Events';
43
+
44
+ return $classes;
45
+
46
+ }
47
+ add_filter( 'wpsbc_register_database_classes', 'wpsbc_register_database_classes_events' );
48
+
49
+
50
+ /**
51
+ * Returns an array with WPSBC_Event objects from the database
52
+ *
53
+ * @param array $args
54
+ * @param bool $count
55
+ *
56
+ * @return array
57
+ *
58
+ */
59
+ function wpsbc_get_events( $args = array(), $count = false ) {
60
+
61
+ return wp_simple_booking_calendar()->db['events']->get_events( $args, $count );
62
+
63
+ }
64
+
65
+
66
+ /**
67
+ * Gets an event from the database
68
+ *
69
+ * @param mixed int|object - event id or object representing the event
70
+ *
71
+ * @return WPSBC_Event|false
72
+ *
73
+ */
74
+ function wpsbc_get_event( $event ) {
75
+
76
+ return wp_simple_booking_calendar()->db['events']->get_object( $event );
77
+
78
+ }
79
+
80
+
81
+ /**
82
+ * Inserts a new event into the database
83
+ *
84
+ * @param array $data
85
+ *
86
+ * @return mixed int|false
87
+ *
88
+ */
89
+ function wpsbc_insert_event( $data ) {
90
+
91
+ return wp_simple_booking_calendar()->db['events']->insert( $data );
92
+
93
+ }
94
+
95
+ /**
96
+ * Updates an event from the database
97
+ *
98
+ * @param int $event_id
99
+ * @param array $data
100
+ *
101
+ * @return bool
102
+ *
103
+ */
104
+ function wpsbc_update_event( $event_id, $data ) {
105
+
106
+ return wp_simple_booking_calendar()->db['events']->update( $event_id, $data );
107
+
108
+ }
109
+
110
+ /**
111
+ * Deletes an event from the database
112
+ *
113
+ * @param int $event_id
114
+ *
115
+ * @return bool
116
+ *
117
+ */
118
+ function wpsbc_delete_event( $event_id ) {
119
+
120
+ return wp_simple_booking_calendar()->db['events']->delete( $event_id );
121
+
122
+ }
123
+
124
+ /**
125
+ * Inserts a new meta entry for the event
126
+ *
127
+ * @param int $event_id
128
+ * @param string $meta_key
129
+ * @param string $meta_value
130
+ * @param bool $unique
131
+ *
132
+ * @return mixed int|false
133
+ *
134
+ */
135
+ function wpsbc_add_event_meta( $event_id, $meta_key, $meta_value, $unique = false ) {
136
+
137
+ return wp_simple_booking_calendar()->db['eventmeta']->add( $event_id, $meta_key, $meta_value, $unique );
138
+
139
+ }
140
+
141
+ /**
142
+ * Updates a meta entry for the event
143
+ *
144
+ * @param int $event_id
145
+ * @param string $meta_key
146
+ * @param string $meta_value
147
+ * @param bool $prev_value
148
+ *
149
+ * @return bool
150
+ *
151
+ */
152
+ function wpsbc_update_event_meta( $event_id, $meta_key, $meta_value, $prev_value = '' ) {
153
+
154
+ return wp_simple_booking_calendar()->db['eventmeta']->update( $event_id, $meta_key, $meta_value, $prev_value );
155
+
156
+ }
157
+
158
+ /**
159
+ * Returns a meta entry for the event
160
+ *
161
+ * @param int $event_id
162
+ * @param string $meta_key
163
+ * @param bool $single
164
+ *
165
+ * @return mixed
166
+ *
167
+ */
168
+ function wpsbc_get_event_meta( $event_id, $meta_key = '', $single = false ) {
169
+
170
+ return wp_simple_booking_calendar()->db['eventmeta']->get( $event_id, $meta_key, $single );
171
+
172
+ }
173
+
174
+ /**
175
+ * Removes a meta entry for the event
176
+ *
177
+ * @param int $event_id
178
+ * @param string $meta_key
179
+ * @param string $meta_value
180
+ * @param bool $delete_all
181
+ *
182
+ * @return bool
183
+ *
184
+ */
185
+ function wpsbc_delete_event_meta( $event_id, $meta_key, $meta_value = '', $delete_all = '' ) {
186
+
187
+ return wp_simple_booking_calendar()->db['eventmeta']->delete( $event_id, $meta_key, $meta_value, $delete_all );
188
+
189
+ }
includes/base/functions-languages.php ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Returns an array with supported languages
9
+ *
10
+ * @return array
11
+ *
12
+ */
13
+ function wpsbc_get_languages() {
14
+
15
+ $languages = array(
16
+ 'en' => 'English',
17
+ 'bg' => 'Bulgarian',
18
+ 'ca' => 'Catalan',
19
+ 'hr' => 'Croatian',
20
+ 'cz' => 'Czech',
21
+ 'da' => 'Danish',
22
+ 'nl' => 'Dutch',
23
+ 'eu' => 'Euskara',
24
+ 'et' => 'Estonian',
25
+ 'fi' => 'Finnish',
26
+ 'fr' => 'French',
27
+ 'gl' => 'Galician',
28
+ 'de' => 'German',
29
+ 'el' => 'Greek',
30
+ 'hu' => 'Hungarian',
31
+ 'it' => 'Italian',
32
+ 'jp' => 'Japanese',
33
+ 'lt' => 'Lithuanian',
34
+ 'no' => 'Norwegian',
35
+ 'pl' => 'Polish',
36
+ 'pt' => 'Portugese',
37
+ 'ro' => 'Romanian',
38
+ 'ru' => 'Russian',
39
+ 'sr' => 'Serbian',
40
+ 'sk' => 'Slovak',
41
+ 'sl' => 'Slovenian',
42
+ 'es' => 'Spanish',
43
+ 'sv' => 'Swedish',
44
+ 'tr' => 'Turkish',
45
+ 'ua' => 'Ukrainian'
46
+ );
47
+
48
+ /**
49
+ * Filter to modify the languages array just before returning
50
+ *
51
+ * @param array $languages
52
+ *
53
+ */
54
+ $languages = apply_filters( 'wpsbc_get_languages', $languages );
55
+
56
+ return $languages;
57
+
58
+ }
59
+
60
+
61
+ /**
62
+ * Returns an array with the first letter of each day of the week in order, starting
63
+ * from Monday until Sunday
64
+ *
65
+ * @param string $language_code
66
+ *
67
+ * @return array
68
+ *
69
+ */
70
+ function wpsbc_get_days_first_letters( $language_code = 'en' ) {
71
+
72
+ $days_first_letters = array(
73
+ 'af' => array( 'M','D','W','D','V','S','S' ), /* South Africa */
74
+ 'bg' => array( 'П','В','С','Ч','П','С','Н' ), /* Bulgarian */
75
+ 'ca' => array( 'D','D','D','D','D','D','D' ), /* Catalan */
76
+ 'hr' => array( 'P','U','S','Č','P','S','N' ), /* Croatian */
77
+ 'cz' => array( 'P','Ú','S','Č','P','S','N' ), /* Czech */
78
+ 'cs' => array( 'P','Ú','S','Č','P','S','N' ), /* Czech */
79
+ 'da' => array( 'M','T','O','T','F','L','S' ), /* Danish */
80
+ 'nl' => array( 'M','D','W','D','V','Z','Z' ), /* Dutch */
81
+ 'en' => array( 'M','T','W','T','F','S','S' ), /* English */
82
+ 'eu' => array( 'A','A','A','O','O','L','I' ), /* Euskara */
83
+ 'et' => array( 'E','T','K','N','R','L','P' ), /* Estonian */
84
+ 'fi' => array( 'M','T','K','T','P','L','S' ), /* Finnish */
85
+ 'fr' => array( 'L','M','M','J','V','S','D' ), /* French */
86
+ 'gl' => array( 'L','M','M','X','V','S','D' ), /* Galician */
87
+ 'de' => array( 'M','D','M','D','F','S','S' ), /* German */
88
+ 'el' => array( 'Δ','Τ','Τ','Π','Π','Σ','Κ' ), /* Greek */
89
+ 'hu' => array( 'H','K','S','C','P','S','V' ), /* Hungarian */
90
+ 'it' => array( 'L','M','M','G','V','S','D' ), /* Italian */
91
+ 'jp' => array( '月','火','水','木','金','土','日' ), /* Japanese */
92
+ 'ja' => array( '月','火','水','木','金','土','日' ), /* Japanese */
93
+ 'no' => array( 'M','T','O','T','F','L','S' ), /* Norwegian */
94
+ 'lt' => array( 'P','A','T','K','P','Š','S' ), /* Lithuanian */
95
+ 'nb' => array( 'M','T','O','T','F','L','S' ), /* Norwegian */
96
+ 'pl' => array( 'P','W','S','C','P','S','N' ), /* Polish */
97
+ 'pt' => array( 'S','T','Q','Q','S','S','D' ), /* Portugese */
98
+ 'ro' => array( 'L','M','M','J','V','S','D' ), /* Romanian */
99
+ 'ru' => array( 'П','В','С','Ч','П','С','В' ), /* Russian */
100
+ 'sr' => array( 'P','U','S','Č','P','S','N' ), /* Serbian */
101
+ 'sk' => array( 'P','U','S','Š','P','S','N' ), /* Slovak */
102
+ 'sl' => array( 'P','T','S','Č','P','S','N' ), /* Slovenian */
103
+ 'es' => array( 'L','M','M','J','V','S','D' ), /* Spanish */
104
+ 'sv' => array( 'M','T','O','T','F','L','S' ), /* Swedish */
105
+ 'tr' => array( 'P','S','Ç','P','C','C','P' ), /* Turkish */
106
+ 'uk' => array( 'П','В','С','Ч','П','С','Н' ) /* Ukrainian */
107
+ );
108
+
109
+ return ( ! empty( $days_first_letters[$language_code] ) ? $days_first_letters[$language_code] : $days_first_letters['en'] );
110
+
111
+ }
112
+
113
+
114
+ /**
115
+ * Returns an array with the names of the months
116
+ *
117
+ * @param int $month
118
+ * @param string $language_code
119
+ *
120
+ * @return array
121
+ *
122
+ */
123
+ function wpsbc_get_month_name( $month, $language_code = 'en' ) {
124
+
125
+ $month_names = array(
126
+
127
+ /* South Africa */
128
+ 'af' => array( 'Januarie', 'Februarie', 'Maart', 'April', 'Mei', 'Junie', 'Julie', 'Augustus', 'September', 'Oktober', 'November', 'Desember' ),
129
+
130
+ /* Bulgarian */
131
+ 'bg' => array( 'Януари', 'Февруари', 'Март', 'Април', 'Май', 'Юни', 'Юли', 'Август', 'Септември', 'Октомври', 'Ноември', 'Декември' ),
132
+
133
+ /* Catalan */
134
+ 'ca' => array( 'Gener', 'Febrer', 'Març', 'Abril', 'Maig', 'Juny', 'Juliol', 'Agost', 'Setembre', 'Octubre', 'Novembre', 'Desembre' ),
135
+
136
+ /* Croatian */
137
+ 'hr' => array( 'Siječanj', 'Veljača', 'Ožujak', 'Travanj', 'Svibanj', 'Lipanj', 'Srpanj', 'Kolovoz', 'Rujan', 'Listopad', 'Studeni', 'Prosinac' ),
138
+
139
+ /* Czech */
140
+ 'cz' => array( 'Leden', 'Únor', 'Březen', 'Duben', 'Květen', 'Červen', 'Červenec', 'Srpen', 'Září', 'Říjen', 'Listopad', 'Prosinec' ),
141
+
142
+ /* Czech */
143
+ 'cs' => array( 'Leden', 'Únor', 'Březen', 'Duben', 'Květen', 'Červen', 'Červenec', 'Srpen', 'Září', 'Říjen', 'Listopad', 'Prosinec' ),
144
+
145
+ /* Danish */
146
+ 'da' => array( 'Januar', 'Februar', 'Marts', 'April', 'Maj', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'December' ),
147
+
148
+ /* Dutch */
149
+ 'nl' => array( 'Januari', 'Februari', 'Maart', 'April', 'Mei', 'Juni', 'Juli', 'Augustus', 'September', 'Oktober', 'November', 'December' ),
150
+
151
+ /* English */
152
+ 'en' => array( 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ),
153
+
154
+ /* Euskara */
155
+ 'eu' => array( 'Urtarrila', 'Otsaila', 'Martxoa', 'Apirila', 'Maiatza', 'Ekaina', 'Uztaila', 'Abuztua', 'Iraila', 'Urria', 'Azaroa', 'Abendua' ),
156
+
157
+ /* Estonian */
158
+ 'et' => array( 'Jaanuar', 'Veebruar', 'Märts', 'Aprill', 'Mai', 'Juuni', 'Juuli', 'August', 'September', 'Oktoober', 'November', 'Detsember' ),
159
+
160
+ /* Finnish */
161
+ 'fi' => array( 'Tammikuu', 'Helmikuu', 'Maaliskuu', 'Huhtikuu', 'Toukokuu', 'Kesäkuu', 'Heinäkuu', 'Elokuu', 'Syyskuu', 'Lokakuu', 'Marraskuu', 'Joulukuu' ),
162
+
163
+ /* French */
164
+ 'fr' => array( 'Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre' ),
165
+
166
+ /* Galician */
167
+ 'gl' => array( 'Xaneiro', 'Febreiro', 'Marzo', 'Abril', 'Maio', 'Xuño', 'Xullo', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Decembro' ),
168
+
169
+ /* German */
170
+ 'de' => array( 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember' ),
171
+
172
+ /* Greek */
173
+ 'el' => array( 'Ιανουάριος', 'Φεβρουάριος', 'Μάρτιος', 'Απρίλιος', 'Μάιος', 'Ιούνιος', 'Ιούλιος', 'Αύγουστος', 'Σεπτέμβριος', 'Οκτώβριος', 'Νοέμβριος', 'Δεκέμβριος' ),
174
+
175
+ /* Hungarian */
176
+ 'hu' => array( 'Január', 'Február', 'Március', 'Április', 'Május', 'Június', 'Július', 'Augusztus', 'Szeptember', 'Október', 'November', 'December' ),
177
+
178
+ /* Italian */
179
+ 'it' => array( 'Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre' ),
180
+
181
+ /* Japanese */
182
+ 'jp' => array( '1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月' ),
183
+ 'ja' => array( '1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月' ),
184
+
185
+ /* Lithuanian */
186
+ 'lt' => array( 'Sausis', 'Vasaris', 'Kovas', 'Balandis', 'Gegužis', 'Birželis', 'Liepa', 'Rugpjūtis', 'Rugsėjis', 'Spalis', 'Lapkritis', 'Gruodis' ),
187
+
188
+ /* Norwegian */
189
+ 'no' => array( 'Januar', 'Februar', 'Mars', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Desember' ),
190
+
191
+ /* Norwegian */
192
+ 'nb' => array( 'Januar', 'Februar', 'Mars', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Desember' ),
193
+
194
+ /* Polish */
195
+ 'pl' => array( 'Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec', 'Lipiec', 'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień' ),
196
+
197
+ /* Portugese */
198
+ 'pt' => array( 'Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', 'Julho', 'Agosto', 'Septembro', 'Outubro', 'Novembro', 'Dezembro' ),
199
+
200
+ /* Romanian */
201
+ 'ro' => array( 'Ianuarie', 'Februarie', 'Martie', 'Aprilie', 'Mai', 'Iunie', 'Iulie', 'August', 'Septembrie', 'Octombrie', 'Noiembrie', 'Decembrie' ),
202
+
203
+ /* Russian */
204
+ 'ru' => array( 'Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь' ),
205
+
206
+ /* Serbian */
207
+ 'sr' => array( 'Januar', 'Februar', 'Mart', 'April', 'Maj', 'Jun', 'Jul', 'Avgust', 'Septembar', 'Oktobar', 'Novembar', 'Decembar' ),
208
+
209
+ /* Slovak */
210
+ 'sk' => array( 'Január', 'Február', 'Marec', 'Apríl', 'Máj', 'Jún', 'Júl', 'August', 'September', 'Október', 'November', 'December' ),
211
+
212
+ /* Slovenian */
213
+ 'sl' => array( 'Januar', 'Februar', 'Marec', 'April', 'Maj', 'Junij', 'Julij', 'Avgust', 'September', 'Oktober', 'November', 'December' ),
214
+
215
+ /* Spanish */
216
+ 'es' => array( 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre' ),
217
+
218
+ /* Swedish */
219
+ 'sv' => array( 'Januari', 'Februari', 'Mars', 'April', 'Maj', 'Juni', 'Juli', 'Augusti', 'September', 'Oktober', 'November', 'December' ),
220
+
221
+ /* Turkish */
222
+ 'tr' => array( 'Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran', 'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık' ),
223
+
224
+ /* Ukrainian */
225
+ 'uk' => array( 'Січень', 'Лютий', 'Березень', 'Квітень', 'Травень', 'Червень', 'Липень', 'Серпень', 'Вересень', 'Жовтень', 'Листопад', 'Грудень' )
226
+
227
+ );
228
+
229
+ return ( ! empty( $month_names[$language_code][$month - 1] ) ? $month_names[$language_code][$month - 1] : $month_names['en'][$month - 1] );
230
+
231
+ }
includes/base/functions-utils.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Sanitizes the values of an array recursivelly
9
+ *
10
+ * @param array $array
11
+ *
12
+ * @return array
13
+ *
14
+ */
15
+ function _wpsbc_array_sanitize_text_field( $array = array() ) {
16
+
17
+ if( empty( $array ) || ! is_array( $array ) )
18
+ return array();
19
+
20
+ foreach( $array as $key => $value ) {
21
+
22
+ if( is_array( $value ) )
23
+ $array[$key] = _wpsbc_array_sanitize_text_field( $value );
24
+
25
+ else
26
+ $array[$key] = sanitize_text_field( $value );
27
+
28
+ }
29
+
30
+ return $array;
31
+
32
+ }
33
+
34
+
35
+ /**
36
+ * Sanitizes the values of an array recursivelly and allows HTML tags
37
+ *
38
+ * @param array $array
39
+ *
40
+ * @return array
41
+ *
42
+ */
43
+ function _wpsbc_array_wp_kses_post( $array = array() ) {
44
+
45
+ if( empty( $array ) || ! is_array( $array ) )
46
+ return array();
47
+
48
+ foreach( $array as $key => $value ) {
49
+
50
+ if( is_array( $value ) )
51
+ $array[$key] = _wpsbc_array_wp_kses_post( $value );
52
+
53
+ else
54
+ $array[$key] = wp_kses_post( $value );
55
+
56
+ }
57
+
58
+ return $array;
59
+
60
+ }
61
+
62
+ /**
63
+ * Returns the current locale
64
+ *
65
+ * @return string
66
+ *
67
+ */
68
+ function wpsbc_get_locale() {
69
+
70
+ return substr( get_locale(), 0, 2 );
71
+
72
+ }
includes/base/functions.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Includes the Base files
9
+ *
10
+ */
11
+ function wpsbc_include_files_base() {
12
+
13
+ // Get legend dir path
14
+ $dir_path = plugin_dir_path( __FILE__ );
15
+
16
+ // Include languages functions
17
+ if( file_exists( $dir_path . 'functions-languages.php' ) )
18
+ include $dir_path . 'functions-languages.php';
19
+
20
+ // Include utils functions
21
+ if( file_exists( $dir_path . 'functions-utils.php' ) )
22
+ include $dir_path . 'functions-utils.php';
23
+
24
+ // Include the shortcodes class
25
+ if( file_exists( $dir_path . 'class-shortcodes.php' ) )
26
+ include $dir_path . 'class-shortcodes.php';
27
+
28
+ // Include the widget class
29
+ if( file_exists( $dir_path . 'class-widget-calendar.php' ) )
30
+ include $dir_path . 'class-widget-calendar.php';
31
+
32
+ // Include the widget class
33
+ if( file_exists( $dir_path . 'class-widget-calendar-search.php' ) )
34
+ include $dir_path . 'class-widget-calendar-search.php';
35
+
36
+ }
37
+ add_action( 'wpsbc_include_files', 'wpsbc_include_files_base' );
38
+
39
+
40
+ /**
41
+ * Returns an array with the weekdays
42
+ *
43
+ * @return array
44
+ *
45
+ */
46
+ function wpsbc_get_weekdays() {
47
+
48
+ $weekdays = array(
49
+ __( 'Monday', 'wp-simple-booking-calendar' ),
50
+ __( 'Tuesday', 'wp-simple-booking-calendar' ),
51
+ __( 'Wednesday', 'wp-simple-booking-calendar' ),
52
+ __( 'Thursday', 'wp-simple-booking-calendar' ),
53
+ __( 'Friday', 'wp-simple-booking-calendar' ),
54
+ __( 'Saturday', 'wp-simple-booking-calendar' ),
55
+ __( 'Sunday', 'wp-simple-booking-calendar' )
56
+ );
57
+
58
+ return $weekdays;
59
+
60
+ }
61
+
62
+ /**
63
+ * Returns true if there are any active languages in settings, false if not.
64
+ *
65
+ * @return bool
66
+ *
67
+ */
68
+ function wpsbc_translations_active()
69
+ {
70
+
71
+ $settings = get_option('wpsbc_settings', array());
72
+
73
+ if (!isset($settings['active_languages'])) {
74
+ return false;
75
+ }
76
+
77
+ if (empty($settings['active_languages'])) {
78
+ return false;
79
+ }
80
+
81
+ return true;
82
+
83
+ }
includes/base/legend/class-legend-item.php ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * The main class for the Legend Item
9
+ *
10
+ */
11
+ class WPSBC_Legend_Item extends WPSBC_Base_Object {
12
+
13
+ /**
14
+ * The Id of the legend item
15
+ *
16
+ * @access protected
17
+ * @var int
18
+ *
19
+ */
20
+ protected $id;
21
+
22
+ /**
23
+ * The legend item type
24
+ *
25
+ * @access protected
26
+ * @var string
27
+ *
28
+ */
29
+ protected $type;
30
+
31
+ /**
32
+ * The legend item name
33
+ *
34
+ * @access protected
35
+ * @var string
36
+ *
37
+ */
38
+ protected $name;
39
+
40
+ /**
41
+ * The legend item background color list
42
+ *
43
+ * @access protected
44
+ * @var array
45
+ *
46
+ */
47
+ protected $color;
48
+
49
+ /**
50
+ * The legend item text color
51
+ *
52
+ * @access protected
53
+ * @var string
54
+ *
55
+ */
56
+ protected $color_text;
57
+
58
+ /**
59
+ * Bool value (0/1) to determine if the legend item is the default one
60
+ * for the legend
61
+ *
62
+ * @access protected
63
+ * @var int
64
+ *
65
+ */
66
+ protected $is_default;
67
+
68
+ /**
69
+ * Bool value (0/1) to determine if the legend item is visible in the front-end
70
+ *
71
+ * @access protected
72
+ * @var int
73
+ *
74
+ */
75
+ protected $is_visible;
76
+
77
+ /**
78
+ * The ID of the calendar for which the legend item is available
79
+ *
80
+ * @access protected
81
+ * @var int
82
+ *
83
+ */
84
+ protected $calendar_id;
85
+
86
+ /**
87
+ * Returns the name property for the current object, or the translation for it
88
+ * if the the language code is provided and the translation for that language exists
89
+ *
90
+ * @param string $language_code
91
+ *
92
+ * @return string
93
+ *
94
+ */
95
+ public function get_name( $language_code = '' ) {
96
+
97
+ if( empty( $language_code ) )
98
+ return $this->name;
99
+
100
+ $translation = wpsbc_get_legend_item_meta( $this->id, 'translation_' . $language_code, true );
101
+
102
+ return ( ! empty( $translation ) ? $translation : $this->name );
103
+
104
+ }
105
+
106
+ }
includes/base/legend/class-object-db-legend-items.php ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Class that handles database queries for the Legend Items
9
+ *
10
+ */
11
+ Class WPSBC_Object_DB_Legend_Items extends WPSBC_Object_DB {
12
+
13
+ /**
14
+ * Construct
15
+ *
16
+ */
17
+ public function __construct() {
18
+
19
+ global $wpdb;
20
+
21
+ $this->table_name = $wpdb->prefix . 'wpsbc_legend_items';
22
+ $this->primary_key = 'id';
23
+ $this->context = 'legend_item';
24
+ $this->query_object_type = 'WPSBC_Legend_Item';
25
+
26
+ }
27
+
28
+
29
+ /**
30
+ * Return the table columns
31
+ *
32
+ */
33
+ public function get_columns() {
34
+
35
+ return array(
36
+ 'id' => '%d',
37
+ 'type' => '%s',
38
+ 'name' => '%s',
39
+ 'color' => '%s',
40
+ 'color_text' => '%s',
41
+ 'is_default' => '%d',
42
+ 'is_visible' => '%d',
43
+ 'calendar_id' => '%d'
44
+ );
45
+
46
+ }
47
+
48
+
49
+ /**
50
+ * Returns an array of WPSBC_Legend_Items objects from the database
51
+ *
52
+ * @param array $args
53
+ * @param bool $count - whether to return just the count for the query or not
54
+ *
55
+ * @return mixed array|int
56
+ *
57
+ */
58
+ public function get_legend_items( $args = array(), $count = false ) {
59
+
60
+ $defaults = array(
61
+ 'number' => -1,
62
+ 'offset' => 0,
63
+ 'orderby' => 'id',
64
+ 'order' => 'ASC',
65
+ 'calendar_id' => 0
66
+ );
67
+
68
+ $args = wp_parse_args( $args, $defaults );
69
+
70
+ // Number args
71
+ if( $args['number'] < 1 )
72
+ $args['number'] = 999999;
73
+
74
+ // Where clause
75
+ $where = '';
76
+
77
+ // Calendar ID where clause
78
+ if( ! empty( $args['calendar_id'] ) ) {
79
+
80
+ $calendar_id = absint( $args['calendar_id'] );
81
+ $where .= "WHERE calendar_id = {$calendar_id}";
82
+
83
+ }
84
+
85
+ // Is visible where clause
86
+ if( isset( $args['is_visible'] ) ) {
87
+
88
+ $is_visible = ( ! empty( $args['is_visible'] ) ? 1 : 0 );
89
+ $where .= " AND is_visible = {$is_visible}";
90
+
91
+ }
92
+
93
+ // Is default where clause
94
+ if( isset( $args['is_default'] ) ) {
95
+
96
+ $is_default = ( ! empty( $args['is_default'] ) ? 1 : 0 );
97
+ $where .= " AND is_default = {$is_default}";
98
+
99
+ }
100
+
101
+ $where .= " AND type = 'single'";
102
+
103
+ // Orderby
104
+ $orderby = sanitize_text_field( $args['orderby'] );
105
+
106
+ // Order
107
+ $order = ( 'DESC' === strtoupper( $args['order'] ) ? 'DESC' : 'ASC' );
108
+
109
+ $clauses = compact( 'where', 'orderby', 'order', 'count' );
110
+
111
+ $results = $this->get_results( $clauses, $args, 'wpsbc_get_legend_item' );
112
+
113
+ /**
114
+ * Filter the legend items results just before returning
115
+ *
116
+ * @param array $results
117
+ * @param array $args
118
+ * @param bool $count
119
+ *
120
+ */
121
+ $results = apply_filters( 'wpsbc_get_legend_items', $results, $args, $count );
122
+
123
+ return $results;
124
+
125
+ }
126
+
127
+
128
+ /**
129
+ * Creates and updates the database table for the legend items
130
+ *
131
+ */
132
+ public function create_table() {
133
+
134
+ global $wpdb;
135
+
136
+ $table_name = $this->table_name;
137
+ $charset_collate = $wpdb->get_charset_collate();
138
+
139
+ $query = "CREATE TABLE {$table_name} (
140
+ id bigint(10) NOT NULL AUTO_INCREMENT,
141
+ type varchar(255) NOT NULL,
142
+ name text NOT NULL,
143
+ color text NOT NULL,
144
+ color_text text,
145
+ is_default tinyint(1) NOT NULL,
146
+ is_visible tinyint(1) NOT NULL,
147
+ calendar_id bigint(10) NOT NULL,
148
+ PRIMARY KEY id (id)
149
+ ) {$charset_collate};";
150
+
151
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
152
+ dbDelta( $query );
153
+
154
+ }
155
+
156
+ }
includes/base/legend/class-object-meta-db-legend-items.php ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ /**
7
+ * Class that handles database queries for the Legend Items
8
+ *
9
+ */
10
+ Class WPSBC_Object_Meta_DB_Legend_Items extends WPSBC_Object_Meta_DB {
11
+
12
+ /**
13
+ * Construct
14
+ *
15
+ */
16
+ public function __construct() {
17
+
18
+ global $wpdb;
19
+
20
+ $this->table_name = $wpdb->prefix . 'wpsbc_legend_item_meta';
21
+ $this->primary_key = 'legend_item_id';
22
+ $this->context = 'legend_item';
23
+
24
+ add_action( 'plugins_loaded', array( $this, 'register_wpdb_column' ) );
25
+
26
+ }
27
+
28
+
29
+ /**
30
+ * Register the meta table for the legend items with the $wpdb global
31
+ *
32
+ */
33
+ public function register_wpdb_column() {
34
+
35
+ global $wpdb;
36
+
37
+ $meta_table_name = $this->context . 'meta';
38
+ $wpdb->{$meta_table_name} = $this->table_name;
39
+
40
+ }
41
+
42
+
43
+ /**
44
+ * Return the table columns
45
+ *
46
+ */
47
+ public function get_columns() {
48
+
49
+ return array(
50
+ 'meta_id' => '%d',
51
+ 'legend_item_id' => '%d',
52
+ 'meta_key' => '%s',
53
+ 'meta_value' => '%s'
54
+ );
55
+
56
+ }
57
+
58
+
59
+ /**
60
+ * Creates and updates the database table for the legend items
61
+ *
62
+ */
63
+ public function create_table() {
64
+
65
+ global $wpdb;
66
+
67
+ $table_name = $this->table_name;
68
+ $charset_collate = $wpdb->get_charset_collate();
69
+
70
+ $query = "CREATE TABLE {$table_name} (
71
+ meta_id bigint(20) NOT NULL AUTO_INCREMENT,
72
+ legend_item_id bigint(20) NOT NULL DEFAULT '0',
73
+ meta_key varchar(255) DEFAULT NULL,
74
+ meta_value longtext,
75
+ PRIMARY KEY (meta_id),
76
+ KEY legend_item_id (legend_item_id),
77
+ KEY meta_key (meta_key(191))
78
+ ) {$charset_collate};";
79
+
80
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
81
+ dbDelta( $query );
82
+
83
+ }
84
+
85
+ }
includes/base/legend/functions.php ADDED
@@ -0,0 +1,387 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+
7
+ /**
8
+ * Includes the files needed for the Legend items
9
+ *
10
+ */
11
+ function wpsbc_include_files_legend() {
12
+
13
+ // Get legend dir path
14
+ $dir_path = plugin_dir_path( __FILE__ );
15
+
16
+ // Include main Legend Item class
17
+ if( file_exists( $dir_path . 'class-legend-item.php' ) )
18
+ include $dir_path . 'class-legend-item.php';
19
+
20
+ // Include the db layer classes
21
+ if( file_exists( $dir_path . 'class-object-db-legend-items.php' ) )
22
+ include $dir_path . 'class-object-db-legend-items.php';
23
+
24
+ if( file_exists( $dir_path . 'class-object-meta-db-legend-items.php' ) )
25
+ include $dir_path . 'class-object-meta-db-legend-items.php';
26
+
27
+ }
28
+ add_action( 'wpsbc_include_files', 'wpsbc_include_files_legend' );
29
+
30
+
31
+ /**
32
+ * Register the class that handles database queries for the Legend Items
33
+ *
34
+ * @param array $classes
35
+ *
36
+ * @return array
37
+ *
38
+ */
39
+ function wpsbc_register_database_classes_legend( $classes ) {
40
+
41
+ $classes['legend_items'] = 'WPSBC_Object_DB_Legend_Items';
42
+ $classes['legend_itemmeta'] = 'WPSBC_Object_Meta_DB_Legend_Items';
43
+
44
+ return $classes;
45
+
46
+ }
47
+ add_filter( 'wpsbc_register_database_classes', 'wpsbc_register_database_classes_legend' );
48
+
49
+
50
+ /**
51
+ * Returns an array with WPSBC_Legend_Items objects from the database
52
+ *
53
+ * @param array $args
54
+ * @param bool $count
55
+ *
56
+ * @return array
57
+ *
58
+ */
59
+ function wpsbc_get_legend_items( $args = array(), $count = false ) {
60
+
61
+ return wp_simple_booking_calendar()->db['legend_items']->get_legend_items( $args, $count );
62
+
63
+ }
64
+
65
+
66
+ /**
67
+ * Gets a legend item from the database
68
+ *
69
+ * @param int $legend_item_id
70
+ *
71
+ * @return WPSBC_Legend_Item|false
72
+ *
73
+ */
74
+ function wpsbc_get_legend_item( $legend_item_id ) {
75
+
76
+ return wp_simple_booking_calendar()->db['legend_items']->get_object( $legend_item_id );
77
+
78
+ }
79
+
80
+
81
+ /**
82
+ * Inserts a new legend item into the database
83
+ *
84
+ * @param array $data
85
+ *
86
+ * @return mixed int|false
87
+ *
88
+ */
89
+ function wpsbc_insert_legend_item( $data ) {
90
+
91
+ return wp_simple_booking_calendar()->db['legend_items']->insert( $data );
92
+
93
+ }
94
+
95
+ /**
96
+ * Updates a legend item from the database
97
+ *
98
+ * @param int $legend_item_id
99
+ * @param array $data
100
+ *
101
+ * @return bool
102
+ *
103
+ */
104
+ function wpsbc_update_legend_item( $legend_item_id, $data ) {
105
+
106
+ return wp_simple_booking_calendar()->db['legend_items']->update( $legend_item_id, $data );
107
+
108
+ }
109
+
110
+ /**
111
+ * Deletes a legend item from the database
112
+ *
113
+ * @param int $legend_item_id
114
+ *
115
+ * @return bool
116
+ *
117
+ */
118
+ function wpsbc_delete_legend_item( $legend_item_id ) {
119
+
120
+ return wp_simple_booking_calendar()->db['legend_items']->delete( $legend_item_id );
121
+
122
+ }
123
+
124
+ /**
125
+ * Inserts a new meta entry for the legend item
126
+ *
127
+ * @param int $legend_item_id
128
+ * @param string $meta_key
129
+ * @param string $meta_value
130
+ * @param bool $unique
131
+ *
132
+ * @return mixed int|false
133
+ *
134
+ */
135
+ function wpsbc_add_legend_item_meta( $legend_item_id, $meta_key, $meta_value, $unique = false ) {
136
+
137
+ return wp_simple_booking_calendar()->db['legend_itemmeta']->add( $legend_item_id, $meta_key, $meta_value, $unique );
138
+
139
+ }
140
+
141
+ /**
142
+ * Updates a meta entry for the legend item
143
+ *
144
+ * @param int $legend_item_id
145
+ * @param string $meta_key
146
+ * @param string $meta_value
147
+ * @param bool $prev_value
148
+ *
149
+ * @return bool
150
+ *
151
+ */
152
+ function wpsbc_update_legend_item_meta( $legend_item_id, $meta_key, $meta_value, $prev_value = '' ) {
153
+
154
+ return wp_simple_booking_calendar()->db['legend_itemmeta']->update( $legend_item_id, $meta_key, $meta_value, $prev_value );
155
+
156
+ }
157
+
158
+ /**
159
+ * Returns a meta entry for the legend item
160
+ *
161
+ * @param int $legend_item_id
162
+ * @param string $meta_key
163
+ * @param bool $single
164
+ *
165
+ * @return mixed
166
+ *
167
+ */
168
+ function wpsbc_get_legend_item_meta( $legend_item_id, $meta_key = '', $single = false ) {
169
+
170
+ return wp_simple_booking_calendar()->db['legend_itemmeta']->get( $legend_item_id, $meta_key, $single );
171
+
172
+ }
173
+
174
+ /**
175
+ * Removes a meta entry for the legend item
176
+ *
177
+ * @param int $legend_item_id
178
+ * @param string $meta_key
179
+ * @param string $meta_value
180
+ * @param bool $delete_all
181
+ *
182
+ * @return bool
183
+ *
184
+ */
185
+ function wpsbc_delete_legend_item_meta( $legend_item_id, $meta_key, $meta_value = '', $delete_all = '' ) {
186
+
187
+ return wp_simple_booking_calendar()->db['legend_itemmeta']->delete( $legend_item_id, $meta_key, $meta_value, $delete_all );
188
+
189
+ }
190
+
191
+
192
+ /**
193
+ * Returns an array with the default legend items
194
+ *
195
+ * @return array
196
+ *
197
+ */
198
+ function wpsbc_get_default_legend_items_data() {
199
+
200
+ $legend_items_data = array(
201
+ array(
202
+ 'name' => __( 'Available', 'wp-simple-booking-calendar' ),
203
+ 'type' => 'single',
204
+ 'color' => array( '#ddffcc' ),
205
+ 'is_visible' => 1,
206
+ 'is_default' => 1,
207
+ 'translations' => array(
208
+ 'en' => 'Available',
209
+ 'bg' => 'Свободен',
210
+ 'ca' => 'Disponible',
211
+ 'hr' => 'Dostupno',
212
+ 'cz' => 'Dostupný',
213
+ 'da' => 'Ledig',
214
+ 'nl' => 'Beschikbaar',
215
+ 'et' => 'Saadaval',
216
+ 'fi' => 'Käytettävissä',
217
+ 'fr' => 'Disponible',
218
+ 'gl' => 'Dispoñible',
219
+ 'de' => 'Verfügbar',
220
+ 'el' => 'Διαθέσιμος',
221
+ 'hu' => 'Elérhető',
222
+ 'it' => 'Disponibile',
223
+ 'jp' => '利用可能',
224
+ 'lt' => 'Yra',
225
+ 'no' => 'Tilgjengelig',
226
+ 'pl' => 'Dostępny',
227
+ 'pt' => 'Disponível',
228
+ 'ro' => 'Disponibil',
229
+ 'ru' => 'Доступный',
230
+ 'sr' => 'Доступан',
231
+ 'sk' => 'Dostupný',
232
+ 'sl' => 'Veljaven',
233
+ 'es' => 'Disponible',
234
+ 'sv' => 'Tillgängliga',
235
+ 'tr' => 'Mevcut',
236
+ 'ua' => 'Доступні',
237
+ ),
238
+ ),
239
+ array(
240
+ 'name' => __( 'Booked', 'wp-simple-booking-calendar' ),
241
+ 'type' => 'single',
242
+ 'color' => array( '#ffc0bd' ),
243
+ 'is_visible' => 1,
244
+ 'translations' => array(
245
+ 'en' => 'Booked',
246
+ 'bg' => 'Резервирано',
247
+ 'ca' => 'Reservat',
248
+ 'hr' => 'Rezerviran',
249
+ 'cs' => 'Rezervováno',
250
+ 'da' => 'Reserveret',
251
+ 'nl' => 'Geboekt',
252
+ 'et' => 'Broneeritud',
253
+ 'fi' => 'Varattu',
254
+ 'fr' => 'Réservé',
255
+ 'gl' => 'Reservado',
256
+ 'de' => 'Gebucht',
257
+ 'el' => 'Κράτηση',
258
+ 'hu' => 'Foglalt',
259
+ 'it' => 'Riservato',
260
+ 'jp' => '予約済み',
261
+ 'lt' => 'Užsakyta',
262
+ 'no' => 'Bestilt',
263
+ 'pl' => 'Zarezerwowane',
264
+ 'pt' => 'Reservado',
265
+ 'ro' => 'Rezervat',
266
+ 'ru' => 'Бронирования',
267
+ 'sr' => 'Резервисан',
268
+ 'sk' => 'Rezervovaný',
269
+ 'sl' => 'Rezervirano',
270
+ 'es' => 'Reservado',
271
+ 'sv' => 'Bokad',
272
+ 'tr' => 'Rezervasyon',
273
+ 'ua' => 'Забронювали',
274
+ ),
275
+ ),
276
+ array(
277
+ 'name' => __( 'Changeover', 'wp-simple-booking-calendar' ),
278
+ 'type' => 'single',
279
+ 'color' => array( '#fee2a0' ),
280
+ 'is_visible' => 1,
281
+ 'translations' => array(
282
+ 'en' => 'Changeover',
283
+ 'bg' => 'смяна',
284
+ 'ca' => 'Canvi',
285
+ 'hr' => 'Prebacivanje',
286
+ 'cs' => 'Přechod',
287
+ 'da' => 'Skiftedag',
288
+ 'nl' => 'Omschakeling',
289
+ 'et' => 'Üleminek',
290
+ 'fi' => 'Siirtyminen',
291
+ 'fr' => 'Passage',
292
+ 'gl' => 'Cambio',
293
+ 'de' => 'Umstellung',
294
+ 'el' => 'Εναλλαγή',
295
+ 'hu' => 'Átállás',
296
+ 'it' => 'Passaggio',
297
+ 'jp' => '切り替え',
298
+ 'lt' => 'Euro įvedimas',
299
+ 'no' => 'Ankomst',
300
+ 'pl' => 'Przełączenie',
301
+ 'pt' => 'Mudança',
302
+ 'ro' => 'Schimbare',
303
+ 'ru' => 'переналадка',
304
+ 'sr' => 'Промјена',
305
+ 'sk' => 'Zmena',
306
+ 'sl' => 'Zamenjava',
307
+ 'es' => 'Cambio',
308
+ 'sv' => 'Övergång',
309
+ 'tr' => 'Değiştirme',
310
+ 'ua' => 'Перехід',
311
+ ),
312
+ ),
313
+ array(
314
+ 'name' => __( 'Changeover 1', 'wp-simple-booking-calendar' ),
315
+ 'type' => 'split',
316
+ 'color' => array( '#ddffcc', '#ffc0bd' ),
317
+ 'is_visible' => 0
318
+ ),
319
+ array(
320
+ 'name' => __( 'Changeover 2', 'wp-simple-booking-calendar' ),
321
+ 'type' => 'split',
322
+ 'color' => array( '#ffc0bd', '#ddffcc' ),
323
+ 'is_visible' => 0
324
+ )
325
+ );
326
+
327
+ return $legend_items_data;
328
+
329
+ }
330
+
331
+
332
+ /**
333
+ * Modifies the order of the legend items when being called through the DB Legend Items object
334
+ * to match the order saved in the calendar meta table under the "legend_items_sort_order" key
335
+ *
336
+ * @param array $results
337
+ * @param array $args
338
+ * @param bool $count
339
+ *
340
+ * @return array
341
+ *
342
+ */
343
+ function wpsbc_get_legend_items_filter_by_order( $results, $args, $count = false ) {
344
+
345
+ if( true === $count )
346
+ return $results;
347
+
348
+ if( empty( $args['calendar_id'] ) )
349
+ return $results;
350
+
351
+ $legend_items_sort_order = wpsbc_get_calendar_meta( $args['calendar_id'], 'legend_items_sort_order', true );
352
+
353
+
354
+ if( empty( $legend_items_sort_order ) || ! is_array( $legend_items_sort_order ) )
355
+ return $results;
356
+
357
+ // New results array
358
+ $new_results = array();
359
+
360
+ // Go through each legend item id in the order list and place the legend item object
361
+ // with the corresponding id in the new results array
362
+ foreach( $legend_items_sort_order as $legend_item_id ) {
363
+
364
+ foreach( $results as $legend_item ) {
365
+
366
+ if( $legend_item->get('id') == $legend_item_id )
367
+ $new_results[$legend_item_id] = $legend_item;
368
+
369
+ }
370
+
371
+ }
372
+
373
+ // Go through each results and add to the new results array the legend item objects
374
+ // that did not have the id in the legend items sort order
375
+ foreach( $results as $legend_item ) {
376
+
377
+ if( ! in_array( $legend_item->get('id'), array_keys( $new_results ) ) )
378
+ $new_results[$legend_item->get('id')] = $legend_item;
379
+
380
+ }
381
+
382
+ $results = array_values( $new_results );
383
+
384
+ return $results;
385
+
386
+ }
387
+ add_filter( 'wpsbc_get_legend_items', 'wpsbc_get_legend_items_filter_by_order', 10, 3 );
includes/libs/iCal/iCalcreator.php ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*********************************************************************************/
3
+ /**
4
+ *
5
+ * A PHP implementation of rfc2445/rfc5545.
6
+ *
7
+ * @copyright Copyright (c) 2007-2015 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
8
+ * @link http://kigkonsult.se/iCalcreator/index.php
9
+ * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
10
+ * @package iCalcreator
11
+ * @version 2.22.1
12
+ */
13
+ /**
14
+ * This library is free software; you can redistribute it and/or
15
+ * modify it under the terms of the GNU Lesser General Public
16
+ * License as published by the Free Software Foundation; either
17
+ * version 2.1 of the License, or (at your option) any later version.
18
+ *
19
+ * This library is distributed in the hope that it will be useful,
20
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
+ * Lesser General Public License for more details.
23
+ *
24
+ * You should have received a copy of the GNU Lesser General Public
25
+ * License along with this library; if not, write to the Free Software
26
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
+ */
28
+ /**
29
+ * iCalcreator.php
30
+ *
31
+ * iCalcreator (class) files includes
32
+ *
33
+ * @package icalcreator
34
+ * @copyright Copyright (c) 2007-2015 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
35
+ * @since 2.22.1 - 2015-06-06
36
+ */
37
+ /*********************************************************************************/
38
+ /**
39
+ * Do NOT remove or change version!!
40
+ */
41
+
42
+ // ini_set('display_errors', 0);
43
+ // ini_set('display_errors', 'Off');
44
+ define( 'ICALCREATOR_VERSION', 'iCalcreator 2.22.1' );
45
+ /*********************************************************************************/
46
+ if( !defined( 'ICALCREATOR_LIB_DIR' ))
47
+ define( 'ICALCREATOR_LIB_DIR', dirname(__FILE__) . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR );
48
+ /**
49
+ * iCalLoader
50
+ *
51
+ * load iCalcreator src and util classes
52
+ *
53
+ * @param string $class
54
+ * @return void
55
+ */
56
+ function iCalLoader( $class ) {
57
+ $file = ICALCREATOR_LIB_DIR . $class . '.class.php';
58
+ if( file_exists( $file ))
59
+ include $file;
60
+ }
61
+ spl_autoload_register( 'iCalLoader' );
62
+ /**
63
+ * iCalcreator add-on functionality functions
64
+ */
65
+ include ICALCREATOR_LIB_DIR . 'iCal.XML.inc.php';
66
+ include ICALCREATOR_LIB_DIR . 'iCal.vCard.inc.php';
67
+ include ICALCREATOR_LIB_DIR . 'iCal.tz.inc.php';
includes/libs/iCal/lib/calendarComponent.class.php ADDED
@@ -0,0 +1,4661 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*********************************************************************************/
3
+ /**
4
+ *
5
+ * iCalcreator, a PHP rfc2445/rfc5545 solution.
6
+ *
7
+ * @copyright Copyright (c) 2007-2015 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
8
+ * @link http://kigkonsult.se/iCalcreator/index.php
9
+ * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
10
+ * @package iCalcreator
11
+ * @version 2.22
12
+ */
13
+ /**
14
+ * This library is free software; you can redistribute it and/or
15
+ * modify it under the terms of the GNU Lesser General Public
16
+ * License as published by the Free Software Foundation; either
17
+ * version 2.1 of the License, or (at your option) any later version.
18
+ *
19
+ * This library is distributed in the hope that it will be useful,
20
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
+ * Lesser General Public License for more details.
23
+ *
24
+ * You should have received a copy of the GNU Lesser General Public
25
+ * License along with this library; if not, write to the Free Software
26
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
+ */
28
+ /*********************************************************************************/
29
+ /**
30
+ * abstract class for calendar components
31
+ *
32
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
33
+ * @since 2.2.11 - 2015-03-31
34
+ */
35
+ class calendarComponent extends iCalBase {
36
+ /** @var array component property UID value */
37
+ protected $uid;
38
+ /** @var array component property DTSTAMP value */
39
+ protected $dtstamp;
40
+ /** @var string component type */
41
+ public $objName;
42
+ /**
43
+ * constructor for calendar component object
44
+ *
45
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
46
+ * @since 2.9.6 - 2011-05-17
47
+ * @uses calendarComponent::$objName
48
+ * @uses calendarComponent::$timezonetype
49
+ * @uses calendarComponent::$uid
50
+ * @uses calendarComponent::$dtstamp
51
+ * @uses calendarComponent::$language
52
+ * @uses calendarComponent::$nl
53
+ * @uses calendarComponent::$unique_id
54
+ * @uses calendarComponent::$format
55
+ * @uses calendarComponent::$dtzid
56
+ * @uses calendarComponent::$allowEmpty
57
+ * @uses calendarComponent::$xcaldecl
58
+ * @uses calendarComponent::_createFormat()
59
+ * @uses calendarComponent::_makeDtstamp()
60
+ */
61
+ function component() {
62
+ $this->objName = ( isset( $this->timezonetype )) ?
63
+ strtolower( $this->timezonetype ) : get_class ( $this );
64
+ $this->uid = array();
65
+ $this->dtstamp = array();
66
+
67
+ $this->language = null;
68
+ $this->nl = null;
69
+ $this->unique_id = null;
70
+ $this->format = null;
71
+ $this->dtzid = null;
72
+ $this->allowEmpty = TRUE;
73
+ $this->xcaldecl = array();
74
+
75
+ $this->_createFormat();
76
+ $this->_makeDtstamp();
77
+ }
78
+ /*********************************************************************************/
79
+ /**
80
+ * Property Name: ACTION
81
+ */
82
+ /**
83
+ * creates formatted output for calendar component property action
84
+ *
85
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
86
+ * @since 2.4.8 - 2008-10-22
87
+ * @uses calendarComponent::$action
88
+ * @uses calendarComponent::getConfig()
89
+ * @uses calendarComponent::_createElement()
90
+ * @uses calendarComponent::_createParams()
91
+ * @uses calendarComponent::_createElement()
92
+ * @return string
93
+ */
94
+ function createAction() {
95
+ if( empty( $this->action )) return FALSE;
96
+ if( empty( $this->action['value'] ))
97
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ACTION' ) : FALSE;
98
+ $attributes = $this->_createParams( $this->action['params'] );
99
+ return $this->_createElement( 'ACTION', $attributes, $this->action['value'] );
100
+ }
101
+ /**
102
+ * set calendar component property action
103
+ *
104
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
105
+ * @since 2.16.21 - 2013-06-23
106
+ * @param string $value "AUDIO" / "DISPLAY" / "EMAIL" / "PROCEDURE"
107
+ * @param mixed $params
108
+ * @uses calendarComponent::getConfig()
109
+ * @uses calendarComponent::$action
110
+ * @uses calendarComponent::$action
111
+ * @uses iCalUtilityFunctions::_setParams()
112
+ * @return bool
113
+ */
114
+ function setAction( $value, $params=FALSE ) {
115
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
116
+ $this->action = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
117
+ return TRUE;
118
+ }
119
+ /*********************************************************************************/
120
+ /**
121
+ * Property Name: ATTACH
122
+ */
123
+ /**
124
+ * creates formatted output for calendar component property attach
125
+ *
126
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
127
+ * @since 2.11.16 - 2012-02-04
128
+ * @uses calendarComponent::$attach
129
+ * @uses calendarComponent::_createParams()
130
+ * @uses calendarComponent::$format
131
+ * @uses calendarComponent::$intAttrDelimiter
132
+ * @uses calendarComponent::$attributeDelimiter
133
+ * @uses calendarComponent::$valueInit
134
+ * @uses calendarComponent::$nl
135
+ * @uses calendarComponent::_createElement()
136
+ * @return string
137
+ */
138
+ function createAttach() {
139
+ if( empty( $this->attach )) return FALSE;
140
+ $output = null;
141
+ foreach( $this->attach as $attachPart ) {
142
+ if( !empty( $attachPart['value'] )) {
143
+ $attributes = $this->_createParams( $attachPart['params'] );
144
+ if(( 'xcal' != $this->format ) && isset( $attachPart['params']['VALUE'] ) && ( 'BINARY' == $attachPart['params']['VALUE'] )) {
145
+ $attributes = str_replace( $this->intAttrDelimiter, $this->attributeDelimiter, $attributes );
146
+ $str = 'ATTACH'.$attributes.$this->valueInit.$attachPart['value'];
147
+ $output = substr( $str, 0, 75 ).$this->nl;
148
+ $str = substr( $str, 75 );
149
+ $output .= ' '.chunk_split( $str, 74, $this->nl.' ' );
150
+ if( ' ' == substr( $output, -1 ))
151
+ $output = rtrim( $output );
152
+ if( $this->nl != substr( $output, ( 0 - strlen( $this->nl ))))
153
+ $output .= $this->nl;
154
+ return $output;
155
+ }
156
+ $output .= $this->_createElement( 'ATTACH', $attributes, $attachPart['value'] );
157
+ }
158
+ elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'ATTACH' );
159
+ }
160
+ return $output;
161
+ }
162
+ /**
163
+ * set calendar component property attach
164
+ *
165
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
166
+ * @since 2.16.21 - 2013-06-23
167
+ * @param string $value
168
+ * @param array $params
169
+ * @param integer $index
170
+ * @uses calendarComponent::$attach
171
+ * @uses calendarComponent::getConfig()
172
+ * @uses iCalUtilityFunctions::_setMval()
173
+ * @return bool
174
+ */
175
+ function setAttach( $value, $params=FALSE, $index=FALSE ) {
176
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
177
+ iCalUtilityFunctions::_setMval( $this->attach, $value, $params, FALSE, $index );
178
+ return TRUE;
179
+ }
180
+ /*********************************************************************************/
181
+ /**
182
+ * Property Name: ATTENDEE
183
+ */
184
+ /**
185
+ * creates formatted output for calendar component property attendee
186
+ *
187
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
188
+ * @since 2.11.12 - 2012-01-31
189
+ * @uses calendarComponent::$attendee
190
+ * @uses calendarComponent::getConfig()
191
+ * @uses calendarComponent::_createElement()
192
+ * @uses calendarComponent::$intAttrDelimiter
193
+ * @return string
194
+ */
195
+ function createAttendee() {
196
+ if( empty( $this->attendee )) return FALSE;
197
+ $output = null;
198
+ foreach( $this->attendee as $attendeePart ) { // start foreach 1
199
+ if( empty( $attendeePart['value'] )) {
200
+ if( $this->getConfig( 'allowEmpty' ))
201
+ $output .= $this->_createElement( 'ATTENDEE' );
202
+ continue;
203
+ }
204
+ $attendee1 = $attendee2 = null;
205
+ foreach( $attendeePart as $paramlabel => $paramvalue ) { // start foreach 2
206
+ if( 'value' == $paramlabel )
207
+ $attendee2 .= $paramvalue;
208
+ elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue ))) { // start elseif
209
+ $mParams = array( 'MEMBER', 'DELEGATED-TO', 'DELEGATED-FROM' );
210
+ foreach( $paramvalue as $pKey => $pValue ) { // fix (opt) quotes
211
+ if( is_array( $pValue ) || in_array( $pKey, $mParams ))
212
+ continue;
213
+ if(( FALSE !== strpos( $pValue, ':' )) ||
214
+ ( FALSE !== strpos( $pValue, ';' )) ||
215
+ ( FALSE !== strpos( $pValue, ',' )))
216
+ $paramvalue[$pKey] = '"'.$pValue.'"';
217
+ }
218
+ // set attenddee parameters in rfc2445 order
219
+ if( isset( $paramvalue['CUTYPE'] ))
220
+ $attendee1 .= $this->intAttrDelimiter.'CUTYPE='.$paramvalue['CUTYPE'];
221
+ if( isset( $paramvalue['MEMBER'] )) {
222
+ $attendee1 .= $this->intAttrDelimiter.'MEMBER=';
223
+ foreach( $paramvalue['MEMBER'] as $cix => $opv )
224
+ $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ;
225
+ }
226
+ if( isset( $paramvalue['ROLE'] ))
227
+ $attendee1 .= $this->intAttrDelimiter.'ROLE='.$paramvalue['ROLE'];
228
+ if( isset( $paramvalue['PARTSTAT'] ))
229
+ $attendee1 .= $this->intAttrDelimiter.'PARTSTAT='.$paramvalue['PARTSTAT'];
230
+ if( isset( $paramvalue['RSVP'] ))
231
+ $attendee1 .= $this->intAttrDelimiter.'RSVP='.$paramvalue['RSVP'];
232
+ if( isset( $paramvalue['DELEGATED-TO'] )) {
233
+ $attendee1 .= $this->intAttrDelimiter.'DELEGATED-TO=';
234
+ foreach( $paramvalue['DELEGATED-TO'] as $cix => $opv )
235
+ $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ;
236
+ }
237
+ if( isset( $paramvalue['DELEGATED-FROM'] )) {
238
+ $attendee1 .= $this->intAttrDelimiter.'DELEGATED-FROM=';
239
+ foreach( $paramvalue['DELEGATED-FROM'] as $cix => $opv )
240
+ $attendee1 .= ( $cix ) ? ',"'.$opv.'"' : '"'.$opv.'"' ;
241
+ }
242
+ if( isset( $paramvalue['SENT-BY'] ))
243
+ $attendee1 .= $this->intAttrDelimiter.'SENT-BY='.$paramvalue['SENT-BY'];
244
+ if( isset( $paramvalue['CN'] ))
245
+ $attendee1 .= $this->intAttrDelimiter.'CN='.$paramvalue['CN'];
246
+ if( isset( $paramvalue['DIR'] )) {
247
+ $delim = ( FALSE === strpos( $paramvalue['DIR'], '"' )) ? '"' : '';
248
+ $attendee1 .= $this->intAttrDelimiter.'DIR='.$delim.$paramvalue['DIR'].$delim;
249
+ }
250
+ if( isset( $paramvalue['LANGUAGE'] ))
251
+ $attendee1 .= $this->intAttrDelimiter.'LANGUAGE='.$paramvalue['LANGUAGE'];
252
+ $xparams = array();
253
+ foreach( $paramvalue as $optparamlabel => $optparamvalue ) { // start foreach 3
254
+ if( ctype_digit( (string) $optparamlabel )) {
255
+ $xparams[] = $optparamvalue;
256
+ continue;
257
+ }
258
+ if( !in_array( $optparamlabel, array( 'CUTYPE', 'MEMBER', 'ROLE', 'PARTSTAT', 'RSVP', 'DELEGATED-TO', 'DELEGATED-FROM', 'SENT-BY', 'CN', 'DIR', 'LANGUAGE' )))
259
+ $xparams[$optparamlabel] = $optparamvalue;
260
+ } // end foreach 3
261
+ ksort( $xparams, SORT_STRING );
262
+ foreach( $xparams as $paramKey => $paramValue ) {
263
+ if( ctype_digit( (string) $paramKey ))
264
+ $attendee1 .= $this->intAttrDelimiter.$paramValue;
265
+ else
266
+ $attendee1 .= $this->intAttrDelimiter."$paramKey=$paramValue";
267
+ } // end foreach 3
268
+ } // end elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue )))
269
+ } // end foreach 2
270
+ $output .= $this->_createElement( 'ATTENDEE', $attendee1, $attendee2 );
271
+ } // end foreach 1
272
+ return $output;
273
+ }
274
+ /**
275
+ * set calendar component property attach
276
+ *
277
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
278
+ * @since 2.18.13 - 2013-09-22
279
+ * @param string $value
280
+ * @param array $params
281
+ * @param integer $index
282
+ * @uses calendarComponent::getConfig()
283
+ * @uses calendarComponent::$objName
284
+ * @uses iCalUtilityFunctions::_existRem()
285
+ * @uses iCalUtilityFunctions::_setMval()
286
+ * @return bool
287
+ */
288
+ function setAttendee( $value, $params=FALSE, $index=FALSE ) {
289
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
290
+ // ftp://, http://, mailto:, file://, gopher://, news:, nntp://, telnet://, wais://, prospero:// may exist.. . also in params
291
+ if( !empty( $value )) {
292
+ if( FALSE === ( $pos = strpos( substr( $value, 0, 9 ), ':' )))
293
+ $value = 'MAILTO:'.$value;
294
+ elseif( !empty( $value ))
295
+ $value = strtolower( substr( $value, 0, $pos )).substr( $value, $pos );
296
+ $value = str_replace( 'mailto:', 'MAILTO:', $value );
297
+ }
298
+ $params2 = array();
299
+ if( is_array($params )) {
300
+ $optarrays = array();
301
+ $params = array_change_key_case( $params, CASE_UPPER );
302
+ foreach( $params as $optparamlabel => $optparamvalue ) {
303
+ if(( 'X-' != substr( $optparamlabel, 0, 2 )) && (( 'vfreebusy' == $this->objName ) || ( 'valarm' == $this->objName )))
304
+ continue;
305
+ switch( $optparamlabel ) {
306
+ case 'MEMBER':
307
+ case 'DELEGATED-TO':
308
+ case 'DELEGATED-FROM':
309
+ if( !is_array( $optparamvalue ))
310
+ $optparamvalue = array( $optparamvalue );
311
+ foreach( $optparamvalue as $part ) {
312
+ $part = trim( $part );
313
+ if(( '"' == substr( $part, 0, 1 )) &&
314
+ ( '"' == substr( $part, -1 )))
315
+ $part = substr( $part, 1, ( strlen( $part ) - 2 ));
316
+ if( 'mailto:' != strtolower( substr( $part, 0, 7 )))
317
+ $part = "MAILTO:$part";
318
+ else
319
+ $part = 'MAILTO:'.substr( $part, 7 );
320
+ $optarrays[$optparamlabel][] = $part;
321
+ }
322
+ break;
323
+ default:
324
+ if(( '"' == substr( $optparamvalue, 0, 1 )) &&
325
+ ( '"' == substr( $optparamvalue, -1 )))
326
+ $optparamvalue = substr( $optparamvalue, 1, ( strlen( $optparamvalue ) - 2 ));
327
+ if( 'SENT-BY' == $optparamlabel ) {
328
+ if( 'mailto:' != strtolower( substr( $optparamvalue, 0, 7 )))
329
+ $optparamvalue = "MAILTO:$optparamvalue";
330
+ else
331
+ $optparamvalue = 'MAILTO:'.substr( $optparamvalue, 7 );
332
+ }
333
+ $params2[$optparamlabel] = $optparamvalue;
334
+ break;
335
+ } // end switch( $optparamlabel.. .
336
+ } // end foreach( $optparam.. .
337
+ foreach( $optarrays as $optparamlabel => $optparams )
338
+ $params2[$optparamlabel] = $optparams;
339
+ }
340
+ // remove defaults
341
+ iCalUtilityFunctions::_existRem( $params2, 'CUTYPE', 'INDIVIDUAL' );
342
+ iCalUtilityFunctions::_existRem( $params2, 'PARTSTAT', 'NEEDS-ACTION' );
343
+ iCalUtilityFunctions::_existRem( $params2, 'ROLE', 'REQ-PARTICIPANT' );
344
+ iCalUtilityFunctions::_existRem( $params2, 'RSVP', 'FALSE' );
345
+ // check language setting
346
+ if( isset( $params2['CN' ] )) {
347
+ $lang = $this->getConfig( 'language' );
348
+ if( !isset( $params2['LANGUAGE' ] ) && !empty( $lang ))
349
+ $params2['LANGUAGE' ] = $lang;
350
+ }
351
+ iCalUtilityFunctions::_setMval( $this->attendee, $value, $params2, FALSE, $index );
352
+ return TRUE;
353
+ }
354
+ /*********************************************************************************/
355
+ /**
356
+ * Property Name: CATEGORIES
357
+ */
358
+ /**
359
+ * creates formatted output for calendar component property categories
360
+ *
361
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
362
+ * @since 2.16.2 - 2012-12-18
363
+ * @uses calendarComponent::$categories
364
+ * @uses calendarComponent::_createElement()
365
+ * @uses calendarComponent::_createParams()
366
+ * @uses iCalUtilityFunctions::_strrep()
367
+ * @uses calendarComponent::$format
368
+ * @uses calendarComponent::$nl
369
+ * @return string
370
+ */
371
+ function createCategories() {
372
+ if( empty( $this->categories )) return FALSE;
373
+ $output = null;
374
+ foreach( $this->categories as $category ) {
375
+ if( empty( $category['value'] )) {
376
+ if ( $this->getConfig( 'allowEmpty' ))
377
+ $output .= $this->_createElement( 'CATEGORIES' );
378
+ continue;
379
+ }
380
+ $attributes = $this->_createParams( $category['params'], array( 'LANGUAGE' ));
381
+ if( is_array( $category['value'] )) {
382
+ foreach( $category['value'] as $cix => $categoryPart )
383
+ $category['value'][$cix] = iCalUtilityFunctions::_strrep( $categoryPart, $this->format, $this->nl );
384
+ $content = implode( ',', $category['value'] );
385
+ }
386
+ else
387
+ $content = iCalUtilityFunctions::_strrep( $category['value'], $this->format, $this->nl );
388
+ $output .= $this->_createElement( 'CATEGORIES', $attributes, $content );
389
+ }
390
+ return $output;
391
+ }
392
+ /**
393
+ * set calendar component property categories
394
+ *
395
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
396
+ * @since 2.16.21 - 2013-06-23
397
+ * @param mixed $value
398
+ * @param array $params
399
+ * @param integer $index
400
+ * @uses calendarComponent::getConfig()
401
+ * @uses calendarComponent::$categories
402
+ * @uses iCalUtilityFunctions::_setMval()
403
+ * @return bool
404
+ */
405
+ function setCategories( $value, $params=FALSE, $index=FALSE ) {
406
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
407
+ iCalUtilityFunctions::_setMval( $this->categories, $value, $params, FALSE, $index );
408
+ return TRUE;
409
+ }
410
+ /*********************************************************************************/
411
+ /**
412
+ * Property Name: CLASS
413
+ */
414
+ /**
415
+ * creates formatted output for calendar component property class
416
+ *
417
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
418
+ * @since 0.9.7 - 2006-11-20
419
+ * @uses calendarComponent::$class
420
+ * @uses calendarComponent::getConfig()
421
+ * @uses calendarComponent::_createElement()
422
+ * @uses calendarComponent::_createParams()
423
+ * @return string
424
+ */
425
+ function createClass() {
426
+ if( empty( $this->class )) return FALSE;
427
+ if( empty( $this->class['value'] ))
428
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'CLASS' ) : FALSE;
429
+ $attributes = $this->_createParams( $this->class['params'] );
430
+ return $this->_createElement( 'CLASS', $attributes, $this->class['value'] );
431
+ }
432
+ /**
433
+ * set calendar component property class
434
+ *
435
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
436
+ * @since 2.16.21 - 2013-06-23
437
+ * @param string $value "PUBLIC" / "PRIVATE" / "CONFIDENTIAL" / iana-token / x-name
438
+ * @param array $params optional
439
+ * @uses calendarComponent::getConfig()
440
+ * @uses calendarComponent::$class
441
+ * @uses iCalUtilityFunctions::_setParams()
442
+ * @return bool
443
+ */
444
+ function setClass( $value, $params=FALSE ) {
445
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
446
+ $this->class = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
447
+ return TRUE;
448
+ }
449
+ /*********************************************************************************/
450
+ /**
451
+ * Property Name: COMMENT
452
+ */
453
+ /**
454
+ * creates formatted output for calendar component property comment
455
+ *
456
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
457
+ * @since 2.16.2 - 2012-12-18
458
+ * @uses calendarComponent::$comment
459
+ * @uses calendarComponent::getConfig()
460
+ * @uses calendarComponent::_createElement()
461
+ * @uses calendarComponent::_createParams()
462
+ * @uses iCalUtilityFunctions::_strrep()
463
+ * @uses calendarComponent::$format
464
+ * @uses calendarComponent::$nl
465
+ * @return string
466
+ */
467
+ function createComment() {
468
+ if( empty( $this->comment )) return FALSE;
469
+ $output = null;
470
+ foreach( $this->comment as $commentPart ) {
471
+ if( empty( $commentPart['value'] )) {
472
+ if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'COMMENT' );
473
+ continue;
474
+ }
475
+ $attributes = $this->_createParams( $commentPart['params'], array( 'ALTREP', 'LANGUAGE' ));
476
+ $content = iCalUtilityFunctions::_strrep( $commentPart['value'], $this->format, $this->nl );
477
+ $output .= $this->_createElement( 'COMMENT', $attributes, $content );
478
+ }
479
+ return $output;
480
+ }
481
+ /**
482
+ * set calendar component property comment
483
+ *
484
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
485
+ * @since 2.16.21 - 2013-06-23
486
+ * @param string $value
487
+ * @param array $params
488
+ * @param integer $index
489
+ * @uses calendarComponent::getConfig()
490
+ * @uses iCalUtilityFunctions::_setMval()
491
+ * @uses calendarComponent::$comment
492
+ * @return bool
493
+ */
494
+ function setComment( $value, $params=FALSE, $index=FALSE ) {
495
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
496
+ iCalUtilityFunctions::_setMval( $this->comment, $value, $params, FALSE, $index );
497
+ return TRUE;
498
+ }
499
+ /*********************************************************************************/
500
+ /**
501
+ * Property Name: COMPLETED
502
+ */
503
+ /**
504
+ * creates formatted output for calendar component property completed
505
+ *
506
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
507
+ * @since 2.4.8 - 2008-10-22
508
+ * @return string
509
+ * @uses calendarComponent::$completed
510
+ * @uses calendarComponent::getConfig()
511
+ * @uses calendarComponent::_createElement()
512
+ * @uses iCalUtilityFunctions::_date2strdate();
513
+ * @uses calendarComponent::_createParams()
514
+ */
515
+ function createCompleted( ) {
516
+ if( empty( $this->completed )) return FALSE;
517
+ if( !isset( $this->completed['value']['year'] ) &&
518
+ !isset( $this->completed['value']['month'] ) &&
519
+ !isset( $this->completed['value']['day'] ) &&
520
+ !isset( $this->completed['value']['hour'] ) &&
521
+ !isset( $this->completed['value']['min'] ) &&
522
+ !isset( $this->completed['value']['sec'] ))
523
+ if( $this->getConfig( 'allowEmpty' ))
524
+ return $this->_createElement( 'COMPLETED' );
525
+ else return FALSE;
526
+ $formatted = iCalUtilityFunctions::_date2strdate( $this->completed['value'], 7 );
527
+ $attributes = $this->_createParams( $this->completed['params'] );
528
+ return $this->_createElement( 'COMPLETED', $attributes, $formatted );
529
+ }
530
+ /**
531
+ * set calendar component property completed
532
+ *
533
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
534
+ * @since 2.16.21 - 2013-06-23
535
+ * @param mixed $year
536
+ * @param mixed $month
537
+ * @param int $day
538
+ * @param int $hour
539
+ * @param int $min
540
+ * @param int $sec
541
+ * @param array $params
542
+ * @uses calendarComponent::getConfig()
543
+ * @uses iCalUtilityFunctions::_setParams()
544
+ * @uses calendarComponent::$completed
545
+ * @uses iCalUtilityFunctions::_setDate2()
546
+ * @return bool
547
+ */
548
+ function setCompleted( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
549
+ if( empty( $year )) {
550
+ if( $this->getConfig( 'allowEmpty' )) {
551
+ $this->completed = array( 'value' => '', 'params' => iCalUtilityFunctions::_setParams( $params ));
552
+ return TRUE;
553
+ }
554
+ else
555
+ return FALSE;
556
+ }
557
+ $this->completed = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
558
+ return TRUE;
559
+ }
560
+ /*********************************************************************************/
561
+ /**
562
+ * Property Name: CONTACT
563
+ */
564
+ /**
565
+ * creates formatted output for calendar component property contact
566
+ *
567
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
568
+ * @since 2.16.2 - 2012-12-18
569
+ * @uses calendarComponent::$contact
570
+ * @uses calendarComponent::_createParams()
571
+ * @uses iCalUtilityFunctions::_strrep()
572
+ * @uses calendarComponent::getConfig()
573
+ * @uses calendarComponent::_createElement()
574
+ * @return string
575
+ */
576
+ function createContact() {
577
+ if( empty( $this->contact )) return FALSE;
578
+ $output = null;
579
+ foreach( $this->contact as $contact ) {
580
+ if( !empty( $contact['value'] )) {
581
+ $attributes = $this->_createParams( $contact['params'], array( 'ALTREP', 'LANGUAGE' ));
582
+ $content = iCalUtilityFunctions::_strrep( $contact['value'], $this->format, $this->nl );
583
+ $output .= $this->_createElement( 'CONTACT', $attributes, $content );
584
+ }
585
+ elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'CONTACT' );
586
+ }
587
+ return $output;
588
+ }
589
+ /**
590
+ * set calendar component property contact
591
+ *
592
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
593
+ * @since 2.16.21 - 2013-06-23
594
+ * @param string $value
595
+ * @param array $params
596
+ * @param integer $index
597
+ * @uses calendarComponent::getConfig()
598
+ * @uses iCalUtilityFunctions::_setMval()
599
+ * @uses calendarComponent::$contact
600
+ * @return bool
601
+ */
602
+ function setContact( $value, $params=FALSE, $index=FALSE ) {
603
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
604
+ iCalUtilityFunctions::_setMval( $this->contact, $value, $params, FALSE, $index );
605
+ return TRUE;
606
+ }
607
+ /*********************************************************************************/
608
+ /**
609
+ * Property Name: CREATED
610
+ */
611
+ /**
612
+ * creates formatted output for calendar component property created
613
+ *
614
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
615
+ * @since 2.4.8 - 2008-10-21
616
+ * @uses calendarComponent::$created
617
+ * @uses iCalUtilityFunctions::_date2strdate()
618
+ * @uses calendarComponent::_createParams()
619
+ * @uses calendarComponent::_createElement()
620
+ * @return string
621
+ */
622
+ function createCreated() {
623
+ if( empty( $this->created )) return FALSE;
624
+ $formatted = iCalUtilityFunctions::_date2strdate( $this->created['value'], 7 );
625
+ $attributes = $this->_createParams( $this->created['params'] );
626
+ return $this->_createElement( 'CREATED', $attributes, $formatted );
627
+ }
628
+ /**
629
+ * set calendar component property created
630
+ *
631
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
632
+ * @since 2.19.5 - 2014-03-29
633
+ * @param mixed $year
634
+ * @param mixed $month
635
+ * @param int $day
636
+ * @param int $hour
637
+ * @param int $min
638
+ * @param int $sec
639
+ * @param mixed $params
640
+ * @uses calendarComponent::$created
641
+ * @uses iCalUtilityFunctions::_setDate2()
642
+ * @return bool
643
+ */
644
+ function setCreated( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
645
+ if( !isset( $year ))
646
+ $year = gmdate( 'Ymd\THis' );
647
+ $this->created = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
648
+ return TRUE;
649
+ }
650
+ /*********************************************************************************/
651
+ /**
652
+ * Property Name: DESCRIPTION
653
+ */
654
+ /**
655
+ * creates formatted output for calendar component property description
656
+ *
657
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
658
+ * @since 2.16.2 - 2012-12-18
659
+ * @uses calendarComponent::$description
660
+ * @uses calendarComponent::_createParams()
661
+ * @uses iCalUtilityFunctions::_strrep()
662
+ * @uses calendarComponent::_createElement()
663
+ * @uses calendarComponent::getConfig()
664
+ * @return string
665
+ */
666
+ function createDescription() {
667
+ if( empty( $this->description )) return FALSE;
668
+ $output = null;
669
+ foreach( $this->description as $description ) {
670
+ if( !empty( $description['value'] )) {
671
+ $attributes = $this->_createParams( $description['params'], array( 'ALTREP', 'LANGUAGE' ));
672
+ $content = iCalUtilityFunctions::_strrep( $description['value'], $this->format, $this->nl );
673
+ $output .= $this->_createElement( 'DESCRIPTION', $attributes, $content );
674
+ }
675
+ elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'DESCRIPTION' );
676
+ }
677
+ return $output;
678
+ }
679
+ /**
680
+ * set calendar component property description
681
+ *
682
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
683
+ * @since 2.16.21 - 2013-06-23
684
+ * @param string $value
685
+ * @param array $params
686
+ * @param integer $index
687
+ * @uses calendarComponent::getConfig()
688
+ * @uses calendarComponent::$objName
689
+ * @uses iCalUtilityFunctions::_setMval()
690
+ * @uses calendarComponent::$description
691
+ * @return bool
692
+ */
693
+ function setDescription( $value, $params=FALSE, $index=FALSE ) {
694
+ if( empty( $value )) { if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE; }
695
+ if( 'vjournal' != $this->objName )
696
+ $index = 1;
697
+ iCalUtilityFunctions::_setMval( $this->description, $value, $params, FALSE, $index );
698
+ return TRUE;
699
+ }
700
+ /*********************************************************************************/
701
+ /**
702
+ * Property Name: DTEND
703
+ */
704
+ /**
705
+ * creates formatted output for calendar component property dtend
706
+ *
707
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
708
+ * @since 2.14.4 - 2012-09-26
709
+ * @uses calendarComponent::$dtend
710
+ * @uses calendarComponent::getConfig()
711
+ * @uses iCalUtilityFunctions::_date2strdate()
712
+ * @uses calendarComponent::_createParams()
713
+ * @uses calendarComponent::_createElement()
714
+ * @return string
715
+ */
716
+ function createDtend() {
717
+ if( empty( $this->dtend )) return FALSE;
718
+ if( !isset( $this->dtend['value']['year'] ) &&
719
+ !isset( $this->dtend['value']['month'] ) &&
720
+ !isset( $this->dtend['value']['day'] ) &&
721
+ !isset( $this->dtend['value']['hour'] ) &&
722
+ !isset( $this->dtend['value']['min'] ) &&
723
+ !isset( $this->dtend['value']['sec'] ))
724
+ if( $this->getConfig( 'allowEmpty' ))
725
+ return $this->_createElement( 'DTEND' );
726
+ else return FALSE;
727
+ $parno = ( isset( $this->dtend['params']['VALUE'] ) && ( 'DATE' == $this->dtend['params']['VALUE'] )) ? 3 : null;
728
+ $formatted = iCalUtilityFunctions::_date2strdate( $this->dtend['value'], $parno );
729
+ $attributes = $this->_createParams( $this->dtend['params'] );
730
+ return $this->_createElement( 'DTEND', $attributes, $formatted );
731
+ }
732
+ /**
733
+ * set calendar component property dtend
734
+ *
735
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
736
+ * @since 2.16.21 - 2013-06-23
737
+ * @param mixed $year
738
+ * @param mixed $month
739
+ * @param int $day
740
+ * @param int $hour
741
+ * @param int $min
742
+ * @param int $sec
743
+ * @param string $tz
744
+ * @param array $params
745
+ * @uses calendarComponent::getConfig()
746
+ * @uses calendarComponent::$dtend
747
+ * @uses iCalUtilityFunctions::_setParams()
748
+ * @uses iCalUtilityFunctions::_setDate()
749
+ * @return bool
750
+ */
751
+ function setDtend( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
752
+ if( empty( $year )) {
753
+ if( $this->getConfig( 'allowEmpty' )) {
754
+ $this->dtend = array( 'value' => '', 'params' => iCalUtilityFunctions::_setParams( $params ));
755
+ return TRUE;
756
+ }
757
+ else
758
+ return FALSE;
759
+ }
760
+ $this->dtend = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' ));
761
+ return TRUE;
762
+ }
763
+ /*********************************************************************************/
764
+ /**
765
+ * Property Name: DTSTAMP
766
+ */
767
+ /**
768
+ * creates formatted output for calendar component property dtstamp
769
+ *
770
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
771
+ * @since 2.4.4 - 2008-03-07
772
+ * @uses calendarComponent::$dtstamp
773
+ * @uses calendarComponent::_makeDtstamp()
774
+ * @uses iCalUtilityFunctions::_date2strdate()
775
+ * @uses calendarComponent::_createParams()
776
+ * @uses calendarComponent::_createElement()
777
+ * @return string
778
+ */
779
+ function createDtstamp() {
780
+ if( !isset( $this->dtstamp['value']['year'] ) &&
781
+ !isset( $this->dtstamp['value']['month'] ) &&
782
+ !isset( $this->dtstamp['value']['day'] ) &&
783
+ !isset( $this->dtstamp['value']['hour'] ) &&
784
+ !isset( $this->dtstamp['value']['min'] ) &&
785
+ !isset( $this->dtstamp['value']['sec'] ))
786
+ $this->_makeDtstamp();
787
+ $formatted = iCalUtilityFunctions::_date2strdate( $this->dtstamp['value'], 7 );
788
+ $attributes = $this->_createParams( $this->dtstamp['params'] );
789
+ return $this->_createElement( 'DTSTAMP', $attributes, $formatted );
790
+ }
791
+ /**
792
+ * computes datestamp for calendar component object instance dtstamp
793
+ *
794
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
795
+ * @since 2.21.11 - 2015-03-21
796
+ * @uses iCalUtilityFunctions::$fmt
797
+ * @uses calendarComponent::$dtstamp
798
+ * @return void
799
+ */
800
+ function _makeDtstamp() {
801
+ $d = gmdate( iCalUtilityFunctions::$fmt['YmdHis3'], time());
802
+ $date = explode( '-', $d );
803
+ $this->dtstamp['value'] = array( 'year' => $date[0], 'month' => $date[1], 'day' => $date[2], 'hour' => $date[3], 'min' => $date[4], 'sec' => $date[5], 'tz' => 'Z' );
804
+ $this->dtstamp['params'] = null;
805
+ }
806
+ /**
807
+ * set calendar component property dtstamp
808
+ *
809
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
810
+ * @since 2.4.8 - 2008-10-23
811
+ * @param mixed $year
812
+ * @param mixed $month
813
+ * @param int $day
814
+ * @param int $hour
815
+ * @param int $min
816
+ * @param int $sec
817
+ * @param array $params
818
+ * @uses calendarComponent::_makeDtstamp()
819
+ * @uses calendarComponent::$dtstamp
820
+ * @uses iCalUtilityFunctions::_setDate2()
821
+ * @return TRUE
822
+ */
823
+ function setDtstamp( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
824
+ if( empty( $year ))
825
+ $this->_makeDtstamp();
826
+ else
827
+ $this->dtstamp = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
828
+ return TRUE;
829
+ }
830
+ /*********************************************************************************/
831
+ /**
832
+ * Property Name: DTSTART
833
+ */
834
+ /**
835
+ * creates formatted output for calendar component property dtstart
836
+ *
837
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
838
+ * @since 2.14.4 - 2012-09-26
839
+ * @uses calendarComponent::$dtstart
840
+ * @uses calendarComponent::getConfig()
841
+ * @uses calendarComponent::_createElement()
842
+ * @uses iCalUtilityFunctions::_date2strdate()
843
+ * @uses calendarComponent::_createParams()
844
+ * @return string
845
+ */
846
+ function createDtstart() {
847
+ if( empty( $this->dtstart )) return FALSE;
848
+ if( !isset( $this->dtstart['value']['year'] ) &&
849
+ !isset( $this->dtstart['value']['month'] ) &&
850
+ !isset( $this->dtstart['value']['day'] ) &&
851
+ !isset( $this->dtstart['value']['hour'] ) &&
852
+ !isset( $this->dtstart['value']['min'] ) &&
853
+ !isset( $this->dtstart['value']['sec'] )) {
854
+ if( $this->getConfig( 'allowEmpty' ))
855
+ return $this->_createElement( 'DTSTART' );
856
+ else return FALSE;
857
+ }
858
+ if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' )))
859
+ unset( $this->dtstart['value']['tz'], $this->dtstart['params']['TZID'] );
860
+ $parno = ( isset( $this->dtstart['params']['VALUE'] ) && ( 'DATE' == $this->dtstart['params']['VALUE'] )) ? 3 : null;
861
+ $formatted = iCalUtilityFunctions::_date2strdate( $this->dtstart['value'], $parno );
862
+ $attributes = $this->_createParams( $this->dtstart['params'] );
863
+ return $this->_createElement( 'DTSTART', $attributes, $formatted );
864
+ }
865
+ /**
866
+ * set calendar component property dtstart
867
+ *
868
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
869
+ * @since 2.16.21 - 2013-06-23
870
+ * @param mixed $year
871
+ * @param mixed $month
872
+ * @param int $day
873
+ * @param int $hour
874
+ * @param int $min
875
+ * @param int $sec
876
+ * @param string $tz
877
+ * @param array $params
878
+ * @uses calendarComponent::getConfig()
879
+ * @uses iCalUtilityFunctions::_setParams()
880
+ * @uses calendarComponent::$dtstart
881
+ * @uses iCalUtilityFunctions::_setDate()
882
+ * @return bool
883
+ */
884
+ function setDtstart( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
885
+ if( empty( $year )) {
886
+ if( $this->getConfig( 'allowEmpty' )) {
887
+ $this->dtstart = array( 'value' => '', 'params' => iCalUtilityFunctions::_setParams( $params ));
888
+ return TRUE;
889
+ }
890
+ else
891
+ return FALSE;
892
+ }
893
+ $this->dtstart = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, 'dtstart', $this->objName, $this->getConfig( 'TZID' ));
894
+ return TRUE;
895
+ }
896
+ /*********************************************************************************/
897
+ /**
898
+ * Property Name: DUE
899
+ */
900
+ /**
901
+ * creates formatted output for calendar component property due
902
+ *
903
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
904
+ * @since 2.14.4 - 2012-09-26
905
+ * @uses calendarComponent::$due
906
+ * @uses calendarComponent::getConfig()
907
+ * @uses calendarComponent::_createElement()
908
+ * @uses iCalUtilityFunctions::_date2strdate()
909
+ * @uses calendarComponent::_createParams()
910
+ * @return string
911
+ */
912
+ function createDue() {
913
+ if( empty( $this->due )) return FALSE;
914
+ if( !isset( $this->due['value']['year'] ) &&
915
+ !isset( $this->due['value']['month'] ) &&
916
+ !isset( $this->due['value']['day'] ) &&
917
+ !isset( $this->due['value']['hour'] ) &&
918
+ !isset( $this->due['value']['min'] ) &&
919
+ !isset( $this->due['value']['sec'] )) {
920
+ if( $this->getConfig( 'allowEmpty' ))
921
+ return $this->_createElement( 'DUE' );
922
+ else
923
+ return FALSE;
924
+ }
925
+ $parno = ( isset( $this->due['params']['VALUE'] ) && ( 'DATE' == $this->due['params']['VALUE'] )) ? 3 : null;
926
+ $formatted = iCalUtilityFunctions::_date2strdate( $this->due['value'], $parno );
927
+ $attributes = $this->_createParams( $this->due['params'] );
928
+ return $this->_createElement( 'DUE', $attributes, $formatted );
929
+ }
930
+ /**
931
+ * set calendar component property due
932
+ *
933
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
934
+ * @since 2.16.21 - 2013-06-23
935
+ * @param mixed $year
936
+ * @param mixed $month
937
+ * @param int $day
938
+ * @param int $hour
939
+ * @param int $min
940
+ * @param int $sec
941
+ * @param string $tz
942
+ * @param array $params
943
+ * @uses calendarComponent::getConfig()
944
+ * @uses calendarComponent::$due
945
+ * @uses iCalUtilityFunctions::_setParams()
946
+ * @uses iCalUtilityFunctions::_setDate()
947
+ * @return bool
948
+ */
949
+ function setDue( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
950
+ if( empty( $year )) {
951
+ if( $this->getConfig( 'allowEmpty' )) {
952
+ $this->due = array( 'value' => '', 'params' => iCalUtilityFunctions::_setParams( $params ));
953
+ return TRUE;
954
+ }
955
+ else
956
+ return FALSE;
957
+ }
958
+ $this->due = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' ));
959
+ return TRUE;
960
+ }
961
+ /*********************************************************************************/
962
+ /**
963
+ * Property Name: DURATION
964
+ */
965
+ /**
966
+ * creates formatted output for calendar component property duration
967
+ *
968
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
969
+ * @since 2.16.21 - 2013-05-25
970
+ * @uses calendarComponent::$duration
971
+ * @uses calendarComponent::getConfig()
972
+ * @uses calendarComponent::_createElement()
973
+ * @uses calendarComponent::_createParams()
974
+ * @uses iCalUtilityFunctions::_duration2str()
975
+ * @return string
976
+ */
977
+ function createDuration() {
978
+ if( empty( $this->duration )) return FALSE;
979
+ if( !isset( $this->duration['value']['week'] ) &&
980
+ !isset( $this->duration['value']['day'] ) &&
981
+ !isset( $this->duration['value']['hour'] ) &&
982
+ !isset( $this->duration['value']['min'] ) &&
983
+ !isset( $this->duration['value']['sec'] ))
984
+ if( $this->getConfig( 'allowEmpty' ))
985
+ return $this->_createElement( 'DURATION' );
986
+ else return FALSE;
987
+ $attributes = $this->_createParams( $this->duration['params'] );
988
+ return $this->_createElement( 'DURATION', $attributes, iCalUtilityFunctions::_duration2str( $this->duration['value'] ));
989
+ }
990
+ /**
991
+ * set calendar component property duration
992
+ *
993
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
994
+ * @since 2.16.21 - 2013-05-25
995
+ * @param mixed $week
996
+ * @param mixed $day
997
+ * @param int $hour
998
+ * @param int $min
999
+ * @param int $sec
1000
+ * @param array $params
1001
+ * @uses calendarComponent::getConfig()
1002
+ * @uses calendarComponent::$duration
1003
+ * @uses iCalUtilityFunctions::_duration2arr()
1004
+ * @uses iCalUtilityFunctions::_durationStr2arr()
1005
+ * @uses iCalUtilityFunctions::_setParams()
1006
+ * @uses iCalUtilityFunctions::_duration2arr()
1007
+ * @return bool
1008
+ */
1009
+ function setDuration( $week, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
1010
+ if( empty( $week ) && empty( $day ) && empty( $hour ) && empty( $min ) && empty( $sec )) {
1011
+ if( $this->getConfig( 'allowEmpty' ))
1012
+ $week = $day = null;
1013
+ else
1014
+ return FALSE;
1015
+ }
1016
+ if( is_array( $week ) && ( 1 <= count( $week )))
1017
+ $this->duration = array( 'value' => iCalUtilityFunctions::_duration2arr( $week ), 'params' => iCalUtilityFunctions::_setParams( $day ));
1018
+ elseif( is_string( $week ) && ( 3 <= strlen( trim( $week )))) {
1019
+ $week = trim( $week );
1020
+ if( in_array( substr( $week, 0, 1 ), array( '+', '-' )))
1021
+ $week = substr( $week, 1 );
1022
+ $this->duration = array( 'value' => iCalUtilityFunctions::_durationStr2arr( $week ), 'params' => iCalUtilityFunctions::_setParams( $day ));
1023
+ }
1024
+ else
1025
+ $this->duration = array( 'value' => iCalUtilityFunctions::_duration2arr( array( 'week' => $week, 'day' => $day, 'hour' => $hour, 'min' => $min, 'sec' => $sec ))
1026
+ , 'params' => iCalUtilityFunctions::_setParams( $params ));
1027
+ return TRUE;
1028
+ }
1029
+ /*********************************************************************************/
1030
+ /**
1031
+ * Property Name: EXDATE
1032
+ */
1033
+ /**
1034
+ * creates formatted output for calendar component property exdate
1035
+ *
1036
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1037
+ * @since 2.16.5 - 2012-12-28
1038
+ * @uses calendarComponent::$exdate
1039
+ * @uses calendarComponent::getConfig()
1040
+ * @uses calendarComponent::_createElement()
1041
+ * @uses iCalUtilityFunctions::_sortExdate1()
1042
+ * @uses iCalUtilityFunctions::_sortExdate2()
1043
+ * @uses iCalUtilityFunctions::_date2strdate()
1044
+ * @uses calendarComponent::_createParams()
1045
+ * @return string
1046
+ */
1047
+ function createExdate() {
1048
+ if( empty( $this->exdate )) return FALSE;
1049
+ $output = null;
1050
+ $exdates = array();
1051
+ foreach( $this->exdate as $theExdate ) {
1052
+ if( empty( $theExdate['value'] )) {
1053
+ if( $this->getConfig( 'allowEmpty' ))
1054
+ $output .= $this->_createElement( 'EXDATE' );
1055
+ continue;
1056
+ }
1057
+ if( 1 < count( $theExdate['value'] ))
1058
+ usort( $theExdate['value'], array( 'iCalUtilityFunctions', '_sortExdate1' ));
1059
+ $exdates[] = $theExdate;
1060
+ }
1061
+ if( 1 < count( $exdates ))
1062
+ usort( $exdates, array( 'iCalUtilityFunctions', '_sortExdate2' ));
1063
+ foreach( $exdates as $theExdate ) {
1064
+ $content = $attributes = null;
1065
+ foreach( $theExdate['value'] as $eix => $exdatePart ) {
1066
+ $parno = count( $exdatePart );
1067
+ $formatted = iCalUtilityFunctions::_date2strdate( $exdatePart, $parno );
1068
+ if( isset( $theExdate['params']['TZID'] ))
1069
+ $formatted = str_replace( 'Z', '', $formatted);
1070
+ if( 0 < $eix ) {
1071
+ if( isset( $theExdate['value'][0]['tz'] )) {
1072
+ if( ctype_digit( substr( $theExdate['value'][0]['tz'], -4 )) ||
1073
+ ( 'Z' == $theExdate['value'][0]['tz'] )) {
1074
+ if( 'Z' != substr( $formatted, -1 ))
1075
+ $formatted .= 'Z';
1076
+ }
1077
+ else
1078
+ $formatted = str_replace( 'Z', '', $formatted );
1079
+ }
1080
+ else
1081
+ $formatted = str_replace( 'Z', '', $formatted );
1082
+ } // end if( 0 < $eix )
1083
+ $content .= ( 0 < $eix ) ? ','.$formatted : $formatted;
1084
+ } // end foreach( $theExdate['value'] as $eix => $exdatePart )
1085
+ $attributes .= $this->_createParams( $theExdate['params'] );
1086
+ $output .= $this->_createElement( 'EXDATE', $attributes, $content );
1087
+ } // end foreach( $exdates as $theExdate )
1088
+ return $output;
1089
+ }
1090
+ /**
1091
+ * set calendar component property exdate
1092
+ *
1093
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1094
+ * @since 2.21.11 - 2015-03-10
1095
+ * @param array $exdates
1096
+ * @param array $params
1097
+ * @param integer $index
1098
+ * @uses calendarComponent::getConfig()
1099
+ * @uses iCalUtilityFunctions::_setMval()
1100
+ * @uses calendarComponent::$exdate
1101
+ * @uses iCalUtilityFunctions::_setParams()
1102
+ * @uses iCalUtilityFunctions::_chkdatecfg()
1103
+ * @uses iCalUtilityFunctions::_existRem()
1104
+ * @uses iCalUtilityFunctions::_strDate2arr()
1105
+ * @uses iCalUtilityFunctions::_isArrayTimestampDate()
1106
+ * @uses iCalUtilityFunctions::_isOffset()
1107
+ * @uses iCalUtilityFunctions::_timestamp2date()
1108
+ * @uses iCalUtilityFunctions::_chkDateArr()
1109
+ * @uses iCalUtilityFunctions::$fmt
1110
+ * @uses iCalUtilityFunctions::_strdate2date()
1111
+ * @return bool
1112
+ */
1113
+ function setExdate( $exdates, $params=FALSE, $index=FALSE ) {
1114
+ if( empty( $exdates )) {
1115
+ if( $this->getConfig( 'allowEmpty' )) {
1116
+ iCalUtilityFunctions::_setMval( $this->exdate, '', $params, FALSE, $index );
1117
+ return TRUE;
1118
+ }
1119
+ else
1120
+ return FALSE;
1121
+ }
1122
+ $input = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )));
1123
+ $toZ = ( isset( $input['params']['TZID'] ) && in_array( strtoupper( $input['params']['TZID'] ), array( 'GMT', 'UTC', 'Z' ))) ? TRUE : FALSE;
1124
+ /* ev. check 1:st date and save ev. timezone **/
1125
+ iCalUtilityFunctions::_chkdatecfg( reset( $exdates ), $parno, $input['params'] );
1126
+ iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default parameter
1127
+ foreach( $exdates as $eix => $theExdate ) {
1128
+ iCalUtilityFunctions::_strDate2arr( $theExdate );
1129
+ if( iCalUtilityFunctions::_isArrayTimestampDate( $theExdate )) {
1130
+ if( isset( $theExdate['tz'] ) && !iCalUtilityFunctions::_isOffset( $theExdate['tz'] )) {
1131
+ if( isset( $input['params']['TZID'] ))
1132
+ $theExdate['tz'] = $input['params']['TZID'];
1133
+ else
1134
+ $input['params']['TZID'] = $theExdate['tz'];
1135
+ }
1136
+ $exdatea = iCalUtilityFunctions::_timestamp2date( $theExdate, $parno );
1137
+ }
1138
+ elseif( is_array( $theExdate )) {
1139
+ $d = iCalUtilityFunctions::_chkDateArr( $theExdate, $parno );
1140
+ if( isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) {
1141
+ $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $d['tz'] );
1142
+ $exdatea = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
1143
+ unset( $exdatea['unparsedtext'] );
1144
+ }
1145
+ else
1146
+ $exdatea = $d;
1147
+ }
1148
+ elseif( 8 <= strlen( trim( $theExdate ))) { // ex. 2006-08-03 10:12:18
1149
+ $exdatea = iCalUtilityFunctions::_strdate2date( $theExdate, $parno );
1150
+ unset( $exdatea['unparsedtext'] );
1151
+ }
1152
+ if( 3 == $parno )
1153
+ unset( $exdatea['hour'], $exdatea['min'], $exdatea['sec'], $exdatea['tz'] );
1154
+ elseif( isset( $exdatea['tz'] ))
1155
+ $exdatea['tz'] = (string) $exdatea['tz'];
1156
+ if( isset( $input['params']['TZID'] ) ||
1157
+ ( isset( $exdatea['tz'] ) && !iCalUtilityFunctions::_isOffset( $exdatea['tz'] )) ||
1158
+ ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] ))) ||
1159
+ ( isset( $input['value'][0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value'][0]['tz'] )))
1160
+ unset( $exdatea['tz'] );
1161
+ if( $toZ ) // time zone Z
1162
+ $exdatea['tz'] = 'Z';
1163
+ $input['value'][] = $exdatea;
1164
+ }
1165
+ if( 0 >= count( $input['value'] ))
1166
+ return FALSE;
1167
+ if( 3 == $parno ) {
1168
+ $input['params']['VALUE'] = 'DATE';
1169
+ unset( $input['params']['TZID'] );
1170
+ }
1171
+ if( $toZ ) // time zone Z
1172
+ unset( $input['params']['TZID'] );
1173
+ iCalUtilityFunctions::_setMval( $this->exdate, $input['value'], $input['params'], FALSE, $index );
1174
+ return TRUE;
1175
+ }
1176
+ /*********************************************************************************/
1177
+ /**
1178
+ * Property Name: EXRULE
1179
+ */
1180
+ /**
1181
+ * creates formatted output for calendar component property exrule
1182
+ *
1183
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1184
+ * @since 2.4.8 - 2008-10-22
1185
+ * @uses calendarComponent::$exrule
1186
+ * @uses calendarComponent::_format_recur()
1187
+ * @return string
1188
+ */
1189
+ function createExrule() {
1190
+ if( empty( $this->exrule )) return FALSE;
1191
+ return $this->_format_recur( 'EXRULE', $this->exrule );
1192
+ }
1193
+ /**
1194
+ * set calendar component property exdate
1195
+ *
1196
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1197
+ * @since 2.16.21 - 2013-06-23
1198
+ * @param array $exruleset
1199
+ * @param array $params
1200
+ * @param integer $index
1201
+ * @uses calendarComponent::getConfig()
1202
+ * @uses iCalUtilityFunctions::_setMval()
1203
+ * @uses calendarComponent::$exrule
1204
+ * @uses iCalUtilityFunctions::_setRexrule()
1205
+ * @return bool
1206
+ */
1207
+ function setExrule( $exruleset, $params=FALSE, $index=FALSE ) {
1208
+ if( empty( $exruleset )) if( $this->getConfig( 'allowEmpty' )) $exruleset = ''; else return FALSE;
1209
+ iCalUtilityFunctions::_setMval( $this->exrule, iCalUtilityFunctions::_setRexrule( $exruleset ), $params, FALSE, $index );
1210
+ return TRUE;
1211
+ }
1212
+ /*********************************************************************************/
1213
+ /**
1214
+ * Property Name: FREEBUSY
1215
+ */
1216
+ /**
1217
+ * creates formatted output for calendar component property freebusy
1218
+ *
1219
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1220
+ * @since 2.16.27 - 2013-07-05
1221
+ * @uses calendarComponent::$freebusy
1222
+ * @uses calendarComponent::getConfig()
1223
+ * @uses calendarComponent::_createElement()
1224
+ * @uses calendarComponent::$intAttrDelimiter
1225
+ * @uses calendarComponent::_createParams()
1226
+ * @uses iCalUtilityFunctions::_sortRdate1()
1227
+ * @uses iCalUtilityFunctions::_date2strdate()
1228
+ * @uses iCalUtilityFunctions::_duration2str()
1229
+ * @return string
1230
+ */
1231
+ function createFreebusy() {
1232
+ if( empty( $this->freebusy )) return FALSE;
1233
+ $output = null;
1234
+ foreach( $this->freebusy as $freebusyPart ) {
1235
+ if( empty( $freebusyPart['value'] ) || (( 1 == count( $freebusyPart['value'] )) && isset( $freebusyPart['value']['fbtype'] ))) {
1236
+ if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'FREEBUSY' );
1237
+ continue;
1238
+ }
1239
+ $attributes = $content = null;
1240
+ if( isset( $freebusyPart['value']['fbtype'] )) {
1241
+ $attributes .= $this->intAttrDelimiter.'FBTYPE='.$freebusyPart['value']['fbtype'];
1242
+ unset( $freebusyPart['value']['fbtype'] );
1243
+ $freebusyPart['value'] = array_values( $freebusyPart['value'] );
1244
+ }
1245
+ else
1246
+ $attributes .= $this->intAttrDelimiter.'FBTYPE=BUSY';
1247
+ $attributes .= $this->_createParams( $freebusyPart['params'] );
1248
+ $fno = 1;
1249
+ $cnt = count( $freebusyPart['value']);
1250
+ if( 1 < $cnt )
1251
+ usort( $freebusyPart['value'], array( 'iCalUtilityFunctions', '_sortRdate1' ));
1252
+ foreach( $freebusyPart['value'] as $periodix => $freebusyPeriod ) {
1253
+ $formatted = iCalUtilityFunctions::_date2strdate( $freebusyPeriod[0] );
1254
+ $content .= $formatted;
1255
+ $content .= '/';
1256
+ $cnt2 = count( $freebusyPeriod[1]);
1257
+ if( array_key_exists( 'year', $freebusyPeriod[1] )) // date-time
1258
+ $cnt2 = 7;
1259
+ elseif( array_key_exists( 'week', $freebusyPeriod[1] )) // duration
1260
+ $cnt2 = 5;
1261
+ if(( 7 == $cnt2 ) && // period= -> date-time
1262
+ isset( $freebusyPeriod[1]['year'] ) &&
1263
+ isset( $freebusyPeriod[1]['month'] ) &&
1264
+ isset( $freebusyPeriod[1]['day'] )) {
1265
+ $content .= iCalUtilityFunctions::_date2strdate( $freebusyPeriod[1] );
1266
+ }
1267
+ else { // period= -> dur-time
1268
+ $content .= iCalUtilityFunctions::_duration2str( $freebusyPeriod[1] );
1269
+ }
1270
+ if( $fno < $cnt )
1271
+ $content .= ',';
1272
+ $fno++;
1273
+ }
1274
+ $output .= $this->_createElement( 'FREEBUSY', $attributes, $content );
1275
+ }
1276
+ return $output;
1277
+ }
1278
+ /**
1279
+ * set calendar component property freebusy
1280
+ *
1281
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1282
+ * @since 2.16.21 - 2013-06-23
1283
+ * @param string $fbType
1284
+ * @param array $fbValues
1285
+ * @param array $params
1286
+ * @param integer $index
1287
+ * @uses calendarComponent::getConfig()
1288
+ * @uses calendarComponent::$freebusy
1289
+ * @uses iCalUtilityFunctions::_isArrayDate()
1290
+ * @uses iCalUtilityFunctions::_setMval()
1291
+ * @uses iCalUtilityFunctions::_chkDateArr()
1292
+ * @uses iCalUtilityFunctions::_isArrayTimestampDate()
1293
+ * @uses iCalUtilityFunctions::_timestamp2date()
1294
+ * @uses iCalUtilityFunctions::_duration2arr()
1295
+ * @uses iCalUtilityFunctions::_durationStr2arr()
1296
+ * @uses iCalUtilityFunctions::_strdate2date()
1297
+ * @return bool
1298
+ */
1299
+ function setFreebusy( $fbType, $fbValues, $params=FALSE, $index=FALSE ) {
1300
+ if( empty( $fbValues )) {
1301
+ if( $this->getConfig( 'allowEmpty' )) {
1302
+ iCalUtilityFunctions::_setMval( $this->freebusy, '', $params, FALSE, $index );
1303
+ return TRUE;
1304
+ }
1305
+ else
1306
+ return FALSE;
1307
+ }
1308
+ $fbType = strtoupper( $fbType );
1309
+ if(( !in_array( $fbType, array( 'FREE', 'BUSY', 'BUSY-UNAVAILABLE', 'BUSY-TENTATIVE' ))) &&
1310
+ ( 'X-' != substr( $fbType, 0, 2 )))
1311
+ $fbType = 'BUSY';
1312
+ $input = array( 'fbtype' => $fbType );
1313
+ foreach( $fbValues as $fbPeriod ) { // periods => period
1314
+ if( empty( $fbPeriod ))
1315
+ continue;
1316
+ $freebusyPeriod = array();
1317
+ foreach( $fbPeriod as $fbMember ) { // pairs => singlepart
1318
+ $freebusyPairMember = array();
1319
+ if( is_array( $fbMember )) {
1320
+ if( iCalUtilityFunctions::_isArrayDate( $fbMember )) { // date-time value
1321
+ $freebusyPairMember = iCalUtilityFunctions::_chkDateArr( $fbMember, 7 );
1322
+ $freebusyPairMember['tz'] = 'Z';
1323
+ }
1324
+ elseif( iCalUtilityFunctions::_isArrayTimestampDate( $fbMember )) { // timestamp value
1325
+ $freebusyPairMember = iCalUtilityFunctions::_timestamp2date( $fbMember['timestamp'], 7 );
1326
+ $freebusyPairMember['tz'] = 'Z';
1327
+ }
1328
+ else { // array format duration
1329
+ $freebusyPairMember = iCalUtilityFunctions::_duration2arr( $fbMember );
1330
+ }
1331
+ }
1332
+ elseif(( 3 <= strlen( trim( $fbMember ))) && // string format duration
1333
+ ( in_array( $fbMember{0}, array( 'P', '+', '-' )))) {
1334
+ if( 'P' != $fbMember{0} )
1335
+ $fbmember = substr( $fbMember, 1 );
1336
+ $freebusyPairMember = iCalUtilityFunctions::_durationStr2arr( $fbMember );
1337
+ }
1338
+ elseif( 8 <= strlen( trim( $fbMember ))) { // text date ex. 2006-08-03 10:12:18
1339
+ $freebusyPairMember = iCalUtilityFunctions::_strdate2date( $fbMember, 7 );
1340
+ unset( $freebusyPairMember['unparsedtext'] );
1341
+ $freebusyPairMember['tz'] = 'Z';
1342
+ }
1343
+ $freebusyPeriod[] = $freebusyPairMember;
1344
+ }
1345
+ $input[] = $freebusyPeriod;
1346
+ }
1347
+ iCalUtilityFunctions::_setMval( $this->freebusy, $input, $params, FALSE, $index );
1348
+ return TRUE;
1349
+ }
1350
+ /*********************************************************************************/
1351
+ /**
1352
+ * Property Name: GEO
1353
+ */
1354
+ /**
1355
+ * creates formatted output for calendar component property geo
1356
+ *
1357
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1358
+ * @since 2.18.10 - 2013-09-03
1359
+ * @uses calendarComponent::$geo
1360
+ * @uses calendarComponent::getConfig()
1361
+ * @uses calendarComponent::_createElement()
1362
+ * @uses calendarComponent::_createParams()
1363
+ * @uses iCalUtilityFunctions::_geo2str2()
1364
+ * @uses iCalUtilityFunctions::$geoLongFmt
1365
+ * @uses iCalUtilityFunctions::$geoLatFmt
1366
+ * @return string
1367
+ */
1368
+ function createGeo() {
1369
+ if( empty( $this->geo )) return FALSE;
1370
+ if( empty( $this->geo['value'] ))
1371
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'GEO' ) : FALSE;
1372
+ return $this->_createElement( 'GEO', $this->_createParams( $this->geo['params'] ),
1373
+ iCalUtilityFunctions::_geo2str2( $this->geo['value']['latitude'], iCalUtilityFunctions::$geoLatFmt ).
1374
+ ';'.iCalUtilityFunctions::_geo2str2( $this->geo['value']['longitude'], iCalUtilityFunctions::$geoLongFmt ));
1375
+ }
1376
+ /**
1377
+ * set calendar component property geo
1378
+ *
1379
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1380
+ * @since 2.18.10 - 2013-09-03
1381
+ * @param mixed $latitude
1382
+ * @param mixed $longitude
1383
+ * @param array $params
1384
+ * @uses calendarComponent::$geo
1385
+ * @uses iCalUtilityFunctions::_setParams()
1386
+ * @uses calendarComponent::getConfig()
1387
+ * @return bool
1388
+ */
1389
+ function setGeo( $latitude, $longitude, $params=FALSE ) {
1390
+ if( isset( $latitude ) && isset( $longitude )) {
1391
+ if( !is_array( $this->geo )) $this->geo = array();
1392
+ $this->geo['value']['latitude'] = floatval( $latitude );
1393
+ $this->geo['value']['longitude'] = floatval( $longitude );
1394
+ $this->geo['params'] = iCalUtilityFunctions::_setParams( $params );
1395
+ }
1396
+ elseif( $this->getConfig( 'allowEmpty' ))
1397
+ $this->geo = array( 'value' => '', 'params' => iCalUtilityFunctions::_setParams( $params ) );
1398
+ else
1399
+ return FALSE;
1400
+ return TRUE;
1401
+ }
1402
+ /*********************************************************************************/
1403
+ /**
1404
+ * Property Name: LAST-MODIFIED
1405
+ */
1406
+ /**
1407
+ * creates formatted output for calendar component property last-modified
1408
+ *
1409
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1410
+ * @since 2.4.8 - 2008-10-21
1411
+ * @uses calendarComponent::$lastmodified
1412
+ * @uses calendarComponent::_createParams()
1413
+ * @uses iCalUtilityFunctions::_date2strdate()
1414
+ * @uses calendarComponent::_createElement()
1415
+ * @return string
1416
+ */
1417
+ function createLastModified() {
1418
+ if( empty( $this->lastmodified )) return FALSE;
1419
+ $attributes = $this->_createParams( $this->lastmodified['params'] );
1420
+ $formatted = iCalUtilityFunctions::_date2strdate( $this->lastmodified['value'], 7 );
1421
+ return $this->_createElement( 'LAST-MODIFIED', $attributes, $formatted );
1422
+ }
1423
+ /**
1424
+ * set calendar component property completed
1425
+ *
1426
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1427
+ * @since 2.19.5 - 2014-03-29
1428
+ * @param mixed $year optional
1429
+ * @param mixed $month optional
1430
+ * @param int $day optional
1431
+ * @param int $hour optional
1432
+ * @param int $min optional
1433
+ * @param int $sec optional
1434
+ * @param array $params optional
1435
+ * @uses calendarComponent::$lastmodified
1436
+ * @uses iCalUtilityFunctions::_setDate2()
1437
+ * @return boll
1438
+ */
1439
+ function setLastModified( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
1440
+ if( empty( $year ))
1441
+ $year = gmdate( 'Ymd\THis' );
1442
+ $this->lastmodified = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
1443
+ return TRUE;
1444
+ }
1445
+ /*********************************************************************************/
1446
+ /**
1447
+ * Property Name: LOCATION
1448
+ */
1449
+ /**
1450
+ * creates formatted output for calendar component property location
1451
+ *
1452
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1453
+ * @since 2.16.2 - 2012-12-18
1454
+ * @uses calendarComponent::$location
1455
+ * @uses calendarComponent::getConfig()
1456
+ * @uses calendarComponent::_createElement()
1457
+ * @uses calendarComponent::_createParams()
1458
+ * @uses iCalUtilityFunctions::_strrep()
1459
+ * @return string
1460
+ */
1461
+ function createLocation() {
1462
+ if( empty( $this->location )) return FALSE;
1463
+ if( empty( $this->location['value'] ))
1464
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'LOCATION' ) : FALSE;
1465
+ $attributes = $this->_createParams( $this->location['params'], array( 'ALTREP', 'LANGUAGE' ));
1466
+ $content = iCalUtilityFunctions::_strrep( $this->location['value'], $this->format, $this->nl );
1467
+ return $this->_createElement( 'LOCATION', $attributes, $content );
1468
+ }
1469
+ /**
1470
+ * set calendar component property location
1471
+ '
1472
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1473
+ * @since 2.16.21 - 2013-06-23
1474
+ * @param string $value
1475
+ * @param array $params
1476
+ * @uses calendarComponent::$location
1477
+ * @uses calendarComponent::getConfig()
1478
+ * @uses iCalUtilityFunctions::_setParams()
1479
+ * @return bool
1480
+ */
1481
+ function setLocation( $value, $params=FALSE ) {
1482
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
1483
+ $this->location = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
1484
+ return TRUE;
1485
+ }
1486
+ /*********************************************************************************/
1487
+ /**
1488
+ * Property Name: ORGANIZER
1489
+ */
1490
+ /**
1491
+ * creates formatted output for calendar component property organizer
1492
+ *
1493
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1494
+ * @since 2.6.33 - 2010-12-17
1495
+ * @uses calendarComponent::$organizer
1496
+ * @uses calendarComponent::getConfig()
1497
+ * @uses calendarComponent::_createElement()
1498
+ * @uses calendarComponent::_createParams()
1499
+ * @return string
1500
+ */
1501
+ function createOrganizer() {
1502
+ if( empty( $this->organizer )) return FALSE;
1503
+ if( empty( $this->organizer['value'] ))
1504
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ORGANIZER' ) : FALSE;
1505
+ $attributes = $this->_createParams( $this->organizer['params']
1506
+ , array( 'CN', 'DIR', 'SENT-BY', 'LANGUAGE' ));
1507
+ return $this->_createElement( 'ORGANIZER', $attributes, $this->organizer['value'] );
1508
+ }
1509
+ /**
1510
+ * set calendar component property organizer
1511
+ *
1512
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1513
+ * @since 2.16.21 - 2013-06-23
1514
+ * @param string $value
1515
+ * @param array $params
1516
+ * @uses calendarComponent::getConfig()
1517
+ * @uses calendarComponent::$organizer
1518
+ * @uses iCalUtilityFunctions::_setParams()
1519
+ * @return bool
1520
+ */
1521
+ function setOrganizer( $value, $params=FALSE ) {
1522
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
1523
+ if( !empty( $value )) {
1524
+ if( FALSE === ( $pos = strpos( substr( $value, 0, 9 ), ':' )))
1525
+ $value = 'MAILTO:'.$value;
1526
+ elseif( !empty( $value ))
1527
+ $value = strtolower( substr( $value, 0, $pos )).substr( $value, $pos );
1528
+ $value = str_replace( 'mailto:', 'MAILTO:', $value );
1529
+ }
1530
+ $this->organizer = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
1531
+ if( isset( $this->organizer['params']['SENT-BY'] )){
1532
+ if( 'mailto:' !== strtolower( substr( $this->organizer['params']['SENT-BY'], 0, 7 )))
1533
+ $this->organizer['params']['SENT-BY'] = 'MAILTO:'.$this->organizer['params']['SENT-BY'];
1534
+ else
1535
+ $this->organizer['params']['SENT-BY'] = 'MAILTO:'.substr( $this->organizer['params']['SENT-BY'], 7 );
1536
+ }
1537
+ return TRUE;
1538
+ }
1539
+ /*********************************************************************************/
1540
+ /**
1541
+ * Property Name: PERCENT-COMPLETE
1542
+ */
1543
+ /**
1544
+ * creates formatted output for calendar component property percent-complete
1545
+ *
1546
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1547
+ * @since 2.9.3 - 2011-05-14
1548
+ * @uses calendarComponent::$percentcomplete
1549
+ * @uses calendarComponent::getConfig()
1550
+ * @uses calendarComponent::_createElement()
1551
+ * @uses calendarComponent::_createParams()
1552
+ * @return string
1553
+ */
1554
+ function createPercentComplete() {
1555
+ if( !isset($this->percentcomplete) || ( empty( $this->percentcomplete ) && !is_numeric( $this->percentcomplete ))) return FALSE;
1556
+ if( !isset( $this->percentcomplete['value'] ) || ( empty( $this->percentcomplete['value'] ) && !is_numeric( $this->percentcomplete['value'] )))
1557
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PERCENT-COMPLETE' ) : FALSE;
1558
+ $attributes = $this->_createParams( $this->percentcomplete['params'] );
1559
+ return $this->_createElement( 'PERCENT-COMPLETE', $attributes, $this->percentcomplete['value'] );
1560
+ }
1561
+ /**
1562
+ * set calendar component property percent-complete
1563
+ *
1564
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1565
+ * @since 2.16.21 - 2013-06-23
1566
+ * @param int $value
1567
+ * @param array $params
1568
+ * @uses calendarComponent::$percentcomplete
1569
+ * @uses calendarComponent::getConfig()
1570
+ * @uses iCalUtilityFunctions::_setParams()
1571
+ * @return bool
1572
+ */
1573
+ function setPercentComplete( $value, $params=FALSE ) {
1574
+ if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
1575
+ $this->percentcomplete = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
1576
+ return TRUE;
1577
+ }
1578
+ /*********************************************************************************/
1579
+ /**
1580
+ * Property Name: PRIORITY
1581
+ */
1582
+ /**
1583
+ * creates formatted output for calendar component property priority
1584
+ *
1585
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1586
+ * @since 2.9.3 - 2011-05-14
1587
+ * @uses calendarComponent::$priority
1588
+ * @uses calendarComponent::getConfig()
1589
+ * @uses calendarComponent::_createElement()
1590
+ * @uses calendarComponent::_createParams()
1591
+ * @return string
1592
+ */
1593
+ function createPriority() {
1594
+ if( !isset($this->priority) || ( empty( $this->priority ) && !is_numeric( $this->priority ))) return FALSE;
1595
+ if( !isset( $this->priority['value'] ) || ( empty( $this->priority['value'] ) && !is_numeric( $this->priority['value'] )))
1596
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PRIORITY' ) : FALSE;
1597
+ $attributes = $this->_createParams( $this->priority['params'] );
1598
+ return $this->_createElement( 'PRIORITY', $attributes, $this->priority['value'] );
1599
+ }
1600
+ /**
1601
+ * set calendar component property priority
1602
+ *
1603
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1604
+ * @since 2.16.21 - 2013-06-23
1605
+ * @param int $value
1606
+ * @param array $params
1607
+ * @uses calendarComponent::getConfig()
1608
+ * @uses calendarComponent::$priority
1609
+ * @uses iCalUtilityFunctions::_setParams()
1610
+ * @return bool
1611
+ */
1612
+ function setPriority( $value, $params=FALSE ) {
1613
+ if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
1614
+ $this->priority = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
1615
+ return TRUE;
1616
+ }
1617
+ /*********************************************************************************/
1618
+ /**
1619
+ * Property Name: RDATE
1620
+ */
1621
+ /**
1622
+ * creates formatted output for calendar component property rdate
1623
+ *
1624
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1625
+ * @since 2.16.9 - 2013-01-09
1626
+ * @uses calendarComponent::$rdate
1627
+ * @uses calendarComponent::$objName
1628
+ * @uses calendarComponent::getConfig()
1629
+ * @uses calendarComponent::_createElement()
1630
+ * @uses iCalUtilityFunctions::_sortRdate1()
1631
+ * @uses iCalUtilityFunctions::_sortRdate2()
1632
+ * @uses calendarComponent::_createParams()
1633
+ * @uses iCalUtilityFunctions::_date2strdate()
1634
+ * @return string
1635
+ */
1636
+ function createRdate() {
1637
+ if( empty( $this->rdate )) return FALSE;
1638
+ $utctime = ( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) ? TRUE : FALSE;
1639
+ $output = null;
1640
+ $rdates = array();
1641
+ foreach( $this->rdate as $rpix => $theRdate ) {
1642
+ if( empty( $theRdate['value'] )) {
1643
+ if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RDATE' );
1644
+ continue;
1645
+ }
1646
+ if( $utctime )
1647
+ unset( $theRdate['params']['TZID'] );
1648
+ if( 1 < count( $theRdate['value'] ))
1649
+ usort( $theRdate['value'], array( 'iCalUtilityFunctions', '_sortRdate1' ));
1650
+ $rdates[] = $theRdate;
1651
+ }
1652
+ if( 1 < count( $rdates ))
1653
+ usort( $rdates, array( 'iCalUtilityFunctions', '_sortRdate2' ));
1654
+ foreach( $rdates as $rpix => $theRdate ) {
1655
+ $attributes = $this->_createParams( $theRdate['params'] );
1656
+ $cnt = count( $theRdate['value'] );
1657
+ $content = null;
1658
+ $rno = 1;
1659
+ foreach( $theRdate['value'] as $rix => $rdatePart ) {
1660
+ $contentPart = null;
1661
+ if( is_array( $rdatePart ) &&
1662
+ isset( $theRdate['params']['VALUE'] ) && ( 'PERIOD' == $theRdate['params']['VALUE'] )) { // PERIOD
1663
+ if( $utctime )
1664
+ unset( $rdatePart[0]['tz'] );
1665
+ $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart[0] ); // PERIOD part 1
1666
+ if( $utctime || !empty( $theRdate['params']['TZID'] ))
1667
+ $formatted = str_replace( 'Z', '', $formatted);
1668
+ $contentPart .= $formatted;
1669
+ $contentPart .= '/';
1670
+ $cnt2 = count( $rdatePart[1]);
1671
+ if( array_key_exists( 'year', $rdatePart[1] )) {
1672
+ if( array_key_exists( 'hour', $rdatePart[1] ))
1673
+ $cnt2 = 7; // date-time
1674
+ else
1675
+ $cnt2 = 3; // date
1676
+ }
1677
+ elseif( array_key_exists( 'week', $rdatePart[1] )) // duration
1678
+ $cnt2 = 5;
1679
+ if(( 7 == $cnt2 ) && // period= -> date-time
1680
+ isset( $rdatePart[1]['year'] ) &&
1681
+ isset( $rdatePart[1]['month'] ) &&
1682
+ isset( $rdatePart[1]['day'] )) {
1683
+ if( $utctime )
1684
+ unset( $rdatePart[1]['tz'] );
1685
+ $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart[1] ); // PERIOD part 2
1686
+ if( $utctime || !empty( $theRdate['params']['TZID'] ))
1687
+ $formatted = str_replace( 'Z', '', $formatted );
1688
+ $contentPart .= $formatted;
1689
+ }
1690
+ else { // period= -> dur-time
1691
+ $contentPart .= iCalUtilityFunctions::_duration2str( $rdatePart[1] );
1692
+ }
1693
+ } // PERIOD end
1694
+ else { // SINGLE date start
1695
+ if( $utctime )
1696
+ unset( $rdatePart['tz'] );
1697
+ $parno = ( isset( $theRdate['params']['VALUE'] ) && ( 'DATE' == isset( $theRdate['params']['VALUE'] ))) ? 3 : null;
1698
+ $formatted = iCalUtilityFunctions::_date2strdate( $rdatePart, $parno );
1699
+ if( $utctime || !empty( $theRdate['params']['TZID'] ))
1700
+ $formatted = str_replace( 'Z', '', $formatted);
1701
+ $contentPart .= $formatted;
1702
+ }
1703
+ $content .= $contentPart;
1704
+ if( $rno < $cnt )
1705
+ $content .= ',';
1706
+ $rno++;
1707
+ }
1708
+ $output .= $this->_createElement( 'RDATE', $attributes, $content );
1709
+ }
1710
+ return $output;
1711
+ }
1712
+ /**
1713
+ * set calendar component property rdate
1714
+ *
1715
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1716
+ * @since 2.21.11 - 2015-03-10
1717
+ * @param array $rdates
1718
+ * @param array $params
1719
+ * @param integer $index
1720
+ * @uses calendarComponent::getConfig()
1721
+ * @uses iCalUtilityFunctions::_setMval()
1722
+ * @uses calendarComponent::$rdate
1723
+ * @uses iCalUtilityFunctions::_setParams()
1724
+ * @uses calendarComponent::$objName
1725
+ * @uses iCalUtilityFunctions::_isArrayDate()
1726
+ * @uses iCalUtilityFunctions::_chkdatecfg()
1727
+ * @uses iCalUtilityFunctions::_existRem()
1728
+ * @uses iCalUtilityFunctions::_strDate2arr()
1729
+ * @uses iCalUtilityFunctions::_isArrayTimestampDate()
1730
+ * @uses iCalUtilityFunctions::_isOffset()
1731
+ * @uses iCalUtilityFunctions::_timestamp2date()
1732
+ * @uses iCalUtilityFunctions::_chkDateArr()
1733
+ * @uses iCalUtilityFunctions::$fmt
1734
+ * @uses iCalUtilityFunctions::_strdate2date()
1735
+ * @uses iCalUtilityFunctions::_duration2arr()
1736
+ * @uses iCalUtilityFunctions::_durationStr2arr()
1737
+ * @return bool
1738
+ */
1739
+ function setRdate( $rdates, $params=FALSE, $index=FALSE ) {
1740
+ if( empty( $rdates )) {
1741
+ if( $this->getConfig( 'allowEmpty' )) {
1742
+ iCalUtilityFunctions::_setMval( $this->rdate, '', $params, FALSE, $index );
1743
+ return TRUE;
1744
+ }
1745
+ else
1746
+ return FALSE;
1747
+ }
1748
+ $input = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )));
1749
+ if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) {
1750
+ unset( $input['params']['TZID'] );
1751
+ $input['params']['VALUE'] = 'DATE-TIME';
1752
+ }
1753
+ $zArr = array( 'GMT', 'UTC', 'Z' );
1754
+ $toZ = ( isset( $params['TZID'] ) && in_array( strtoupper( $params['TZID'] ), $zArr )) ? TRUE : FALSE;
1755
+ /* check if PERIOD, if not set */
1756
+ if((!isset( $input['params']['VALUE'] ) || !in_array( $input['params']['VALUE'], array( 'DATE', 'PERIOD' ))) &&
1757
+ isset( $rdates[0] ) && is_array( $rdates[0] ) && ( 2 == count( $rdates[0] )) &&
1758
+ isset( $rdates[0][0] ) && isset( $rdates[0][1] ) && !isset( $rdates[0]['timestamp'] ) &&
1759
+ (( is_array( $rdates[0][0] ) && ( isset( $rdates[0][0]['timestamp'] ) ||
1760
+ iCalUtilityFunctions::_isArrayDate( $rdates[0][0] ))) ||
1761
+ ( is_string( $rdates[0][0] ) && ( 8 <= strlen( trim( $rdates[0][0] ))))) &&
1762
+ ( is_array( $rdates[0][1] ) || ( is_string( $rdates[0][1] ) && ( 3 <= strlen( trim( $rdates[0][1] ))))))
1763
+ $input['params']['VALUE'] = 'PERIOD';
1764
+ /* check 1:st date, upd. $parno (opt) and save ev. timezone **/
1765
+ $date = reset( $rdates );
1766
+ if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) // PERIOD
1767
+ $date = reset( $date );
1768
+ iCalUtilityFunctions::_chkdatecfg( $date, $parno, $input['params'] );
1769
+ iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default
1770
+ foreach( $rdates as $rpix => $theRdate ) {
1771
+ $inputa = null;
1772
+ iCalUtilityFunctions::_strDate2arr( $theRdate );
1773
+ if( is_array( $theRdate )) {
1774
+ if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) { // PERIOD
1775
+ foreach( $theRdate as $rix => $rPeriod ) {
1776
+ iCalUtilityFunctions::_strDate2arr( $theRdate );
1777
+ if( is_array( $rPeriod )) {
1778
+ if( iCalUtilityFunctions::_isArrayTimestampDate( $rPeriod )) { // timestamp
1779
+ if( isset( $rPeriod['tz'] ) && !iCalUtilityFunctions::_isOffset( $rPeriod['tz'] )) {
1780
+ if( isset( $input['params']['TZID'] ))
1781
+ $rPeriod['tz'] = $input['params']['TZID'];
1782
+ else
1783
+ $input['params']['TZID'] = $rPeriod['tz'];
1784
+ }
1785
+ $inputab = iCalUtilityFunctions::_timestamp2date( $rPeriod, $parno );
1786
+ }
1787
+ elseif( iCalUtilityFunctions::_isArrayDate( $rPeriod )) {
1788
+ $d = ( 3 < count ( $rPeriod )) ? iCalUtilityFunctions::_chkDateArr( $rPeriod, $parno ) : iCalUtilityFunctions::_chkDateArr( $rPeriod, 6 );
1789
+ if( isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) {
1790
+ $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $d['tz'] );
1791
+ $inputab = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
1792
+ unset( $inputab['unparsedtext'] );
1793
+ }
1794
+ else
1795
+ $inputab = $d;
1796
+ }
1797
+ elseif (( 1 == count( $rPeriod )) && ( 8 <= strlen( reset( $rPeriod )))) { // text-date
1798
+ $inputab = iCalUtilityFunctions::_strdate2date( reset( $rPeriod ), $parno );
1799
+ unset( $inputab['unparsedtext'] );
1800
+ }
1801
+ else // array format duration
1802
+ $inputab = iCalUtilityFunctions::_duration2arr( $rPeriod );
1803
+ }
1804
+ elseif(( 3 <= strlen( trim( $rPeriod ))) && // string format duration
1805
+ ( in_array( $rPeriod[0], array( 'P', '+', '-' )))) {
1806
+ if( 'P' != $rPeriod[0] )
1807
+ $rPeriod = substr( $rPeriod, 1 );
1808
+ $inputab = iCalUtilityFunctions::_durationStr2arr( $rPeriod );
1809
+ }
1810
+ elseif( 8 <= strlen( trim( $rPeriod ))) { // text date ex. 2006-08-03 10:12:18
1811
+ $inputab = iCalUtilityFunctions::_strdate2date( $rPeriod, $parno );
1812
+ unset( $inputab['unparsedtext'] );
1813
+ }
1814
+ if(( 0 == $rpix ) && ( 0 == $rix )) {
1815
+ if( isset( $inputab['tz'] ) && in_array( strtoupper( $inputab['tz'] ), $zArr )) {
1816
+ $inputab['tz'] = 'Z';
1817
+ $toZ = TRUE;
1818
+ }
1819
+ }
1820
+ else {
1821
+ if( isset( $inputa[0]['tz'] ) && ( 'Z' == $inputa[0]['tz'] ) && isset( $inputab['year'] ))
1822
+ $inputab['tz'] = 'Z';
1823
+ else
1824
+ unset( $inputab['tz'] );
1825
+ }
1826
+ if( $toZ && isset( $inputab['year'] ) )
1827
+ $inputab['tz'] = 'Z';
1828
+ $inputa[] = $inputab;
1829
+ }
1830
+ } // PERIOD end
1831
+ elseif ( iCalUtilityFunctions::_isArrayTimestampDate( $theRdate )) { // timestamp
1832
+ if( isset( $theRdate['tz'] ) && !iCalUtilityFunctions::_isOffset( $theRdate['tz'] )) {
1833
+ if( isset( $input['params']['TZID'] ))
1834
+ $theRdate['tz'] = $input['params']['TZID'];
1835
+ else
1836
+ $input['params']['TZID'] = $theRdate['tz'];
1837
+ }
1838
+ $inputa = iCalUtilityFunctions::_timestamp2date( $theRdate, $parno );
1839
+ }
1840
+ else { // date[-time]
1841
+ $inputa = iCalUtilityFunctions::_chkDateArr( $theRdate, $parno );
1842
+ if( isset( $inputa['tz'] ) && ( 'Z' != $inputa['tz'] ) && iCalUtilityFunctions::_isOffset( $inputa['tz'] )) {
1843
+ $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $inputa['year'], (int) $inputa['month'], (int) $inputa['day'], (int) $inputa['hour'], (int) $inputa['min'], (int) $inputa['sec'], $inputa['tz'] );
1844
+ $inputa = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
1845
+ unset( $inputa['unparsedtext'] );
1846
+ }
1847
+ }
1848
+ }
1849
+ elseif( 8 <= strlen( trim( $theRdate ))) { // text date ex. 2006-08-03 10:12:18
1850
+ $inputa = iCalUtilityFunctions::_strdate2date( $theRdate, $parno );
1851
+ unset( $inputa['unparsedtext'] );
1852
+ if( $toZ )
1853
+ $inputa['tz'] = 'Z';
1854
+ }
1855
+ if( !isset( $input['params']['VALUE'] ) || ( 'PERIOD' != $input['params']['VALUE'] )) { // no PERIOD
1856
+ if(( 0 == $rpix ) && !$toZ )
1857
+ $toZ = ( isset( $inputa['tz'] ) && in_array( strtoupper( $inputa['tz'] ), $zArr )) ? TRUE : FALSE;
1858
+ if( $toZ )
1859
+ $inputa['tz'] = 'Z';
1860
+ if( 3 == $parno )
1861
+ unset( $inputa['hour'], $inputa['min'], $inputa['sec'], $inputa['tz'] );
1862
+ elseif( isset( $inputa['tz'] ))
1863
+ $inputa['tz'] = (string) $inputa['tz'];
1864
+ if( isset( $input['params']['TZID'] ) || ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] ))))
1865
+ if( !$toZ )
1866
+ unset( $inputa['tz'] );
1867
+ }
1868
+ $input['value'][] = $inputa;
1869
+ }
1870
+ if( 3 == $parno ) {
1871
+ $input['params']['VALUE'] = 'DATE';
1872
+ unset( $input['params']['TZID'] );
1873
+ }
1874
+ if( $toZ )
1875
+ unset( $input['params']['TZID'] );
1876
+ iCalUtilityFunctions::_setMval( $this->rdate, $input['value'], $input['params'], FALSE, $index );
1877
+ return TRUE;
1878
+ }
1879
+ /*********************************************************************************/
1880
+ /**
1881
+ * Property Name: RECURRENCE-ID
1882
+ */
1883
+ /**
1884
+ * creates formatted output for calendar component property recurrence-id
1885
+ *
1886
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1887
+ * @since 2.14.4 - 2012-09-26
1888
+ * @uses calendarComponent::$recurrenceid
1889
+ * @uses calendarComponent::getConfig()
1890
+ * @uses calendarComponent::_createElement()
1891
+ * @uses iCalUtilityFunctions::_date2strdate()
1892
+ * @uses calendarComponent::_createParams()
1893
+ * @return string
1894
+ */
1895
+ function createRecurrenceid() {
1896
+ if( empty( $this->recurrenceid )) return FALSE;
1897
+ if( empty( $this->recurrenceid['value'] ))
1898
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'RECURRENCE-ID' ) : FALSE;
1899
+ $parno = ( isset( $this->recurrenceid['params']['VALUE'] ) && ( 'DATE' == $this->recurrenceid['params']['VALUE'] )) ? 3 : null;
1900
+ $formatted = iCalUtilityFunctions::_date2strdate( $this->recurrenceid['value'], $parno );
1901
+ $attributes = $this->_createParams( $this->recurrenceid['params'] );
1902
+ return $this->_createElement( 'RECURRENCE-ID', $attributes, $formatted );
1903
+ }
1904
+ /**
1905
+ * set calendar component property recurrence-id
1906
+ *
1907
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1908
+ * @since 2.16.21 - 2013-06-23
1909
+ * @param mixed $year
1910
+ * @param mixed $month
1911
+ * @param int $day
1912
+ * @param int $hour
1913
+ * @param int $min
1914
+ * @param int $sec
1915
+ * @param string $tz
1916
+ * @param array $params
1917
+ * @uses calendarComponent::getConfig()
1918
+ * @uses calendarComponent::$recurrenceid
1919
+ * @uses iCalUtilityFunctions::_setDate()
1920
+ * @return bool
1921
+ */
1922
+ function setRecurrenceid( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
1923
+ if( empty( $year )) {
1924
+ if( $this->getConfig( 'allowEmpty' )) {
1925
+ $this->recurrenceid = array( 'value' => '', 'params' => null );
1926
+ return TRUE;
1927
+ }
1928
+ else
1929
+ return FALSE;
1930
+ }
1931
+ $this->recurrenceid = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' ));
1932
+ return TRUE;
1933
+ }
1934
+ /*********************************************************************************/
1935
+ /**
1936
+ * Property Name: RELATED-TO
1937
+ */
1938
+ /**
1939
+ * creates formatted output for calendar component property related-to
1940
+ *
1941
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1942
+ * @since 2.16.21 - 2013-05-25
1943
+ * @uses calendarComponent::$relatedto
1944
+ * @uses calendarComponent::_createElement()
1945
+ * @uses calendarComponent::_createParams()
1946
+ * @uses iCalUtilityFunctions::_strrep()
1947
+ * @uses calendarComponent::getConfig()
1948
+ * @return string
1949
+ */
1950
+ function createRelatedTo() {
1951
+ if( empty( $this->relatedto )) return FALSE;
1952
+ $output = null;
1953
+ foreach( $this->relatedto as $relation ) {
1954
+ if( !empty( $relation['value'] ))
1955
+ $output .= $this->_createElement( 'RELATED-TO', $this->_createParams( $relation['params'] ), iCalUtilityFunctions::_strrep( $relation['value'], $this->format, $this->nl ));
1956
+ elseif( $this->getConfig( 'allowEmpty' ))
1957
+ $output .= $this->_createElement( 'RELATED-TO' );
1958
+ }
1959
+ return $output;
1960
+ }
1961
+ /**
1962
+ * set calendar component property related-to
1963
+ *
1964
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1965
+ * @since 2.16.21 - 2013-06-23
1966
+ * @param string $value
1967
+ * @param array $params
1968
+ * @param int $index
1969
+ * @uses calendarComponent::getConfig()
1970
+ * @uses iCalUtilityFunctions::_existRem()
1971
+ * @uses iCalUtilityFunctions::_setMval()
1972
+ * @return bool
1973
+ */
1974
+ function setRelatedTo( $value, $params=FALSE, $index=FALSE ) {
1975
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
1976
+ iCalUtilityFunctions::_existRem( $params, 'RELTYPE', 'PARENT', TRUE ); // remove default
1977
+ iCalUtilityFunctions::_setMval( $this->relatedto, $value, $params, FALSE, $index );
1978
+ return TRUE;
1979
+ }
1980
+ /*********************************************************************************/
1981
+ /**
1982
+ * Property Name: REPEAT
1983
+ */
1984
+ /**
1985
+ * creates formatted output for calendar component property repeat
1986
+ *
1987
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1988
+ * @since 2.9.3 - 2011-05-14
1989
+ * @uses calendarComponent::$repeat
1990
+ * @uses calendarComponent::getConfig()
1991
+ * @uses calendarComponent::_createElement()
1992
+ * @uses calendarComponent::_createParams()
1993
+ * @return string
1994
+ */
1995
+ function createRepeat() {
1996
+ if( !isset( $this->repeat ) || ( empty( $this->repeat ) && !is_numeric( $this->repeat ))) return FALSE;
1997
+ if( !isset( $this->repeat['value']) || ( empty( $this->repeat['value'] ) && !is_numeric( $this->repeat['value'] )))
1998
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'REPEAT' ) : FALSE;
1999
+ $attributes = $this->_createParams( $this->repeat['params'] );
2000
+ return $this->_createElement( 'REPEAT', $attributes, $this->repeat['value'] );
2001
+ }
2002
+ /**
2003
+ * set calendar component property repeat
2004
+ *
2005
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2006
+ * @since 2.16.21 - 2013-06-23
2007
+ * @param string $value
2008
+ * @param array $params
2009
+ * @uses calendarComponent::getConfig()
2010
+ * @uses calendarComponent::$repeat
2011
+ * @uses iCalUtilityFunctions::_setParams()
2012
+ * @return void
2013
+ */
2014
+ function setRepeat( $value, $params=FALSE ) {
2015
+ if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
2016
+ $this->repeat = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
2017
+ return TRUE;
2018
+ }
2019
+ /*********************************************************************************/
2020
+ /**
2021
+ * Property Name: REQUEST-STATUS
2022
+ */
2023
+ /**
2024
+ * creates formatted output for calendar component property request-status
2025
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2026
+ * @since 2.16.2 - 2012-12-18
2027
+ * @uses calendarComponent::$requeststatus
2028
+ * @uses calendarComponent::getConfig()
2029
+ * @uses calendarComponent::_createElement()
2030
+ * @uses calendarComponent::_createParams()
2031
+ * @uses iCalUtilityFunctions::_strrep()
2032
+ * @return string
2033
+ */
2034
+ function createRequestStatus() {
2035
+ if( empty( $this->requeststatus )) return FALSE;
2036
+ $output = null;
2037
+ foreach( $this->requeststatus as $rstat ) {
2038
+ if( empty( $rstat['value']['statcode'] )) {
2039
+ if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'REQUEST-STATUS' );
2040
+ continue;
2041
+ }
2042
+ $attributes = $this->_createParams( $rstat['params'], array( 'LANGUAGE' ));
2043
+ $content = number_format( (float) $rstat['value']['statcode'], 2, '.', '');
2044
+ $content .= ';'.iCalUtilityFunctions::_strrep( $rstat['value']['text'], $this->format, $this->nl );
2045
+ if( isset( $rstat['value']['extdata'] ))
2046
+ $content .= ';'.iCalUtilityFunctions::_strrep( $rstat['value']['extdata'], $this->format, $this->nl );
2047
+ $output .= $this->_createElement( 'REQUEST-STATUS', $attributes, $content );
2048
+ }
2049
+ return $output;
2050
+ }
2051
+ /**
2052
+ * set calendar component property request-status
2053
+ *
2054
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2055
+ * @since 2.16.21 - 2013-06-23
2056
+ * @param float $statcode
2057
+ * @param string $text
2058
+ * @param string $extdata
2059
+ * @param array $params
2060
+ * @param integer $index
2061
+ * @uses calendarComponent::getConfig()
2062
+ * @uses calendarComponent::$requeststatus
2063
+ * @uses iCalUtilityFunctions::_setMval()
2064
+ * @return bool
2065
+ */
2066
+ function setRequestStatus( $statcode, $text, $extdata=FALSE, $params=FALSE, $index=FALSE ) {
2067
+ if( empty( $statcode ) || empty( $text )) if( $this->getConfig( 'allowEmpty' )) $statcode = $text = ''; else return FALSE;
2068
+ $input = array( 'statcode' => $statcode, 'text' => $text );
2069
+ if( $extdata )
2070
+ $input['extdata'] = $extdata;
2071
+ iCalUtilityFunctions::_setMval( $this->requeststatus, $input, $params, FALSE, $index );
2072
+ return TRUE;
2073
+ }
2074
+ /*********************************************************************************/
2075
+ /**
2076
+ * Property Name: RESOURCES
2077
+ */
2078
+ /**
2079
+ * creates formatted output for calendar component property resources
2080
+ *
2081
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2082
+ * @since 2.16.2 - 2012-12-18
2083
+ * @uses calendarComponent::$resources
2084
+ * @uses calendarComponent::getConfig()
2085
+ * @uses calendarComponent::_createElement()
2086
+ * @uses calendarComponent::_createParams()
2087
+ * @uses iCalUtilityFunctions::_strrep()
2088
+ * @return string
2089
+ */
2090
+ function createResources() {
2091
+ if( empty( $this->resources )) return FALSE;
2092
+ $output = null;
2093
+ foreach( $this->resources as $resource ) {
2094
+ if( empty( $resource['value'] )) {
2095
+ if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RESOURCES' );
2096
+ continue;
2097
+ }
2098
+ $attributes = $this->_createParams( $resource['params'], array( 'ALTREP', 'LANGUAGE' ));
2099
+ if( is_array( $resource['value'] )) {
2100
+ foreach( $resource['value'] as $rix => $resourcePart )
2101
+ $resource['value'][$rix] = iCalUtilityFunctions::_strrep( $resourcePart, $this->format, $this->nl );
2102
+ $content = implode( ',', $resource['value'] );
2103
+ }
2104
+ else
2105
+ $content = iCalUtilityFunctions::_strrep( $resource['value'], $this->format, $this->nl );
2106
+ $output .= $this->_createElement( 'RESOURCES', $attributes, $content );
2107
+ }
2108
+ return $output;
2109
+ }
2110
+ /**
2111
+ * set calendar component property recources
2112
+ *
2113
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2114
+ * @since 2.16.21 - 2013-06-23
2115
+ * @param mixed $value
2116
+ * @param array $params
2117
+ * @param integer $index
2118
+ * @uses calendarComponent::getConfig()
2119
+ * @uses iCalUtilityFunctions::_setMval()
2120
+ * @uses calendarComponent::$resources
2121
+ * @return bool
2122
+ */
2123
+ function setResources( $value, $params=FALSE, $index=FALSE ) {
2124
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
2125
+ iCalUtilityFunctions::_setMval( $this->resources, $value, $params, FALSE, $index );
2126
+ return TRUE;
2127
+ }
2128
+ /*********************************************************************************/
2129
+ /**
2130
+ * Property Name: RRULE
2131
+ */
2132
+ /**
2133
+ * creates formatted output for calendar component property rrule
2134
+ *
2135
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2136
+ * @since 2.4.8 - 2008-10-21
2137
+ * @uses calendarComponent::$rrule
2138
+ * @uses calendarComponent::_format_recur()
2139
+ * @return string
2140
+ */
2141
+ function createRrule() {
2142
+ if( empty( $this->rrule )) return FALSE;
2143
+ return $this->_format_recur( 'RRULE', $this->rrule );
2144
+ }
2145
+ /**
2146
+ * set calendar component property rrule
2147
+ *
2148
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2149
+ * @since 2.16.21 - 2013-06-23
2150
+ * @param array $rruleset
2151
+ * @param array $params
2152
+ * @param integer $index
2153
+ * @uses calendarComponent::getConfig()
2154
+ * @uses iCalUtilityFunctions::_setMval()
2155
+ * @uses iCalUtilityFunctions::_setRexrule()
2156
+ * @return void
2157
+ */
2158
+ function setRrule( $rruleset, $params=FALSE, $index=FALSE ) {
2159
+ if( empty( $rruleset )) if( $this->getConfig( 'allowEmpty' )) $rruleset = ''; else return FALSE;
2160
+ iCalUtilityFunctions::_setMval( $this->rrule, iCalUtilityFunctions::_setRexrule( $rruleset ), $params, FALSE, $index );
2161
+ return TRUE;
2162
+ }
2163
+ /*********************************************************************************/
2164
+ /**
2165
+ * Property Name: SEQUENCE
2166
+ */
2167
+ /**
2168
+ * creates formatted output for calendar component property sequence
2169
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2170
+ * @since 2.9.3 - 2011-05-14
2171
+ * @uses calendarComponent::$sequence
2172
+ * @uses calendarComponent::getConfig()
2173
+ * @uses calendarComponent::_createElement()
2174
+ * @uses calendarComponent::_createParams()
2175
+ * @return string
2176
+ */
2177
+ function createSequence() {
2178
+ if( !isset( $this->sequence ) || ( empty( $this->sequence ) && !is_numeric( $this->sequence ))) return FALSE;
2179
+ if(( !isset($this->sequence['value'] ) || ( empty( $this->sequence['value'] ) && !is_numeric( $this->sequence['value'] ))) &&
2180
+ ( '0' != $this->sequence['value'] ))
2181
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SEQUENCE' ) : FALSE;
2182
+ $attributes = $this->_createParams( $this->sequence['params'] );
2183
+ return $this->_createElement( 'SEQUENCE', $attributes, $this->sequence['value'] );
2184
+ }
2185
+ /**
2186
+ * set calendar component property sequence
2187
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2188
+ * @since 2.10.8 - 2011-09-19
2189
+ * @param int $value
2190
+ * @param array $params
2191
+ * @uses calendarComponent::$sequence
2192
+ * @uses iCalUtilityFunctions::_setParams();
2193
+ * @return bool
2194
+ */
2195
+ function setSequence( $value=FALSE, $params=FALSE ) {
2196
+ if(( empty( $value ) && !is_numeric( $value )) && ( '0' != $value ))
2197
+ $value = ( isset( $this->sequence['value'] ) && ( -1 < $this->sequence['value'] )) ? $this->sequence['value'] + 1 : '0';
2198
+ $this->sequence = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
2199
+ return TRUE;
2200
+ }
2201
+ /*********************************************************************************/
2202
+ /**
2203
+ * Property Name: STATUS
2204
+ */
2205
+ /**
2206
+ * creates formatted output for calendar component property status
2207
+ *
2208
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2209
+ * @since 2.4.8 - 2008-10-21
2210
+ * @uses calendarComponent::$status
2211
+ * @uses calendarComponent::getConfig()
2212
+ * @uses calendarComponent::_createElement()
2213
+ * @uses calendarComponent::_createParams()
2214
+ * @return string
2215
+ */
2216
+ function createStatus() {
2217
+ if( empty( $this->status )) return FALSE;
2218
+ if( empty( $this->status['value'] ))
2219
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'STATUS' ) : FALSE;
2220
+ $attributes = $this->_createParams( $this->status['params'] );
2221
+ return $this->_createElement( 'STATUS', $attributes, $this->status['value'] );
2222
+ }
2223
+ /**
2224
+ * set calendar component property status
2225
+ *
2226
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2227
+ * @since 2.16.21 - 2013-06-23
2228
+ * @param string $value
2229
+ * @param array $params
2230
+ * @uses calendarComponent::getConfig()
2231
+ * @uses calendarComponent::$status
2232
+ * @uses iCalUtilityFunctions::_setParams()
2233
+ * @return bool
2234
+ */
2235
+ function setStatus( $value, $params=FALSE ) {
2236
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
2237
+ $this->status = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
2238
+ return TRUE;
2239
+ }
2240
+ /*********************************************************************************/
2241
+ /**
2242
+ * Property Name: SUMMARY
2243
+ */
2244
+ /**
2245
+ * creates formatted output for calendar component property summary
2246
+ *
2247
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2248
+ * @since 2.16.2 - 2012-12-18
2249
+ * @uses calendarComponent::$summary
2250
+ * @uses calendarComponent::getConfig()
2251
+ * @uses calendarComponent::_createElement()
2252
+ * @uses calendarComponent::_createParams()
2253
+ * @uses iCalUtilityFunctions::_strrep()
2254
+ * @return string
2255
+ */
2256
+ function createSummary() {
2257
+ if( empty( $this->summary )) return FALSE;
2258
+ if( empty( $this->summary['value'] ))
2259
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SUMMARY' ) : FALSE;
2260
+ $attributes = $this->_createParams( $this->summary['params'], array( 'ALTREP', 'LANGUAGE' ));
2261
+ $content = iCalUtilityFunctions::_strrep( $this->summary['value'], $this->format, $this->nl );
2262
+ return $this->_createElement( 'SUMMARY', $attributes, $content );
2263
+ }
2264
+ /**
2265
+ * set calendar component property summary
2266
+ *
2267
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2268
+ * @since 2.16.21 - 2013-06-23
2269
+ * @param string $value
2270
+ * @param string $params
2271
+ * @uses calendarComponent::getConfig()
2272
+ * @uses iCalUtilityFunctions::_setParams()
2273
+ * @return bool
2274
+ */
2275
+ function setSummary( $value, $params=FALSE ) {
2276
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
2277
+ $this->summary = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
2278
+ return TRUE;
2279
+ }
2280
+ /*********************************************************************************/
2281
+ /**
2282
+ * Property Name: TRANSP
2283
+ */
2284
+ /**
2285
+ * creates formatted output for calendar component property transp
2286
+ *
2287
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2288
+ * @since 2.4.8 - 2008-10-21
2289
+ * @uses calendarComponent::$transp
2290
+ * @uses calendarComponent::getConfig()
2291
+ * @uses calendarComponent::_createElement()
2292
+ * @uses calendarComponent::_createParams()
2293
+ * @return string
2294
+ */
2295
+ function createTransp() {
2296
+ if( empty( $this->transp )) return FALSE;
2297
+ if( empty( $this->transp['value'] ))
2298
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRANSP' ) : FALSE;
2299
+ $attributes = $this->_createParams( $this->transp['params'] );
2300
+ return $this->_createElement( 'TRANSP', $attributes, $this->transp['value'] );
2301
+ }
2302
+ /**
2303
+ * set calendar component property transp
2304
+ *
2305
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2306
+ * @since 2.16.21 - 2013-06-23
2307
+ * @param string $value
2308
+ * @param string $params
2309
+ * @uses calendarComponent::getConfig()
2310
+ * @uses calendarComponent::$transp
2311
+ * @uses iCalUtilityFunctions::_setParams()
2312
+ * @return bool
2313
+ */
2314
+ function setTransp( $value, $params=FALSE ) {
2315
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
2316
+ $this->transp = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
2317
+ return TRUE;
2318
+ }
2319
+ /*********************************************************************************/
2320
+ /**
2321
+ * Property Name: TRIGGER
2322
+ */
2323
+ /**
2324
+ * creates formatted output for calendar component property trigger
2325
+ *
2326
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2327
+ * @since 2.4.16 - 2008-10-21
2328
+ * @uses calendarComponent::$trigger
2329
+ * @uses calendarComponent::getConfig()
2330
+ * @uses calendarComponent::_createElement()
2331
+ * @uses iCalUtilityFunctions::_date2strdate()
2332
+ * @uses calendarComponent::$intAttrDelimiter
2333
+ * @uses iCalUtilityFunctions::_duration2str()
2334
+ * @uses calendarComponent::_createParams()
2335
+ * @return string
2336
+ */
2337
+ function createTrigger() {
2338
+ if( empty( $this->trigger )) return FALSE;
2339
+ if( empty( $this->trigger['value'] ))
2340
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRIGGER' ) : FALSE;
2341
+ $content = $attributes = null;
2342
+ if( isset( $this->trigger['value']['year'] ) &&
2343
+ isset( $this->trigger['value']['month'] ) &&
2344
+ isset( $this->trigger['value']['day'] ))
2345
+ $content .= iCalUtilityFunctions::_date2strdate( $this->trigger['value'] );
2346
+ else {
2347
+ if( TRUE !== $this->trigger['value']['relatedStart'] )
2348
+ $attributes .= $this->intAttrDelimiter.'RELATED=END';
2349
+ if( $this->trigger['value']['before'] )
2350
+ $content .= '-';
2351
+ $content .= iCalUtilityFunctions::_duration2str( $this->trigger['value'] );
2352
+ }
2353
+ $attributes .= $this->_createParams( $this->trigger['params'] );
2354
+ return $this->_createElement( 'TRIGGER', $attributes, $content );
2355
+ }
2356
+ /**
2357
+ * set calendar component property trigger
2358
+ *
2359
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2360
+ * @since 2.16.21 - 2013-06-23
2361
+ * @param mixed $year
2362
+ * @param mixed $month
2363
+ * @param int $day
2364
+ * @param int $week
2365
+ * @param int $hour
2366
+ * @param int $min
2367
+ * @param int $sec
2368
+ * @param bool $relatedStart
2369
+ * @param bool $before
2370
+ * @param array $params
2371
+ * @uses calendarComponent::getConfig()
2372
+ * @uses calendarComponent::$trigger
2373
+ * @uses iCalUtilityFunctions::_setParams()
2374
+ * @uses iCalUtilityFunctions::_isArrayTimestampDate()
2375
+ * @uses iCalUtilityFunctions::_timestamp2date()
2376
+ * @uses iCalUtilityFunctions::_strdate2date()
2377
+ * @uses iCalUtilityFunctions::_duration2arr()
2378
+ * @return bool
2379
+ */
2380
+ function setTrigger( $year, $month=null, $day=null, $week=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $relatedStart=TRUE, $before=TRUE, $params=FALSE ) {
2381
+ if( empty( $year ) && ( empty( $month ) || is_array( $month )) && empty( $day ) && empty( $week ) && empty( $hour ) && empty( $min ) && empty( $sec ))
2382
+ if( $this->getConfig( 'allowEmpty' )) {
2383
+ $this->trigger = array( 'value' => '', 'params' => iCalUtilityFunctions::_setParams( $month ) );
2384
+ return TRUE;
2385
+ }
2386
+ else
2387
+ return FALSE;
2388
+ if( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { // timestamp UTC
2389
+ $params = iCalUtilityFunctions::_setParams( $month );
2390
+ $date = iCalUtilityFunctions::_timestamp2date( $year, 7 );
2391
+ foreach( $date as $k => $v )
2392
+ $$k = $v;
2393
+ }
2394
+ elseif( is_array( $year ) && ( is_array( $month ) || empty( $month ))) {
2395
+ $params = iCalUtilityFunctions::_setParams( $month );
2396
+ if(!(array_key_exists( 'year', $year ) && // exclude date-time
2397
+ array_key_exists( 'month', $year ) &&
2398
+ array_key_exists( 'day', $year ))) { // when this must be a duration
2399
+ if( isset( $params['RELATED'] ) && ( 'END' == strtoupper( $params['RELATED'] )))
2400
+ $relatedStart = FALSE;
2401
+ else
2402
+ $relatedStart = ( array_key_exists( 'relatedStart', $year ) && ( TRUE !== $year['relatedStart'] )) ? FALSE : TRUE;
2403
+ $before = ( array_key_exists( 'before', $year ) && ( TRUE !== $year['before'] )) ? FALSE : TRUE;
2404
+ }
2405
+ $SSYY = ( array_key_exists( 'year', $year )) ? $year['year'] : null;
2406
+ $month = ( array_key_exists( 'month', $year )) ? $year['month'] : null;
2407
+ $day = ( array_key_exists( 'day', $year )) ? $year['day'] : null;
2408
+ $week = ( array_key_exists( 'week', $year )) ? $year['week'] : null;
2409
+ $hour = ( array_key_exists( 'hour', $year )) ? $year['hour'] : 0; //null;
2410
+ $min = ( array_key_exists( 'min', $year )) ? $year['min'] : 0; //null;
2411
+ $sec = ( array_key_exists( 'sec', $year )) ? $year['sec'] : 0; //null;
2412
+ $year = $SSYY;
2413
+ }
2414
+ elseif(is_string( $year ) && ( is_array( $month ) || empty( $month ))) { // duration or date in a string
2415
+ $params = iCalUtilityFunctions::_setParams( $month );
2416
+ if( in_array( $year{0}, array( 'P', '+', '-' ))) { // duration
2417
+ $relatedStart = ( isset( $params['RELATED'] ) && ( 'END' == strtoupper( $params['RELATED'] ))) ? FALSE : TRUE;
2418
+ $before = ( '-' == $year[0] ) ? TRUE : FALSE;
2419
+ if( 'P' != $year[0] )
2420
+ $year = substr( $year, 1 );
2421
+ $date = iCalUtilityFunctions::_durationStr2arr( $year);
2422
+ }
2423
+ else // date
2424
+ $date = iCalUtilityFunctions::_strdate2date( $year, 7 );
2425
+ unset( $year, $month, $day, $date['unparsedtext'] );
2426
+ if( empty( $date ))
2427
+ $sec = 0;
2428
+ else
2429
+ foreach( $date as $k => $v )
2430
+ $$k = $v;
2431
+ }
2432
+ else // single values in function input parameters
2433
+ $params = iCalUtilityFunctions::_setParams( $params );
2434
+ if( !empty( $year ) && !empty( $month ) && !empty( $day )) { // date
2435
+ $params['VALUE'] = 'DATE-TIME';
2436
+ $hour = ( $hour ) ? $hour : 0;
2437
+ $min = ( $min ) ? $min : 0;
2438
+ $sec = ( $sec ) ? $sec : 0;
2439
+ $this->trigger = array( 'params' => $params );
2440
+ $this->trigger['value'] = array( 'year' => $year
2441
+ , 'month' => $month
2442
+ , 'day' => $day
2443
+ , 'hour' => $hour
2444
+ , 'min' => $min
2445
+ , 'sec' => $sec
2446
+ , 'tz' => 'Z' );
2447
+ return TRUE;
2448
+ }
2449
+ elseif(( empty( $year ) && empty( $month )) && // duration
2450
+ (( !empty( $week ) || ( 0 == $week )) ||
2451
+ ( !empty( $day ) || ( 0 == $day )) ||
2452
+ ( !empty( $hour ) || ( 0 == $hour )) ||
2453
+ ( !empty( $min ) || ( 0 == $min )) ||
2454
+ ( !empty( $sec ) || ( 0 == $sec )))) {
2455
+ unset( $params['RELATED'] ); // set at output creation (END only)
2456
+ unset( $params['VALUE'] ); // 'DURATION' default
2457
+ $this->trigger = array( 'params' => $params );
2458
+ $this->trigger['value'] = array();
2459
+ if( !empty( $week )) $this->trigger['value']['week'] = $week;
2460
+ if( !empty( $day )) $this->trigger['value']['day'] = $day;
2461
+ if( !empty( $hour )) $this->trigger['value']['hour'] = $hour;
2462
+ if( !empty( $min )) $this->trigger['value']['min'] = $min;
2463
+ if( !empty( $sec )) $this->trigger['value']['sec'] = $sec;
2464
+ if( empty( $this->trigger['value'] )) {
2465
+ $this->trigger['value']['sec'] = 0;
2466
+ $before = FALSE;
2467
+ }
2468
+ else
2469
+ $this->trigger['value'] = iCalUtilityFunctions::_duration2arr( $this->trigger['value'] );
2470
+ $relatedStart = ( FALSE !== $relatedStart ) ? TRUE : FALSE;
2471
+ $before = ( FALSE !== $before ) ? TRUE : FALSE;
2472
+ $this->trigger['value']['relatedStart'] = $relatedStart;
2473
+ $this->trigger['value']['before'] = $before;
2474
+ return TRUE;
2475
+ }
2476
+ return FALSE;
2477
+ }
2478
+ /*********************************************************************************/
2479
+ /**
2480
+ * Property Name: TZID
2481
+ */
2482
+ /**
2483
+ * creates formatted output for calendar component property tzid
2484
+ *
2485
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2486
+ * @since 2.16.2 - 2012-12-18
2487
+ * @uses calendarComponent::$tzid
2488
+ * @uses calendarComponent::getConfig()
2489
+ * @uses calendarComponent::_createElement()
2490
+ * @uses calendarComponent::_createParams()
2491
+ * @uses iCalUtilityFunctions::_strrep()
2492
+ * @uses calendarComponent::$format
2493
+ * @uses calendarComponent::$nl
2494
+ * @return string
2495
+ */
2496
+ function createTzid() {
2497
+ if( empty( $this->tzid )) return FALSE;
2498
+ if( empty( $this->tzid['value'] ))
2499
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZID' ) : FALSE;
2500
+ $attributes = $this->_createParams( $this->tzid['params'] );
2501
+ return $this->_createElement( 'TZID', $attributes, iCalUtilityFunctions::_strrep( $this->tzid['value'], $this->format, $this->nl ));
2502
+ }
2503
+ /**
2504
+ * set calendar component property tzid
2505
+ *
2506
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2507
+ * @since 2.16.21 - 2013-06-23
2508
+ * @param string $value
2509
+ * @param array $params
2510
+ * @uses calendarComponent::getConfig()
2511
+ * @uses calendarComponent::$tzid
2512
+ * @uses iCalUtilityFunctions::_setParams()
2513
+ * @return bool
2514
+ */
2515
+ function setTzid( $value, $params=FALSE ) {
2516
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
2517
+ $this->tzid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
2518
+ return TRUE;
2519
+ }
2520
+ /*********************************************************************************/
2521
+ /**
2522
+ * .. .
2523
+ * Property Name: TZNAME
2524
+ */
2525
+ /**
2526
+ * creates formatted output for calendar component property tzname
2527
+ *
2528
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2529
+ * @since 2.16.2 - 2012-12-18
2530
+ * @uses calendarComponent::$tzname
2531
+ * @uses calendarComponent::_createParams()
2532
+ * @uses calendarComponent::_createElement()
2533
+ * @uses iCalUtilityFunctions::_strrep(
2534
+ * @uses calendarComponent::$format
2535
+ * @uses calendarComponent::$nl
2536
+ * @uses calendarComponent::getConfig()
2537
+ * @return string
2538
+ */
2539
+ function createTzname() {
2540
+ if( empty( $this->tzname )) return FALSE;
2541
+ $output = null;
2542
+ foreach( $this->tzname as $theName ) {
2543
+ if( !empty( $theName['value'] )) {
2544
+ $attributes = $this->_createParams( $theName['params'], array( 'LANGUAGE' ));
2545
+ $output .= $this->_createElement( 'TZNAME', $attributes, iCalUtilityFunctions::_strrep( $theName['value'], $this->format, $this->nl ));
2546
+ }
2547
+ elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'TZNAME' );
2548
+ }
2549
+ return $output;
2550
+ }
2551
+ /**
2552
+ * set calendar component property tzname
2553
+ *
2554
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2555
+ * @since 2.16.21 - 2013-06-23
2556
+ * @param string $value
2557
+ * @param array $params
2558
+ * @param integer $index
2559
+ * @uses calendarComponent::_createParams()
2560
+ * @uses calendarComponent::$tzname
2561
+ * @uses iCalUtilityFunctions::_setMval()
2562
+ * @return bool
2563
+ */
2564
+ function setTzname( $value, $params=FALSE, $index=FALSE ) {
2565
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
2566
+ iCalUtilityFunctions::_setMval( $this->tzname, $value, $params, FALSE, $index );
2567
+ return TRUE;
2568
+ }
2569
+ /*********************************************************************************/
2570
+ /**
2571
+ * Property Name: TZOFFSETFROM
2572
+ */
2573
+ /**
2574
+ * creates formatted output for calendar component property tzoffsetfrom
2575
+ *
2576
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2577
+ * @since 2.4.8 - 2008-10-21
2578
+ * @uses calendarComponent::$tzoffsetfrom
2579
+ * @uses calendarComponent::getConfig()
2580
+ * @uses calendarComponent::_createElement()
2581
+ * @uses calendarComponent::_createParams()
2582
+ * @return string
2583
+ */
2584
+ function createTzoffsetfrom() {
2585
+ if( empty( $this->tzoffsetfrom )) return FALSE;
2586
+ if( empty( $this->tzoffsetfrom['value'] ))
2587
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETFROM' ) : FALSE;
2588
+ $attributes = $this->_createParams( $this->tzoffsetfrom['params'] );
2589
+ return $this->_createElement( 'TZOFFSETFROM', $attributes, $this->tzoffsetfrom['value'] );
2590
+ }
2591
+ /**
2592
+ * set calendar component property tzoffsetfrom
2593
+ *
2594
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2595
+ * @since 2.16.21 - 2013-06-23
2596
+ * @param string $value
2597
+ * @param array $params
2598
+ * @uses calendarComponent::getConfig()
2599
+ * @uses calendarComponent::$tzoffsetfrom
2600
+ * @uses iCalUtilityFunctions::_setParams()
2601
+ * @return bool
2602
+ */
2603
+ function setTzoffsetfrom( $value, $params=FALSE ) {
2604
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
2605
+ $this->tzoffsetfrom = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
2606
+ return TRUE;
2607
+ }
2608
+ /*********************************************************************************/
2609
+ /**
2610
+ * Property Name: TZOFFSETTO
2611
+ */
2612
+ /**
2613
+ * creates formatted output for calendar component property tzoffsetto
2614
+ *
2615
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2616
+ * @since 2.4.8 - 2008-10-21
2617
+ * @uses calendarComponent::$tzoffsetto
2618
+ * @uses calendarComponent::getConfig()
2619
+ * @uses calendarComponent::_createElement()
2620
+ * @uses calendarComponent::_createParams()
2621
+ * @return string
2622
+ */
2623
+ function createTzoffsetto() {
2624
+ if( empty( $this->tzoffsetto )) return FALSE;
2625
+ if( empty( $this->tzoffsetto['value'] ))
2626
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETTO' ) : FALSE;
2627
+ $attributes = $this->_createParams( $this->tzoffsetto['params'] );
2628
+ return $this->_createElement( 'TZOFFSETTO', $attributes, $this->tzoffsetto['value'] );
2629
+ }
2630
+ /**
2631
+ * set calendar component property tzoffsetto
2632
+ *
2633
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2634
+ * @since 2.16.21 - 2013-06-23
2635
+ * @param string $value
2636
+ * @param array $params
2637
+ * @uses calendarComponent::getConfig()
2638
+ * @uses calendarComponent::$tzoffsetto
2639
+ * @uses iCalUtilityFunctions::_setParams()
2640
+ * @return bool
2641
+ */
2642
+ function setTzoffsetto( $value, $params=FALSE ) {
2643
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
2644
+ $this->tzoffsetto = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
2645
+ return TRUE;
2646
+ }
2647
+ /*********************************************************************************/
2648
+ /**
2649
+ * Property Name: TZURL
2650
+ */
2651
+ /**
2652
+ * creates formatted output for calendar component property tzurl
2653
+ *
2654
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2655
+ * @since 2.4.8 - 2008-10-21
2656
+ * @uses calendarComponent::$tzurl
2657
+ * @uses calendarComponent::getConfig()
2658
+ * @uses calendarComponent::_createElement()
2659
+ * @uses calendarComponent::_createParams()
2660
+ * @return string
2661
+ */
2662
+ function createTzurl() {
2663
+ if( empty( $this->tzurl )) return FALSE;
2664
+ if( empty( $this->tzurl['value'] ))
2665
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZURL' ) : FALSE;
2666
+ $attributes = $this->_createParams( $this->tzurl['params'] );
2667
+ return $this->_createElement( 'TZURL', $attributes, $this->tzurl['value'] );
2668
+ }
2669
+ /**
2670
+ * set calendar component property tzurl
2671
+ *
2672
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2673
+ * @since 2.16.21 - 2013-06-23
2674
+ * @param string $value
2675
+ * @param array $params
2676
+ * @uses calendarComponent::$tzurl
2677
+ * @uses calendarComponent::getConfig()
2678
+ * @uses calendarComponent::$tzurl
2679
+ * @uses iCalUtilityFunctions::_setParams()
2680
+ * @return boll
2681
+ */
2682
+ function setTzurl( $value, $params=FALSE ) {
2683
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
2684
+ $this->tzurl = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
2685
+ return TRUE;
2686
+ }
2687
+ /*********************************************************************************/
2688
+ /**
2689
+ * Property Name: UID
2690
+ */
2691
+ /**
2692
+ * creates formatted output for calendar component property uid
2693
+ *
2694
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2695
+ * @since 2.19.1 - 2014-02-21
2696
+ * @uses calendarComponent::$uid
2697
+ * @uses calendarComponent::_makeuid();
2698
+ * @uses calendarComponent::_createParams()
2699
+ * @uses calendarComponent::_createElement()
2700
+ * @return string
2701
+ */
2702
+ function createUid() {
2703
+ if( empty( $this->uid ))
2704
+ $this->_makeuid();
2705
+ $attributes = $this->_createParams( $this->uid['params'] );
2706
+ return $this->_createElement( 'UID', $attributes, $this->uid['value'] );
2707
+ }
2708
+ /**
2709
+ * create an unique id for this calendar component object instance
2710
+ *
2711
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2712
+ * @since 2.2.7 - 2007-09-04
2713
+ * @uses calendarComponent::$uid
2714
+ * @uses calendarComponent::getConfig()
2715
+ * @return void
2716
+ */
2717
+ function _makeUid() {
2718
+ $date = date('Ymd\THisT');
2719
+ $unique = substr(microtime(), 2, 4);
2720
+ $base = 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPrRsStTuUvVxXuUvVwWzZ1234567890';
2721
+ $start = 0;
2722
+ $end = strlen( $base ) - 1;
2723
+ $length = 6;
2724
+ $str = null;
2725
+ for( $p = 0; $p < $length; $p++ )
2726
+ $unique .= $base{mt_rand( $start, $end )};
2727
+ $this->uid = array( 'params' => null );
2728
+ $this->uid['value'] = $date.'-'.$unique.'@'.$this->getConfig( 'unique_id' );
2729
+ }
2730
+ /**
2731
+ * set calendar component property uid
2732
+ *
2733
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2734
+ * @since 2.19.1 - 2014-02-21
2735
+ * @param string $value
2736
+ * @param array $params
2737
+ * @uses calendarComponent::$uid
2738
+ * @uses calendarComponent::_setParams()
2739
+ * @return bool
2740
+ */
2741
+ function setUid( $value, $params=FALSE ) {
2742
+ if( empty( $value ) && ( '0' != $value )) return FALSE; // no allowEmpty check here !!!!
2743
+ $this->uid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
2744
+ return TRUE;
2745
+ }
2746
+ /*********************************************************************************/
2747
+ /**
2748
+ * Property Name: URL
2749
+ */
2750
+ /**
2751
+ * creates formatted output for calendar component property url
2752
+ *
2753
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2754
+ * @since 2.4.8 - 2008-10-21
2755
+ * @uses calendarComponent::$url
2756
+ * @uses calendarComponent::getConfig()
2757
+ * @uses calendarComponent::_createElement()
2758
+ * @uses calendarComponent::_createParams()
2759
+ * @return string
2760
+ */
2761
+ function createUrl() {
2762
+ if( empty( $this->url )) return FALSE;
2763
+ if( empty( $this->url['value'] ))
2764
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'URL' ) : FALSE;
2765
+ $attributes = $this->_createParams( $this->url['params'] );
2766
+ return $this->_createElement( 'URL', $attributes, $this->url['value'] );
2767
+ }
2768
+ /**
2769
+ * set calendar component property url
2770
+ *
2771
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2772
+ * @since 2.16.21 - 2013-06-23
2773
+ * @param string $value
2774
+ * @param array $params
2775
+ * @uses calendarComponent::getConfig()
2776
+ * @uses calendarComponent::$url
2777
+ * @uses calendarComponent::_setParams()
2778
+ * @return bool
2779
+ */
2780
+ function setUrl( $value, $params=FALSE ) {
2781
+ if( !empty( $value )) {
2782
+ if( !filter_var( $value, FILTER_VALIDATE_URL ) && ( 'urn' != strtolower( substr( $value, 0, 3 ))))
2783
+ return FALSE;
2784
+ }
2785
+ elseif( $this->getConfig( 'allowEmpty' ))
2786
+ $value = '';
2787
+ else
2788
+ return FALSE;
2789
+ $this->url = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
2790
+ return TRUE;
2791
+ }
2792
+ /*********************************************************************************/
2793
+ /*********************************************************************************/
2794
+ /**
2795
+ * create element format parts
2796
+ *
2797
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2798
+ * @since 2.0.6 - 2006-06-20
2799
+ * @uses iCalBase::_createFormat()
2800
+ * @uses calendarComponent::$timezonetype
2801
+ * @return string
2802
+ */
2803
+ function _createFormat() {
2804
+ parent::_createFormat();
2805
+ $objectname = null;
2806
+ switch( $this->format ) {
2807
+ case 'xcal':
2808
+ return ( isset( $this->timezonetype )) ? strtolower( $this->timezonetype ) : strtolower( $this->objName );
2809
+ break;
2810
+ default:
2811
+ return ( isset( $this->timezonetype )) ? strtoupper( $this->timezonetype ) : strtoupper( $this->objName );
2812
+ break;
2813
+ }
2814
+ }
2815
+ /**
2816
+ * creates formatted output for calendar component property data value type recur
2817
+ *
2818
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2819
+ * @since 2.16.25 - 2013-06-30
2820
+ * @param array $recurlabel
2821
+ * @param array $recurdata
2822
+ * @uses calendarComponent::getConfig()
2823
+ * @uses calendarComponent::_createElement()
2824
+ * @uses calendarComponent::_createParams()
2825
+ * @uses iCalUtilityFunctions::_date2strdate()
2826
+ * @uses iCalUtilityFunctions::_recurBydaySort()
2827
+ * @return string
2828
+ */
2829
+ function _format_recur( $recurlabel, $recurdata ) {
2830
+ $output = null;
2831
+ foreach( $recurdata as $therule ) {
2832
+ if( empty( $therule['value'] )) {
2833
+ if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( $recurlabel );
2834
+ continue;
2835
+ }
2836
+ $attributes = ( isset( $therule['params'] )) ? $this->_createParams( $therule['params'] ) : null;
2837
+ $content1 = $content2 = null;
2838
+ foreach( $therule['value'] as $rulelabel => $rulevalue ) {
2839
+ switch( strtoupper( $rulelabel )) {
2840
+ case 'FREQ': {
2841
+ $content1 .= "FREQ=$rulevalue";
2842
+ break;
2843
+ }
2844
+ case 'UNTIL': {
2845
+ $parno = ( isset( $rulevalue['hour'] )) ? 7 : 3;
2846
+ $content2 .= ';UNTIL='.iCalUtilityFunctions::_date2strdate( $rulevalue, $parno );
2847
+ break;
2848
+ }
2849
+ case 'COUNT':
2850
+ case 'INTERVAL':
2851
+ case 'WKST': {
2852
+ $content2 .= ";$rulelabel=$rulevalue";
2853
+ break;
2854
+ }
2855
+ case 'BYSECOND':
2856
+ case 'BYMINUTE':
2857
+ case 'BYHOUR':
2858
+ case 'BYMONTHDAY':
2859
+ case 'BYYEARDAY':
2860
+ case 'BYWEEKNO':
2861
+ case 'BYMONTH':
2862
+ case 'BYSETPOS': {
2863
+ $content2 .= ";$rulelabel=";
2864
+ if( is_array( $rulevalue )) {
2865
+ foreach( $rulevalue as $vix => $valuePart ) {
2866
+ $content2 .= ( $vix ) ? ',' : null;
2867
+ $content2 .= $valuePart;
2868
+ }
2869
+ }
2870
+ else
2871
+ $content2 .= $rulevalue;
2872
+ break;
2873
+ }
2874
+ case 'BYDAY': {
2875
+ $byday = array( '' );
2876
+ $bx = 0;
2877
+ foreach( $rulevalue as $bix => $bydayPart ) {
2878
+ if( ! empty( $byday[$bx] ) && ! ctype_digit( substr( $byday[$bx], -1 ))) // new day
2879
+ $byday[++$bx] = '';
2880
+ if( ! is_array( $bydayPart )) // day without order number
2881
+ $byday[$bx] .= (string) $bydayPart;
2882
+ else { // day with order number
2883
+ foreach( $bydayPart as $bix2 => $bydayPart2 )
2884
+ $byday[$bx] .= (string) $bydayPart2;
2885
+ }
2886
+ } // end foreach( $rulevalue as $bix => $bydayPart )
2887
+ if( 1 < count( $byday ))
2888
+ usort( $byday, array( 'iCalUtilityFunctions', '_recurBydaySort' ));
2889
+ $content2 .= ';BYDAY='.implode( ',', $byday );
2890
+ break;
2891
+ }
2892
+ default: {
2893
+ $content2 .= ";$rulelabel=$rulevalue";
2894
+ break;
2895
+ }
2896
+ }
2897
+ }
2898
+ $output .= $this->_createElement( $recurlabel, $attributes, $content1.$content2 );
2899
+ }
2900
+ return $output;
2901
+ }
2902
+ /**
2903
+ * check if property not exists within component
2904
+ *
2905
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2906
+ * @since 2.5.1 - 2008-10-15
2907
+ * @param string $propName
2908
+ * @uses calendarComponent::$lastmodified
2909
+ * @uses calendarComponent::$percentcomplete
2910
+ * @uses calendarComponent::$recurrenceid
2911
+ * @uses calendarComponent::$relatedto
2912
+ * @uses calendarComponent::$requeststatus
2913
+ * @uses calendarComponent::getConfig()
2914
+ * @uses calendarComponent::{$propname}
2915
+ * @return bool
2916
+ */
2917
+ function _notExistProp( $propName ) {
2918
+ if( empty( $propName )) return FALSE; // when deleting x-prop, an empty propName may be used=allowed
2919
+ $propName = strtolower( $propName );
2920
+ if( 'last-modified' == $propName ) { if( !isset( $this->lastmodified )) return TRUE; }
2921
+ elseif( 'percent-complete' == $propName ) { if( !isset( $this->percentcomplete )) return TRUE; }
2922
+ elseif( 'recurrence-id' == $propName ) { if( !isset( $this->recurrenceid )) return TRUE; }
2923
+ elseif( 'related-to' == $propName ) { if( !isset( $this->relatedto )) return TRUE; }
2924
+ elseif( 'request-status' == $propName ) { if( !isset( $this->requeststatus )) return TRUE; }
2925
+ elseif(( 'x-' != substr($propName,0,2)) && !isset( $this->$propName )) return TRUE;
2926
+ return FALSE;
2927
+ }
2928
+ /*********************************************************************************/
2929
+ /*********************************************************************************/
2930
+ /**
2931
+ * get general component config variables or info about subcomponents
2932
+ *
2933
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2934
+ * @since 2.21.11 - 2015-03-21
2935
+ * @param mixed $config
2936
+ * @uses calendarComponent::getConfig()
2937
+ * @uses calendarComponent::$allowEmpty
2938
+ * @uses calendarComponent::$compix
2939
+ * @uses calendarComponent::$components
2940
+ * @uses calendarComponent::$objName
2941
+ * @uses calendarComponent::getProperty()
2942
+ * @uses calendarComponent::$format
2943
+ * @uses calendarComponent::$language
2944
+ * @uses calendarComponent::$nl
2945
+ * @uses iCalUtilityFunctions::$miscComps
2946
+ * @uses calendarComponent::$uid
2947
+ * @uses calendarComponent::_makeuid()
2948
+ * @uses calendarComponent::dtstamp
2949
+ * @uses calendarComponent::_makeDtstamp()
2950
+ * @uses calendarComponent::$summary
2951
+ * @uses calendarComponent::$description
2952
+ * @uses calendarComponent::$dtstart
2953
+ * @uses calendarComponent::$dtend
2954
+ * @uses calendarComponent::$due
2955
+ * @uses calendarComponent::$duration
2956
+ * @uses calendarComponent::$rrule
2957
+ * @uses calendarComponent::$rdate
2958
+ * @uses calendarComponent::$exdate
2959
+ * @uses calendarComponent::$exrule
2960
+ * @uses calendarComponent::$action
2961
+ * @uses calendarComponent::$attach
2962
+ * @uses calendarComponent::$attendee
2963
+ * @uses calendarComponent::$categories
2964
+ * @uses calendarComponent::$class
2965
+ * @uses calendarComponent::$comment
2966
+ * @uses calendarComponent::$completed
2967
+ * @uses calendarComponent::$contact
2968
+ * @uses calendarComponent::$created
2969
+ * @uses calendarComponent::$freebusy
2970
+ * @uses calendarComponent::$geo
2971
+ * @uses calendarComponent::$lastmodified
2972
+ * @uses calendarComponent::$location
2973
+ * @uses calendarComponent::$organizer
2974
+ * @uses calendarComponent::$percentcomplete
2975
+ * @uses calendarComponent::$priority
2976
+ * @uses calendarComponent::$recurrenceid
2977
+ * @uses calendarComponent::$relatedto
2978
+ * @uses calendarComponent::$repeat
2979
+ * @uses calendarComponent::$requeststatus
2980
+ * @uses calendarComponent::$resources
2981
+ * @uses calendarComponent::$sequence
2982
+ * @uses calendarComponent::$sequence
2983
+ * @uses calendarComponent::$status
2984
+ * @uses calendarComponent::$transp
2985
+ * @uses calendarComponent::$trigger
2986
+ * @uses calendarComponent::$tzid
2987
+ * @uses calendarComponent::$tzname
2988
+ * @uses calendarComponent::$tzoffsetfrom
2989
+ * @uses calendarComponent::$tzoffsetto
2990
+ * @uses calendarComponent::$tzurl
2991
+ * @uses calendarComponent::$url
2992
+ * @uses calendarComponent::$xprop
2993
+ * @uses calendarComponent::$dtzid
2994
+ * @uses calendarComponent::$unique_id
2995
+ * @return value
2996
+ */
2997
+ function getConfig( $config = FALSE) {
2998
+ if( !$config ) {
2999
+ $return = array();
3000
+ $return['ALLOWEMPTY'] = $this->getConfig( 'ALLOWEMPTY' );
3001
+ $return['FORMAT'] = $this->getConfig( 'FORMAT' );
3002
+ if( FALSE !== ( $lang = $this->getConfig( 'LANGUAGE' )))
3003
+ $return['LANGUAGE'] = $lang;
3004
+ $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' );
3005
+ $return['TZTD'] = $this->getConfig( 'TZID' );
3006
+ $return['UNIQUE_ID'] = $this->getConfig( 'UNIQUE_ID' );
3007
+ return $return;
3008
+ }
3009
+ switch( strtoupper( $config )) {
3010
+ case 'ALLOWEMPTY':
3011
+ return $this->allowEmpty;
3012
+ break;
3013
+ case 'COMPSINFO':
3014
+ unset( $this->compix );
3015
+ $info = array();
3016
+ if( isset( $this->components )) {
3017
+ foreach( $this->components as $cix => $component ) {
3018
+ if( empty( $component )) continue;
3019
+ $info[$cix]['ordno'] = $cix + 1;
3020
+ $info[$cix]['type'] = $component->objName;
3021
+ $info[$cix]['uid'] = $component->getProperty( 'uid' );
3022
+ $info[$cix]['props'] = $component->getConfig( 'propinfo' );
3023
+ $info[$cix]['sub'] = $component->getConfig( 'compsinfo' );
3024
+ }
3025
+ }
3026
+ return $info;
3027
+ break;
3028
+ case 'FORMAT':
3029
+ return $this->format;
3030
+ break;
3031
+ case 'LANGUAGE':
3032
+ // get language for calendar component as defined in [RFC 1766]
3033
+ return $this->language;
3034
+ break;
3035
+ case 'NL':
3036
+ case 'NEWLINECHAR':
3037
+ return $this->nl;
3038
+ break;
3039
+ case 'PROPINFO':
3040
+ $output = array();
3041
+ if( ! in_array( $this->objName, iCalUtilityFunctions::$miscComps )) {
3042
+ if( empty( $this->uid )) $this->_makeuid();
3043
+ $output['UID'] = 1;
3044
+ if( empty( $this->dtstamp )) $this->_makeDtstamp();
3045
+ $output['DTSTAMP'] = 1;
3046
+ }
3047
+ if( !empty( $this->summary )) $output['SUMMARY'] = 1;
3048
+ if( !empty( $this->description )) $output['DESCRIPTION'] = count( $this->description );
3049
+ if( !empty( $this->dtstart )) $output['DTSTART'] = 1;
3050
+ if( !empty( $this->dtend )) $output['DTEND'] = 1;
3051
+ if( !empty( $this->due )) $output['DUE'] = 1;
3052
+ if( !empty( $this->duration )) $output['DURATION'] = 1;
3053
+ if( !empty( $this->rrule )) $output['RRULE'] = count( $this->rrule );
3054
+ if( !empty( $this->rdate )) $output['RDATE'] = count( $this->rdate );
3055
+ if( !empty( $this->exdate )) $output['EXDATE'] = count( $this->exdate );
3056
+ if( !empty( $this->exrule )) $output['EXRULE'] = count( $this->exrule );
3057
+ if( !empty( $this->action )) $output['ACTION'] = 1;
3058
+ if( !empty( $this->attach )) $output['ATTACH'] = count( $this->attach );
3059
+ if( !empty( $this->attendee )) $output['ATTENDEE'] = count( $this->attendee );
3060
+ if( !empty( $this->categories )) $output['CATEGORIES'] = count( $this->categories );
3061
+ if( !empty( $this->class )) $output['CLASS'] = 1;
3062
+ if( !empty( $this->comment )) $output['COMMENT'] = count( $this->comment );
3063
+ if( !empty( $this->completed )) $output['COMPLETED'] = 1;
3064
+ if( !empty( $this->contact )) $output['CONTACT'] = count( $this->contact );
3065
+ if( !empty( $this->created )) $output['CREATED'] = 1;
3066
+ if( !empty( $this->freebusy )) $output['FREEBUSY'] = count( $this->freebusy );
3067
+ if( !empty( $this->geo )) $output['GEO'] = 1;
3068
+ if( !empty( $this->lastmodified )) $output['LAST-MODIFIED'] = 1;
3069
+ if( !empty( $this->location )) $output['LOCATION'] = 1;
3070
+ if( !empty( $this->organizer )) $output['ORGANIZER'] = 1;
3071
+ if( !empty( $this->percentcomplete )) $output['PERCENT-COMPLETE'] = 1;
3072
+ if( !empty( $this->priority )) $output['PRIORITY'] = 1;
3073
+ if( !empty( $this->recurrenceid )) $output['RECURRENCE-ID'] = 1;
3074
+ if( !empty( $this->relatedto )) $output['RELATED-TO'] = count( $this->relatedto );
3075
+ if( !empty( $this->repeat )) $output['REPEAT'] = 1;
3076
+ if( !empty( $this->requeststatus )) $output['REQUEST-STATUS'] = count( $this->requeststatus );
3077
+ if( !empty( $this->resources )) $output['RESOURCES'] = count( $this->resources );
3078
+ if( !empty( $this->sequence )) $output['SEQUENCE'] = 1;
3079
+ if( !empty( $this->sequence )) $output['SEQUENCE'] = 1;
3080
+ if( !empty( $this->status )) $output['STATUS'] = 1;
3081
+ if( !empty( $this->transp )) $output['TRANSP'] = 1;
3082
+ if( !empty( $this->trigger )) $output['TRIGGER'] = 1;
3083
+ if( !empty( $this->tzid )) $output['TZID'] = 1;
3084
+ if( !empty( $this->tzname )) $output['TZNAME'] = count( $this->tzname );
3085
+ if( !empty( $this->tzoffsetfrom )) $output['TZOFFSETFROM'] = 1;
3086
+ if( !empty( $this->tzoffsetto )) $output['TZOFFSETTO'] = 1;
3087
+ if( !empty( $this->tzurl )) $output['TZURL'] = 1;
3088
+ if( !empty( $this->url )) $output['URL'] = 1;
3089
+ if( !empty( $this->xprop )) $output['X-PROP'] = count( $this->xprop );
3090
+ return $output;
3091
+ break;
3092
+ case 'SETPROPERTYNAMES':
3093
+ return array_keys( $this->getConfig( 'propinfo' ));
3094
+ break;
3095
+ case 'TZID':
3096
+ return $this->dtzid;
3097
+ break;
3098
+ case 'UNIQUE_ID':
3099
+ if( empty( $this->unique_id ))
3100
+ $this->unique_id = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost';
3101
+ return $this->unique_id;
3102
+ break;
3103
+ }
3104
+ }
3105
+ /**
3106
+ * general component config setting
3107
+ *
3108
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3109
+ * @since 2.10.18 - 2013-09-06
3110
+ * @param mixed $config
3111
+ * @param string $value
3112
+ * @param bool $softUpdate
3113
+ * @uses calendarComponent::getConfig()
3114
+ * @uses calendarComponent::$allowEmpty
3115
+ * @uses calendarComponent::$format
3116
+ * @uses calendarComponent::$language
3117
+ * @uses calendarComponent::$nl
3118
+ * @uses calendarComponent::$dtzid
3119
+ * @uses calendarComponent::$unique_id
3120
+ * @uses calendarComponent::$components
3121
+ * @uses calendarComponent::copy()
3122
+ * @return void
3123
+ */
3124
+ function setConfig( $config, $value = FALSE, $softUpdate = FALSE ) {
3125
+ if( is_array( $config )) {
3126
+ $config = array_change_key_case( $config, CASE_UPPER );
3127
+ if( isset( $config['NEWLINECHAR'] ) || isset( $config['NL'] )) {
3128
+ $k = ( isset( $config['NEWLINECHAR'] )) ? 'NEWLINECHAR' : 'NL';
3129
+ if( FALSE === $this->setConfig( 'NL', $config[$k] ))
3130
+ return FALSE;
3131
+ unset( $config[$k] );
3132
+ }
3133
+ foreach( $config as $cKey => $cValue ) {
3134
+ if( FALSE === $this->setConfig( $cKey, $cValue, $softUpdate ))
3135
+ return FALSE;
3136
+ }
3137
+ return TRUE;
3138
+ }
3139
+ else
3140
+ $config = strtoupper( $config );
3141
+ $res = FALSE;
3142
+ switch( $config ) {
3143
+ case 'ALLOWEMPTY':
3144
+ $this->allowEmpty = $value;
3145
+ $subcfg = array( 'ALLOWEMPTY' => $value );
3146
+ $res = TRUE;
3147
+ break;
3148
+ case 'FORMAT':
3149
+ $value = trim( strtolower( $value ));
3150
+ $this->format = $value;
3151
+ $this->_createFormat();
3152
+ $subcfg = array( 'FORMAT' => $value );
3153
+ $res = TRUE;
3154
+ break;
3155
+ case 'LANGUAGE':
3156
+ // set language for calendar component as defined in [RFC 1766]
3157
+ $value = trim( $value );
3158
+ if( empty( $this->language ) || !$softUpdate )
3159
+ $this->language = $value;
3160
+ $subcfg = array( 'LANGUAGE' => $value );
3161
+ $res = TRUE;
3162
+ break;
3163
+ case 'NL':
3164
+ case 'NEWLINECHAR':
3165
+ $this->nl = $value;
3166
+ $this->_createFormat();
3167
+ $subcfg = array( 'NL' => $value );
3168
+ $res = TRUE;
3169
+ break;
3170
+ case 'TZID':
3171
+ $this->dtzid = $value;
3172
+ $subcfg = array( 'TZID' => $value );
3173
+ $res = TRUE;
3174
+ break;
3175
+ case 'UNIQUE_ID':
3176
+ $value = trim( $value );
3177
+ $this->unique_id = $value;
3178
+ $subcfg = array( 'UNIQUE_ID' => $value );
3179
+ $res = TRUE;
3180
+ break;
3181
+ default: // any unvalid config key.. .
3182
+ return TRUE;
3183
+ }
3184
+ if( !$res ) return FALSE;
3185
+ if( isset( $subcfg ) && !empty( $this->components )) {
3186
+ foreach( $subcfg as $cfgkey => $cfgvalue ) {
3187
+ foreach( $this->components as $cix => $component ) {
3188
+ $res = $component->setConfig( $cfgkey, $cfgvalue, $softUpdate );
3189
+ if( !$res )
3190
+ break 2;
3191
+ $this->components[$cix] = $component->copy(); // PHP4 compliant
3192
+ }
3193
+ }
3194
+ }
3195
+ return $res;
3196
+ }
3197
+ /*********************************************************************************/
3198
+ /**
3199
+ * delete component property value
3200
+ *
3201
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3202
+ * @since 2.8.8 - 2011-03-15
3203
+ * @param mixed $propName bool FALSE => X-property
3204
+ * @param int $propix specific property in case of multiply occurences
3205
+ * @uses calendarComponent::_notExistProp()
3206
+ * @uses iCalUtilityFunctions::$mProps2
3207
+ * @uses calendarComponent::$propdelix
3208
+ * @uses calendarComponent::$action
3209
+ * @uses calendarComponent::deletePropertyM()
3210
+ * @uses calendarComponent::$attendee
3211
+ * @uses calendarComponent::$categories
3212
+ * @uses calendarComponent::$class
3213
+ * @uses calendarComponent::$comment
3214
+ * @uses calendarComponent::$completed
3215
+ * @uses calendarComponent::$contact
3216
+ * @uses calendarComponent::$created
3217
+ * @uses calendarComponent::$description
3218
+ * @uses calendarComponent::$dtend
3219
+ * @uses iCalUtilityFunctions::$miscComps
3220
+ * @uses calendarComponent::$objName
3221
+ * @uses calendarComponent::$dtstamp
3222
+ * @uses calendarComponent::$dtstart
3223
+ * @uses calendarComponent::$due
3224
+ * @uses calendarComponent::$duration
3225
+ * @uses calendarComponent::$exdate
3226
+ * @uses calendarComponent::$exrule
3227
+ * @uses calendarComponent::$freebusy
3228
+ * @uses calendarComponent::$geo
3229
+ * @uses calendarComponent::$lastmodified
3230
+ * @uses calendarComponent::$location
3231
+ * @uses calendarComponent::$organizer
3232
+ * @uses calendarComponent::$percentcomplete
3233
+ * @uses calendarComponent::$priority
3234
+ * @uses calendarComponent::$rdate
3235
+ * @uses calendarComponent::$recurrenceid
3236
+ * @uses calendarComponent::$relatedto
3237
+ * @uses calendarComponent::$repeat
3238
+ * @uses calendarComponent::$requeststatus
3239
+ * @uses calendarComponent::$resources
3240
+ * @uses calendarComponent::$rrule
3241
+ * @uses calendarComponent::$sequence
3242
+ * @uses calendarComponent::$status
3243
+ * @uses calendarComponent::$summary
3244
+ * @uses calendarComponent::$transp
3245
+ * @uses calendarComponent::$trigger
3246
+ * @uses calendarComponent::$tzid
3247
+ * @uses calendarComponent::$tzname
3248
+ * @uses calendarComponent::$tzoffsetfrom
3249
+ * @uses calendarComponent::$tzoffsetto
3250
+ * @uses calendarComponent::$tzurl
3251
+ * @uses calendarComponent::$uid
3252
+ * @uses calendarComponent::$url
3253
+ * @uses calendarComponent::$xprop
3254
+ * @return bool, if successfull delete TRUE
3255
+ */
3256
+ function deleteProperty( $propName=FALSE, $propix=FALSE ) {
3257
+ if( $this->_notExistProp( $propName )) return FALSE;
3258
+ $propName = strtoupper( $propName );
3259
+ if( in_array( $propName, iCalUtilityFunctions::$mProps2 )) {
3260
+ if( !$propix )
3261
+ $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1;
3262
+ $this->propdelix[$propName] = --$propix;
3263
+ }
3264
+ $return = FALSE;
3265
+ switch( $propName ) {
3266
+ case 'ACTION':
3267
+ if( !empty( $this->action )) {
3268
+ $this->action = '';
3269
+ $return = TRUE;
3270
+ }
3271
+ break;
3272
+ case 'ATTACH':
3273
+ return $this->deletePropertyM( $this->attach, $this->propdelix[$propName] );
3274
+ break;
3275
+ case 'ATTENDEE':
3276
+ return $this->deletePropertyM( $this->attendee, $this->propdelix[$propName] );
3277
+ break;
3278
+ case 'CATEGORIES':
3279
+ return $this->deletePropertyM( $this->categories, $this->propdelix[$propName] );
3280
+ break;
3281
+ case 'CLASS':
3282
+ if( !empty( $this->class )) {
3283
+ $this->class = '';
3284
+ $return = TRUE;
3285
+ }
3286
+ break;
3287
+ case 'COMMENT':
3288
+ return $this->deletePropertyM( $this->comment, $this->propdelix[$propName] );
3289
+ break;
3290
+ case 'COMPLETED':
3291
+ if( !empty( $this->completed )) {
3292
+ $this->completed = '';
3293
+ $return = TRUE;
3294
+ }
3295
+ break;
3296
+ case 'CONTACT':
3297
+ return $this->deletePropertyM( $this->contact, $this->propdelix[$propName] );
3298
+ break;
3299
+ case 'CREATED':
3300
+ if( !empty( $this->created )) {
3301
+ $this->created = '';
3302
+ $return = TRUE;
3303
+ }
3304
+ break;
3305
+ case 'DESCRIPTION':
3306
+ return $this->deletePropertyM( $this->description, $this->propdelix[$propName] );
3307
+ break;
3308
+ case 'DTEND':
3309
+ if( !empty( $this->dtend )) {
3310
+ $this->dtend = '';
3311
+ $return = TRUE;
3312
+ }
3313
+ break;
3314
+ case 'DTSTAMP':
3315
+ if( in_array( $this->objName, iCalUtilityFunctions::$miscComps ))
3316
+ return FALSE;
3317
+ if( !empty( $this->dtstamp )) {
3318
+ $this->dtstamp = '';
3319
+ $return = TRUE;
3320
+ }
3321
+ break;
3322
+ case 'DTSTART':
3323
+ if( !empty( $this->dtstart )) {
3324
+ $this->dtstart = '';
3325
+ $return = TRUE;
3326
+ }
3327
+ break;
3328
+ case 'DUE':
3329
+ if( !empty( $this->due )) {
3330
+ $this->due = '';
3331
+ $return = TRUE;
3332
+ }
3333
+ break;
3334
+ case 'DURATION':
3335
+ if( !empty( $this->duration )) {
3336
+ $this->duration = '';
3337
+ $return = TRUE;
3338
+ }
3339
+ break;
3340
+ case 'EXDATE':
3341
+ return $this->deletePropertyM( $this->exdate, $this->propdelix[$propName] );
3342
+ break;
3343
+ case 'EXRULE':
3344
+ return $this->deletePropertyM( $this->exrule, $this->propdelix[$propName] );
3345
+ break;
3346
+ case 'FREEBUSY':
3347
+ return $this->deletePropertyM( $this->freebusy, $this->propdelix[$propName] );
3348
+ break;
3349
+ case 'GEO':
3350
+ if( !empty( $this->geo )) {
3351
+ $this->geo = '';
3352
+ $return = TRUE;
3353
+ }
3354
+ break;
3355
+ case 'LAST-MODIFIED':
3356
+ if( !empty( $this->lastmodified )) {
3357
+ $this->lastmodified = '';
3358
+ $return = TRUE;
3359
+ }
3360
+ break;
3361
+ case 'LOCATION':
3362
+ if( !empty( $this->location )) {
3363
+ $this->location = '';
3364
+ $return = TRUE;
3365
+ }
3366
+ break;
3367
+ case 'ORGANIZER':
3368
+ if( !empty( $this->organizer )) {
3369
+ $this->organizer = '';
3370
+ $return = TRUE;
3371
+ }
3372
+ break;
3373
+ case 'PERCENT-COMPLETE':
3374
+ if( !empty( $this->percentcomplete )) {
3375
+ $this->percentcomplete = '';
3376
+ $return = TRUE;
3377
+ }
3378
+ break;
3379
+ case 'PRIORITY':
3380
+ if( !empty( $this->priority )) {
3381
+ $this->priority = '';
3382
+ $return = TRUE;
3383
+ }
3384
+ break;
3385
+ case 'RDATE':
3386
+ return $this->deletePropertyM( $this->rdate, $this->propdelix[$propName] );
3387
+ break;
3388
+ case 'RECURRENCE-ID':
3389
+ if( !empty( $this->recurrenceid )) {
3390
+ $this->recurrenceid = '';
3391
+ $return = TRUE;
3392
+ }
3393
+ break;
3394
+ case 'RELATED-TO':
3395
+ return $this->deletePropertyM( $this->relatedto, $this->propdelix[$propName] );
3396
+ break;
3397
+ case 'REPEAT':
3398
+ if( !empty( $this->repeat )) {
3399
+ $this->repeat = '';
3400
+ $return = TRUE;
3401
+ }
3402
+ break;
3403
+ case 'REQUEST-STATUS':
3404
+ return $this->deletePropertyM( $this->requeststatus, $this->propdelix[$propName] );
3405
+ break;
3406
+ case 'RESOURCES':
3407
+ return $this->deletePropertyM( $this->resources, $this->propdelix[$propName] );
3408
+ break;
3409
+ case 'RRULE':
3410
+ return $this->deletePropertyM( $this->rrule, $this->propdelix[$propName] );
3411
+ break;
3412
+ case 'SEQUENCE':
3413
+ if( !empty( $this->sequence )) {
3414
+ $this->sequence = '';
3415
+ $return = TRUE;
3416
+ }
3417
+ break;
3418
+ case 'STATUS':
3419
+ if( !empty( $this->status )) {
3420
+ $this->status = '';
3421
+ $return = TRUE;
3422
+ }
3423
+ break;
3424
+ case 'SUMMARY':
3425
+ if( !empty( $this->summary )) {
3426
+ $this->summary = '';
3427
+ $return = TRUE;
3428
+ }
3429
+ break;
3430
+ case 'TRANSP':
3431
+ if( !empty( $this->transp )) {
3432
+ $this->transp = '';
3433
+ $return = TRUE;
3434
+ }
3435
+ break;
3436
+ case 'TRIGGER':
3437
+ if( !empty( $this->trigger )) {
3438
+ $this->trigger = '';
3439
+ $return = TRUE;
3440
+ }
3441
+ break;
3442
+ case 'TZID':
3443
+ if( !empty( $this->tzid )) {
3444
+ $this->tzid = '';
3445
+ $return = TRUE;
3446
+ }
3447
+ break;
3448
+ case 'TZNAME':
3449
+ return $this->deletePropertyM( $this->tzname, $this->propdelix[$propName] );
3450
+ break;
3451
+ case 'TZOFFSETFROM':
3452
+ if( !empty( $this->tzoffsetfrom )) {
3453
+ $this->tzoffsetfrom = '';
3454
+ $return = TRUE;
3455
+ }
3456
+ break;
3457
+ case 'TZOFFSETTO':
3458
+ if( !empty( $this->tzoffsetto )) {
3459
+ $this->tzoffsetto = '';
3460
+ $return = TRUE;
3461
+ }
3462
+ break;
3463
+ case 'TZURL':
3464
+ if( !empty( $this->tzurl )) {
3465
+ $this->tzurl = '';
3466
+ $return = TRUE;
3467
+ }
3468
+ break;
3469
+ case 'UID':
3470
+ if( in_array( $this->objName, iCalUtilityFunctions::$miscComps ))
3471
+ return FALSE;
3472
+ if( ! empty( $this->uid )) {
3473
+ $this->uid = '';
3474
+ $return = TRUE;
3475
+ }
3476
+ break;
3477
+ case 'URL':
3478
+ if( !empty( $this->url )) {
3479
+ $this->url = '';
3480
+ $return = TRUE;
3481
+ }
3482
+ break;
3483
+ default:
3484
+ $reduced = '';
3485
+ if( $propName != 'X-PROP' ) {
3486
+ if( !isset( $this->xprop[$propName] )) return FALSE;
3487
+ foreach( $this->xprop as $k => $a ) {
3488
+ if(( $k != $propName ) && !empty( $a ))
3489
+ $reduced[$k] = $a;
3490
+ }
3491
+ }
3492
+ else {
3493
+ if( count( $this->xprop ) <= $propix ) { unset( $this->propdelix[$propName] ); return FALSE; }
3494
+ $xpropno = 0;
3495
+ foreach( $this->xprop as $xpropkey => $xpropvalue ) {
3496
+ if( $propix != $xpropno )
3497
+ $reduced[$xpropkey] = $xpropvalue;
3498
+ $xpropno++;
3499
+ }
3500
+ }
3501
+ $this->xprop = $reduced;
3502
+ if( empty( $this->xprop )) {
3503
+ unset( $this->propdelix[$propName] );
3504
+ return FALSE;
3505
+ }
3506
+ return TRUE;
3507
+ }
3508
+ return $return;
3509
+ }
3510
+ /*********************************************************************************/
3511
+ /**
3512
+ * delete component property value, fixing components with multiple occurencies
3513
+ *
3514
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3515
+ * @since 2.8.8 - 2011-03-15
3516
+ * @param array $multiprop component (multi-)property
3517
+ * @param int $propix removal counter
3518
+ * @return bool TRUE
3519
+ */
3520
+ function deletePropertyM( & $multiprop, & $propix ) {
3521
+ if( isset( $multiprop[$propix] ))
3522
+ unset( $multiprop[$propix] );
3523
+ if( empty( $multiprop )) {
3524
+ $multiprop = '';
3525
+ unset( $propix );
3526
+ return FALSE;
3527
+ }
3528
+ else
3529
+ return TRUE;
3530
+ }
3531
+ /**
3532
+ * get component property value/params
3533
+ *
3534
+ * if property has multiply values, consequtive function calls are needed
3535
+ *
3536
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3537
+ * @since 2.21.13 - 2015-03-29
3538
+ * @param string $propName
3539
+ * @param int $propix specific property in case of multiply occurences
3540
+ * @param bool $inclParam
3541
+ * @param bool $specform
3542
+ * @uses calendarComponent::getProperty()
3543
+ * @uses iCalUtilityFunctions::_geo2str2()
3544
+ * @uses iCalUtilityFunctions::$geoLatFmt
3545
+ * @uses iCalUtilityFunctions::$geoLongFmt
3546
+ * @uses calendarComponent::_notExistProp()
3547
+ * @uses iCalUtilityFunctions::$mProps2
3548
+ * @uses calendarComponent::$propix
3549
+ * @uses calendarComponent::$action
3550
+ * @uses calendarComponent::$attendee
3551
+ * @uses calendarComponent::$categories
3552
+ * @uses calendarComponent::$class
3553
+ * @uses calendarComponent::$comment
3554
+ * @uses calendarComponent::$completed
3555
+ * @uses calendarComponent::$contact
3556
+ * @uses calendarComponent::$created
3557
+ * @uses calendarComponent::$description
3558
+ * @uses calendarComponent::$dtend
3559
+ * @uses iCalUtilityFunctions::$miscComps
3560
+ * @uses calendarComponent::$dtstamp
3561
+ * @uses calendarComponent::_makeDtstamp()
3562
+ * @uses calendarComponent::$dtstart
3563
+ * @uses calendarComponent::$due
3564
+ * @uses calendarComponent::$duration
3565
+ * @uses iCalUtilityFunctions::_duration2date()
3566
+ * @uses calendarComponent::$exdate
3567
+ * @uses calendarComponent::$exrule
3568
+ * @uses calendarComponent::$freebusy
3569
+ * @uses calendarComponent::$geo
3570
+ * @uses calendarComponent::$lastmodified
3571
+ * @uses calendarComponent::$location
3572
+ * @uses calendarComponent::$organizer
3573
+ * @uses calendarComponent::$percentcomplete
3574
+ * @uses calendarComponent::$priority
3575
+ * @uses calendarComponent::$rdate
3576
+ * @uses calendarComponent::$recurrenceid
3577
+ * @uses calendarComponent::$relatedto
3578
+ * @uses calendarComponent::$repeat
3579
+ * @uses calendarComponent::$requeststatus
3580
+ * @uses calendarComponent::$resources
3581
+ * @uses calendarComponent::$rrule
3582
+ * @uses calendarComponent::$sequence
3583
+ * @uses calendarComponent::$status
3584
+ * @uses calendarComponent::$summary
3585
+ * @uses calendarComponent::$transp
3586
+ * @uses calendarComponent::$trigger
3587
+ * @uses calendarComponent::$tzid
3588
+ * @uses calendarComponent::$tzname
3589
+ * @uses calendarComponent::$tzoffsetfrom
3590
+ * @uses calendarComponent::$tzoffsetto
3591
+ * @uses calendarComponent::$tzurl
3592
+ * @uses calendarComponent::$uid
3593
+ * @uses calendarComponent::$objName
3594
+ * @uses calendarComponent::_makeuid()
3595
+ * @uses calendarComponent::$url
3596
+ * @uses calendarComponent::$xprop
3597
+ * @return mixed
3598
+ */
3599
+ function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE, $specform=FALSE ) {
3600
+ if( 'GEOLOCATION' == strtoupper( $propName )) {
3601
+ $content = ( FALSE === ( $loc = $this->getProperty( 'LOCATION' ))) ? '' : $loc.' ';
3602
+ if( FALSE === ( $geo = $this->getProperty( 'GEO' )))
3603
+ return FALSE;
3604
+ return $content.
3605
+ iCalUtilityFunctions::_geo2str2( $geo['latitude'], iCalUtilityFunctions::$geoLatFmt ).
3606
+ iCalUtilityFunctions::_geo2str2( $geo['longitude'], iCalUtilityFunctions::$geoLongFmt ).'/';
3607
+ }
3608
+ if( $this->_notExistProp( $propName )) return FALSE;
3609
+ $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
3610
+ if( in_array( $propName, iCalUtilityFunctions::$mProps2 )) {
3611
+ if( empty( $propix ))
3612
+ $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1;
3613
+ $this->propix[$propName] = --$propix;
3614
+ }
3615
+ switch( $propName ) {
3616
+ case 'ACTION':
3617
+ if( isset( $this->action['value'] )) return ( $inclParam ) ? $this->action : $this->action['value'];
3618
+ break;
3619
+ case 'ATTACH':
3620
+ $ak = ( is_array( $this->attach )) ? array_keys( $this->attach ) : array();
3621
+ while( is_array( $this->attach ) && !isset( $this->attach[$propix] ) && ( 0 < count( $this->attach )) && ( $propix < end( $ak )))
3622
+ $propix++;
3623
+ $this->propix[$propName] = $propix;
3624
+ if( !isset( $this->attach[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
3625
+ return ( $inclParam ) ? $this->attach[$propix] : $this->attach[$propix]['value'];
3626
+ break;
3627
+ case 'ATTENDEE':
3628
+ $ak = ( is_array( $this->attendee )) ? array_keys( $this->attendee ) : array();
3629
+ while( is_array( $this->attendee ) && !isset( $this->attendee[$propix] ) && ( 0 < count( $this->attendee )) && ( $propix < end( $ak )))
3630
+ $propix++;
3631
+ $this->propix[$propName] = $propix;
3632
+ if( !isset( $this->attendee[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
3633
+ return ( $inclParam ) ? $this->attendee[$propix] : $this->attendee[$propix]['value'];
3634
+ break;
3635
+ case 'CATEGORIES':
3636
+ $ak = ( is_array( $this->categories )) ? array_keys( $this->categories ) : array();
3637
+ while( is_array( $this->categories ) && !isset( $this->categories[$propix] ) && ( 0 < count( $this->categories )) && ( $propix < end( $ak )))
3638
+ $propix++;
3639
+ $this->propix[$propName] = $propix;
3640
+ if( !isset( $this->categories[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
3641
+ return ( $inclParam ) ? $this->categories[$propix] : $this->categories[$propix]['value'];
3642
+ break;
3643
+ case 'CLASS':
3644
+ if( isset( $this->class['value'] )) return ( $inclParam ) ? $this->class : $this->class['value'];
3645
+ break;
3646
+ case 'COMMENT':
3647
+ $ak = ( is_array( $this->comment )) ? array_keys( $this->comment ) : array();
3648
+ while( is_array( $this->comment ) && !isset( $this->comment[$propix] ) && ( 0 < count( $this->comment )) && ( $propix < end( $ak )))
3649
+ $propix++;
3650
+ $this->propix[$propName] = $propix;
3651
+ if( !isset( $this->comment[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
3652
+ return ( $inclParam ) ? $this->comment[$propix] : $this->comment[$propix]['value'];
3653
+ break;
3654
+ case 'COMPLETED':
3655
+ if( isset( $this->completed['value'] )) return ( $inclParam ) ? $this->completed : $this->completed['value'];
3656
+ break;
3657
+ case 'CONTACT':
3658
+ $ak = ( is_array( $this->contact )) ? array_keys( $this->contact ) : array();
3659
+ while( is_array( $this->contact ) && !isset( $this->contact[$propix] ) && ( 0 < count( $this->contact )) && ( $propix < end( $ak )))
3660
+ $propix++;
3661
+ $this->propix[$propName] = $propix;
3662
+ if( !isset( $this->contact[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
3663
+ return ( $inclParam ) ? $this->contact[$propix] : $this->contact[$propix]['value'];
3664
+ break;
3665
+ case 'CREATED':
3666
+ if( isset( $this->created['value'] )) return ( $inclParam ) ? $this->created : $this->created['value'];
3667
+ break;
3668
+ case 'DESCRIPTION':
3669
+ $ak = ( is_array( $this->description )) ? array_keys( $this->description ) : array();
3670
+ while( is_array( $this->description ) && !isset( $this->description[$propix] ) && ( 0 < count( $this->description )) && ( $propix < end( $ak )))
3671
+ $propix++;
3672
+ $this->propix[$propName] = $propix;
3673
+ if( !isset( $this->description[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
3674
+ return ( $inclParam ) ? $this->description[$propix] : $this->description[$propix]['value'];
3675
+ break;
3676
+ case 'DTEND':
3677
+ if( isset( $this->dtend['value'] )) return ( $inclParam ) ? $this->dtend : $this->dtend['value'];
3678
+ break;
3679
+ case 'DTSTAMP':
3680
+ if( in_array( $this->objName, iCalUtilityFunctions::$miscComps ))
3681
+ return;
3682
+ if( !isset( $this->dtstamp['value'] ))
3683
+ $this->_makeDtstamp();
3684
+ return ( $inclParam ) ? $this->dtstamp : $this->dtstamp['value'];
3685
+ break;
3686
+ case 'DTSTART':
3687
+ if( isset( $this->dtstart['value'] )) return ( $inclParam ) ? $this->dtstart : $this->dtstart['value'];
3688
+ break;
3689
+ case 'DUE':
3690
+ if( isset( $this->due['value'] )) return ( $inclParam ) ? $this->due : $this->due['value'];
3691
+ break;
3692
+ case 'DURATION':
3693
+ if( ! isset( $this->duration['value'] )) return FALSE;
3694
+ $value = ( $specform && isset( $this->dtstart['value'] ) && isset( $this->duration['value'] )) ? iCalUtilityFunctions::_duration2date( $this->dtstart['value'], $this->duration['value'] ) : $this->duration['value'];
3695
+ $params = ( $specform && $inclParam && isset( $this->dtstart['params']['TZID'] )) ? array_merge((array) $this->duration['params'], $this->dtstart['params'] ) : $this->duration['params'];
3696
+ return ( $inclParam ) ? array( 'value' => $value, 'params' => $params ) : $value;
3697
+ break;
3698
+ case 'EXDATE':
3699
+ $ak = ( is_array( $this->exdate )) ? array_keys( $this->exdate ) : array();
3700
+ while( is_array( $this->exdate ) && !isset( $this->exdate[$propix] ) && ( 0 < count( $this->exdate )) && ( $propix < end( $ak )))
3701
+ $propix++;
3702
+ $this->propix[$propName] = $propix;
3703
+ if( !isset( $this->exdate[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
3704
+ return ( $inclParam ) ? $this->exdate[$propix] : $this->exdate[$propix]['value'];
3705
+ break;
3706
+ case 'EXRULE':
3707
+ $ak = ( is_array( $this->exrule )) ? array_keys( $this->exrule ) : array();
3708
+ while( is_array( $this->exrule ) && !isset( $this->exrule[$propix] ) && ( 0 < count( $this->exrule )) && ( $propix < end( $ak )))
3709
+ $propix++;
3710
+ $this->propix[$propName] = $propix;
3711
+ if( !isset( $this->exrule[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
3712
+ return ( $inclParam ) ? $this->exrule[$propix] : $this->exrule[$propix]['value'];
3713
+ break;
3714
+ case 'FREEBUSY':
3715
+ $ak = ( is_array( $this->freebusy )) ? array_keys( $this->freebusy ) : array();
3716
+ while( is_array( $this->freebusy ) && !isset( $this->freebusy[$propix] ) && ( 0 < count( $this->freebusy )) && ( $propix < end( $ak )))
3717
+ $propix++;
3718
+ $this->propix[$propName] = $propix;
3719
+ if( !isset( $this->freebusy[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
3720
+ return ( $inclParam ) ? $this->freebusy[$propix] : $this->freebusy[$propix]['value'];
3721
+ break;
3722
+ case 'GEO':
3723
+ if( isset( $this->geo['value'] )) return ( $inclParam ) ? $this->geo : $this->geo['value'];
3724
+ break;
3725
+ case 'LAST-MODIFIED':
3726
+ if( isset( $this->lastmodified['value'] )) return ( $inclParam ) ? $this->lastmodified : $this->lastmodified['value'];
3727
+ break;
3728
+ case 'LOCATION':
3729
+ if( isset( $this->location['value'] )) return ( $inclParam ) ? $this->location : $this->location['value'];
3730
+ break;
3731
+ case 'ORGANIZER':
3732
+ if( isset( $this->organizer['value'] )) return ( $inclParam ) ? $this->organizer : $this->organizer['value'];
3733
+ break;
3734
+ case 'PERCENT-COMPLETE':
3735
+ if( isset( $this->percentcomplete['value'] )) return ( $inclParam ) ? $this->percentcomplete : $this->percentcomplete['value'];
3736
+ break;
3737
+ case 'PRIORITY':
3738
+ if( isset( $this->priority['value'] )) return ( $inclParam ) ? $this->priority : $this->priority['value'];
3739
+ break;
3740
+ case 'RDATE':
3741
+ $ak = ( is_array( $this->rdate )) ? array_keys( $this->rdate ) : array();
3742
+ while( is_array( $this->rdate ) && !isset( $this->rdate[$propix] ) && ( 0 < count( $this->rdate )) && ( $propix < end( $ak )))
3743
+ $propix++;
3744
+ $this->propix[$propName] = $propix;
3745
+ if( !isset( $this->rdate[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
3746
+ return ( $inclParam ) ? $this->rdate[$propix] : $this->rdate[$propix]['value'];
3747
+ break;
3748
+ case 'RECURRENCE-ID':
3749
+ if( isset( $this->recurrenceid['value'] )) return ( $inclParam ) ? $this->recurrenceid : $this->recurrenceid['value'];
3750
+ break;
3751
+ case 'RELATED-TO':
3752
+ $ak = ( is_array( $this->relatedto )) ? array_keys( $this->relatedto ) : array();
3753
+ while( is_array( $this->relatedto ) && !isset( $this->relatedto[$propix] ) && ( 0 < count( $this->relatedto )) && ( $propix < end( $ak )))
3754
+ $propix++;
3755
+ $this->propix[$propName] = $propix;
3756
+ if( !isset( $this->relatedto[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
3757
+ return ( $inclParam ) ? $this->relatedto[$propix] : $this->relatedto[$propix]['value'];
3758
+ break;
3759
+ case 'REPEAT':
3760
+ if( isset( $this->repeat['value'] )) return ( $inclParam ) ? $this->repeat : $this->repeat['value'];
3761
+ break;
3762
+ case 'REQUEST-STATUS':
3763
+ $ak = ( is_array( $this->requeststatus )) ? array_keys( $this->requeststatus ) : array();
3764
+ while( is_array( $this->requeststatus ) && !isset( $this->requeststatus[$propix] ) && ( 0 < count( $this->requeststatus )) && ( $propix < end( $ak )))
3765
+ $propix++;
3766
+ $this->propix[$propName] = $propix;
3767
+ if( !isset( $this->requeststatus[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
3768
+ return ( $inclParam ) ? $this->requeststatus[$propix] : $this->requeststatus[$propix]['value'];
3769
+ break;
3770
+ case 'RESOURCES':
3771
+ $ak = ( is_array( $this->resources )) ? array_keys( $this->resources ) : array();
3772
+ while( is_array( $this->resources ) && !isset( $this->resources[$propix] ) && ( 0 < count( $this->resources )) && ( $propix < end( $ak )))
3773
+ $propix++;
3774
+ $this->propix[$propName] = $propix;
3775
+ if( !isset( $this->resources[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
3776
+ return ( $inclParam ) ? $this->resources[$propix] : $this->resources[$propix]['value'];
3777
+ break;
3778
+ case 'RRULE':
3779
+ $ak = ( is_array( $this->rrule )) ? array_keys( $this->rrule ) : array();
3780
+ while( is_array( $this->rrule ) && !isset( $this->rrule[$propix] ) && ( 0 < count( $this->rrule )) && ( $propix < end( $ak )))
3781
+ $propix++;
3782
+ $this->propix[$propName] = $propix;
3783
+ if( !isset( $this->rrule[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
3784
+ return ( $inclParam ) ? $this->rrule[$propix] : $this->rrule[$propix]['value'];
3785
+ break;
3786
+ case 'SEQUENCE':
3787
+ if( isset( $this->sequence['value'] )) return ( $inclParam ) ? $this->sequence : $this->sequence['value'];
3788
+ break;
3789
+ case 'STATUS':
3790
+ if( isset( $this->status['value'] )) return ( $inclParam ) ? $this->status : $this->status['value'];
3791
+ break;
3792
+ case 'SUMMARY':
3793
+ if( isset( $this->summary['value'] )) return ( $inclParam ) ? $this->summary : $this->summary['value'];
3794
+ break;
3795
+ case 'TRANSP':
3796
+ if( isset( $this->transp['value'] )) return ( $inclParam ) ? $this->transp : $this->transp['value'];
3797
+ break;
3798
+ case 'TRIGGER':
3799
+ if( isset( $this->trigger['value'] )) return ( $inclParam ) ? $this->trigger : $this->trigger['value'];
3800
+ break;
3801
+ case 'TZID':
3802
+ if( isset( $this->tzid['value'] )) return ( $inclParam ) ? $this->tzid : $this->tzid['value'];
3803
+ break;
3804
+ case 'TZNAME':
3805
+ $ak = ( is_array( $this->tzname )) ? array_keys( $this->tzname ) : array();
3806
+ while( is_array( $this->tzname ) && !isset( $this->tzname[$propix] ) && ( 0 < count( $this->tzname )) && ( $propix < end( $ak )))
3807
+ $propix++;
3808
+ $this->propix[$propName] = $propix;
3809
+ if( !isset( $this->tzname[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
3810
+ return ( $inclParam ) ? $this->tzname[$propix] : $this->tzname[$propix]['value'];
3811
+ break;
3812
+ case 'TZOFFSETFROM':
3813
+ if( isset( $this->tzoffsetfrom['value'] )) return ( $inclParam ) ? $this->tzoffsetfrom : $this->tzoffsetfrom['value'];
3814
+ break;
3815
+ case 'TZOFFSETTO':
3816
+ if( isset( $this->tzoffsetto['value'] )) return ( $inclParam ) ? $this->tzoffsetto : $this->tzoffsetto['value'];
3817
+ break;
3818
+ case 'TZURL':
3819
+ if( isset( $this->tzurl['value'] )) return ( $inclParam ) ? $this->tzurl : $this->tzurl['value'];
3820
+ break;
3821
+ case 'UID':
3822
+ if( in_array( $this->objName, iCalUtilityFunctions::$miscComps ))
3823
+ return FALSE;
3824
+ if( empty( $this->uid ))
3825
+ $this->_makeuid();
3826
+ return ( $inclParam ) ? $this->uid : $this->uid['value'];
3827
+ break;
3828
+ case 'URL':
3829
+ if( isset( $this->url['value'] )) return ( $inclParam ) ? $this->url : $this->url['value'];
3830
+ break;
3831
+ default:
3832
+ if( $propName != 'X-PROP' ) {
3833
+ if( !isset( $this->xprop[$propName] )) return FALSE;
3834
+ return ( $inclParam ) ? array( $propName, $this->xprop[$propName] )
3835
+ : array( $propName, $this->xprop[$propName]['value'] );
3836
+ }
3837
+ else {
3838
+ if( empty( $this->xprop )) return FALSE;
3839
+ $xpropno = 0;
3840
+ foreach( $this->xprop as $xpropkey => $xpropvalue ) {
3841
+ if( $propix == $xpropno )
3842
+ return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] )
3843
+ : array( $xpropkey, $this->xprop[$xpropkey]['value'] );
3844
+ else
3845
+ $xpropno++;
3846
+ }
3847
+ return FALSE; // not found ??
3848
+ }
3849
+ }
3850
+ return FALSE;
3851
+ }
3852
+ /**
3853
+ * returns calendar property unique values for 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO' or 'RESOURCES' and for each, number of occurrence
3854
+ *
3855
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3856
+ * @since 2.21.11 - 2015-03-21
3857
+ * @param string $propName
3858
+ * @param array $output incremented result array
3859
+ * @uses iCalUtilityFunctions::$mProps1
3860
+ * @uses calendarComponent::getProperty()
3861
+ * return void
3862
+ */
3863
+ function _getProperties( $propName, & $output ) {
3864
+ if( empty( $output ))
3865
+ $output = array();
3866
+ if( !in_array( strtoupper( $propName ), iCalUtilityFunctions::$mProps1 ))
3867
+ return $output;
3868
+ while( FALSE !== ( $content = $this->getProperty( $propName ))) {
3869
+ if( empty( $content ))
3870
+ continue;
3871
+ if( is_array( $content )) {
3872
+ foreach( $content as $part ) {
3873
+ if( FALSE !== strpos( $part, ',' )) {
3874
+ $part = explode( ',', $part );
3875
+ foreach( $part as $thePart ) {
3876
+ $thePart = trim( $thePart );
3877
+ if( !empty( $thePart )) {
3878
+ if( !isset( $output[$thePart] ))
3879
+ $output[$thePart] = 1;
3880
+ else
3881
+ $output[$thePart] += 1;
3882
+ }
3883
+ }
3884
+ }
3885
+ else {
3886
+ $part = trim( $part );
3887
+ if( !isset( $output[$part] ))
3888
+ $output[$part] = 1;
3889
+ else
3890
+ $output[$part] += 1;
3891
+ }
3892
+ }
3893
+ } // end if( is_array( $content ))
3894
+ elseif( FALSE !== strpos( $content, ',' )) {
3895
+ $content = explode( ',', $content );
3896
+ foreach( $content as $thePart ) {
3897
+ $thePart = trim( $thePart );
3898
+ if( !empty( $thePart )) {
3899
+ if( !isset( $output[$thePart] ))
3900
+ $output[$thePart] = 1;
3901
+ else
3902
+ $output[$thePart] += 1;
3903
+ }
3904
+ }
3905
+ } // end elseif( FALSE !== strpos( $content, ',' ))
3906
+ else {
3907
+ $content = trim( $content );
3908
+ if( !empty( $content )) {
3909
+ if( !isset( $output[$content] ))
3910
+ $output[$content] = 1;
3911
+ else
3912
+ $output[$content] += 1;
3913
+ }
3914
+ }
3915
+ }
3916
+ ksort( $output );
3917
+ }
3918
+ /**
3919
+ * general component property setting
3920
+ *
3921
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3922
+ * @since 2.5.1 - 2008-11-05
3923
+ * @param mixed $args variable number of function arguments,
3924
+ * first argument is ALWAYS component name,
3925
+ * second ALWAYS component value!
3926
+ * @uses calendarComponent::getProperty()
3927
+ * @uses calendarComponent::_notExistProp()
3928
+ * @uses calendarComponent::getConfig()
3929
+ * @uses calendarComponent::setAction()
3930
+ * @uses calendarComponent::setAttendee()
3931
+ * @uses calendarComponent::setCategories()
3932
+ * @uses calendarComponent::setClass()
3933
+ * @uses calendarComponent::setComment()
3934
+ * @uses calendarComponent::setCompleted()
3935
+ * @uses calendarComponent::setContact()
3936
+ * @uses calendarComponent::setCreated()
3937
+ * @uses calendarComponent::setDescription()
3938
+ * @uses calendarComponent::setDtend()
3939
+ * @uses calendarComponent::setDtstamp()
3940
+ * @uses calendarComponent::setDtstart()
3941
+ * @uses calendarComponent::setDue()
3942
+ * @uses calendarComponent::setDuration()
3943
+ * @uses calendarComponent::setExdate()
3944
+ * @uses calendarComponent::setExrule()
3945
+ * @uses calendarComponent::setFreebusy()
3946
+ * @uses calendarComponent::setGeo()
3947
+ * @uses calendarComponent::setLastmodified()
3948
+ * @uses calendarComponent::setLocation()
3949
+ * @uses calendarComponent::setOrganizer()
3950
+ * @uses calendarComponent::setPercentcomplete()
3951
+ * @uses calendarComponent::setPriority()
3952
+ * @uses calendarComponent::setRdate()
3953
+ * @uses calendarComponent::setRecurrenceid()
3954
+ * @uses calendarComponent::setRelatedto()
3955
+ * @uses calendarComponent::setRepeat()
3956
+ * @uses calendarComponent::setRequeststatus()
3957
+ * @uses calendarComponent::setResources()
3958
+ * @uses calendarComponent::setRrule()
3959
+ * @uses calendarComponent::setSequence()
3960
+ * @uses calendarComponent::setStatus()
3961
+ * @uses calendarComponent::setSummary()
3962
+ * @uses calendarComponent::setTransp()
3963
+ * @uses calendarComponent::setTrigger()
3964
+ * @uses calendarComponent::setTzid()
3965
+ * @uses calendarComponent::setTzname()
3966
+ * @uses calendarComponent::setTzoffsetfrom()
3967
+ * @uses calendarComponent::setTzoffsetto()
3968
+ * @uses calendarComponent::setTzurl()
3969
+ * @uses calendarComponent::setUid()
3970
+ * @uses calendarComponent::$objName
3971
+ * @uses calendarComponent::setUrl()
3972
+ * @uses calendarComponent::setXprop()
3973
+ * @return void
3974
+ */
3975
+ function setProperty() {
3976
+ $numargs = func_num_args();
3977
+ if( 1 > $numargs ) return FALSE;
3978
+ $arglist = func_get_args();
3979
+ if( $this->_notExistProp( $arglist[0] )) return FALSE;
3980
+ if( !$this->getConfig( 'allowEmpty' ) && ( !isset( $arglist[1] ) || empty( $arglist[1] )))
3981
+ return FALSE;
3982
+ $arglist[0] = strtoupper( $arglist[0] );
3983
+ for( $argix=$numargs; $argix < 12; $argix++ ) {
3984
+ if( !isset( $arglist[$argix] ))
3985
+ $arglist[$argix] = null;
3986
+ }
3987
+ switch( $arglist[0] ) {
3988
+ case 'ACTION':
3989
+ return $this->setAction( $arglist[1], $arglist[2] );
3990
+ case 'ATTACH':
3991
+ return $this->setAttach( $arglist[1], $arglist[2], $arglist[3] );
3992
+ case 'ATTENDEE':
3993
+ return $this->setAttendee( $arglist[1], $arglist[2], $arglist[3] );
3994
+ case 'CATEGORIES':
3995
+ return $this->setCategories( $arglist[1], $arglist[2], $arglist[3] );
3996
+ case 'CLASS':
3997
+ return $this->setClass( $arglist[1], $arglist[2] );
3998
+ case 'COMMENT':
3999
+ return $this->setComment( $arglist[1], $arglist[2], $arglist[3] );
4000
+ case 'COMPLETED':
4001
+ return $this->setCompleted( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
4002
+ case 'CONTACT':
4003
+ return $this->setContact( $arglist[1], $arglist[2], $arglist[3] );
4004
+ case 'CREATED':
4005
+ return $this->setCreated( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
4006
+ case 'DESCRIPTION':
4007
+ return $this->setDescription( $arglist[1], $arglist[2], $arglist[3] );
4008
+ case 'DTEND':
4009
+ return $this->setDtend( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
4010
+ case 'DTSTAMP':
4011
+ return $this->setDtstamp( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
4012
+ case 'DTSTART':
4013
+ return $this->setDtstart( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
4014
+ case 'DUE':
4015
+ return $this->setDue( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
4016
+ case 'DURATION':
4017
+ return $this->setDuration( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6] );
4018
+ case 'EXDATE':
4019
+ return $this->setExdate( $arglist[1], $arglist[2], $arglist[3] );
4020
+ case 'EXRULE':
4021
+ return $this->setExrule( $arglist[1], $arglist[2], $arglist[3] );
4022
+ case 'FREEBUSY':
4023
+ return $this->setFreebusy( $arglist[1], $arglist[2], $arglist[3], $arglist[4] );
4024
+ case 'GEO':
4025
+ return $this->setGeo( $arglist[1], $arglist[2], $arglist[3] );
4026
+ case 'LAST-MODIFIED':
4027
+ return $this->setLastModified( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
4028
+ case 'LOCATION':
4029
+ return $this->setLocation( $arglist[1], $arglist[2] );
4030
+ case 'ORGANIZER':
4031
+ return $this->setOrganizer( $arglist[1], $arglist[2] );
4032
+ case 'PERCENT-COMPLETE':
4033
+ return $this->setPercentComplete( $arglist[1], $arglist[2] );
4034
+ case 'PRIORITY':
4035
+ return $this->setPriority( $arglist[1], $arglist[2] );
4036
+ case 'RDATE':
4037
+ return $this->setRdate( $arglist[1], $arglist[2], $arglist[3] );
4038
+ case 'RECURRENCE-ID':
4039
+ return $this->setRecurrenceid( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
4040
+ case 'RELATED-TO':
4041
+ return $this->setRelatedTo( $arglist[1], $arglist[2], $arglist[3] );
4042
+ case 'REPEAT':
4043
+ return $this->setRepeat( $arglist[1], $arglist[2] );
4044
+ case 'REQUEST-STATUS':
4045
+ return $this->setRequestStatus( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5] );
4046
+ case 'RESOURCES':
4047
+ return $this->setResources( $arglist[1], $arglist[2], $arglist[3] );
4048
+ case 'RRULE':
4049
+ return $this->setRrule( $arglist[1], $arglist[2], $arglist[3] );
4050
+ case 'SEQUENCE':
4051
+ return $this->setSequence( $arglist[1], $arglist[2] );
4052
+ case 'STATUS':
4053
+ return $this->setStatus( $arglist[1], $arglist[2] );
4054
+ case 'SUMMARY':
4055
+ return $this->setSummary( $arglist[1], $arglist[2] );
4056
+ case 'TRANSP':
4057
+ return $this->setTransp( $arglist[1], $arglist[2] );
4058
+ case 'TRIGGER':
4059
+ return $this->setTrigger( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8], $arglist[9], $arglist[10], $arglist[11] );
4060
+ case 'TZID':
4061
+ return $this->setTzid( $arglist[1], $arglist[2] );
4062
+ case 'TZNAME':
4063
+ return $this->setTzname( $arglist[1], $arglist[2], $arglist[3] );
4064
+ case 'TZOFFSETFROM':
4065
+ return $this->setTzoffsetfrom( $arglist[1], $arglist[2] );
4066
+ case 'TZOFFSETTO':
4067
+ return $this->setTzoffsetto( $arglist[1], $arglist[2] );
4068
+ case 'TZURL':
4069
+ return $this->setTzurl( $arglist[1], $arglist[2] );
4070
+ case 'UID':
4071
+ return $this->setUid( $arglist[1], $arglist[2] );
4072
+ case 'URL':
4073
+ return $this->setUrl( $arglist[1], $arglist[2] );
4074
+ default:
4075
+ return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] );
4076
+ }
4077
+ return FALSE;
4078
+ }
4079
+ /*********************************************************************************/
4080
+ /**
4081
+ * parse component unparsed data into properties
4082
+ *
4083
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4084
+ * @since 2.20.3 - 2015-03-05
4085
+ * @param mixed $unparsedtext strict rfc2445 formatted, single property string or array of strings
4086
+ * @uses calendarComponent::getConfig()
4087
+ * @uses iCalUtilityFunctions::convEolChar()
4088
+ * @uses calendarComponent::$unparsed
4089
+ * @uses calendarComponent::$components
4090
+ * @uses calendarComponent::copy()
4091
+ * @uses iCalUtilityFunctions::_splitContent()
4092
+ * @uses calendarComponent::setProperty()
4093
+ * @uses iCalUtilityFunctions::_strunrep()
4094
+ * @uses calendarComponent::parse()
4095
+ * @return bool FALSE if error occurs during parsing
4096
+ */
4097
+ function parse( $unparsedtext=null ) {
4098
+ $nl = $this->getConfig( 'nl' );
4099
+ if( !empty( $unparsedtext )) {
4100
+ if( is_array( $unparsedtext ))
4101
+ $unparsedtext = implode( '\n'.$nl, $unparsedtext );
4102
+ $unparsedtext = iCalUtilityFunctions::convEolChar( $unparsedtext, $nl );
4103
+ }
4104
+ elseif( !isset( $this->unparsed ))
4105
+ $unparsedtext = array();
4106
+ else
4107
+ $unparsedtext = $this->unparsed;
4108
+ /* skip leading (empty/invalid) lines */
4109
+ foreach( $unparsedtext as $lix => $line ) {
4110
+ if( FALSE !== ( $pos = stripos( $line, 'BEGIN:' ))) {
4111
+ $unparsedtext[$lix] = substr( $unparsedtext[$lix], $pos );
4112
+ break;
4113
+ }
4114
+ $tst = trim( $line );
4115
+ if(( '\n' == $tst ) || empty( $tst ))
4116
+ unset( $unparsedtext[$lix] );
4117
+ }
4118
+ $this->unparsed = array();
4119
+ $comp = & $this;
4120
+ $config = $this->getConfig();
4121
+ $compsync = $subsync = 0;
4122
+ foreach ( $unparsedtext as $lix => $line ) {
4123
+ if( 'END:VALARM' == strtoupper( substr( $line, 0, 10 ))) {
4124
+ if( 1 != $subsync ) return FALSE;
4125
+ $this->components[] = $comp->copy();
4126
+ $subsync--;
4127
+ }
4128
+ elseif( 'END:DAYLIGHT' == strtoupper( substr( $line, 0, 12 ))) {
4129
+ if( 1 != $subsync ) return FALSE;
4130
+ $this->components[] = $comp->copy();
4131
+ $subsync--;
4132
+ }
4133
+ elseif( 'END:STANDARD' == strtoupper( substr( $line, 0, 12 ))) {
4134
+ if( 1 != $subsync ) return FALSE;
4135
+ array_unshift( $this->components, $comp->copy());
4136
+ $subsync--;
4137
+ }
4138
+ elseif( 'END:' == strtoupper( substr( $line, 0, 4 ))) { // end:<component>
4139
+ if( 1 != $compsync ) return FALSE;
4140
+ if( 0 < $subsync )
4141
+ $this->components[] = $comp->copy();
4142
+ $compsync--;
4143
+ break; /* skip trailing empty lines */
4144
+ }
4145
+ elseif( 'BEGIN:VALARM' == strtoupper( substr( $line, 0, 12 ))) {
4146
+ $comp = new valarm( $config);
4147
+ $subsync++;
4148
+ }
4149
+ elseif( 'BEGIN:STANDARD' == strtoupper( substr( $line, 0, 14 ))) {
4150
+ $comp = new vtimezone( 'standard', $config );
4151
+ $subsync++;
4152
+ }
4153
+ elseif( 'BEGIN:DAYLIGHT' == strtoupper( substr( $line, 0, 14 ))) {
4154
+ $comp = new vtimezone( 'daylight', $config );
4155
+ $subsync++;
4156
+ }
4157
+ elseif( 'BEGIN:' == strtoupper( substr( $line, 0, 6 ))) // begin:<component>
4158
+ $compsync++;
4159
+ else
4160
+ $comp->unparsed[] = $line;
4161
+ }
4162
+ if( 0 < $subsync )
4163
+ $this->components[] = $comp->copy();
4164
+ unset( $config );
4165
+ /* concatenate property values spread over several lines */
4166
+ $lastix = -1;
4167
+ $propnames = array( 'action', 'attach', 'attendee', 'categories', 'comment', 'completed'
4168
+ , 'contact', 'class', 'created', 'description', 'dtend', 'dtstart'
4169
+ , 'dtstamp', 'due', 'duration', 'exdate', 'exrule', 'freebusy', 'geo'
4170
+ , 'last-modified', 'location', 'organizer', 'percent-complete'
4171
+ , 'priority', 'rdate', 'recurrence-id', 'related-to', 'repeat'
4172
+ , 'request-status', 'resources', 'rrule', 'sequence', 'status'
4173
+ , 'summary', 'transp', 'trigger', 'tzid', 'tzname', 'tzoffsetfrom'
4174
+ , 'tzoffsetto', 'tzurl', 'uid', 'url', 'x-' );
4175
+ $proprows = array();
4176
+ for( $i = 0; $i < count( $this->unparsed ); $i++ ) { // concatenate lines
4177
+ $line = rtrim( $this->unparsed[$i], $nl );
4178
+ while( isset( $this->unparsed[$i+1] ) && !empty( $this->unparsed[$i+1] ) && ( ' ' == $this->unparsed[$i+1]{0} ))
4179
+ $line .= rtrim( substr( $this->unparsed[++$i], 1 ), $nl );
4180
+ $proprows[] = $line;
4181
+ }
4182
+ /* parse each property 'line' */
4183
+ foreach( $proprows as $line ) {
4184
+ if( '\n' == substr( $line, -2 ))
4185
+ $line = substr( $line, 0, -2 );
4186
+ /* get propname */
4187
+ $propname = null;
4188
+ $cix = 0;
4189
+ while( isset( $line[$cix] )) {
4190
+ if( in_array( $line[$cix], array( ':', ';' )))
4191
+ break;
4192
+ else
4193
+ $propname .= $line[$cix];
4194
+ $cix++;
4195
+ }
4196
+ if(( 'x-' == substr( $propname, 0, 2 )) || ( 'X-' == substr( $propname, 0, 2 ))) {
4197
+ $propname2 = $propname;
4198
+ $propname = 'X-';
4199
+ }
4200
+ if( !in_array( strtolower( $propname ), $propnames )) // skip non standard property names
4201
+ continue;
4202
+ /* rest of the line is opt.params and value */
4203
+ $line = substr( $line, $cix );
4204
+ /* separate attributes from value */
4205
+ iCalUtilityFunctions::_splitContent( $line, $propAttr );
4206
+ /* call setProperty( $propname.. . */
4207
+ switch( strtoupper( $propname )) {
4208
+ case 'ATTENDEE':
4209
+ foreach( $propAttr as $pix => $attr ) {
4210
+ if( !in_array( strtoupper( $pix ), array( 'MEMBER', 'DELEGATED-TO', 'DELEGATED-FROM' )))
4211
+ continue;
4212
+ $attr2 = explode( ',', $attr );
4213
+ if( 1 < count( $attr2 ))
4214
+ $propAttr[$pix] = $attr2;
4215
+ }
4216
+ $this->setProperty( $propname, $line, $propAttr );
4217
+ break;
4218
+ case 'CATEGORIES':
4219
+ case 'RESOURCES':
4220
+ if( FALSE !== strpos( $line, ',' )) {
4221
+ $content = array( 0 => '' );
4222
+ $cix = $lix = 0;
4223
+ while( FALSE !== substr( $line, $lix, 1 )) {
4224
+ if(( ',' == $line[$lix] ) && ( "\\" != $line[( $lix - 1 )])) {
4225
+ $cix++;
4226
+ $content[$cix] = '';
4227
+ }
4228
+ else
4229
+ $content[$cix] .= $line[$lix];
4230
+ $lix++;
4231
+ }
4232
+ if( 1 < count( $content )) {
4233
+ $content = array_values( $content );
4234
+ foreach( $content as $cix => $contentPart )
4235
+ $content[$cix] = iCalUtilityFunctions::_strunrep( $contentPart );
4236
+ $this->setProperty( $propname, $content, $propAttr );
4237
+ break;
4238
+ }
4239
+ else
4240
+ $line = reset( $content );
4241
+ }
4242
+ case 'COMMENT':
4243
+ case 'CONTACT':
4244
+ case 'DESCRIPTION':
4245
+ case 'LOCATION':
4246
+ case 'SUMMARY':
4247
+ if( empty( $line ))
4248
+ $propAttr = null;
4249
+ $this->setProperty( $propname, iCalUtilityFunctions::_strunrep( $line ), $propAttr );
4250
+ break;
4251
+ case 'REQUEST-STATUS':
4252
+ $values = explode( ';', $line, 3 );
4253
+ $values[1] = ( !isset( $values[1] )) ? null : iCalUtilityFunctions::_strunrep( $values[1] );
4254
+ $values[2] = ( !isset( $values[2] )) ? null : iCalUtilityFunctions::_strunrep( $values[2] );
4255
+ $this->setProperty( $propname
4256
+ , $values[0] // statcode
4257
+ , $values[1] // statdesc
4258
+ , $values[2] // extdata
4259
+ , $propAttr );
4260
+ break;
4261
+ case 'FREEBUSY':
4262
+ $fbtype = ( isset( $propAttr['FBTYPE'] )) ? $propAttr['FBTYPE'] : ''; // force setting default, if missing
4263
+ unset( $propAttr['FBTYPE'] );
4264
+ $values = explode( ',', $line );
4265
+ foreach( $values as $vix => $value ) {
4266
+ $value2 = explode( '/', $value );
4267
+ if( 1 < count( $value2 ))
4268
+ $values[$vix] = $value2;
4269
+ }
4270
+ $this->setProperty( $propname, $fbtype, $values, $propAttr );
4271
+ break;
4272
+ case 'GEO':
4273
+ $value = explode( ';', $line, 2 );
4274
+ if( 2 > count( $value ))
4275
+ $value[1] = null;
4276
+ $this->setProperty( $propname, $value[0], $value[1], $propAttr );
4277
+ break;
4278
+ case 'EXDATE':
4279
+ $values = ( !empty( $line )) ? explode( ',', $line ) : null;
4280
+ $this->setProperty( $propname, $values, $propAttr );
4281
+ break;
4282
+ case 'RDATE':
4283
+ if( empty( $line )) {
4284
+ $this->setProperty( $propname, $line, $propAttr );
4285
+ break;
4286
+ }
4287
+ $values = explode( ',', $line );
4288
+ foreach( $values as $vix => $value ) {
4289
+ $value2 = explode( '/', $value );
4290
+ if( 1 < count( $value2 ))
4291
+ $values[$vix] = $value2;
4292
+ }
4293
+ $this->setProperty( $propname, $values, $propAttr );
4294
+ break;
4295
+ case 'EXRULE':
4296
+ case 'RRULE':
4297
+ $values = explode( ';', $line );
4298
+ $recur = array();
4299
+ foreach( $values as $value2 ) {
4300
+ if( empty( $value2 ))
4301
+ continue; // ;-char in ending position ???
4302
+ $value3 = explode( '=', $value2, 2 );
4303
+ $rulelabel = strtoupper( $value3[0] );
4304
+ switch( $rulelabel ) {
4305
+ case 'BYDAY': {
4306
+ $value4 = explode( ',', $value3[1] );
4307
+ if( 1 < count( $value4 )) {
4308
+ foreach( $value4 as $v5ix => $value5 ) {
4309
+ $value6 = array();
4310
+ $dayno = $dayname = null;
4311
+ $value5 = trim( (string) $value5 );
4312
+ if(( ctype_alpha( substr( $value5, -1 ))) &&
4313
+ ( ctype_alpha( substr( $value5, -2, 1 )))) {
4314
+ $dayname = substr( $value5, -2, 2 );
4315
+ if( 2 < strlen( $value5 ))
4316
+ $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 ));
4317
+ }
4318
+ if( $dayno )
4319
+ $value6[] = $dayno;
4320
+ if( $dayname )
4321
+ $value6['DAY'] = $dayname;
4322
+ $value4[$v5ix] = $value6;
4323
+ }
4324
+ }
4325
+ else {
4326
+ $value4 = array();
4327
+ $dayno = $dayname = null;
4328
+ $value5 = trim( (string) $value3[1] );
4329
+ if(( ctype_alpha( substr( $value5, -1 ))) &&
4330
+ ( ctype_alpha( substr( $value5, -2, 1 )))) {
4331
+ $dayname = substr( $value5, -2, 2 );
4332
+ if( 2 < strlen( $value5 ))
4333
+ $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 ));
4334
+ }
4335
+ if( $dayno )
4336
+ $value4[] = $dayno;
4337
+ if( $dayname )
4338
+ $value4['DAY'] = $dayname;
4339
+ }
4340
+ $recur[$rulelabel] = $value4;
4341
+ break;
4342
+ }
4343
+ default: {
4344
+ $value4 = explode( ',', $value3[1] );
4345
+ if( 1 < count( $value4 ))
4346
+ $value3[1] = $value4;
4347
+ $recur[$rulelabel] = $value3[1];
4348
+ break;
4349
+ }
4350
+ } // end - switch $rulelabel
4351
+ } // end - foreach( $values.. .
4352
+ $this->setProperty( $propname, $recur, $propAttr );
4353
+ break;
4354
+ case 'X-':
4355
+ $propname = ( isset( $propname2 )) ? $propname2 : $propname;
4356
+ unset( $propname2 );
4357
+ case 'ACTION':
4358
+ case 'CLASSIFICATION':
4359
+ case 'STATUS':
4360
+ case 'TRANSP':
4361
+ case 'UID':
4362
+ case 'TZID':
4363
+ case 'RELATED-TO':
4364
+ case 'TZNAME':
4365
+ $line = iCalUtilityFunctions::_strunrep( $line );
4366
+ default:
4367
+ $this->setProperty( $propname, $line, $propAttr );
4368
+ break;
4369
+ } // end switch( $propname.. .
4370
+ } // end - foreach( $proprows.. .
4371
+ unset( $unparsedtext, $this->unparsed, $proprows );
4372
+ if( isset( $this->components ) && is_array( $this->components ) && ( 0 < count( $this->components ))) {
4373
+ $ckeys = array_keys( $this->components );
4374
+ foreach( $ckeys as $ckey ) {
4375
+ if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) {
4376
+ $this->components[$ckey]->parse();
4377
+ }
4378
+ }
4379
+ }
4380
+ return TRUE;
4381
+ }
4382
+ /*********************************************************************************/
4383
+ /*********************************************************************************/
4384
+ /**
4385
+ * return a copy of this component
4386
+ *
4387
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4388
+ * @since 2.15.4 - 2012-10-18
4389
+ * @return object
4390
+ */
4391
+ function copy() {
4392
+ return unserialize( serialize( $this ));
4393
+ }
4394
+ /*********************************************************************************/
4395
+ /*********************************************************************************/
4396
+ /**
4397
+ * delete calendar subcomponent from component container
4398
+ *
4399
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4400
+ * @since 2.8.8 - 2011-03-15
4401
+ * @param mixed $arg1 ordno / component type / component uid
4402
+ * @param mixed $arg2 ordno if arg1 = component type
4403
+ * @uses calendarComponent::$components
4404
+ * @uses calendarComponent::$objName
4405
+ * @uses calendarComponent::getProperty()
4406
+ * @return void
4407
+ */
4408
+ function deleteComponent( $arg1, $arg2=FALSE ) {
4409
+ if( !isset( $this->components )) return FALSE;
4410
+ $argType = $index = null;
4411
+ if ( ctype_digit( (string) $arg1 )) {
4412
+ $argType = 'INDEX';
4413
+ $index = (int) $arg1 - 1;
4414
+ }
4415
+ elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
4416
+ $argType = strtolower( $arg1 );
4417
+ $index = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0;
4418
+ }
4419
+ $cix2dC = 0;
4420
+ foreach ( $this->components as $cix => $component) {
4421
+ if( empty( $component )) continue;
4422
+ if(( 'INDEX' == $argType ) && ( $index == $cix )) {
4423
+ unset( $this->components[$cix] );
4424
+ return TRUE;
4425
+ }
4426
+ elseif( $argType == $component->objName ) {
4427
+ if( $index == $cix2dC ) {
4428
+ unset( $this->components[$cix] );
4429
+ return TRUE;
4430
+ }
4431
+ $cix2dC++;
4432
+ }
4433
+ elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) {
4434
+ unset( $this->components[$cix] );
4435
+ return TRUE;
4436
+ }
4437
+ }
4438
+ return FALSE;
4439
+ }
4440
+ /**
4441
+ * get calendar component subcomponent from component container
4442
+ *
4443
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4444
+ * @since 2.8.8 - 2011-03-15
4445
+ * @param mixed $arg1 ordno/component type/ component uid
4446
+ * @param mixed $arg2 ordno if arg1 = component type
4447
+ * @uses calendarComponent::$components
4448
+ * @uses calendarComponent::$compix
4449
+ * @uses calendarComponent::$objName
4450
+ * @uses calendarComponent::copy()
4451
+ * @uses calendarComponent::getProperty()
4452
+ * @return object
4453
+ */
4454
+ function getComponent ( $arg1=FALSE, $arg2=FALSE ) {
4455
+ if( !isset( $this->components )) return FALSE;
4456
+ $index = $argType = null;
4457
+ if ( !$arg1 ) {
4458
+ $argType = 'INDEX';
4459
+ $index = $this->compix['INDEX'] =
4460
+ ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1;
4461
+ }
4462
+ elseif ( ctype_digit( (string) $arg1 )) {
4463
+ $argType = 'INDEX';
4464
+ $index = (int) $arg1;
4465
+ unset( $this->compix );
4466
+ }
4467
+ elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
4468
+ unset( $this->compix['INDEX'] );
4469
+ $argType = strtolower( $arg1 );
4470
+ if( !$arg2 )
4471
+ $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1;
4472
+ else
4473
+ $index = (int) $arg2;
4474
+ }
4475
+ $index -= 1;
4476
+ $ckeys = array_keys( $this->components );
4477
+ if( !empty( $index) && ( $index > end( $ckeys )))
4478
+ return FALSE;
4479
+ $cix2gC = 0;
4480
+ foreach( $this->components as $cix => $component ) {
4481
+ if( empty( $component )) continue;
4482
+ if(( 'INDEX' == $argType ) && ( $index == $cix ))
4483
+ return $component->copy();
4484
+ elseif( $argType == $component->objName ) {
4485
+ if( $index == $cix2gC )
4486
+ return $component->copy();
4487
+ $cix2gC++;
4488
+ }
4489
+ elseif( !$argType && ( $arg1 == $component->getProperty( 'uid' )))
4490
+ return $component->copy();
4491
+ }
4492
+ /* not found.. . */
4493
+ unset( $this->compix );
4494
+ return false;
4495
+ }
4496
+ /**
4497
+ * add calendar component as subcomponent to container for subcomponents
4498
+ *
4499
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4500
+ * @since 1.x.x - 2007-04-24
4501
+ * @param object $component calendar component
4502
+ * @uses calendarComponent::setComponent( $component )
4503
+ * @return void
4504
+ */
4505
+ function addSubComponent ( $component ) {
4506
+ $this->setComponent( $component );
4507
+ }
4508
+ /**
4509
+ * create new calendar component subcomponent, already included within component
4510
+ *
4511
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4512
+ * @since 2.6.33 - 2011-01-03
4513
+ * @param string $compType subcomponent type
4514
+ * @uses calendarComponent::getConfig()
4515
+ * @uses calendarComponent::$components
4516
+ * @uses calendarComponent::calendarComponent()
4517
+ * @return object (reference)
4518
+ */
4519
+ function & newComponent( $compType ) {
4520
+ $config = $this->getConfig();
4521
+ $keys = array_keys( $this->components );
4522
+ $ix = end( $keys) + 1;
4523
+ switch( strtoupper( $compType )) {
4524
+ case 'ALARM':
4525
+ case 'VALARM':
4526
+ $this->components[$ix] = new valarm( $config );
4527
+ break;
4528
+ case 'STANDARD':
4529
+ array_unshift( $this->components, new vtimezone( 'STANDARD', $config ));
4530
+ $ix = 0;
4531
+ break;
4532
+ case 'DAYLIGHT':
4533
+ $this->components[$ix] = new vtimezone( 'DAYLIGHT', $config );
4534
+ break;
4535
+ default:
4536
+ return FALSE;
4537
+ }
4538
+ return $this->components[$ix];
4539
+ }
4540
+ /**
4541
+ * add calendar component as subcomponent to container for subcomponents
4542
+ *
4543
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4544
+ * @since 2.21.11 - 2015-03-21
4545
+ * @param object $component calendar component
4546
+ * @param mixed $arg1 ordno/component type/ component uid
4547
+ * @param mixed $arg2 ordno if arg1 = component type
4548
+ * @uses calendarComponent::$components
4549
+ * @uses calendarComponent::setConfig()
4550
+ * @uses calendarComponent::getConfig()
4551
+ * @uses calendarComponent::$objName
4552
+ * @uses iCalUtilityFunctions::$miscComps
4553
+ * @uses calendarComponent::getProperty()
4554
+ * @uses calendarComponent::copy()
4555
+ * @uses iCalUtilityFunctions::$mComps
4556
+ * @return bool
4557
+ */
4558
+ function setComponent( $component, $arg1=FALSE, $arg2=FALSE ) {
4559
+ if( !isset( $this->components )) return FALSE;
4560
+ $component->setConfig( $this->getConfig(), FALSE, TRUE );
4561
+ if( ! in_array( $component->objName, iCalUtilityFunctions::$miscComps )) {
4562
+ /* make sure dtstamp and uid is set */
4563
+ $dummy = $component->getProperty( 'dtstamp' );
4564
+ $dummy = $component->getProperty( 'uid' );
4565
+ }
4566
+ if( !$arg1 ) { // plain insert, last in chain
4567
+ $this->components[] = $component->copy();
4568
+ return TRUE;
4569
+ }
4570
+ $argType = $index = null;
4571
+ if ( ctype_digit( (string) $arg1 )) { // index insert/replace
4572
+ $argType = 'INDEX';
4573
+ $index = (int) $arg1 - 1;
4574
+ }
4575
+ elseif( in_array( strtolower( $arg1 ), iCalUtilityFunctions::$mComps )) {
4576
+ $argType = strtolower( $arg1 );
4577
+ $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0;
4578
+ }
4579
+ // else if arg1 is set, arg1 must be an UID
4580
+ $cix2sC = 0;
4581
+ foreach ( $this->components as $cix => $component2 ) {
4582
+ if( empty( $component2 )) continue;
4583
+ if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace
4584
+ $this->components[$cix] = $component->copy();
4585
+ return TRUE;
4586
+ }
4587
+ elseif( $argType == $component2->objName ) { // component Type index insert/replace
4588
+ if( $index == $cix2sC ) {
4589
+ $this->components[$cix] = $component->copy();
4590
+ return TRUE;
4591
+ }
4592
+ $cix2sC++;
4593
+ }
4594
+ elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace
4595
+ $this->components[$cix] = $component->copy();
4596
+ return TRUE;
4597
+ }
4598
+ }
4599
+ /* arg1=index and not found.. . insert at index .. .*/
4600
+ if( 'INDEX' == $argType ) {
4601
+ $this->components[$index] = $component->copy();
4602
+ ksort( $this->components, SORT_NUMERIC );
4603
+ }
4604
+ else /* not found.. . insert last in chain anyway .. .*/
4605
+ $this->components[] = $component->copy();
4606
+ return TRUE;
4607
+ }
4608
+ /**
4609
+ * creates formatted output for subcomponents
4610
+ *
4611
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4612
+ * @since 2.21.11 - 2015-03-10
4613
+ * @param array $xcaldecl
4614
+ * @uses calendarComponent::$objName
4615
+ * @uses calendarComponent::$components
4616
+ * @uses calendarComponent::getProperty()
4617
+ * @uses iCalUtilityFunctions::$fmt
4618
+ * @uses calendarComponent::copy()
4619
+ * @uses calendarComponent::setConfig()
4620
+ * @uses calendarComponent::getConfig()
4621
+ * @uses calendarComponent::createComponent()
4622
+ * @uses calendarComponent::$xcaldecl()
4623
+ * @return string
4624
+ */
4625
+ function createSubComponent() {
4626
+ $output = null;
4627
+ if( 'vtimezone' == $this->objName ) { // sort subComponents, first standard, then daylight, in dtstart order
4628
+ $stdarr = $dlarr = array();
4629
+ foreach( $this->components as $component ) {
4630
+ if( empty( $component ))
4631
+ continue;
4632
+ $dt = $component->getProperty( 'dtstart' );
4633
+ $key = (int) sprintf( iCalUtilityFunctions::$fmt['dateKey'], (int) $dt['year'], (int) $dt['month'], (int) $dt['day'], (int) $dt['hour'], (int) $dt['min'], (int) $dt['sec'] );
4634
+ if( 'standard' == $component->objName ) {
4635
+ while( isset( $stdarr[$key] ))
4636
+ $key += 1;
4637
+ $stdarr[$key] = $component->copy();
4638
+ }
4639
+ elseif( 'daylight' == $component->objName ) {
4640
+ while( isset( $dlarr[$key] ))
4641
+ $key += 1;
4642
+ $dlarr[$key] = $component->copy();
4643
+ }
4644
+ } // end foreach( $this->components as $component )
4645
+ $this->components = array();
4646
+ ksort( $stdarr, SORT_NUMERIC );
4647
+ foreach( $stdarr as $std )
4648
+ $this->components[] = $std->copy();
4649
+ unset( $stdarr );
4650
+ ksort( $dlarr, SORT_NUMERIC );
4651
+ foreach( $dlarr as $dl )
4652
+ $this->components[] = $dl->copy();
4653
+ unset( $dlarr );
4654
+ } // end if( 'vtimezone' == $this->objName )
4655
+ foreach( $this->components as $component ) {
4656
+ $component->setConfig( $this->getConfig(), FALSE, TRUE );
4657
+ $output .= $component->createComponent( $this->xcaldecl );
4658
+ }
4659
+ return $output;
4660
+ }
4661
+ }
includes/libs/iCal/lib/iCal.XML.inc.php ADDED
@@ -0,0 +1,897 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*********************************************************************************/
3
+ /**
4
+ *
5
+ * iCalcreator, a PHP rfc2445/rfc5545 solution.
6
+ *
7
+ * @copyright Copyright (c) 2007-2015 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
8
+ * @link http://kigkonsult.se/iCalcreator/index.php
9
+ * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
10
+ * @package iCalcreator
11
+ * @version 2.22
12
+ */
13
+ /**
14
+ * This library is free software; you can redistribute it and/or
15
+ * modify it under the terms of the GNU Lesser General Public
16
+ * License as published by the Free Software Foundation; either
17
+ * version 2.1 of the License, or (at your option) any later version.
18
+ *
19
+ * This library is distributed in the hope that it will be useful,
20
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
+ * Lesser General Public License for more details.
23
+ *
24
+ * You should have received a copy of the GNU Lesser General Public
25
+ * License along with this library; if not, write to the Free Software
26
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
+ */
28
+ /*********************************************************************************/
29
+ /* iCalcreator XML (rfc6321) helper functions */
30
+ /*********************************************************************************/
31
+ /**
32
+ * format iCal XML output, rfc6321, using PHP SimpleXMLElement
33
+ *
34
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
35
+ * @since 2.18.1 - 2013-08-18
36
+ * @param object $calendar iCalcreator vcalendar instance reference
37
+ * @uses ICALCREATOR_VERSION
38
+ * @uses vcalendar::getProperty()
39
+ * @uses wpsbc__addXMLchild()
40
+ * @uses vcalendar::getConfig()
41
+ * @uses vcalendar::getComponent()
42
+ * @uses calendarComponent::$objName
43
+ * @uses calendarComponent::getProperty()
44
+ * @return string
45
+ */
46
+ function wpsbc_iCal2XML( $calendar ) {
47
+ /** fix an SimpleXMLElement instance and create root element */
48
+ $xmlstr = '<?xml version="1.0" encoding="utf-8"?><icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0">';
49
+ $xmlstr .= '<!-- created '.gmdate( 'Ymd\THis\Z' );
50
+ $xmlstr .= ' using kigkonsult.se '.ICALCREATOR_VERSION.' iCal2XMl (rfc6321) -->';
51
+ $xmlstr .= '</icalendar>';
52
+ $xml = new SimpleXMLElement( $xmlstr );
53
+ $vcalendar = $xml->addChild( 'vcalendar' );
54
+ /** fix calendar properties */
55
+ $properties = $vcalendar->addChild( 'properties' );
56
+ $calProps = array( 'version', 'prodid', 'calscale', 'method' );
57
+ foreach( $calProps as $calProp ) {
58
+ if( FALSE !== ( $content = $calendar->getProperty( $calProp )))
59
+ wpsbc__addXMLchild( $properties, $calProp, 'text', $content );
60
+ }
61
+ while( FALSE !== ( $content = $calendar->getProperty( FALSE, FALSE, TRUE )))
62
+ wpsbc__addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] );
63
+ $langCal = $calendar->getConfig( 'language' );
64
+ /** prepare to fix components with properties */
65
+ $components = $vcalendar->addChild( 'components' );
66
+ /** fix component properties */
67
+ while( FALSE !== ( $component = $calendar->getComponent())) {
68
+ $compName = $component->objName;
69
+ $child = $components->addChild( $compName );
70
+ $properties = $child->addChild( 'properties' );
71
+ $langComp = $component->getConfig( 'language' );
72
+ $props = $component->getConfig( 'setPropertyNames' );
73
+ foreach( $props as $prop ) {
74
+ switch( strtolower( $prop )) {
75
+ case 'attach': // may occur multiple times, below
76
+ while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
77
+ $type = ( isset( $content['params']['VALUE'] ) && ( 'BINARY' == $content['params']['VALUE'] )) ? 'binary' : 'uri';
78
+ unset( $content['params']['VALUE'] );
79
+ wpsbc__addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
80
+ }
81
+ break;
82
+ case 'attendee':
83
+ while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
84
+ if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) {
85
+ if( $langComp )
86
+ $content['params']['LANGUAGE'] = $langComp;
87
+ elseif( $langCal )
88
+ $content['params']['LANGUAGE'] = $langCal;
89
+ }
90
+ wpsbc__addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] );
91
+ }
92
+ break;
93
+ case 'exdate':
94
+ while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
95
+ $type = ( isset( $content['params']['VALUE'] ) && ( 'DATE' == $content['params']['VALUE'] )) ? 'date' : 'date-time';
96
+ unset( $content['params']['VALUE'] );
97
+ wpsbc__addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
98
+ }
99
+ break;
100
+ case 'freebusy':
101
+ while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
102
+ if( is_array( $content ) && isset( $content['value']['fbtype'] )) {
103
+ $content['params']['FBTYPE'] = $content['value']['fbtype'];
104
+ unset( $content['value']['fbtype'] );
105
+ }
106
+ wpsbc__addXMLchild( $properties, $prop, 'period', $content['value'], $content['params'] );
107
+ }
108
+ break;
109
+ case 'request-status':
110
+ while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
111
+ if( !isset( $content['params']['LANGUAGE'] )) {
112
+ if( $langComp )
113
+ $content['params']['LANGUAGE'] = $langComp;
114
+ elseif( $langCal )
115
+ $content['params']['LANGUAGE'] = $langCal;
116
+ }
117
+ wpsbc__addXMLchild( $properties, $prop, 'rstatus', $content['value'], $content['params'] );
118
+ }
119
+ break;
120
+ case 'rdate':
121
+ while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
122
+ $type = 'date-time';
123
+ if( isset( $content['params']['VALUE'] )) {
124
+ if( 'DATE' == $content['params']['VALUE'] )
125
+ $type = 'date';
126
+ elseif( 'PERIOD' == $content['params']['VALUE'] )
127
+ $type = 'period';
128
+ }
129
+ unset( $content['params']['VALUE'] );
130
+ wpsbc__addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
131
+ }
132
+ break;
133
+ case 'categories':
134
+ case 'comment':
135
+ case 'contact':
136
+ case 'description':
137
+ case 'related-to':
138
+ case 'resources':
139
+ while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
140
+ if(( 'related-to' != $prop ) && !isset( $content['params']['LANGUAGE'] )) {
141
+ if( $langComp )
142
+ $content['params']['LANGUAGE'] = $langComp;
143
+ elseif( $langCal )
144
+ $content['params']['LANGUAGE'] = $langCal;
145
+ }
146
+ wpsbc__addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
147
+ }
148
+ break;
149
+ case 'x-prop':
150
+ while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
151
+ wpsbc__addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] );
152
+ break;
153
+ case 'created': // single occurence below, if set
154
+ case 'completed':
155
+ case 'dtstamp':
156
+ case 'last-modified':
157
+ $utcDate = TRUE;
158
+ case 'dtstart':
159
+ case 'dtend':
160
+ case 'due':
161
+ case 'recurrence-id':
162
+ if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
163
+ $type = ( isset( $content['params']['VALUE'] ) && ( 'DATE' == $content['params']['VALUE'] )) ? 'date' : 'date-time';
164
+ unset( $content['params']['VALUE'] );
165
+ if(( isset( $content['params']['TZID'] ) && empty( $content['params']['TZID'] )) || @is_null( $content['params']['TZID'] ))
166
+ unset( $content['params']['TZID'] );
167
+ wpsbc__addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
168
+ }
169
+ unset( $utcDate );
170
+ break;
171
+ case 'duration':
172
+ if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
173
+ wpsbc__addXMLchild( $properties, $prop, 'duration', $content['value'], $content['params'] );
174
+ break;
175
+ case 'exrule':
176
+ case 'rrule':
177
+ while( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
178
+ wpsbc__addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] );
179
+ break;
180
+ case 'class':
181
+ case 'location':
182
+ case 'status':
183
+ case 'summary':
184
+ case 'transp':
185
+ case 'tzid':
186
+ case 'uid':
187
+ if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
188
+ if((( 'location' == $prop ) || ( 'summary' == $prop )) && !isset( $content['params']['LANGUAGE'] )) {
189
+ if( $langComp )
190
+ $content['params']['LANGUAGE'] = $langComp;
191
+ elseif( $langCal )
192
+ $content['params']['LANGUAGE'] = $langCal;
193
+ }
194
+ wpsbc__addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
195
+ }
196
+ break;
197
+ case 'geo':
198
+ if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
199
+ wpsbc__addXMLchild( $properties, $prop, 'geo', $content['value'], $content['params'] );
200
+ break;
201
+ case 'organizer':
202
+ if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE ))) {
203
+ if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) {
204
+ if( $langComp )
205
+ $content['params']['LANGUAGE'] = $langComp;
206
+ elseif( $langCal )
207
+ $content['params']['LANGUAGE'] = $langCal;
208
+ }
209
+ wpsbc__addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] );
210
+ }
211
+ break;
212
+ case 'percent-complete':
213
+ case 'priority':
214
+ case 'sequence':
215
+ if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
216
+ wpsbc__addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] );
217
+ break;
218
+ case 'tzurl':
219
+ case 'url':
220
+ if( FALSE !== ( $content = $component->getProperty( $prop, FALSE, TRUE )))
221
+ wpsbc__addXMLchild( $properties, $prop, 'uri', $content['value'], $content['params'] );
222
+ break;
223
+ } // end switch( $prop )
224
+ } // end foreach( $props as $prop )
225
+ /** fix subComponent properties, if any */
226
+ while( FALSE !== ( $subcomp = $component->getComponent())) {
227
+ $subCompName = $subcomp->objName;
228
+ $child2 = $child->addChild( $subCompName );
229
+ $properties = $child2->addChild( 'properties' );
230
+ $langComp = $subcomp->getConfig( 'language' );
231
+ $subCompProps = $subcomp->getConfig( 'setPropertyNames' );
232
+ foreach( $subCompProps as $prop ) {
233
+ switch( strtolower( $prop )) {
234
+ case 'attach': // may occur multiple times, below
235
+ while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
236
+ $type = ( isset( $content['params']['VALUE'] ) && ( 'BINARY' == $content['params']['VALUE'] )) ? 'binary' : 'uri';
237
+ unset( $content['params']['VALUE'] );
238
+ wpsbc__addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
239
+ }
240
+ break;
241
+ case 'attendee':
242
+ while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
243
+ if( isset( $content['params']['CN'] ) && !isset( $content['params']['LANGUAGE'] )) {
244
+ if( $langComp )
245
+ $content['params']['LANGUAGE'] = $langComp;
246
+ elseif( $langCal )
247
+ $content['params']['LANGUAGE'] = $langCal;
248
+ }
249
+ wpsbc__addXMLchild( $properties, $prop, 'cal-address', $content['value'], $content['params'] );
250
+ }
251
+ break;
252
+ case 'comment':
253
+ case 'tzname':
254
+ while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
255
+ if( !isset( $content['params']['LANGUAGE'] )) {
256
+ if( $langComp )
257
+ $content['params']['LANGUAGE'] = $langComp;
258
+ elseif( $langCal )
259
+ $content['params']['LANGUAGE'] = $langCal;
260
+ }
261
+ wpsbc__addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
262
+ }
263
+ break;
264
+ case 'rdate':
265
+ while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
266
+ $type = 'date-time';
267
+ if( isset( $content['params']['VALUE'] )) {
268
+ if( 'DATE' == $content['params']['VALUE'] )
269
+ $type = 'date';
270
+ elseif( 'PERIOD' == $content['params']['VALUE'] )
271
+ $type = 'period';
272
+ }
273
+ unset( $content['params']['VALUE'] );
274
+ wpsbc__addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
275
+ }
276
+ break;
277
+ case 'x-prop':
278
+ while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
279
+ wpsbc__addXMLchild( $properties, $content[0], 'unknown', $content[1]['value'], $content[1]['params'] );
280
+ break;
281
+ case 'action': // single occurence below, if set
282
+ case 'description':
283
+ case 'summary':
284
+ if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
285
+ if(( 'action' != $prop ) && !isset( $content['params']['LANGUAGE'] )) {
286
+ if( $langComp )
287
+ $content['params']['LANGUAGE'] = $langComp;
288
+ elseif( $langCal )
289
+ $content['params']['LANGUAGE'] = $langCal;
290
+ }
291
+ wpsbc__addXMLchild( $properties, $prop, 'text', $content['value'], $content['params'] );
292
+ }
293
+ break;
294
+ case 'dtstart':
295
+ if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
296
+ unset( $content['value']['tz'], $content['params']['VALUE'] ); // always local time
297
+ wpsbc__addXMLchild( $properties, $prop, 'date-time', $content['value'], $content['params'] );
298
+ }
299
+ break;
300
+ case 'duration':
301
+ if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
302
+ wpsbc__addXMLchild( $properties, $prop, 'duration', $content['value'], $content['params'] );
303
+ break;
304
+ case 'repeat':
305
+ if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
306
+ wpsbc__addXMLchild( $properties, $prop, 'integer', $content['value'], $content['params'] );
307
+ break;
308
+ case 'trigger':
309
+ if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE ))) {
310
+ if( isset( $content['value']['year'] ) &&
311
+ isset( $content['value']['month'] ) &&
312
+ isset( $content['value']['day'] ))
313
+ $type = 'date-time';
314
+ else {
315
+ $type = 'duration';
316
+ if( !isset( $content['value']['relatedStart'] ) || ( TRUE !== $content['value']['relatedStart'] ))
317
+ $content['params']['RELATED'] = 'END';
318
+ }
319
+ wpsbc__addXMLchild( $properties, $prop, $type, $content['value'], $content['params'] );
320
+ }
321
+ break;
322
+ case 'tzoffsetto':
323
+ case 'tzoffsetfrom':
324
+ if( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
325
+ wpsbc__addXMLchild( $properties, $prop, 'utc-offset', $content['value'], $content['params'] );
326
+ break;
327
+ case 'rrule':
328
+ while( FALSE !== ( $content = $subcomp->getProperty( $prop, FALSE, TRUE )))
329
+ wpsbc__addXMLchild( $properties, $prop, 'recur', $content['value'], $content['params'] );
330
+ break;
331
+ } // switch( $prop )
332
+ } // end foreach( $subCompProps as $prop )
333
+ } // end while( FALSE !== ( $subcomp = $component->getComponent()))
334
+ } // end while( FALSE !== ( $component = $calendar->getComponent()))
335
+ return $xml->asXML();
336
+ }
337
+ /**
338
+ * Add children to a SimpleXMLelement
339
+ *
340
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
341
+ * @since 2.18.10 - 2013-09-04
342
+ * @param object $parent reference to a SimpleXMLelement node
343
+ * @param string $name new element node name
344
+ * @param string $type content type, subelement(-s) name
345
+ * @param string $content new subelement content
346
+ * @param array $params new element 'attributes'
347
+ * @uses iCalUtilityFunctions::_duration2str()
348
+ * @uses iCalUtilityFunctions::_geo2str2()
349
+ * @uses iCalUtilityFunctions::$geoLatFmt
350
+ * @uses iCalUtilityFunctions::$geoLongFmt
351
+ * @return void
352
+ */
353
+ function wpsbc__addXMLchild( & $parent, $name, $type, $content, $params=array()) {
354
+ static $fmtYmd = '%04d-%02d-%02d';
355
+ static $fmtYmdHis = '%04d-%02d-%02dT%02d:%02d:%02d';
356
+ /** create new child node */
357
+ $name = strtolower( $name );
358
+ $child = $parent->addChild( $name );
359
+ if( !empty( $params )) {
360
+ $parameters = $child->addChild( 'parameters' );
361
+ foreach( $params as $param => $parVal ) {
362
+ if( 'VALUE' == $param )
363
+ continue;
364
+ $param = strtolower( $param );
365
+ if( 'x-' == substr( $param, 0, 2 )) {
366
+ $p1 = $parameters->addChild( $param );
367
+ $p2 = $p1->addChild( 'unknown', htmlspecialchars( $parVal ));
368
+ }
369
+ else {
370
+ $p1 = $parameters->addChild( $param );
371
+ switch( $param ) {
372
+ case 'altrep':
373
+ case 'dir': $ptype = 'uri'; break;
374
+ case 'delegated-from':
375
+ case 'delegated-to':
376
+ case 'member':
377
+ case 'sent-by': $ptype = 'cal-address'; break;
378
+ case 'rsvp': $ptype = 'boolean'; break ;
379
+ default: $ptype = 'text'; break;
380
+ }
381
+ if( is_array( $parVal )) {
382
+ foreach( $parVal as $pV )
383
+ $p2 = $p1->addChild( $ptype, htmlspecialchars( $pV ));
384
+ }
385
+ else
386
+ $p2 = $p1->addChild( $ptype, htmlspecialchars( $parVal ));
387
+ }
388
+ }
389
+ } // end if( !empty( $params ))
390
+ if(( empty( $content ) && ( '0' != $content )) || ( !is_array( $content) && ( '-' != substr( $content, 0, 1 ) && ( 0 > $content ))))
391
+ return;
392
+ /** store content */
393
+ switch( $type ) {
394
+ case 'binary':
395
+ $v = $child->addChild( $type, $content );
396
+ break;
397
+ case 'boolean':
398
+ break;
399
+ case 'cal-address':
400
+ $v = $child->addChild( $type, $content );
401
+ break;
402
+ case 'date':
403
+ if( array_key_exists( 'year', $content ))
404
+ $content = array( $content );
405
+ foreach( $content as $date ) {
406
+ $str = sprintf( $fmtYmd, (int) $date['year'], (int) $date['month'], (int) $date['day'] );
407
+ $v = $child->addChild( $type, $str );
408
+ }
409
+ break;
410
+ case 'date-time':
411
+ if( array_key_exists( 'year', $content ))
412
+ $content = array( $content );
413
+ foreach( $content as $dt ) {
414
+ if( !isset( $dt['hour'] )) $dt['hour'] = 0;
415
+ if( !isset( $dt['min'] )) $dt['min'] = 0;
416
+ if( !isset( $dt['sec'] )) $dt['sec'] = 0;
417
+ $str = sprintf( $fmtYmdHis, (int) $dt['year'], (int) $dt['month'], (int) $dt['day'], (int) $dt['hour'], (int) $dt['min'], (int) $dt['sec'] );
418
+ if( isset( $dt['tz'] ) && ( 'Z' == $dt['tz'] ))
419
+ $str .= 'Z';
420
+ $v = $child->addChild( $type, $str );
421
+ }
422
+ break;
423
+ case 'duration':
424
+ $output = (( 'trigger' == $name ) && ( FALSE !== $content['before'] )) ? '-' : '';
425
+ $v = $child->addChild( $type, $output.iCalUtilityFunctions::_duration2str( $content ) );
426
+ break;
427
+ case 'geo':
428
+ if( !empty( $content )) {
429
+ $v1 = $child->addChild( 'latitude', iCalUtilityFunctions::_geo2str2( $content['latitude'], iCalUtilityFunctions::$geoLatFmt ));
430
+ $v1 = $child->addChild( 'longitude', iCalUtilityFunctions::_geo2str2( $content['longitude'], iCalUtilityFunctions::$geoLongFmt ));
431
+ }
432
+ break;
433
+ case 'integer':
434
+ $v = $child->addChild( $type, (string) $content );
435
+ break;
436
+ case 'period':
437
+ if( !is_array( $content ))
438
+ break;
439
+ foreach( $content as $period ) {
440
+ $v1 = $child->addChild( $type );
441
+ $str = sprintf( $fmtYmdHis, (int) $period[0]['year'], (int) $period[0]['month'], (int) $period[0]['day'], (int) $period[0]['hour'], (int) $period[0]['min'], (int) $period[0]['sec'] );
442
+ if( isset( $period[0]['tz'] ) && ( 'Z' == $period[0]['tz'] ))
443
+ $str .= 'Z';
444
+ $v2 = $v1->addChild( 'start', $str );
445
+ if( array_key_exists( 'year', $period[1] )) {
446
+ $str = sprintf( $fmtYmdHis, (int) $period[1]['year'], (int) $period[1]['month'], (int) $period[1]['day'], (int) $period[1]['hour'], (int) $period[1]['min'], (int) $period[1]['sec'] );
447
+ if( isset($period[1]['tz'] ) && ( 'Z' == $period[1]['tz'] ))
448
+ $str .= 'Z';
449
+ $v2 = $v1->addChild( 'end', $str );
450
+ }
451
+ else
452
+ $v2 = $v1->addChild( 'duration', iCalUtilityFunctions::_duration2str( $period[1] ));
453
+ }
454
+ break;
455
+ case 'recur':
456
+ $content = array_change_key_case( $content );
457
+ foreach( $content as $rulelabel => $rulevalue ) {
458
+ switch( $rulelabel ) {
459
+ case 'until':
460
+ if( isset( $rulevalue['hour'] ))
461
+ $str = sprintf( $fmtYmdHis, (int) $rulevalue['year'], (int) $rulevalue['month'], (int) $rulevalue['day'], (int) $rulevalue['hour'], (int) $rulevalue['min'], (int) $rulevalue['sec'] ).'Z';
462
+ else
463
+ $str = sprintf( $fmtYmd, (int) $rulevalue['year'], (int) $rulevalue['month'], (int) $rulevalue['day'] );
464
+ $v = $child->addChild( $rulelabel, $str );
465
+ break;
466
+ case 'bysecond':
467
+ case 'byminute':
468
+ case 'byhour':
469
+ case 'bymonthday':
470
+ case 'byyearday':
471
+ case 'byweekno':
472
+ case 'bymonth':
473
+ case 'bysetpos': {
474
+ if( is_array( $rulevalue )) {
475
+ foreach( $rulevalue as $vix => $valuePart )
476
+ $v = $child->addChild( $rulelabel, $valuePart );
477
+ }
478
+ else
479
+ $v = $child->addChild( $rulelabel, $rulevalue );
480
+ break;
481
+ }
482
+ case 'byday': {
483
+ if( isset( $rulevalue['DAY'] )) {
484
+ $str = ( isset( $rulevalue[0] )) ? $rulevalue[0] : '';
485
+ $str .= $rulevalue['DAY'];
486
+ $p = $child->addChild( $rulelabel, $str );
487
+ }
488
+ else {
489
+ foreach( $rulevalue as $valuePart ) {
490
+ if( isset( $valuePart['DAY'] )) {
491
+ $str = ( isset( $valuePart[0] )) ? $valuePart[0] : '';
492
+ $str .= $valuePart['DAY'];
493
+ $p = $child->addChild( $rulelabel, $str );
494
+ }
495
+ else
496
+ $p = $child->addChild( $rulelabel, $valuePart );
497
+ }
498
+ }
499
+ break;
500
+ }
501
+ case 'freq':
502
+ case 'count':
503
+ case 'interval':
504
+ case 'wkst':
505
+ default:
506
+ $p = $child->addChild( $rulelabel, $rulevalue );
507
+ break;
508
+ } // end switch( $rulelabel )
509
+ } // end foreach( $content as $rulelabel => $rulevalue )
510
+ break;
511
+ case 'rstatus':
512
+ $v = $child->addChild( 'code', number_format( (float) $content['statcode'], 2, '.', ''));
513
+ $v = $child->addChild( 'description', htmlspecialchars( $content['text'] ));
514
+ if( isset( $content['extdata'] ))
515
+ $v = $child->addChild( 'data', htmlspecialchars( $content['extdata'] ));
516
+ break;
517
+ case 'text':
518
+ if( !is_array( $content ))
519
+ $content = array( $content );
520
+ foreach( $content as $part )
521
+ $v = $child->addChild( $type, htmlspecialchars( $part ));
522
+ break;
523
+ case 'time':
524
+ break;
525
+ case 'uri':
526
+ $v = $child->addChild( $type, $content );
527
+ break;
528
+ case 'utc-offset':
529
+ if( in_array( substr( $content, 0, 1 ), array( '-', '+' ))) {
530
+ $str = substr( $content, 0, 1 );
531
+ $content = substr( $content, 1 );
532
+ }
533
+ else
534
+ $str = '+';
535
+ $str .= substr( $content, 0, 2 ).':'.substr( $content, 2, 2 );
536
+ if( 4 < strlen( $content ))
537
+ $str .= ':'.substr( $content, 4 );
538
+ $v = $child->addChild( $type, $str );
539
+ break;
540
+ case 'unknown':
541
+ default:
542
+ if( is_array( $content ))
543
+ $content = implode( '', $content );
544
+ $v = $child->addChild( 'unknown', htmlspecialchars( $content ));
545
+ break;
546
+ }
547
+ }
548
+ /**
549
+ * parse xml file into iCalcreator instance
550
+ *
551
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
552
+ * @since 2.16.22 - 2013-06-18
553
+ * @param string $xmlfile
554
+ * @param array $iCalcfg iCalcreator config array (opt)
555
+ * @return mixediCalcreator instance or FALSE on error
556
+ */
557
+ function wpsbc_XMLfile2iCal( $xmlfile, $iCalcfg=array()) {
558
+ if( FALSE === ( $xmlstr = file_get_contents( $xmlfile )))
559
+ return FALSE;
560
+ return wpsbc_xml2iCal( $xmlstr, $iCalcfg );
561
+ }
562
+ /**
563
+ * parse xml string into iCalcreator instance, alias of XML2iCal
564
+ *
565
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
566
+ * @since 2.16.22 - 2013-06-18
567
+ * @param string $xmlstr
568
+ * @param array $iCalcfg iCalcreator config array (opt)
569
+ * @return mixed iCalcreator instance or FALSE on error
570
+ */
571
+ function wpsbc_XMLstr2iCal( $xmlstr, $iCalcfg=array()) {
572
+ return wpsbc_XML2iCal( $xmlstr, $iCalcfg);
573
+ }
574
+ /**
575
+ * parse xml string into iCalcreator instance
576
+ *
577
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
578
+ * @since 2.16.22 - 2013-06-20
579
+ * @param string $xmlstr
580
+ * @param array $iCalcfg iCalcreator config array (opt)
581
+ * @uses vcalendar::vcalendar()
582
+ * @uses XMLgetComps()
583
+ * @return mixed iCalcreator instance or FALSE on error
584
+ */
585
+ function wpsbc_XML2iCal( $xmlstr, $iCalcfg=array()) {
586
+ $xmlstr = str_replace( array( "\r\n", "\n\r", "\n", "\r" ), '', $xmlstr );
587
+ $xml = wpsbc_XMLgetTagContent1( $xmlstr, 'vcalendar', $endIx );
588
+ $iCal = new vcalendar( $iCalcfg );
589
+ wpsbc_XMLgetComps( $iCal, $xmlstr );
590
+ unset( $xmlstr );
591
+ return $iCal;
592
+ }
593
+ /**
594
+ * parse XML string into iCalcreator components
595
+ *
596
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
597
+ * @since 2.21.11 - 2015-03-21
598
+ * @param object $iCal iCalcreator vcalendar or component object instance
599
+ * @param string $xml
600
+ * @uses iCalUtilityFunctions::$allComps
601
+ * @uses wpsbc_XMLgetTagContent1()
602
+ * @uses XMLgetProps()
603
+ * @uses wpsbc_XMLgetTagContent2()
604
+ * @uses vcalendar::newComponent()
605
+ * @uses iCalUtilityFunctions::$allComps
606
+ * @uses XMLgetComps()
607
+ * @return object
608
+ */
609
+ function wpsbc_XMLgetComps( $iCal, $xml ) {
610
+ $sx = 0;
611
+ while(( FALSE !== substr( $xml, ( $sx + 11 ), 1 )) &&
612
+ ( '<properties>' != substr( $xml, $sx, 12 )) && ( '<components>' != substr( $xml, $sx, 12 )))
613
+ $sx += 1;
614
+ if( FALSE === substr( $xml, ( $sx + 11 ), 1 ))
615
+ return FALSE;
616
+ if( '<properties>' == substr( $xml, $sx, 12 )) {
617
+ $xml2 = wpsbc_XMLgetTagContent1( $xml, 'properties', $endIx );
618
+ wpsbc_XMLgetProps( $iCal, $xml2 );
619
+ $xml = substr( $xml, $endIx );
620
+ }
621
+ if( '<components>' == substr( $xml, 0, 12 ))
622
+ $xml = wpsbc_XMLgetTagContent1( $xml, 'components', $endIx );
623
+ while( ! empty( $xml )) {
624
+ $xml2 = wpsbc_XMLgetTagContent2( $xml, $tagName, $endIx );
625
+ if( in_array( strtolower( $tagName ), iCalUtilityFunctions::$allComps ) &&
626
+ ( FALSE !== ( $subComp = $iCal->newComponent( $tagName ))))
627
+ XMLgetComps( $subComp, $xml2 );
628
+ $xml = substr( $xml, $endIx);
629
+ }
630
+ unset( $xml );
631
+ return $iCal;
632
+ }
633
+ /**
634
+ * parse XML into iCalcreator properties
635
+ *
636
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
637
+ * @since 2.21.11 - 2015-03-21
638
+ * @param array $iCal iCalcreator calendar/component instance
639
+ * @param string $xml
640
+ * @uses wpsbc_XMLgetTagContent2()
641
+ * @uses vcalendar::setProperty()
642
+ * @uses calendarComponent::setproperty()
643
+ * @uses wpsbc_XMLgetTagContent1()
644
+ * @uses vcalendar::setProperty()
645
+ * @return void
646
+ */
647
+ function wpsbc_XMLgetProps( $iCal, $xml) {
648
+ while( ! empty( $xml )) {
649
+ $xml2 = wpsbc_XMLgetTagContent2( $xml, $propName, $endIx );
650
+ $propName = strtoupper( $propName );
651
+ if( empty( $xml2 ) && ( '0' != $xml2 )) {
652
+ $iCal->setProperty( $propName );
653
+ $xml = substr( $xml, $endIx);
654
+ continue;
655
+ }
656
+ $params = array();
657
+ if( '<parameters/>' == substr( $xml2, 0, 13 ))
658
+ $xml2 = substr( $xml2, 13 );
659
+ elseif( '<parameters>' == substr( $xml2, 0, 12 )) {
660
+ $xml3 = wpsbc_XMLgetTagContent1( $xml2, 'parameters', $endIx2 );
661
+ while( ! empty( $xml3 )) {
662
+ $xml4 = wpsbc_XMLgetTagContent2( $xml3, $paramKey, $endIx3 );
663
+ $pType = FALSE; // skip parameter valueType
664
+ $paramKey = strtoupper( $paramKey );
665
+ static $mParams = array( 'DELEGATED-FROM', 'DELEGATED-TO', 'MEMBER' );
666
+ if( in_array( $paramKey, $mParams )) {
667
+ while( ! empty( $xml4 )) {
668
+ if( ! isset( $params[$paramKey] ))
669
+ $params[$paramKey] = array( wpsbc_XMLgetTagContent1( $xml4, 'cal-address', $endIx4 ));
670
+ else
671
+ $params[$paramKey][] = wpsbc_XMLgetTagContent1( $xml4, 'cal-address', $endIx4 );
672
+ $xml4 = substr( $xml4, $endIx4 );
673
+ }
674
+ }
675
+ else {
676
+ if( ! isset( $params[$paramKey] ))
677
+ $params[$paramKey] = html_entity_decode( wpsbc_XMLgetTagContent2( $xml4, $pType, $endIx4 ));
678
+ else
679
+ $params[$paramKey] .= ','.html_entity_decode( wpsbc_XMLgetTagContent2( $xml4, $pType, $endIx4 ));
680
+ }
681
+ $xml3 = substr( $xml3, $endIx3 );
682
+ }
683
+ $xml2 = substr( $xml2, $endIx2 );
684
+ } // if( '<parameters>' == substr( $xml2, 0, 12 ))
685
+ $valueType = FALSE;
686
+ $value = ( ! empty( $xml2 ) || ( '0' == $xml2 )) ? wpsbc_XMLgetTagContent2( $xml2, $valueType, $endIx3 ) : '';
687
+ switch( $propName ) {
688
+ case 'CATEGORIES':
689
+ case 'RESOURCES':
690
+ $tValue = array();
691
+ while( ! empty( $xml2 )) {
692
+ $tValue[] = html_entity_decode( wpsbc_XMLgetTagContent2( $xml2, $valueType, $endIx4 ));
693
+ $xml2 = substr( $xml2, $endIx4 );
694
+ }
695
+ $value = $tValue;
696
+ break;
697
+ case 'EXDATE': // multiple single-date(-times) may exist
698
+ case 'RDATE':
699
+ if( 'period' != $valueType ) {
700
+ if( 'date' == $valueType )
701
+ $params['VALUE'] = 'DATE';
702
+ $t = array();
703
+ while( ! empty( $xml2 ) && ( '<date' == substr( $xml2, 0, 5 ))) {
704
+ $t[] = wpsbc_XMLgetTagContent2( $xml2, $pType, $endIx4 );
705
+ $xml2 = substr( $xml2, $endIx4 );
706
+ }
707
+ $value = $t;
708
+ break;
709
+ }
710
+ case 'FREEBUSY':
711
+ if( 'RDATE' == $propName )
712
+ $params['VALUE'] = 'PERIOD';
713
+ $value = array();
714
+ while( ! empty( $xml2 ) && ( '<period>' == substr( $xml2, 0, 8 ))) {
715
+ $xml3 = wpsbc_XMLgetTagContent1( $xml2, 'period', $endIx4 ); // period
716
+ $t = array();
717
+ while( ! empty( $xml3 )) {
718
+ $t[] = wpsbc_XMLgetTagContent2( $xml3, $pType, $endIx5 ); // start - end/duration
719
+ $xml3 = substr( $xml3, $endIx5 );
720
+ }
721
+ $value[] = $t;
722
+ $xml2 = substr( $xml2, $endIx4 );
723
+ }
724
+ break;
725
+ case 'TZOFFSETTO':
726
+ case 'TZOFFSETFROM':
727
+ $value = str_replace( ':', '', $value );
728
+ break;
729
+ case 'GEO':
730
+ $tValue = array( 'latitude' => $value );
731
+ $tValue['longitude'] = wpsbc_XMLgetTagContent1( substr( $xml2, $endIx3 ), 'longitude', $endIx3 );
732
+ $value = $tValue;
733
+ break;
734
+ case 'EXRULE':
735
+ case 'RRULE':
736
+ $tValue = array( $valueType => $value );
737
+ $xml2 = substr( $xml2, $endIx3 );
738
+ $valueType = FALSE;
739
+ while( ! empty( $xml2 )) {
740
+ $t = wpsbc_XMLgetTagContent2( $xml2, $valueType, $endIx4 );
741
+ switch( $valueType ) {
742
+ case 'freq':
743
+ case 'count':
744
+ case 'until':
745
+ case 'interval':
746
+ case 'wkst':
747
+ $tValue[$valueType] = $t;
748
+ break;
749
+ case 'byday':
750
+ if( 2 == strlen( $t ))
751
+ $tValue[$valueType][] = array( 'DAY' => $t );
752
+ else {
753
+ $day = substr( $t, -2 );
754
+ $key = substr( $t, 0, ( strlen( $t ) - 2 ));
755
+ $tValue[$valueType][] = array( $key, 'DAY' => $day );
756
+ }
757
+ break;
758
+ default:
759
+ $tValue[$valueType][] = $t;
760
+ }
761
+ $xml2 = substr( $xml2, $endIx4 );
762
+ }
763
+ $value = $tValue;
764
+ break;
765
+ case 'REQUEST-STATUS':
766
+ $tValue = array();
767
+ while( ! empty( $xml2 )) {
768
+ $t = html_entity_decode( wpsbc_XMLgetTagContent2( $xml2, $valueType, $endIx4 ));
769
+ $tValue[$valueType] = $t;
770
+ $xml2 = substr( $xml2, $endIx4 );
771
+ }
772
+ if( ! empty( $tValue ))
773
+ $value = $tValue;
774
+ else
775
+ $value = array( 'code' => null, 'description' => null );
776
+ break;
777
+ default:
778
+ switch( $valueType ) {
779
+ case 'binary': $params['VALUE'] = 'BINARY'; break;
780
+ case 'date': $params['VALUE'] = 'DATE'; break;
781
+ case 'date-time': $params['VALUE'] = 'DATE-TIME'; break;
782
+ case 'text':
783
+ case 'unknown': $value = html_entity_decode( $value ); break;
784
+ }
785
+ break;
786
+ } // end switch( $propName )
787
+ if( 'FREEBUSY' == $propName ) {
788
+ $fbtype = $params['FBTYPE'];
789
+ unset( $params['FBTYPE'] );
790
+ $iCal->setProperty( $propName, $fbtype, $value, $params );
791
+ }
792
+ elseif( 'GEO' == $propName )
793
+ $iCal->setProperty( $propName, $value['latitude'], $value['longitude'], $params );
794
+ elseif( 'REQUEST-STATUS' == $propName ) {
795
+ if( !isset( $value['data'] ))
796
+ $value['data'] = FALSE;
797
+ $iCal->setProperty( $propName, $value['code'], $value['description'], $value['data'], $params );
798
+ }
799
+ else {
800
+ if( empty( $value ) && ( is_array( $value ) || ( '0' > $value )))
801
+ $value = '';
802
+ $iCal->setProperty( $propName, $value, $params );
803
+ }
804
+ $xml = substr( $xml, $endIx);
805
+ } // end while( ! empty( $xml ))
806
+ }
807
+ /**
808
+ * fetch a specific XML tag content
809
+ *
810
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
811
+ * @since 2.16.22 - 2013-06-20
812
+ * @param string $xml
813
+ * @param string $tagName
814
+ * @param int $endIx
815
+ * @return mixed
816
+ */
817
+ function wpsbc_XMLgetTagContent1( $xml, $tagName, & $endIx=0 ) {
818
+ $strlen = strlen( $tagName );
819
+ $sx1 = 0;
820
+ while( FALSE !== substr( $xml, $sx1, 1 )) {
821
+ if(( FALSE !== substr( $xml, ( $sx1 + $strlen + 1 ), 1 )) &&
822
+ ( strtolower( "<$tagName>" ) == strtolower( substr( $xml, $sx1, ( $strlen + 2 )))))
823
+ break;
824
+ if(( FALSE !== substr( $xml, ( $sx1 + $strlen + 3 ), 1 )) &&
825
+ ( strtolower( "<$tagName />" ) == strtolower( substr( $xml, $sx1, ( $strlen + 4 ))))) { // empty tag
826
+ $endIx = $strlen + 5;
827
+ return '';
828
+ }
829
+ if(( FALSE !== substr( $xml, ( $sx1 + $strlen + 2 ), 1 )) &&
830
+ ( strtolower( "<$tagName/>" ) == strtolower( substr( $xml, $sx1, ( $strlen + 3 ))))) { // empty tag
831
+ $endIx = $strlen + 4;
832
+ return '';
833
+ }
834
+ $sx1 += 1;
835
+ }
836
+ if( FALSE === substr( $xml, $sx1, 1 )) {
837
+ $endIx = ( empty( $sx )) ? 0 : $sx - 1;
838
+ return '';
839
+ }
840
+ if( FALSE === ( $pos = stripos( $xml, "</$tagName>" ))) { // missing end tag??
841
+ $endIx = strlen( $xml ) + 1;
842
+ return '';
843
+ }
844
+ $endIx = $pos + $strlen + 3;
845
+ return substr( $xml, ( $sx1 + $strlen + 2 ), ( $pos - $sx1 - 2 - $strlen ));
846
+ }
847
+ /**
848
+ * fetch next (unknown) XML tagname AND content
849
+ *
850
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
851
+ * @since 2.16.22 - 2013-06-20
852
+ * @param string $xml
853
+ * @param string $tagName
854
+ * @param int $endIx
855
+ * @return mixed
856
+ */
857
+ function wpsbc_XMLgetTagContent2( $xml, & $tagName, & $endIx ) {
858
+ $endIx = strlen( $xml ) + 1; // just in case.. .
859
+ $sx1 = 0;
860
+ while( FALSE !== substr( $xml, $sx1, 1 )) {
861
+ if( '<' == substr( $xml, $sx1, 1 )) {
862
+ if(( FALSE !== substr( $xml, ( $sx1 + 3 ), 1 )) && ( '<!--' == substr( $xml, $sx1, 4 ))) // skip comment
863
+ $sx1 += 1;
864
+ else
865
+ break; // tagname start here
866
+ }
867
+ else
868
+ $sx1 += 1;
869
+ }
870
+ $sx2 = $sx1;
871
+ while( FALSE !== substr( $xml, $sx2 )) {
872
+ if(( FALSE !== substr( $xml, ( $sx2 + 1 ), 1 )) && ( '/>' == substr( $xml, $sx2, 2 ))) { // empty tag
873
+ $tagName = trim( substr( $xml, ( $sx1 + 1 ), ( $sx2 - $sx1 - 1 )));
874
+ $endIx = $sx2 + 2;
875
+ return '';
876
+ }
877
+ if( '>' == substr( $xml, $sx2, 1 )) // tagname ends here
878
+ break;
879
+ $sx2 += 1;
880
+ }
881
+ $tagName = substr( $xml, ( $sx1 + 1 ), ( $sx2 - $sx1 - 1 ));
882
+ $endIx = $sx2 + 1;
883
+ if( FALSE === substr( $xml, $sx2, 1 )) {
884
+ return '';
885
+ }
886
+ $strlen = strlen( $tagName );
887
+ if(( 'duration' == $tagName ) &&
888
+ ( FALSE !== ( $pos1 = stripos( $xml, "<duration>", $sx1+1 ))) &&
889
+ ( FALSE !== ( $pos2 = stripos( $xml, "</duration>", $pos1+1 ))) &&
890
+ ( FALSE !== ( $pos3 = stripos( $xml, "</duration>", $pos2+1 ))) &&
891
+ ( $pos1 < $pos2 ) && ( $pos2 < $pos3 ))
892
+ $pos = $pos3;
893
+ elseif( FALSE === ( $pos = stripos( $xml, "</$tagName>", $sx2 )))
894
+ return '';
895
+ $endIx = $pos + $strlen + 3;
896
+ return substr( $xml, ( $sx1 + $strlen + 2 ), ( $pos - $strlen - 2 ));
897
+ }
includes/libs/iCal/lib/iCal.tz.inc.php ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*********************************************************************************/
3
+ /* Additional functions to use with vtimezone components */
4
+ /*********************************************************************************/
5
+ /**
6
+ * For use with
7
+ * iCalcreator (kigkonsult.se/iCalcreator/index.php)
8
+ * copyright (c) 2011 Yitzchok Lavi
9
+ * icalcreator@onebigsystem.com
10
+ * @version 2.22
11
+ *
12
+ * This library is free software; you can redistribute it and/or
13
+ * modify it under the terms of the GNU Lesser General Public
14
+ * License as published by the Free Software Foundation; either
15
+ * version 2.1 of the License, or (at your option) any later version.
16
+ *
17
+ * This library is distributed in the hope that it will be useful,
18
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20
+ * Lesser General Public License for more details.
21
+ *
22
+ * You should have received a copy of the GNU Lesser General Public
23
+ * License along with this library; if not, write to the Free Software
24
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25
+ */
26
+ /**
27
+ * Additional functions to use with vtimezone components
28
+ *
29
+ * Before calling the functions, set time zone 'GMT' ('date_default_timezone_set')!
30
+ *
31
+ * @author Yitzchok Lavi <icalcreator@onebigsystem.com>
32
+ * adjusted for iCalcreator Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
33
+ * @version 1.0.2 - 2011-02-24
34
+ *
35
+ */
36
+ /**
37
+ * Returns array with the offset information from UTC for a (UTC) datetime/timestamp in the
38
+ * timezone, according to the VTIMEZONE information in the input array.
39
+ *
40
+ * @param array $timezonesarray output from function getTimezonesAsDateArrays (below)
41
+ * @param string $tzid time zone identifier
42
+ * @param mixed $timestamp timestamp or a UTC datetime (in array format)
43
+ * @return array time zone data with keys for 'offsetHis', 'offsetSec' and 'tzname'
44
+ *
45
+ */
46
+ function wpsbc_getTzOffsetForDate($timezonesarray, $tzid, $timestamp) {
47
+ if( is_array( $timestamp )) {
48
+ //$disp = sprintf( '%04d%02d%02d %02d%02d%02d', $timestamp['year'], $timestamp['month'], $timestamp['day'], $timestamp['hour'], $timestamp['min'], $timestamp['sec'] ); // test ###
49
+ $timestamp = gmmktime(
50
+ $timestamp['hour'],
51
+ $timestamp['min'],
52
+ $timestamp['sec'],
53
+ $timestamp['month'],
54
+ $timestamp['day'],
55
+ $timestamp['year']
56
+ ) ;
57
+ }
58
+ $tzoffset = array();
59
+ // something to return if all goes wrong (such as if $tzid doesn't find us an array of dates)
60
+ $tzoffset['offsetHis'] = '+0000';
61
+ $tzoffset['offsetSec'] = 0;
62
+ $tzoffset['tzname'] = '?';
63
+ if( !isset( $timezonesarray[$tzid] ))
64
+ return $tzoffset;
65
+ $tzdatearray = $timezonesarray[$tzid];
66
+ if ( is_array($tzdatearray) ) {
67
+ sort($tzdatearray); // just in case
68
+ if ( $timestamp < $tzdatearray[0]['timestamp'] ) {
69
+ // our date is before the first change
70
+ $tzoffset['offsetHis'] = $tzdatearray[0]['tzbefore']['offsetHis'] ;
71
+ $tzoffset['offsetSec'] = $tzdatearray[0]['tzbefore']['offsetSec'] ;
72
+ $tzoffset['tzname'] = $tzdatearray[0]['tzbefore']['offsetHis'] ; // we don't know the tzname in this case
73
+ } elseif ( $timestamp >= $tzdatearray[count($tzdatearray)-1]['timestamp'] ) {
74
+ // our date is after the last change (we do this so our scan can stop at the last record but one)
75
+ $tzoffset['offsetHis'] = $tzdatearray[count($tzdatearray)-1]['tzafter']['offsetHis'] ;
76
+ $tzoffset['offsetSec'] = $tzdatearray[count($tzdatearray)-1]['tzafter']['offsetSec'] ;
77
+ $tzoffset['tzname'] = $tzdatearray[count($tzdatearray)-1]['tzafter']['tzname'] ;
78
+ } else {
79
+ // our date somewhere in between
80
+ // loop through the list of dates and stop at the one where the timestamp is before our date and the next one is after it
81
+ // we don't include the last date in our loop as there isn't one after it to check
82
+ for ( $i = 0 ; $i <= count($tzdatearray)-2 ; $i++ ) {
83
+ if(( $timestamp >= $tzdatearray[$i]['timestamp'] ) && ( $timestamp < $tzdatearray[$i+1]['timestamp'] )) {
84
+ $tzoffset['offsetHis'] = $tzdatearray[$i]['tzafter']['offsetHis'] ;
85
+ $tzoffset['offsetSec'] = $tzdatearray[$i]['tzafter']['offsetSec'] ;
86
+ $tzoffset['tzname'] = $tzdatearray[$i]['tzafter']['tzname'] ;
87
+ break;
88
+ }
89
+ }
90
+ }
91
+ }
92
+ return $tzoffset;
93
+ }
94
+ /**
95
+ * Returns an array containing all the timezone data in the vcalendar object
96
+ *
97
+ * @param object $vcalendar iCalcreator calendar instance
98
+ * @return array time zone transition timestamp, array before(offsetHis, offsetSec), array after(offsetHis, offsetSec, tzname)
99
+ * based on the timezone data in the vcalendar object
100
+ *
101
+ */
102
+ function wpsbc_getTimezonesAsDateArrays($vcalendar) {
103
+ $timezonedata = array();
104
+ while( $vtz = $vcalendar->getComponent( 'vtimezone' )) {
105
+ $tzid = $vtz->getProperty('tzid');
106
+ $alltzdates = array();
107
+ while ( $vtzc = $vtz->getComponent( 'standard' )) {
108
+ $newtzdates = expandTimezoneDates($vtzc);
109
+ $alltzdates = array_merge($alltzdates, $newtzdates);
110
+ }
111
+ while ( $vtzc = $vtz->getComponent( 'daylight' )) {
112
+ $newtzdates = expandTimezoneDates($vtzc);
113
+ $alltzdates = array_merge($alltzdates, $newtzdates);
114
+ }
115
+ sort($alltzdates);
116
+ $timezonedata[$tzid] = $alltzdates;
117
+ }
118
+ return $timezonedata;
119
+ }
120
+ /**
121
+ * Returns an array containing time zone data from vtimezone standard/daylight instances
122
+ *
123
+ * @param object $vtzc an iCalcreator calendar standard/daylight instance
124
+ * @return array time zone data; array before(offsetHis, offsetSec), array after(offsetHis, offsetSec, tzname)
125
+ *
126
+ */
127
+ function wpsbc_expandTimezoneDates($vtzc) {
128
+ $tzdates = array();
129
+ // prepare time zone "description" to attach to each change
130
+ $tzbefore = array();
131
+ $tzbefore['offsetHis'] = $vtzc->getProperty('tzoffsetfrom') ;
132
+ $tzbefore['offsetSec'] = iCalUtilityFunctions::_tz2offset($tzbefore['offsetHis']);
133
+ if(( '-' != substr( (string) $tzbefore['offsetSec'], 0, 1 )) && ( '+' != substr( (string) $tzbefore['offsetSec'], 0, 1 )))
134
+ $tzbefore['offsetSec'] = '+'.$tzbefore['offsetSec'];
135
+ $tzafter = array();
136
+ $tzafter['offsetHis'] = $vtzc->getProperty('tzoffsetto') ;
137
+ $tzafter['offsetSec'] = iCalUtilityFunctions::_tz2offset($tzafter['offsetHis']);
138
+ if(( '-' != substr( (string) $tzafter['offsetSec'], 0, 1 )) && ( '+' != substr( (string) $tzafter['offsetSec'], 0, 1 )))
139
+ $tzafter['offsetSec'] = '+'.$tzafter['offsetSec'];
140
+ if( FALSE === ( $tzafter['tzname'] = $vtzc->getProperty('tzname')))
141
+ $tzafter['tzname'] = $tzafter['offsetHis'];
142
+ // find out where to start from
143
+ $dtstart = $vtzc->getProperty('dtstart');
144
+ $dtstarttimestamp = mktime(
145
+ $dtstart['hour'],
146
+ $dtstart['min'],
147
+ $dtstart['sec'],
148
+ $dtstart['month'],
149
+ $dtstart['day'],
150
+ $dtstart['year']
151
+ ) ;
152
+ if( !isset( $dtstart['unparsedtext'] )) // ??
153
+ $dtstart['unparsedtext'] = sprintf( '%04d%02d%02dT%02d%02d%02d', $dtstart['year'], $dtstart['month'], $dtstart['day'], $dtstart['hour'], $dtstart['min'], $dtstart['sec'] );
154
+ if ( $dtstarttimestamp == 0 ) {
155
+ // it seems that the dtstart string may not have parsed correctly
156
+ // let's set a timestamp starting from 1902, using the time part of the original string
157
+ // so that the time will change at the right time of day
158
+ // at worst we'll get midnight again
159
+ $origdtstartsplit = explode('T',$dtstart['unparsedtext']) ;
160
+ $dtstarttimestamp = strtotime("19020101",0);
161
+ $dtstarttimestamp = strtotime($origdtstartsplit[1],$dtstarttimestamp);
162
+ }
163
+ // the date (in dtstart and opt RDATE/RRULE) is ALWAYS LOCAL (not utc!!), adjust from 'utc' to 'local' timestamp
164
+ $diff = -1 * $tzbefore['offsetSec'];
165
+ $dtstarttimestamp += $diff;
166
+ // add this (start) change to the array of changes
167
+ $tzdates[] = array(
168
+ 'timestamp' => $dtstarttimestamp,
169
+ 'tzbefore' => $tzbefore,
170
+ 'tzafter' => $tzafter
171
+ );
172
+ $datearray = getdate($dtstarttimestamp);
173
+ // save original array to use time parts, because strtotime (used below) apparently loses the time
174
+ $changetime = $datearray ;
175
+ // generate dates according to an RRULE line
176
+ $rrule = $vtzc->getProperty('rrule') ;
177
+ if ( is_array($rrule) ) {
178
+ if ( $rrule['FREQ'] == 'YEARLY' ) {
179
+ // calculate transition dates starting from DTSTART
180
+ $offsetchangetimestamp = $dtstarttimestamp;
181
+ // calculate transition dates until 10 years in the future
182
+ $stoptimestamp = strtotime("+10 year",time());
183
+ // if UNTIL is set, calculate until then (however far ahead)
184
+ if ( isset( $rrule['UNTIL'] ) && ( $rrule['UNTIL'] != '' )) {
185
+ $stoptimestamp = mktime(
186
+ $rrule['UNTIL']['hour'],
187
+ $rrule['UNTIL']['min'],
188
+ $rrule['UNTIL']['sec'],
189
+ $rrule['UNTIL']['month'],
190
+ $rrule['UNTIL']['day'],
191
+ $rrule['UNTIL']['year']
192
+ ) ;
193
+ }
194
+ $count = 0 ;
195
+ $stopcount = isset( $rrule['COUNT'] ) ? $rrule['COUNT'] : 0 ;
196
+ $daynames = array(
197
+ 'SU' => 'Sunday',
198
+ 'MO' => 'Monday',
199
+ 'TU' => 'Tuesday',
200
+ 'WE' => 'Wednesday',
201
+ 'TH' => 'Thursday',
202
+ 'FR' => 'Friday',
203
+ 'SA' => 'Saturday'
204
+ );
205
+ // repeat so long as we're between DTSTART and UNTIL, or we haven't prepared COUNT dates
206
+ while ( $offsetchangetimestamp < $stoptimestamp && ( $stopcount == 0 || $count < $stopcount ) ) {
207
+ // break up the timestamp into its parts
208
+ $datearray = getdate($offsetchangetimestamp);
209
+ if ( isset( $rrule['BYMONTH'] ) && ( $rrule['BYMONTH'] != 0 )) {
210
+ // set the month
211
+ $datearray['mon'] = $rrule['BYMONTH'] ;
212
+ }
213
+ if ( isset( $rrule['BYMONTHDAY'] ) && ( $rrule['BYMONTHDAY'] != 0 )) {
214
+ // set specific day of month
215
+ $datearray['mday'] = $rrule['BYMONTHDAY'];
216
+ } elseif ( is_array($rrule['BYDAY']) ) {
217
+ // find the Xth WKDAY in the month
218
+ // the starting point for this process is the first of the month set above
219
+ $datearray['mday'] = 1 ;
220
+ // turn $datearray as it is now back into a timestamp
221
+ $offsetchangetimestamp = mktime(
222
+ $datearray['hours'],
223
+ $datearray['minutes'],
224
+ $datearray['seconds'],
225
+ $datearray['mon'],
226
+ $datearray['mday'],
227
+ $datearray['year']
228
+ );
229
+ if ($rrule['BYDAY'][0] > 0) {
230
+ // to find Xth WKDAY in month, we find last WKDAY in month before
231
+ // we do that by finding first WKDAY in this month and going back one week
232
+ // then we add X weeks (below)
233
+ $offsetchangetimestamp = strtotime($daynames[$rrule['BYDAY']['DAY']],$offsetchangetimestamp);
234
+ $offsetchangetimestamp = strtotime("-1 week",$offsetchangetimestamp);
235
+ } else {
236
+ // to find Xth WKDAY before the end of the month, we find the first WKDAY in the following month
237
+ // we do that by going forward one month and going to WKDAY there
238
+ // then we subtract X weeks (below)
239
+ $offsetchangetimestamp = strtotime("+1 month",$offsetchangetimestamp);
240
+ $offsetchangetimestamp = strtotime($daynames[$rrule['BYDAY']['DAY']],$offsetchangetimestamp);
241
+ }
242
+ // now move forward or back the appropriate number of weeks, into the month we want
243
+ $offsetchangetimestamp = strtotime($rrule['BYDAY'][0] . " week",$offsetchangetimestamp);
244
+ $datearray = getdate($offsetchangetimestamp);
245
+ }
246
+ // convert the date parts back into a timestamp, setting the time parts according to the
247
+ // original time data which we stored
248
+ $offsetchangetimestamp = mktime(
249
+ $changetime['hours'],
250
+ $changetime['minutes'],
251
+ $changetime['seconds'] + $diff,
252
+ $datearray['mon'],
253
+ $datearray['mday'],
254
+ $datearray['year']
255
+ );
256
+ // add this change to the array of changes
257
+ $tzdates[] = array(
258
+ 'timestamp' => $offsetchangetimestamp,
259
+ 'tzbefore' => $tzbefore,
260
+ 'tzafter' => $tzafter
261
+ );
262
+ // update counters (timestamp and count)
263
+ $offsetchangetimestamp = strtotime("+" . (( isset( $rrule['INTERVAL'] ) && ( $rrule['INTERVAL'] != 0 )) ? $rrule['INTERVAL'] : 1 ) . " year",$offsetchangetimestamp);
264
+ $count += 1 ;
265
+ }
266
+ }
267
+ }
268
+ // generate dates according to RDATE lines
269
+ while ($rdates = $vtzc->getProperty('rdate')) {
270
+ if ( is_array($rdates) ) {
271
+
272
+ foreach ( $rdates as $rdate ) {
273
+ // convert the explicit change date to a timestamp
274
+ $offsetchangetimestamp = mktime(
275
+ $rdate['hour'],
276
+ $rdate['min'],
277
+ $rdate['sec'] + $diff,
278
+ $rdate['month'],
279
+ $rdate['day'],
280
+ $rdate['year']
281
+ ) ;
282
+ // add this change to the array of changes
283
+ $tzdates[] = array(
284
+ 'timestamp' => $offsetchangetimestamp,
285
+ 'tzbefore' => $tzbefore,
286
+ 'tzafter' => $tzafter
287
+ );
288
+ }
289
+ }
290
+ }
291
+ return $tzdates;
292
+ }
includes/libs/iCal/lib/iCal.vCard.inc.php ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*********************************************************************************/
3
+ /**
4
+ *
5
+ * iCalcreator, a PHP rfc2445/rfc5545 solution.
6
+ *
7
+ * @copyright Copyright (c) 2007-2015 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
8
+ * @link http://kigkonsult.se/iCalcreator/index.php
9
+ * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
10
+ * @package iCalcreator
11
+ * @version 2.22
12
+ */
13
+ /**
14
+ * This library is free software; you can redistribute it and/or
15
+ * modify it under the terms of the GNU Lesser General Public
16
+ * License as published by the Free Software Foundation; either
17
+ * version 2.1 of the License, or (at your option) any later version.
18
+ *
19
+ * This library is distributed in the hope that it will be useful,
20
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
+ * Lesser General Public License for more details.
23
+ *
24
+ * You should have received a copy of the GNU Lesser General Public
25
+ * License along with this library; if not, write to the Free Software
26
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
+ */
28
+ /*********************************************************************************/
29
+ /* iCalcreator vCard helper functions */
30
+ /*********************************************************************************/
31
+ /**
32
+ * convert single ATTENDEE, CONTACT or ORGANIZER (in email format) to vCard
33
+ * returns vCard/TRUE or if directory (if set) or file write is unvalid, FALSE
34
+ *
35
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
36
+ * @since 2.12.2 - 2012-07-11
37
+ * @param string $email
38
+ * @param string $version vCard version (default 2.1)
39
+ * @param string $directory where to save vCards (default FALSE)
40
+ * @param string $ext vCard file extension (default 'vcf')
41
+ * @uses ICALCREATOR_VERSION
42
+ * @return mixed
43
+ */
44
+ function wpsbc_iCal2vCard( $email, $version='2.1', $directory=FALSE, $ext='vcf' ) {
45
+ if( FALSE === ( $pos = strpos( $email, '@' )))
46
+ return FALSE;
47
+ if( $directory ) {
48
+ if( DIRECTORY_SEPARATOR != substr( $directory, ( 0 - strlen( DIRECTORY_SEPARATOR ))))
49
+ $directory .= DIRECTORY_SEPARATOR;
50
+ if( !is_dir( $directory ) || !is_writable( $directory ))
51
+ return FALSE;
52
+ }
53
+ /* prepare vCard */
54
+ $email = str_replace( 'MAILTO:', '', $email );
55
+ $name = $person = substr( $email, 0, $pos );
56
+ if( ctype_upper( $name ) || ctype_lower( $name ))
57
+ $name = array( $name );
58
+ else {
59
+ if( FALSE !== ( $pos = strpos( $name, '.' ))) {
60
+ $name = explode( '.', $name );
61
+ foreach( $name as $k => $part )
62
+ $name[$k] = ucfirst( $part );
63
+ }
64
+ else { // split camelCase
65
+ $chars = $name;
66
+ $name = array( $chars[0] );
67
+ $k = 0;
68
+ $x = 1;
69
+ while( FALSE !== ( $char = substr( $chars, $x, 1 ))) {
70
+ if( ctype_upper( $char )) {
71
+ $k += 1;
72
+ $name[$k] = '';
73
+ }
74
+ $name[$k] .= $char;
75
+ $x++;
76
+ }
77
+ }
78
+ }
79
+ $nl = "\r\n";
80
+ $FN = 'FN:'.implode( ' ', $name ).$nl;
81
+ $name = array_reverse( $name );
82
+ $N = 'N:'.array_shift( $name );
83
+ $scCnt = 0;
84
+ while( NULL != ( $part = array_shift( $name ))) {
85
+ if(( '4.0' != $version ) || ( 4 > $scCnt ))
86
+ $scCnt += 1;
87
+ $N .= ';'.$part;
88
+ }
89
+ while(( '4.0' == $version ) && ( 4 > $scCnt )) {
90
+ $N .= ';';
91
+ $scCnt += 1;
92
+ }
93
+ $N .= $nl;
94
+ $EMAIL = 'EMAIL:'.$email.$nl;
95
+ /* create vCard */
96
+ $vCard = 'BEGIN:VCARD'.$nl;
97
+ $vCard .= "VERSION:$version$nl";
98
+ $vCard .= 'PRODID:-//kigkonsult.se '.ICALCREATOR_VERSION."//$nl";
99
+ $vCard .= $N;
100
+ $vCard .= $FN;
101
+ $vCard .= $EMAIL;
102
+ $vCard .= 'REV:'.gmdate( 'Ymd\THis\Z' ).$nl;
103
+ $vCard .= 'END:VCARD'.$nl;
104
+ /* save each vCard as (unique) single file */
105
+ if( $directory ) {
106
+ $fname = $directory.preg_replace( '/[^a-z0-9.]/i', '', $email );
107
+ $cnt = 1;
108
+ $dbl = '';
109
+ while( is_file ( $fname.$dbl.'.'.$ext )) {
110
+ $cnt += 1;
111
+ $dbl = "_$cnt";
112
+ }
113
+ if( FALSE === file_put_contents( $fname, $fname.$dbl.'.'.$ext ))
114
+ return FALSE;
115
+ return TRUE;
116
+ }
117
+ /* return vCard */
118
+ else
119
+ return $vCard;
120
+ }
121
+ /**
122
+ * convert ATTENDEEs, CONTACTs and ORGANIZERs (in email format) to vCards
123
+ *
124
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
125
+ * @since 2.12.2 - 2012-05-07
126
+ * @param object $calendar iCalcreator vcalendar instance reference
127
+ * @param string $version vCard version (default 2.1)
128
+ * @param string $directory where to save vCards (default FALSE)
129
+ * @param string $ext vCard file extension (default 'vcf')
130
+ * @uses vcalendar::getProperty()
131
+ * @return mixed
132
+ */
133
+ function wpsbc_iCal2vCards( & $calendar, $version='2.1', $directory=FALSE, $ext='vcf' ) {
134
+ $hits = array();
135
+ $vCardP = array( 'ATTENDEE', 'CONTACT', 'ORGANIZER' );
136
+ foreach( $vCardP as $prop ) {
137
+ $hits2 = $calendar->getProperty( $prop );
138
+ foreach( $hits2 as $propValue => $occCnt ) {
139
+ if( FALSE === ( $pos = strpos( $propValue, '@' )))
140
+ continue;
141
+ $propValue = str_replace( 'MAILTO:', '', $propValue );
142
+ if( isset( $hits[$propValue] ))
143
+ $hits[$propValue] += $occCnt;
144
+ else
145
+ $hits[$propValue] = $occCnt;
146
+ }
147
+ }
148
+ if( empty( $hits ))
149
+ return FALSE;
150
+ ksort( $hits );
151
+ $output = '';
152
+ foreach( $hits as $email => $skip ) {
153
+ $res = wpsbc_iCal2vCard( $email, $version, $directory, $ext );
154
+ if( $directory && !$res )
155
+ return FALSE;
156
+ elseif( !$res )
157
+ return $res;
158
+ else
159
+ $output .= $res;
160
+ }
161
+ if( $directory )
162
+ return TRUE;
163
+ if( !empty( $output ))
164
+ return $output;
165
+ return FALSE;
166
+ }
includes/libs/iCal/lib/iCalBase.class.php ADDED
@@ -0,0 +1,427 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*********************************************************************************/
3
+ /**
4
+ *
5
+ * iCalcreator, a PHP rfc2445/rfc5545 solution.
6
+ *
7
+ * @copyright Copyright (c) 2007-2015 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
8
+ * @link http://kigkonsult.se/iCalcreator/index.php
9
+ * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
10
+ * @package iCalcreator
11
+ * @version 2.22
12
+ */
13
+ /**
14
+ * This library is free software; you can redistribute it and/or
15
+ * modify it under the terms of the GNU Lesser General Public
16
+ * License as published by the Free Software Foundation; either
17
+ * version 2.1 of the License, or (at your option) any later version.
18
+ *
19
+ * This library is distributed in the hope that it will be useful,
20
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
+ * Lesser General Public License for more details.
23
+ *
24
+ * You should have received a copy of the GNU Lesser General Public
25
+ * License along with this library; if not, write to the Free Software
26
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
+ */
28
+ /*********************************************************************************/
29
+ /**
30
+ * iCalcreator base class
31
+ *
32
+ * properties and functions shared by vcalendar and calendarComponents
33
+ *
34
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
35
+ * @since 2.21.11 - 2015-03-31
36
+ */
37
+ abstract class iCalBase {
38
+ /**
39
+ * @var array component property X-property value
40
+ * @access protected
41
+ */
42
+ protected $xprop;
43
+ /** @var array container for sub-components */
44
+ public $components;
45
+ /** @var array $unparsed calendar/components in 'raw' text... */
46
+ public $unparsed;
47
+ /**
48
+ * @var bool $allowEmpty config variable
49
+ * @var string $language config variable
50
+ * @var string $nl config variable
51
+ * @var string $unique_id config variable
52
+ * @var string $format config variable
53
+ * @var string $dtzid config variable
54
+ * @access protected
55
+ */
56
+ protected $allowEmpty;
57
+ protected $language;
58
+ protected $nl;
59
+ protected $unique_id;
60
+ protected $format;
61
+ protected $dtzid;
62
+ /**
63
+ * @var string $componentStart1 valendar/component internal variable
64
+ * @var string $componentStart2 valendar/component internal variable
65
+ * @var string $componentEnd1 valendar/component internal variable
66
+ * @var string $componentEnd2 valendar/component internal variable
67
+ * @var string $elementStart1 valendar/component internal variable
68
+ * @var string $elementStart2 valendar/component internal variable
69
+ * @var string $elementEnd1 valendar/component internal variable
70
+ * @var string $elementEnd2 valendar/component internal variable
71
+ * @var string $intAttrDelimiter valendar/component internal variable
72
+ * @var string $attributeDelimiter valendar/component internal variable
73
+ * @var string $valueInit valendar/component internal variable
74
+ * @access protected
75
+ */
76
+ protected $componentStart1;
77
+ protected $componentStart2;
78
+ protected $componentEnd1;
79
+ protected $componentEnd2;
80
+ protected $elementStart1;
81
+ protected $elementStart2;
82
+ protected $elementEnd1;
83
+ protected $elementEnd2;
84
+ protected $intAttrDelimiter;
85
+ protected $attributeDelimiter;
86
+ protected $valueInit;
87
+ /**
88
+ * @var array $xcaldecl xCal declaration container
89
+ * @access protected
90
+ */
91
+ protected $xcaldecl;
92
+ /**
93
+ * Property Name: x-prop
94
+ */
95
+ /**
96
+ * creates formatted output for calendar/component property x-prop
97
+ *
98
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
99
+ * @since 2.21.11 - 2015-03-31
100
+ * @uses iCalBase::$xprop
101
+ * @uses vcalendar::getConfig()
102
+ * @uses calendarComponent::getConfig()
103
+ * @uses iCalBase::_createElement()
104
+ * @uses iCalBase::_createParams()
105
+ * @uses iCalUtilityFunctions::_strrep()
106
+ * @uses iCalBase::$format
107
+ * @uses iCalBase::$nl
108
+ * @return string
109
+ */
110
+ public function createXprop() {
111
+ if( empty( $this->xprop ) || !is_array( $this->xprop )) return FALSE;
112
+ $output = null;
113
+ foreach( $this->xprop as $label => $xpropPart ) {
114
+ if( ! isset( $xpropPart['value']) || ( empty( $xpropPart['value'] ) && !is_numeric( $xpropPart['value'] ))) {
115
+ if( $this->getConfig( 'allowEmpty' ))
116
+ $output .= $this->_createElement( $label );
117
+ continue;
118
+ }
119
+ $attributes = $this->_createParams( $xpropPart['params'], array( 'LANGUAGE' ));
120
+ if( is_array( $xpropPart['value'] )) {
121
+ foreach( $xpropPart['value'] as $pix => $theXpart )
122
+ $xpropPart['value'][$pix] = iCalUtilityFunctions::_strrep( $theXpart, $this->format, $this->nl );
123
+ $xpropPart['value'] = implode( ',', $xpropPart['value'] );
124
+ }
125
+ else
126
+ $xpropPart['value'] = iCalUtilityFunctions::_strrep( $xpropPart['value'], $this->format, $this->nl );
127
+ $output .= $this->_createElement( $label, $attributes, $xpropPart['value'] );
128
+ }
129
+ return $output;
130
+ }
131
+ /**
132
+ * set calendar property x-prop
133
+ *
134
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
135
+ * @since 2.21.11 - 2015-03-31
136
+ * @param string $label
137
+ * @param string $value
138
+ * @param array $params optional
139
+ * @uses vcalendar::getConfig()
140
+ * @uses iCalUtilityFunctions::_setParams()
141
+ * @uses iCalBase::$xprop
142
+ * @return bool
143
+ */
144
+ public function setXprop( $label, $value, $params=FALSE ) {
145
+ if( empty( $label ))
146
+ return FALSE;
147
+ $label = strtoupper( $label );
148
+ if( 'X-' != substr( $label, 0, 2 ))
149
+ return FALSE;
150
+ if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = ''; else return FALSE;
151
+ $xprop = array( 'value' => $value );
152
+ $xprop['params'] = iCalUtilityFunctions::_setParams( $params );
153
+ if( ! is_array( $this->xprop ))
154
+ $this->xprop = array();
155
+ $this->xprop[$label] = $xprop;
156
+ return TRUE;
157
+ }
158
+ /**
159
+ * create element format parts
160
+ *
161
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
162
+ * @since 2.21.11 - 2015-03-31
163
+ * @uses iCalBase::$format
164
+ * @uses iCalBase::$objName
165
+ * @uses iCalBase::$componentStart1
166
+ * @uses iCalBase::$elementStart1
167
+ * @uses iCalBase::$componentStart2
168
+ * @uses iCalBase::$elementStart2
169
+ * @uses iCalBase::$componentEnd1
170
+ * @uses iCalBase::$elementEnd1
171
+ * @uses iCalBase::$componentEnd2
172
+ * @uses iCalBase::$elementEnd2
173
+ * @uses iCalBase::$nl;
174
+ * @uses iCalBase::$intAttrDelimiter
175
+ * @uses iCalBase::$attributeDelimiter
176
+ * @uses iCalBase::$valueInit
177
+ * @return bool
178
+ */
179
+ function _createFormat() {
180
+ switch( $this->format ) {
181
+ case 'xcal':
182
+ $this->componentStart1 = $this->elementStart1 = '<';
183
+ $this->componentStart2 = $this->elementStart2 = '>';
184
+ $this->componentEnd1 = $this->elementEnd1 = '</';
185
+ $this->componentEnd2 = $this->elementEnd2 = '>'.$this->nl;
186
+ $this->intAttrDelimiter = '<!-- -->';
187
+ $this->attributeDelimiter = $this->nl;
188
+ $this->valueInit = null;
189
+ break;
190
+ default:
191
+ $this->componentStart1 = 'BEGIN:';
192
+ $this->componentStart2 = null;
193
+ $this->componentEnd1 = 'END:';
194
+ $this->componentEnd2 = $this->nl;
195
+ $this->elementStart1 = null;
196
+ $this->elementStart2 = null;
197
+ $this->elementEnd1 = null;
198
+ $this->elementEnd2 = $this->nl;
199
+ $this->intAttrDelimiter = '<!-- -->';
200
+ $this->attributeDelimiter = ';';
201
+ $this->valueInit = ':';
202
+ break;
203
+ }
204
+ return TRUE;
205
+ }
206
+ /**
207
+ * creates formatted output for calendar component property
208
+ *
209
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
210
+ * @since 2.21.11 - 2015-03-31
211
+ * @param string $label property name
212
+ * @param string $attributes property attributes
213
+ * @param string $content property content (optional)
214
+ * @uses iCalBase::$format
215
+ * @uses iCalBase::$elementStart1
216
+ * @uses iCalBase::$xcaldecl
217
+ * @uses iCalBase::$intAttrDelimiter
218
+ * @uses iCalBase::$attributeDelimiter
219
+ * @uses iCalBase::_createElement()
220
+ * @uses iCalBase::$elementStart2
221
+ * @uses iCalBase::$nl
222
+ * @uses iCalBase::$valueInit
223
+ * @uses iCalUtilityFunctions::_size75()
224
+ * @uses iCalBase::$elementEnd1
225
+ * @uses iCalBase::$elementEnd2
226
+ * @access protected
227
+ * @return string
228
+ */
229
+ protected function _createElement( $label, $attributes=null, $content=FALSE ) {
230
+ switch( $this->format ) {
231
+ case 'xcal':
232
+ $label = strtolower( $label );
233
+ break;
234
+ default:
235
+ $label = strtoupper( $label );
236
+ break;
237
+ }
238
+ $output = $this->elementStart1.$label;
239
+ $categoriesAttrLang = null;
240
+ $attachInlineBinary = FALSE;
241
+ $attachfmttype = null;
242
+ if (( 'xcal' == $this->format) && ( 'x-' == substr( $label, 0, 2 ))) {
243
+ $this->xcaldecl[] = array( 'xmldecl' => 'ELEMENT'
244
+ , 'ref' => $label
245
+ , 'type2' => '(#PCDATA)' );
246
+ }
247
+ if( !empty( $attributes )) {
248
+ $attributes = trim( $attributes );
249
+ if ( 'xcal' == $this->format ) {
250
+ $attributes2 = explode( $this->intAttrDelimiter, $attributes );
251
+ $attributes = null;
252
+ foreach( $attributes2 as $aix => $attribute ) {
253
+ $attrKVarr = explode( '=', $attribute );
254
+ if( empty( $attrKVarr[0] ))
255
+ continue;
256
+ if( !isset( $attrKVarr[1] )) {
257
+ $attrValue = $attrKVarr[0];
258
+ $attrKey = $aix;
259
+ }
260
+ elseif( 2 == count( $attrKVarr)) {
261
+ $attrKey = strtolower( $attrKVarr[0] );
262
+ $attrValue = $attrKVarr[1];
263
+ }
264
+ else {
265
+ $attrKey = strtolower( $attrKVarr[0] );
266
+ unset( $attrKVarr[0] );
267
+ $attrValue = implode( '=', $attrKVarr );
268
+ }
269
+ if(( 'attach' == $label ) && ( in_array( $attrKey, array( 'fmttype', 'encoding', 'value' )))) {
270
+ $attachInlineBinary = TRUE;
271
+ if( 'fmttype' == $attrKey )
272
+ $attachfmttype = $attrKey.'='.$attrValue;
273
+ continue;
274
+ }
275
+ elseif(( 'categories' == $label ) && ( 'language' == $attrKey ))
276
+ $categoriesAttrLang = $attrKey.'='.$attrValue;
277
+ else {
278
+ $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' ';
279
+ $attributes .= ( !empty( $attrKey )) ? $attrKey.'=' : null;
280
+ if(( '"' == substr( $attrValue, 0, 1 )) && ( '"' == substr( $attrValue, -1 ))) {
281
+ $attrValue = substr( $attrValue, 1, ( strlen( $attrValue ) - 2 ));
282
+ $attrValue = str_replace( '"', '', $attrValue );
283
+ }
284
+ $attributes .= '"'.htmlspecialchars( $attrValue ).'"';
285
+ }
286
+ }
287
+ }
288
+ else {
289
+ $attributes = str_replace( $this->intAttrDelimiter, $this->attributeDelimiter, $attributes );
290
+ }
291
+ }
292
+ if(( 'xcal' == $this->format) &&
293
+ ((( 'attach' == $label ) && !$attachInlineBinary ) || ( in_array( $label, array( 'tzurl', 'url' ))))) {
294
+ $pos = strrpos( $content, "/" );
295
+ $docname = ( $pos !== false) ? substr( $content, (1 - strlen( $content ) + $pos )) : $content;
296
+ $this->xcaldecl[] = array( 'xmldecl' => 'ENTITY'
297
+ , 'uri' => $docname
298
+ , 'ref' => 'SYSTEM'
299
+ , 'external' => $content
300
+ , 'type' => 'NDATA'
301
+ , 'type2' => 'BINERY' );
302
+ $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' ';
303
+ $attributes .= 'uri="'.$docname.'"';
304
+ $content = null;
305
+ if( 'attach' == $label ) {
306
+ $attributes = str_replace( $this->attributeDelimiter, $this->intAttrDelimiter, $attributes );
307
+ $content = $this->nl.$this->_createElement( 'extref', $attributes, null );
308
+ $attributes = null;
309
+ }
310
+ }
311
+ elseif(( 'xcal' == $this->format) && ( 'attach' == $label ) && $attachInlineBinary ) {
312
+ $content = $this->nl.$this->_createElement( 'b64bin', $attachfmttype, $content ); // max one attribute
313
+ }
314
+ $output .= $attributes;
315
+ if( !$content && ( '0' != $content )) {
316
+ switch( $this->format ) {
317
+ case 'xcal':
318
+ $output .= ' /';
319
+ $output .= $this->elementStart2.$this->nl;
320
+ return $output;
321
+ break;
322
+ default:
323
+ $output .= $this->elementStart2.$this->valueInit;
324
+ return iCalUtilityFunctions::_size75( $output, $this->nl );
325
+ break;
326
+ }
327
+ }
328
+ $output .= $this->elementStart2;
329
+ $output .= $this->valueInit.$content;
330
+ switch( $this->format ) {
331
+ case 'xcal':
332
+ return $output.$this->elementEnd1.$label.$this->elementEnd2;
333
+ break;
334
+ default:
335
+ return iCalUtilityFunctions::_size75( $output, $this->nl );
336
+ break;
337
+ }
338
+ }
339
+ /**
340
+ * creates formatted output for calendar component property parameters
341
+ *
342
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
343
+ * @since 2.21.11 - 2015-03-31
344
+ * @param array $params optional
345
+ * @param array $ctrKeys optional
346
+ * @uses iCalBase::$intAttrDelimiter
347
+ * @uses vcalendar::getConfig()
348
+ * @uses calendarComponent::getConfig()
349
+ * @access protected
350
+ * @return string
351
+ */
352
+ protected function _createParams( $params=array(), $ctrKeys=array() ) {
353
+ if( !is_array( $params ) || empty( $params ))
354
+ $params = array();
355
+ $attrLANG = $attr1 = $attr2 = $lang = null;
356
+ $CNattrKey = ( in_array( 'CN', $ctrKeys )) ? TRUE : FALSE ;
357
+ $LANGattrKey = ( in_array( 'LANGUAGE', $ctrKeys )) ? TRUE : FALSE ;
358
+ $CNattrExist = $LANGattrExist = FALSE;
359
+ $xparams = array();
360
+ $params = array_change_key_case( $params, CASE_UPPER );
361
+ foreach( $params as $paramKey => $paramValue ) {
362
+ if(( FALSE !== strpos( $paramValue, ':' )) ||
363
+ ( FALSE !== strpos( $paramValue, ';' )) ||
364
+ ( FALSE !== strpos( $paramValue, ',' )))
365
+ $paramValue = '"'.$paramValue.'"';
366
+ if( ctype_digit( (string) $paramKey )) {
367
+ $xparams[] = $paramValue;
368
+ continue;
369
+ }
370
+ if( !in_array( $paramKey, array( 'ALTREP', 'CN', 'DIR', 'ENCODING', 'FMTTYPE', 'LANGUAGE', 'RANGE', 'RELTYPE', 'SENT-BY', 'TZID', 'VALUE' )))
371
+ $xparams[$paramKey] = $paramValue;
372
+ else
373
+ $params[$paramKey] = $paramValue;
374
+ }
375
+ ksort( $xparams, SORT_STRING );
376
+ foreach( $xparams as $paramKey => $paramValue ) {
377
+ if( ctype_digit( (string) $paramKey ))
378
+ $attr2 .= $this->intAttrDelimiter.$paramValue;
379
+ else
380
+ $attr2 .= $this->intAttrDelimiter."$paramKey=$paramValue";
381
+ }
382
+ if( isset( $params['FMTTYPE'] ) && !in_array( 'FMTTYPE', $ctrKeys )) {
383
+ $attr1 .= $this->intAttrDelimiter.'FMTTYPE='.$params['FMTTYPE'].$attr2;
384
+ $attr2 = null;
385
+ }
386
+ if( isset( $params['ENCODING'] ) && !in_array( 'ENCODING', $ctrKeys )) {
387
+ if( !empty( $attr2 )) {
388
+ $attr1 .= $attr2;
389
+ $attr2 = null;
390
+ }
391
+ $attr1 .= $this->intAttrDelimiter.'ENCODING='.$params['ENCODING'];
392
+ }
393
+ if( isset( $params['VALUE'] ) && !in_array( 'VALUE', $ctrKeys ))
394
+ $attr1 .= $this->intAttrDelimiter.'VALUE='.$params['VALUE'];
395
+ if( isset( $params['TZID'] ) && !in_array( 'TZID', $ctrKeys )) {
396
+ $attr1 .= $this->intAttrDelimiter.'TZID='.$params['TZID'];
397
+ }
398
+ if( isset( $params['RANGE'] ) && !in_array( 'RANGE', $ctrKeys ))
399
+ $attr1 .= $this->intAttrDelimiter.'RANGE='.$params['RANGE'];
400
+ if( isset( $params['RELTYPE'] ) && !in_array( 'RELTYPE', $ctrKeys ))
401
+ $attr1 .= $this->intAttrDelimiter.'RELTYPE='.$params['RELTYPE'];
402
+ if( isset( $params['CN'] ) && $CNattrKey ) {
403
+ $attr1 = $this->intAttrDelimiter.'CN='.$params['CN'];
404
+ $CNattrExist = TRUE;
405
+ }
406
+ if( isset( $params['DIR'] ) && in_array( 'DIR', $ctrKeys )) {
407
+ $delim = ( FALSE !== strpos( $params['DIR'], '"' )) ? '' : '"';
408
+ $attr1 .= $this->intAttrDelimiter.'DIR='.$delim.$params['DIR'].$delim;
409
+ }
410
+ if( isset( $params['SENT-BY'] ) && in_array( 'SENT-BY', $ctrKeys ))
411
+ $attr1 .= $this->intAttrDelimiter.'SENT-BY='.$params['SENT-BY'];
412
+ if( isset( $params['ALTREP'] ) && in_array( 'ALTREP', $ctrKeys )) {
413
+ $delim = ( FALSE !== strpos( $params['ALTREP'], '"' )) ? '' : '"';
414
+ $attr1 .= $this->intAttrDelimiter.'ALTREP='.$delim.$params['ALTREP'].$delim;
415
+ }
416
+ if( isset( $params['LANGUAGE'] ) && $LANGattrKey ) {
417
+ $attrLANG .= $this->intAttrDelimiter.'LANGUAGE='.$params['LANGUAGE'];
418
+ $LANGattrExist = TRUE;
419
+ }
420
+ if( !$LANGattrExist ) {
421
+ $lang = $this->getConfig( 'language' );
422
+ if(( $CNattrExist || $LANGattrKey ) && $lang )
423
+ $attrLANG .= $this->intAttrDelimiter.'LANGUAGE='.$lang;
424
+ }
425
+ return $attr1.$attrLANG.$attr2;
426
+ }
427
+ }
includes/libs/iCal/lib/iCalUtilityFunctions.class.php ADDED
@@ -0,0 +1,2510 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*********************************************************************************/
3
+ /**
4
+ *
5
+ * A PHP implementation of rfc2445/rfc5545.
6
+ *
7
+ * @copyright Copyright (c) 2007-2015 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
8
+ * @link http://kigkonsult.se/iCalcreator/index.php
9
+ * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
10
+ * @package iCalcreator
11
+ * @version 2.22
12
+ */
13
+ /**
14
+ * This library is free software; you can redistribute it and/or
15
+ * modify it under the terms of the GNU Lesser General Public
16
+ * License as published by the Free Software Foundation; either
17
+ * version 2.1 of the License, or (at your option) any later version.
18
+ *
19
+ * This library is distributed in the hope that it will be useful,
20
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
+ * Lesser General Public License for more details.
23
+ *
24
+ * You should have received a copy of the GNU Lesser General Public
25
+ * License along with this library; if not, write to the Free Software
26
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
+ */
28
+ /*********************************************************************************/
29
+ /**
30
+ * moving all utility (static) functions to a utility class
31
+ *
32
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
33
+ * @since 2.21.11 - 2015-04-03
34
+ */
35
+ class iCalUtilityFunctions {
36
+ /** @var string tmp line delimiter, used in convEolChar (parse) */
37
+ private static $baseDelim = null;
38
+ /** @var array protocol prefix, used in _splitContent() */
39
+ private static $parValPrefix = array ( 'MStz' => array( 'utc-', 'utc+', 'gmt-', 'gmt+' )
40
+ , 'Proto3' => array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' )
41
+ , 'Proto4' => array( 'crid:', 'news:', 'pres:' )
42
+ , 'Proto6' => array( 'mailto:' ));
43
+ /** @var string output format for geo latitude and longitude (before rtrim) */
44
+ public static $geoLatFmt = '%09.6f';
45
+ public static $geoLongFmt = '%8.6f';
46
+ /** @var array date/datetime formats */
47
+ public static $fmt = array( 'Ymd' => '%04d%02d%02d',
48
+ 'His' => '%02d%02d%02d',
49
+ 'dayOfDays' => 'day %d of %d',
50
+ 'durDHis' => '%a days, %h hours, %i min, %s sec',
51
+ 'Ymd2' => 'Y-m-d',
52
+ 'YmdHis2' => 'Y-m-d H:i:s',
53
+ 'YmdHis2e' => 'Y-m-d H:i:s e',
54
+ 'YmdHis3' => 'Y-m-d-H-i-s',
55
+ 'YmdHise' => '%04d-%02d-%02d %02d:%02d:%02d %s',
56
+ 'YmdTHisO' => 'Y-m-d\TH:i:s O',
57
+ 'dateKey' => '%04d%02d%02d%02d%02d%02d000',
58
+ );
59
+ /** @var array component property UID value */
60
+ public static $vComps = array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy' );
61
+ public static $mComps = array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm', 'vtimezone' );
62
+ public static $miscComps = array( 'valarm', 'vtimezone', 'standard', 'daylight' );
63
+ public static $tzComps = array( 'vtimezone', 'standard', 'daylight' );
64
+ public static $allComps = array( 'vtimezone', 'standard', 'daylight', 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm' );
65
+ /** @var array component property collections */
66
+ public static $mProps1 = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'RELATED-TO', 'RESOURCES' );
67
+ public static $mProps2 = array( 'ATTACH', 'ATTENDEE', 'CATEGORIES', 'COMMENT', 'CONTACT', 'DESCRIPTION', 'EXDATE', 'EXRULE',
68
+ 'FREEBUSY', 'RDATE', 'RELATED-TO', 'RESOURCES', 'RRULE', 'REQUEST-STATUS', 'TZNAME', 'X-PROP' );
69
+ public static $dateProps = array( 'DTSTART', 'DTEND', 'DUE', 'CREATED', 'COMPLETED', 'DTSTAMP', 'LAST-MODIFIED', 'RECURRENCE-ID' );
70
+ public static $otherProps = array( 'ATTENDEE', 'CATEGORIES', 'CONTACT', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RELATED-TO', 'RESOURCES', 'STATUS', 'SUMMARY', 'UID', 'URL' );
71
+ /** @var object Store the single instance of iCalUtilityFunctions */
72
+ private static $m_pInstance;
73
+ /**
74
+ * Private constructor to limit object instantiation to within the class
75
+ *
76
+ * @access private
77
+ */
78
+ private function __construct() {
79
+ $m_pInstance = FALSE;
80
+ }
81
+ /** @var array component property UID value */
82
+ /**
83
+ * Getter method for creating/returning the single instance of this class
84
+ *
85
+ * @uses iCalUtilityFunctions::$m_pInstance
86
+ */
87
+ public static function getInstance() {
88
+ if (!self::$m_pInstance)
89
+ self::$m_pInstance = new iCalUtilityFunctions();
90
+ return self::$m_pInstance;
91
+ }
92
+ /**
93
+ * ensures internal date-time/date format (keyed array) for an input date-time/date array (keyed or unkeyed)
94
+ *
95
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
96
+ * @since 2.16.24 - 2013-06-26
97
+ * @param array $datetime
98
+ * @param int $parno optional, default FALSE
99
+ * @return array
100
+ */
101
+ public static function _chkDateArr( $datetime, $parno=FALSE ) {
102
+ $output = array();
103
+ if(( !$parno || ( 6 <= $parno )) && isset( $datetime[3] ) && !isset( $datetime[4] )) { // Y-m-d with tz
104
+ $temp = $datetime[3];
105
+ $datetime[3] = $datetime[4] = $datetime[5] = 0;
106
+ $datetime[6] = $temp;
107
+ }
108
+ foreach( $datetime as $dateKey => $datePart ) {
109
+ switch ( $dateKey ) {
110
+ case '0': case 'year': $output['year'] = $datePart; break;
111
+ case '1': case 'month': $output['month'] = $datePart; break;
112
+ case '2': case 'day': $output['day'] = $datePart; break;
113
+ }
114
+ if( 3 != $parno ) {
115
+ switch ( $dateKey ) {
116
+ case '0':
117
+ case '1':
118
+ case '2': break;
119
+ case '3': case 'hour': $output['hour'] = $datePart; break;
120
+ case '4': case 'min' : $output['min'] = $datePart; break;
121
+ case '5': case 'sec' : $output['sec'] = $datePart; break;
122
+ case '6': case 'tz' : $output['tz'] = $datePart; break;
123
+ }
124
+ }
125
+ }
126
+ if( 3 != $parno ) {
127
+ if( !isset( $output['hour'] )) $output['hour'] = 0;
128
+ if( !isset( $output['min'] )) $output['min'] = 0;
129
+ if( !isset( $output['sec'] )) $output['sec'] = 0;
130
+ if( isset( $output['tz'] ) &&
131
+ (( '+0000' == $output['tz'] ) || ( '-0000' == $output['tz'] ) || ( '+000000' == $output['tz'] ) || ( '-000000' == $output['tz'] )))
132
+ $output['tz'] = 'Z';
133
+ }
134
+ return $output;
135
+ }
136
+ /**
137
+ * check date(-time) and params arrays for an opt. timezone and if it is a DATE-TIME or DATE (updates $parno and params)
138
+ *
139
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
140
+ * @since 2.10.30 - 2012-01-16
141
+ * @param array $theDate date to check
142
+ * @param int $parno no of date parts (i.e. year, month.. .)
143
+ * @param array $params property parameters
144
+ * @uses iCalUtilityFunctions::_strdate2date()
145
+ * @uses iCalUtilityFunctions::_isOffset()
146
+ * @return void
147
+ */
148
+ public static function _chkdatecfg( $theDate, & $parno, & $params ) {
149
+ if( isset( $params['TZID'] ))
150
+ $parno = 6;
151
+ elseif( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] ))
152
+ $parno = 3;
153
+ else {
154
+ if( isset( $params['VALUE'] ) && ( 'PERIOD' == $params['VALUE'] ))
155
+ $parno = 7;
156
+ if( is_array( $theDate )) {
157
+ if( isset( $theDate['timestamp'] ))
158
+ $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : null;
159
+ else
160
+ $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : ( 7 == count( $theDate )) ? end( $theDate ) : null;
161
+ if( !empty( $tzid )) {
162
+ $parno = 7;
163
+ if( !iCalUtilityFunctions::_isOffset( $tzid ))
164
+ $params['TZID'] = '"' . $tzid . '"'; // save only timezone
165
+ }
166
+ elseif( !$parno && ( 3 == count( $theDate )) &&
167
+ ( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] )))
168
+ $parno = 3;
169
+ else
170
+ $parno = 6;
171
+ }
172
+ else { // string
173
+ $date = trim( $theDate );
174
+ if( 'Z' == substr( $date, -1 ))
175
+ $parno = 7; // UTC DATE-TIME
176
+ elseif((( 8 == strlen( $date ) && ctype_digit( $date )) || ( 11 >= strlen( $date ))) &&
177
+ ( !isset( $params['VALUE'] ) || !in_array( $params['VALUE'], array( 'DATE-TIME', 'PERIOD' ))))
178
+ $parno = 3; // DATE
179
+ $date = iCalUtilityFunctions::_strdate2date( $date, $parno );
180
+ unset( $date['unparsedtext'] );
181
+ if( !empty( $date['tz'] )) {
182
+ $parno = 7;
183
+ if( !iCalUtilityFunctions::_isOffset( $date['tz'] ))
184
+ $params['TZID'] = $date['tz']; // save only timezone
185
+ }
186
+ elseif( empty( $parno ))
187
+ $parno = 6;
188
+ }
189
+ if( isset( $params['TZID'] ))
190
+ $parno = 6;
191
+ }
192
+ }
193
+ /**
194
+ * vcalendar sort callback function
195
+ *
196
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
197
+ * @since 2.16.2 - 2012-12-17
198
+ * @param array $a
199
+ * @param array $b
200
+ * @uses calendarComponent::$objName
201
+ * @return int
202
+ */
203
+ public static function _cmpfcn( $a, $b ) {
204
+ if( empty( $a )) return -1;
205
+ if( empty( $b )) return 1;
206
+ if( 'vtimezone' == $a->objName ) {
207
+ if( 'vtimezone' != $b->objName ) return -1;
208
+ elseif( $a->srtk[0] <= $b->srtk[0] ) return -1;
209
+ else return 1;
210
+ }
211
+ elseif( 'vtimezone' == $b->objName ) return 1;
212
+ $sortkeys = array( 'year', 'month', 'day', 'hour', 'min', 'sec' );
213
+ for( $k = 0; $k < 4 ; $k++ ) {
214
+ if( empty( $a->srtk[$k] )) return -1;
215
+ elseif( empty( $b->srtk[$k] )) return 1;
216
+ if( is_array( $a->srtk[$k] )) {
217
+ if( is_array( $b->srtk[$k] )) {
218
+ foreach( $sortkeys as $key ) {
219
+ if ( !isset( $a->srtk[$k][$key] )) return -1;
220
+ elseif( !isset( $b->srtk[$k][$key] )) return 1;
221
+ if ( empty( $a->srtk[$k][$key] )) return -1;
222
+ elseif( empty( $b->srtk[$k][$key] )) return 1;
223
+ if ( $a->srtk[$k][$key] == $b->srtk[$k][$key])
224
+ continue;
225
+ if (( (int) $a->srtk[$k][$key] ) < ((int) $b->srtk[$k][$key] ))
226
+ return -1;
227
+ elseif(( (int) $a->srtk[$k][$key] ) > ((int) $b->srtk[$k][$key] ))
228
+ return 1;
229
+ }
230
+ }
231
+ else return -1;
232
+ }
233
+ elseif( is_array( $b->srtk[$k] )) return 1;
234
+ elseif( $a->srtk[$k] < $b->srtk[$k] ) return -1;
235
+ elseif( $a->srtk[$k] > $b->srtk[$k] ) return 1;
236
+ }
237
+ return 0;
238
+ }
239
+ /**
240
+ * byte oriented line folding fix
241
+ *
242
+ * remove any line-endings that may include spaces or tabs
243
+ * and convert all line endings (iCal default '\r\n'),
244
+ * takes care of '\r\n', '\r' and '\n' and mixed '\r\n'+'\r', '\r\n'+'\n'
245
+ *
246
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
247
+ * @since 2.18.16 - 2014-04-04
248
+ * @param string $text
249
+ * @param string $nl
250
+ * @uses iCalUtilityFunctions::$baseDelim
251
+ * @return string
252
+ */
253
+ public static function convEolChar( & $text, $nl ) {
254
+ /* fix dummy line separator */
255
+ if( empty( iCalUtilityFunctions::$baseDelim )) {
256
+ iCalUtilityFunctions::$baseDelim = substr( microtime(), 2, 4 );
257
+ $base = 'aAbB!cCdD"eEfF#gGhHiIjJ%kKlL&mMnN/oOpP(rRsS)tTuU=vVxX?uUvV*wWzZ-1234_5678|90';
258
+ $len = strlen( $base ) - 1;
259
+ for( $p = 0; $p < 6; $p++ )
260
+ iCalUtilityFunctions::$baseDelim .= $base{mt_rand( 0, $len )};
261
+ }
262
+ /* fix eol chars */
263
+ $text = str_replace( array( "\r\n", "\n\r", "\n", "\r" ), iCalUtilityFunctions::$baseDelim, $text );
264
+ /* fix empty lines */
265
+ $text = str_replace( iCalUtilityFunctions::$baseDelim.iCalUtilityFunctions::$baseDelim, iCalUtilityFunctions::$baseDelim.str_pad( '', 75 ).iCalUtilityFunctions::$baseDelim, $text );
266
+ /* fix line folding */
267
+ $text = str_replace( iCalUtilityFunctions::$baseDelim, $nl, $text );
268
+ $text = str_replace( array( $nl.' ', $nl."\t" ), '', $text );
269
+ /* split in component/property lines */
270
+ $text = explode( $nl, $text );
271
+ return $text;
272
+ }
273
+ /**
274
+ * create a calendar timezone and standard/daylight components
275
+ *
276
+ * Result when 'Europe/Stockholm' and no from/to arguments is used as timezone:
277
+ *
278
+ * BEGIN:VTIMEZONE
279
+ * TZID:Europe/Stockholm
280
+ * BEGIN:STANDARD
281
+ * DTSTART:20101031T020000
282
+ * TZOFFSETFROM:+0200
283
+ * TZOFFSETTO:+0100
284
+ * TZNAME:CET
285
+ * END:STANDARD
286
+ * BEGIN:DAYLIGHT
287
+ * DTSTART:20100328T030000
288
+ * TZOFFSETFROM:+0100
289
+ * TZOFFSETTO:+0200
290
+ * TZNAME:CEST
291
+ * END:DAYLIGHT
292
+ * END:VTIMEZONE
293
+ *
294
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
295
+ * @since 2.21.11 - 2015-04-03
296
+ * Generates components for all transitions in a date range, based on contribution by Yitzchok Lavi <icalcreator@onebigsystem.com>
297
+ * Additional changes jpirkey
298
+ * @param object $calendar iCalcreator calendar instance
299
+ * @param string $timezone PHP5 (DateTimeZone) valid timezone
300
+ * @param array $xProp *[x-propName => x-propValue], optional
301
+ * @param int $from unix timestamp
302
+ * @param int $to unix timestamp
303
+ * @uses vcalendar::getProperty()
304
+ * @uses iCalUtilityFunctions::$fmt
305
+ * @uses vcalendar::newComponent()
306
+ * @uses calendarComponent::setproperty()
307
+ * @uses iCalUtilityFunctions::offsetSec2His()
308
+ * @return bool
309
+ */
310
+ public static function createTimezone( & $calendar, $timezone, $xProp=array(), $from=null, $to=null ) {
311
+ var_dump($timezone);
312
+ if( empty( $timezone ))
313
+ return FALSE;
314
+ if( !empty( $from ) && !is_int( $from ))
315
+ return FALSE;
316
+ if( !empty( $to ) && !is_int( $to ))
317
+ return FALSE;
318
+ try {
319
+ $dtz = new DateTimeZone( $timezone );
320
+ $transitions = $dtz->getTransitions();
321
+ $utcTz = new DateTimeZone( 'UTC' );
322
+ }
323
+ catch( Exception $e ) { return FALSE; }
324
+
325
+ var_dump('dasdsadsa');
326
+ if( empty( $from ) || empty( $to )) {
327
+ $dates = array_keys( $calendar->getProperty( 'dtstart' ));
328
+ if( empty( $dates ))
329
+ $dates = array( date( 'Ymd' ));
330
+ }
331
+ if( ! empty( $from )) {
332
+ $dateFrom = new DateTime( "@$from" ); // set lowest date (UTC)
333
+ $dateFrom->modify( '-7 month' ); // set $dateFrom to seven month before the lowest date
334
+ }
335
+ else {
336
+ $from = reset( $dates ); // set lowest date to the lowest dtstart date
337
+ $dateFrom = new DateTime( $from.'T000000', $dtz );
338
+ $dateFrom->modify( '-7 month' ); // set $dateFrom to seven month before the lowest date
339
+ $dateFrom->setTimezone( $utcTz ); // convert local date to UTC
340
+ }
341
+ $dateFromYmd = $dateFrom->format( iCalUtilityFunctions::$fmt['Ymd2'] );
342
+ if( ! empty( $to ))
343
+ $dateTo = new DateTime( "@$to" ); // set end date (UTC)
344
+ else {
345
+ $to = end( $dates ); // set highest date to the highest dtstart date
346
+ $dateTo = new DateTime( $to.'T235959', $dtz );
347
+ $dateTo->modify( '+18 month' ); // set $dateTo to 18 month after the highest date
348
+ $dateTo->setTimezone( $utcTz ); // convert local date to UTC
349
+ }
350
+ $dateToYmd = $dateTo->format( iCalUtilityFunctions::$fmt['Ymd2'] );
351
+ unset( $dtz );
352
+ $transTemp = array();
353
+ $prevOffsetfrom = 0;
354
+ $stdIx = $dlghtIx = null;
355
+ $prevTrans = FALSE;
356
+ foreach( $transitions as $tix => $trans ) { // all transitions in date-time order!!
357
+ $date = new DateTime( "@{$trans['ts']}" ); // set transition date (UTC)
358
+ $transDateYmd = $date->format( iCalUtilityFunctions::$fmt['Ymd2'] );
359
+ if ( $transDateYmd < $dateFromYmd ) {
360
+ $prevOffsetfrom = $trans['offset']; // previous trans offset will be 'next' trans offsetFrom
361
+ $prevTrans = $trans; // save it in case we don't find any that match
362
+ $prevTrans['offsetfrom'] = ( 0 < $tix ) ? $transitions[$tix-1]['offset'] : 0;
363
+ continue;
364
+ }
365
+ if( $transDateYmd > $dateToYmd )
366
+ break; // loop always (?) breaks here
367
+ if( !empty( $prevOffsetfrom ) || ( 0 == $prevOffsetfrom )) {
368
+ $trans['offsetfrom'] = $prevOffsetfrom; // i.e. set previous offsetto as offsetFrom
369
+ $date->modify( $trans['offsetfrom'].'seconds' ); // convert utc date to local date
370
+ $d = $date->format( iCalUtilityFunctions::$fmt['YmdHis3'] );
371
+ $d = explode( '-', $d ); // set date to array to ease up dtstart and (opt) rdate setting
372
+ $trans['time'] = array( 'year' => (int) $d[0], 'month' => (int) $d[1], 'day' => (int) $d[2], 'hour' => (int) $d[3], 'min' => (int) $d[4], 'sec' => (int) $d[5] );
373
+ }
374
+ $prevOffsetfrom = $trans['offset'];
375
+ if( TRUE !== $trans['isdst'] ) { // standard timezone
376
+ if( !empty( $stdIx ) && isset( $transTemp[$stdIx]['offsetfrom'] ) && // check for any repeating rdate's (in order)
377
+ ( $transTemp[$stdIx]['abbr'] == $trans['abbr'] ) &&
378
+ ( $transTemp[$stdIx]['offsetfrom'] == $trans['offsetfrom'] ) &&
379
+ ( $transTemp[$stdIx]['offset'] == $trans['offset'] )) {
380
+ $transTemp[$stdIx]['rdate'][] = $trans['time'];
381
+ continue;
382
+ }
383
+ $stdIx = $tix;
384
+ } // end standard timezone
385
+ else { // daylight timezone
386
+ if( !empty( $dlghtIx ) && isset( $transTemp[$dlghtIx]['offsetfrom'] ) && // check for any repeating rdate's (in order)
387
+ ( $transTemp[$dlghtIx]['abbr'] == $trans['abbr'] ) &&
388
+ ( $transTemp[$dlghtIx]['offsetfrom'] == $trans['offsetfrom'] ) &&
389
+ ( $transTemp[$dlghtIx]['offset'] == $trans['offset'] )) {
390
+ $transTemp[$dlghtIx]['rdate'][] = $trans['time'];
391
+ continue;
392
+ }
393
+ $dlghtIx = $tix;
394
+ } // end daylight timezone
395
+ $transTemp[$tix] = $trans;
396
+ } // end foreach( $transitions as $tix => $trans )
397
+ $tz = $calendar->newComponent( 'vtimezone' );
398
+ $tz->setproperty( 'tzid', $timezone );
399
+ if( !empty( $xProp )) {
400
+ foreach( $xProp as $xPropName => $xPropValue )
401
+ if( 'x-' == strtolower( substr( $xPropName, 0, 2 )))
402
+ $tz->setproperty( $xPropName, $xPropValue );
403
+ }
404
+ if( empty( $transTemp )) { // if no match found
405
+ if( $prevTrans ) { // then we use the last transition (before startdate) for the tz info
406
+ $date = new DateTime( "@{$prevTrans['ts']}" );// set transition date (UTC)
407
+ $date->modify( $prevTrans['offsetfrom'].'seconds' ); // convert utc date to local date
408
+ $d = $date->format( iCalUtilityFunctions::$fmt['YmdHis3'] );
409
+ $d = explode( '-', $d ); // set date to array to ease up dtstart setting
410
+ $prevTrans['time'] = array( 'year' => (int) $d[0], 'month' => (int) $d[1], 'day' => (int) $d[2], 'hour' => (int) $d[3], 'min' => (int) $d[4], 'sec' => (int) $d[5] );
411
+ $transTemp[0] = $prevTrans;
412
+ }
413
+ else { // or we use the timezone identifier to BUILD the standard tz info (?)
414
+ $date = new DateTime( 'now', new DateTimeZone( $timezone ));
415
+ $transTemp[0] = array( 'time' => $date->format( iCalUtilityFunctions::$fmt['YmdTHisO'] ),
416
+ 'offset' => $date->format( 'Z' ),
417
+ 'offsetfrom' => $date->format( 'Z' ),
418
+ 'isdst' => FALSE );
419
+ }
420
+ }
421
+ unset( $transitions, $date, $prevTrans );
422
+ foreach( $transTemp as $tix => $trans ) { // create standard/daylight subcomponents
423
+ $type = ( TRUE !== $trans['isdst'] ) ? 'standard' : 'daylight';
424
+ $scomp = $tz->newComponent( $type );
425
+ $scomp->setProperty( 'dtstart', $trans['time'] );
426
+ // $scomp->setProperty( 'x-utc-timestamp', $tix.' : '.$trans['ts'] ); // test ###
427
+ if( !empty( $trans['abbr'] ))
428
+ $scomp->setProperty( 'tzname', $trans['abbr'] );
429
+ if( isset( $trans['offsetfrom'] ))
430
+ $scomp->setProperty( 'tzoffsetfrom', iCalUtilityFunctions::offsetSec2His( $trans['offsetfrom'] ));
431
+ $scomp->setProperty( 'tzoffsetto', iCalUtilityFunctions::offsetSec2His( $trans['offset'] ));
432
+ if( isset( $trans['rdate'] ))
433
+ $scomp->setProperty( 'RDATE', $trans['rdate'] );
434
+ }
435
+ return TRUE;
436
+ }
437
+ /**
438
+ * creates formatted output for calendar component property data value type date/date-time
439
+ *
440
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
441
+ * @since 2.21.11 - 2015-03-10
442
+ * @param array $datetime
443
+ * @param int $parno optional, default 6
444
+ * @uses iCalUtilityFunctions::$fmt
445
+ * @uses iCalUtilityFunctions::_isOffset()
446
+ * @uses iCalUtilityFunctions::_tz2offset()
447
+ * @return string
448
+ */
449
+ public static function _date2strdate( $datetime, $parno=6 ) {
450
+ if( !isset( $datetime['year'] ) &&
451
+ !isset( $datetime['month'] ) &&
452
+ !isset( $datetime['day'] ) &&
453
+ !isset( $datetime['hour'] ) &&
454
+ !isset( $datetime['min'] ) &&
455
+ !isset( $datetime['sec'] ))
456
+ return;
457
+ $output = null;
458
+ foreach( $datetime as $dkey => & $dvalue )
459
+ if( 'tz' != $dkey ) $dvalue = (int) $dvalue;
460
+ $output = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $datetime['year'], $datetime['month'], $datetime['day'] );
461
+ if( 3 == $parno )
462
+ return $output;
463
+ if( !isset( $datetime['hour'] )) $datetime['hour'] = 0;
464
+ if( !isset( $datetime['min'] )) $datetime['min'] = 0;
465
+ if( !isset( $datetime['sec'] )) $datetime['sec'] = 0;
466
+ $output .= 'T'.sprintf( iCalUtilityFunctions::$fmt['His'], $datetime['hour'], $datetime['min'], $datetime['sec'] );
467
+ if( isset( $datetime['tz'] ) && ( '' < trim( $datetime['tz'] ))) {
468
+ $datetime['tz'] = trim( $datetime['tz'] );
469
+ if( 'Z' == $datetime['tz'] )
470
+ $parno = 7;
471
+ elseif( iCalUtilityFunctions::_isOffset( $datetime['tz'] )) {
472
+ $parno = 7;
473
+ $offset = iCalUtilityFunctions::_tz2offset( $datetime['tz'] );
474
+ try {
475
+ $d = new DateTime( $output, new DateTimeZone( 'UTC' ));
476
+ if( 0 != $offset ) // adjust för offset
477
+ $d->modify( "$offset seconds" );
478
+ $output = $d->format( 'Ymd\THis' );
479
+ }
480
+ catch( Exception $e ) {
481
+ $output = date( 'Ymd\THis', mktime( $datetime['hour'], $datetime['min'], ($datetime['sec'] - $offset), $datetime['month'], $datetime['day'], $datetime['year'] ));
482
+ }
483
+ }
484
+ if( 7 == $parno )
485
+ $output .= 'Z';
486
+ }
487
+ return $output;
488
+ }
489
+ /**
490
+ * ensures internal duration format for input in array format
491
+ *
492
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
493
+ * @since 2.19.4 - 2014-03-14
494
+ * @param array $duration
495
+ * @return array
496
+ */
497
+ public static function _duration2arr( $duration ) {
498
+ $seconds = 0;
499
+ foreach( $duration as $durKey => $durValue ) {
500
+ if( empty( $durValue )) continue;
501
+ switch ( $durKey ) {
502
+ case '0': case 'week':
503
+ $seconds += (((int) $durValue ) * 60 * 60 * 24 * 7 );
504
+ break;
505
+ case '1': case 'day':
506
+ $seconds += (((int) $durValue ) * 60 * 60 * 24 );
507
+ break;
508
+ case '2': case 'hour':
509
+ $seconds += (((int) $durValue ) * 60 * 60 );
510
+ break;
511
+ case '3': case 'min':
512
+ $seconds += (((int) $durValue ) * 60 );
513
+ break;
514
+ case '4': case 'sec':
515
+ $seconds += (int) $durValue;
516
+ break;
517
+ }
518
+ }
519
+ $output = array();
520
+ $output['week'] = (int) floor( $seconds / ( 60 * 60 * 24 * 7 ));
521
+ if(( 0 < $output['week'] ) && ( 0 == ( $seconds % ( 60 * 60 * 24 * 7 ))))
522
+ return $output;
523
+ unset( $output['week'] );
524
+ $output['day'] = (int) floor( $seconds / ( 60 * 60 * 24 ));
525
+ $seconds = ( $seconds % ( 60 * 60 * 24 ));
526
+ $output['hour'] = (int) floor( $seconds / ( 60 * 60 ));
527
+ $seconds = ( $seconds % ( 60 * 60 ));
528
+ $output['min'] = (int) floor( $seconds / 60 );
529
+ $output['sec'] = ( $seconds % 60 );
530
+ if( empty( $output['day'] ))
531
+ unset( $output['day'] );
532
+ if(( 0 == $output['hour'] ) && ( 0 == $output['min'] ) && ( 0 == $output['sec'] ))
533
+ unset( $output['hour'], $output['min'], $output['sec'] );
534
+ return $output;
535
+ }
536
+ /**
537
+ * convert startdate+duration to a array format datetime
538
+ *
539
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
540
+ * @since 2.21.11 - 2015-03-21
541
+ * @param array $startdate
542
+ * @param array $duration
543
+ * @uses iCalUtilityFunctions::$fmt
544
+ * @return array, date format
545
+ */
546
+ public static function _duration2date( $startdate, $duration ) {
547
+ $dateOnly = ( isset( $startdate['hour'] ) || isset( $startdate['min'] ) || isset( $startdate['sec'] )) ? FALSE : TRUE;
548
+ $startdate['hour'] = ( isset( $startdate['hour'] )) ? $startdate['hour'] : 0;
549
+ $startdate['min'] = ( isset( $startdate['min'] )) ? $startdate['min'] : 0;
550
+ $startdate['sec'] = ( isset( $startdate['sec'] )) ? $startdate['sec'] : 0;
551
+ $dtend = 0;
552
+ if( isset( $duration['week'] )) $dtend += ( $duration['week'] * 7 * 24 * 60 * 60 );
553
+ if( isset( $duration['day'] )) $dtend += ( $duration['day'] * 24 * 60 * 60 );
554
+ if( isset( $duration['hour'] )) $dtend += ( $duration['hour'] * 60 *60 );
555
+ if( isset( $duration['min'] )) $dtend += ( $duration['min'] * 60 );
556
+ if( isset( $duration['sec'] )) $dtend += $duration['sec'];
557
+ $date = date( iCalUtilityFunctions::$fmt['YmdHis3'],
558
+ mktime((int) $startdate['hour'], (int) $startdate['min'], (int) ( $startdate['sec'] + $dtend ), (int) $startdate['month'], (int) $startdate['day'], (int) $startdate['year'] ));
559
+ $d = explode( '-', $date );
560
+ $dtend2 = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2], 'hour' => $d[3], 'min' => $d[4], 'sec' => $d[5] );
561
+ if( isset( $startdate['tz'] ))
562
+ $dtend2['tz'] = $startdate['tz'];
563
+ if( $dateOnly && (( 0 == $dtend2['hour'] ) && ( 0 == $dtend2['min'] ) && ( 0 == $dtend2['sec'] )))
564
+ unset( $dtend2['hour'], $dtend2['min'], $dtend2['sec'] );
565
+ return $dtend2;
566
+ }
567
+ /**
568
+ * ensures internal duration format for an input string (iCal) formatted duration
569
+ *
570
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
571
+ * @since 2.14.1 - 2012-09-25
572
+ * @param string $duration
573
+ * @uses iCalUtilityFunctions::_duration2arr()
574
+ * @return array
575
+ */
576
+ public static function _durationStr2arr( $duration ) {
577
+ $duration = (string) trim( $duration );
578
+ while( 'P' != strtoupper( substr( $duration, 0, 1 ))) {
579
+ if( 0 < strlen( $duration ))
580
+ $duration = substr( $duration, 1 );
581
+ else
582
+ return false; // no leading P !?!?
583
+ }
584
+ $duration = substr( $duration, 1 ); // skip P
585
+ $duration = str_replace ( 't', 'T', $duration );
586
+ $duration = str_replace ( 'T', '', $duration );
587
+ $output = array();
588
+ $val = null;
589
+ for( $ix=0; $ix < strlen( $duration ); $ix++ ) {
590
+ switch( strtoupper( substr( $duration, $ix, 1 ))) {
591
+ case 'W':
592
+ $output['week'] = $val;
593
+ $val = null;
594
+ break;
595
+ case 'D':
596
+ $output['day'] = $val;
597
+ $val = null;
598
+ break;
599
+ case 'H':
600
+ $output['hour'] = $val;
601
+ $val = null;
602
+ break;
603
+ case 'M':
604
+ $output['min'] = $val;
605
+ $val = null;
606
+ break;
607
+ case 'S':
608
+ $output['sec'] = $val;
609
+ $val = null;
610
+ break;
611
+ default:
612
+ if( !ctype_digit( substr( $duration, $ix, 1 )))
613
+ return false; // unknown duration control character !?!?
614
+ else
615
+ $val .= substr( $duration, $ix, 1 );
616
+ }
617
+ }
618
+ return iCalUtilityFunctions::_duration2arr( $output );
619
+ }
620
+ /**
621
+ * creates formatted output for calendar component property data value type duration
622
+ *
623
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
624
+ * @since 2.15.8 - 2012-10-30
625
+ * @param array $duration, array( week, day, hour, min, sec )
626
+ * @return string
627
+ */
628
+ public static function _duration2str( array $duration ) {
629
+ if( isset( $duration['week'] ) ||
630
+ isset( $duration['day'] ) ||
631
+ isset( $duration['hour'] ) ||
632
+ isset( $duration['min'] ) ||
633
+ isset( $duration['sec'] ))
634
+ $ok = TRUE;
635
+ else
636
+ return;
637
+ if( isset( $duration['week'] ) && ( 0 < $duration['week'] ))
638
+ return 'P'.$duration['week'].'W';
639
+ $output = 'P';
640
+ if( isset($duration['day'] ) && ( 0 < $duration['day'] ))
641
+ $output .= $duration['day'].'D';
642
+ if(( isset( $duration['hour']) && ( 0 < $duration['hour'] )) ||
643
+ ( isset( $duration['min']) && ( 0 < $duration['min'] )) ||
644
+ ( isset( $duration['sec']) && ( 0 < $duration['sec'] ))) {
645
+ $output .= 'T';
646
+ $output .= ( isset( $duration['hour']) && ( 0 < $duration['hour'] )) ? $duration['hour'].'H' : '0H';
647
+ $output .= ( isset( $duration['min']) && ( 0 < $duration['min'] )) ? $duration['min']. 'M' : '0M';
648
+ $output .= ( isset( $duration['sec']) && ( 0 < $duration['sec'] )) ? $duration['sec']. 'S' : '0S';
649
+ }
650
+ if( 'P' == $output )
651
+ $output = 'PT0H0M0S';
652
+ return $output;
653
+ }
654
+ /**
655
+ * removes expkey+expvalue from array and returns hitval (if found) else returns elseval
656
+ *
657
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
658
+ * @since 2.4.16 - 2008-11-08
659
+ * @param array $array iCal property parameters
660
+ * @param string $expkey expected key
661
+ * @param string $expval expected value
662
+ * @param int $hitVal return value if found
663
+ * @param int $elseVal return value if not found
664
+ * @param int $preSet return value if already preset
665
+ * @return int
666
+ */
667
+ public static function _existRem( & $array, $expkey, $expval=FALSE, $hitVal=null, $elseVal=null, $preSet=null ) {
668
+ if( $preSet )
669
+ return $preSet;
670
+ if( !is_array( $array ) || ( 0 == count( $array )))
671
+ return $elseVal;
672
+ foreach( $array as $key => $value ) {
673
+ if( strtoupper( $expkey ) == strtoupper( $key )) {
674
+ if( !$expval || ( strtoupper( $expval ) == strtoupper( $array[$key] ))) {
675
+ unset( $array[$key] );
676
+ return $hitVal;
677
+ }
678
+ }
679
+ }
680
+ return $elseVal;
681
+ }
682
+ /**
683
+ * check if dates are in scope
684
+ *
685
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
686
+ * @since 2.21.7 - 2015-03-25
687
+ * @param object $start datetime
688
+ * @param object $scopeStart datetime
689
+ * @param object $end datetime
690
+ * @param object $scopeEnd datetime
691
+ * @param string $format
692
+ * @return bool
693
+ */
694
+ public static function _inScope( $start, $scopeStart, $end, $scopeEnd, $format ) {
695
+ return (( $start->format( $format ) >= $scopeStart->format( $format )) &&
696
+ ( $end->format( $format ) <= $scopeEnd->format( $format )));
697
+ }
698
+ /**
699
+ * mgnt geo part output
700
+ *
701
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
702
+ * @since 2.8.10 - 2013-09-02
703
+ * @param float $ll
704
+ * @param string $format
705
+ * @return string
706
+ */
707
+ public static function _geo2str2( $ll, $format ) {
708
+ if( 0.0 < $ll )
709
+ $sign = '+';
710
+ else
711
+ $sign = ( 0.0 > $ll ) ? '-' : '';
712
+ return rtrim( rtrim( $sign.sprintf( $format, abs( $ll )), '0' ), '.' );
713
+ }
714
+ /**
715
+ * checks if input contains a (array formatted) date/time
716
+ *
717
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
718
+ * @since 2.16.24 - 2013-07-02
719
+ * @param array $input
720
+ * @uses iCalUtilityFunctions::_strdate2date()
721
+ * @return bool
722
+ */
723
+ public static function _isArrayDate( $input ) {
724
+ if( !is_array( $input ) || isset( $input['week'] ) || isset( $input['timestamp'] ) || ( 3 > count( $input )))
725
+ return FALSE;
726
+ if( 7 == count( $input ))
727
+ return TRUE;
728
+ if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] ))
729
+ return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] );
730
+ if( isset( $input['day'] ) || isset( $input['hour'] ) || isset( $input['min'] ) || isset( $input['sec'] ))
731
+ return FALSE;
732
+ if(( 0 == $input[0] ) || ( 0 == $input[1] ) || ( 0 == $input[2] ))
733
+ return FALSE;
734
+ if(( 1970 > $input[0] ) || ( 12 < $input[1] ) || ( 31 < $input[2] ))
735
+ return FALSE;
736
+ if(( isset( $input[0] ) && isset( $input[1] ) && isset( $input[2] )) &&
737
+ checkdate((int) $input[1], (int) $input[2], (int) $input[0] ))
738
+ return TRUE;
739
+ $input = iCalUtilityFunctions::_strdate2date( $input[1].'/'.$input[2].'/'.$input[0], 3 ); // m - d - Y
740
+ if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] ))
741
+ return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] );
742
+ return FALSE;
743
+ }
744
+ /**
745
+ * checks if input array contains a timestamp date
746
+ *
747
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
748
+ * @since 2.4.16 - 2008-10-18
749
+ * @param array $input
750
+ * @return bool
751
+ */
752
+ public static function _isArrayTimestampDate( $input ) {
753
+ return ( is_array( $input ) && isset( $input['timestamp'] )) ? TRUE : FALSE ;
754
+ }
755
+ /**
756
+ * controls if input string contains (trailing) UTC/iCal offset
757
+ *
758
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
759
+ * @since 2.14.1 - 2012-09-21
760
+ * @param string $input
761
+ * @return bool
762
+ */
763
+ public static function _isOffset( $input ) {
764
+ $input = trim( (string) $input );
765
+ if( 'Z' == substr( $input, -1 ))
766
+ return TRUE;
767
+ elseif(( 5 <= strlen( $input )) &&
768
+ ( in_array( substr( $input, -5, 1 ), array( '+', '-' ))) &&
769
+ ( '0000' <= substr( $input, -4 )) && ( '9999' >= substr( $input, -4 )))
770
+ return TRUE;
771
+ elseif(( 7 <= strlen( $input )) &&
772
+ ( in_array( substr( $input, -7, 1 ), array( '+', '-' ))) &&
773
+ ( '000000' <= substr( $input, -6 )) && ( '999999' >= substr( $input, -6 )))
774
+ return TRUE;
775
+ return FALSE;
776
+ }
777
+ /**
778
+ * (very simple) conversion of a MS timezone to a PHP5 valid (Date-)timezone
779
+ * matching (MS) UCT offset and time zone descriptors
780
+ *
781
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
782
+ * @since 2.14.1 - 2012-09-16
783
+ * @param string $timezone to convert
784
+ * @uses iCalUtilityFunctions::_tz2offset()
785
+ * @return bool
786
+ */
787
+ public static function ms2phpTZ( & $timezone ) {
788
+ if( empty( $timezone ))
789
+ return FALSE;
790
+ // $search = str_replace( '"', '', $timezone );
791
+ $search = str_replace( array('GMT', 'gmt', 'utc' ), 'UTC', $search );
792
+ if( '(UTC' != substr( $search, 0, 4 ))
793
+ return FALSE;
794
+ if( FALSE === ( $pos = strpos( $search, ')' )))
795
+ return FALSE;
796
+ $pos = strpos( $search, ')' );
797
+ $searchOffset = substr( $search, 4, ( $pos - 4 ));
798
+ $searchOffset = iCalUtilityFunctions::_tz2offset( str_replace( ':', '', $searchOffset ));
799
+ while( ' ' ==substr( $search, ( $pos + 1 )))
800
+ $pos += 1;
801
+ $searchText = trim( str_replace( array( '(', ')', '&', ',', ' ' ), ' ', substr( $search, ( $pos + 1 )) ));
802
+ $searchWords = explode( ' ', $searchText );
803
+ $timezone_abbreviations = DateTimeZone::listAbbreviations();
804
+ $hits = array();
805
+ foreach( $timezone_abbreviations as $name => $transitions ) {
806
+ foreach( $transitions as $cnt => $transition ) {
807
+ if( empty( $transition['offset'] ) ||
808
+ empty( $transition['timezone_id'] ) ||
809
+ ( $transition['offset'] != $searchOffset ))
810
+ continue;
811
+ $cWords = explode( '/', $transition['timezone_id'] );
812
+ $cPrio = $hitCnt = $rank = 0;
813
+ foreach( $cWords as $cWord ) {
814
+ if( empty( $cWord ))
815
+ continue;
816
+ $cPrio += 1;
817
+ $sPrio = 0;
818
+ foreach( $searchWords as $sWord ) {
819
+ if( empty( $sWord ) || ( 'time' == strtolower( $sWord )))
820
+ continue;
821
+ $sPrio += 1;
822
+ if( strtolower( $cWord ) == strtolower( $sWord )) {
823
+ $hitCnt += 1;
824
+ $rank += ( $cPrio + $sPrio );
825
+ }
826
+ else
827
+ $rank += 10;
828
+ }
829
+ }
830
+ if( 0 < $hitCnt ) {
831
+ $hits[$rank][] = $transition['timezone_id'];
832
+ }
833
+ }
834
+ }
835
+ unset( $timezone_abbreviations );
836
+ if( empty( $hits ))
837
+ return FALSE;
838
+ ksort( $hits );
839
+ foreach( $hits as $rank => $tzs ) {
840
+ if( !empty( $tzs )) {
841
+ $timezone = reset( $tzs );
842
+ return TRUE;
843
+ }
844
+ }
845
+ return FALSE;
846
+ }
847
+ /**
848
+ * transforms offset in seconds to [-/+]hhmm[ss]
849
+ *
850
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
851
+ * @since 2011-05-02
852
+ * @param string $seconds
853
+ * @return string
854
+ */
855
+ public static function offsetSec2His( $seconds ) {
856
+ if( '-' == substr( $seconds, 0, 1 )) {
857
+ $prefix = '-';
858
+ $seconds = substr( $seconds, 1 );
859
+ }
860
+ elseif( '+' == substr( $seconds, 0, 1 )) {
861
+ $prefix = '+';
862
+ $seconds = substr( $seconds, 1 );
863
+ }
864
+ else
865
+ $prefix = '+';
866
+ $output = '';
867
+ $hour = (int) floor( $seconds / 3600 );
868
+ if( 10 > $hour )
869
+ $hour = '0'.$hour;
870
+ $seconds = $seconds % 3600;
871
+ $min = (int) floor( $seconds / 60 );
872
+ if( 10 > $min )
873
+ $min = '0'.$min;
874
+ $output = $hour.$min;
875
+ $seconds = $seconds % 60;
876
+ if( 0 < $seconds) {
877
+ if( 9 < $seconds)
878
+ $output .= $seconds;
879
+ else
880
+ $output .= '0'.$seconds;
881
+ }
882
+ return $prefix.$output;
883
+ }
884
+ /**
885
+ * updates an array with dates based on a recur pattern
886
+ *
887
+ * if missing, UNTIL is set 1 year from startdate (emergency break)
888
+ *
889
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
890
+ * @since 2.21.11 - 2015-03-10
891
+ * @param array $result array to update, array([Y-m-d] => bool)
892
+ * @param array $recur pattern for recurrency (only value part, params ignored)
893
+ * @param mixed $wdate component start date, string / array / (datetime) obj
894
+ * @param mixed $fcnStart start date, string / array / (datetime) obj
895
+ * @param mixed $fcnEnd end date, string / array / (datetime) obj
896
+ * @uses iCalUtilityFunctions::_strDate2arr()
897
+ * @uses iCalUtilityFunctions::$fmt
898
+ * @uses iCalUtilityFunctions::_stepdate()
899
+ * @uses iCalUtilityFunctions::_recurIntervalIx()
900
+ * @uses iCalUtilityFunctions::_recurBYcntcheck()
901
+ * @return void
902
+ * @todo BYHOUR, BYMINUTE, BYSECOND, WEEKLY at year end/start OR not at all
903
+ */
904
+ public static function _recur2date( & $result, $recur, $wdate, $fcnStart, $fcnEnd=FALSE ) {
905
+ if( is_string( $wdate ))
906
+ iCalUtilityFunctions::_strDate2arr( $wdate );
907
+ elseif( is_a( $wdate, 'DateTime' )) {
908
+ $wdate = $wdate->format( iCalUtilityFunctions::$fmt['YmdHis2'] );
909
+ iCalUtilityFunctions::_strDate2arr( $wdate );
910
+ }
911
+ foreach( $wdate as $k => $v ) if( ctype_digit( $v )) $wdate[$k] = (int) $v;
912
+ $wdateYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $wdate['year'], $wdate['month'], $wdate['day'] );
913
+ $wdateHis = sprintf( iCalUtilityFunctions::$fmt['His'], $wdate['hour'], $wdate['min'], $wdate['sec'] );
914
+ $untilHis = $wdateHis;
915
+ if( is_string( $fcnStart ))
916
+ iCalUtilityFunctions::_strDate2arr( $fcnStart );
917
+ elseif( is_a( $fcnStart, 'DateTime' )) {
918
+ $fcnStart = $fcnStart->format( iCalUtilityFunctions::$fmt['YmdHis2'] );
919
+ iCalUtilityFunctions::_strDate2arr( $fcnStart );
920
+ }
921
+ foreach( $fcnStart as $k => $v ) if( ctype_digit( $v )) $fcnStart[$k] = (int) $v;
922
+ $fcnStartYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $fcnStart['year'], $fcnStart['month'], $fcnStart['day'] );
923
+ if( is_string( $fcnEnd ))
924
+ iCalUtilityFunctions::_strDate2arr( $fcnEnd );
925
+ elseif( is_a( $fcnEnd, 'DateTime' )) {
926
+ $fcnEnd = $fcnEnd->format( iCalUtilityFunctions::$fmt['YmdHis2'] );
927
+ iCalUtilityFunctions::_strDate2arr( $fcnEnd );
928
+ }
929
+ if( !$fcnEnd ) {
930
+ $fcnEnd = $fcnStart;
931
+ $fcnEnd['year'] += 1;
932
+ }
933
+ foreach( $fcnEnd as $k => $v ) if( ctype_digit( $v )) $fcnEnd[$k] = (int) $v;
934
+ $fcnEndYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $fcnEnd['year'], $fcnEnd['month'], $fcnEnd['day'] );
935
+ // echo "<b>recur _in_ comp</b> start ".implode('-',$wdate)." period start ".implode('-',$fcnStart)." period end ".implode('-',$fcnEnd)."<br>\n";
936
+ // echo 'recur='.str_replace( array( PHP_EOL, ' ' ), '', var_export( $recur, TRUE ))."<br> \n"; // test ###
937
+ if( !isset( $recur['COUNT'] ) && !isset( $recur['UNTIL'] ))
938
+ $recur['UNTIL'] = $fcnEnd; // create break
939
+ if( isset( $recur['UNTIL'] )) {
940
+ foreach( $recur['UNTIL'] as $k => $v ) if( ctype_digit( $v )) $recur['UNTIL'][$k] = (int) $v;
941
+ unset( $recur['UNTIL']['tz'] );
942
+ if( $fcnEnd > $recur['UNTIL'] ) {
943
+ $fcnEnd = $recur['UNTIL']; // emergency break
944
+ $fcnEndYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $fcnEnd['year'], $fcnEnd['month'], $fcnEnd['day'] );
945
+ }
946
+ if( isset( $recur['UNTIL']['hour'] ))
947
+ $untilHis = sprintf( iCalUtilityFunctions::$fmt['His'], $recur['UNTIL']['hour'], $recur['UNTIL']['min'], $recur['UNTIL']['sec'] );
948
+ else
949
+ $untilHis = sprintf( iCalUtilityFunctions::$fmt['His'], 23, 59, 59 );
950
+ // echo 'recurUNTIL='.str_replace( array( PHP_EOL, ' ' ), '', var_export( $recur['UNTIL'], TRUE )).", untilHis={$untilHis}<br> \n"; // test ###
951
+ }
952
+ // echo 'fcnEnd:'.$fcnEndYMD.$untilHis."<br>\n";//test
953
+ if( $wdateYMD > $fcnEndYMD ) {
954
+ // echo 'recur out of date, '.implode('-',$wdate).', end='.implode('-',$fcnEnd)."<br>\n";//test
955
+ return array(); // nothing to do.. .
956
+ }
957
+ if( !isset( $recur['FREQ'] )) // "MUST be specified.. ."
958
+ $recur['FREQ'] = 'DAILY'; // ??
959
+ $wkst = ( isset( $recur['WKST'] ) && ( 'SU' == $recur['WKST'] )) ? 24*60*60 : 0; // ??
960
+ if( !isset( $recur['INTERVAL'] ))
961
+ $recur['INTERVAL'] = 1;
962
+ $countcnt = ( !isset( $recur['BYSETPOS'] )) ? 1 : 0; // DTSTART counts as the first occurrence
963
+ /* find out how to step up dates and set index for interval count */
964
+ $step = array();
965
+ if( 'YEARLY' == $recur['FREQ'] )
966
+ $step['year'] = 1;
967
+ elseif( 'MONTHLY' == $recur['FREQ'] )
968
+ $step['month'] = 1;
969
+ elseif( 'WEEKLY' == $recur['FREQ'] )
970
+ $step['day'] = 7;
971
+ else
972
+ $step['day'] = 1;
973
+ if( isset( $step['year'] ) && isset( $recur['BYMONTH'] ))
974
+ $step = array( 'month' => 1 );
975
+ if( empty( $step ) && isset( $recur['BYWEEKNO'] )) // ??
976
+ $step = array( 'day' => 7 );
977
+ if( isset( $recur['BYYEARDAY'] ) || isset( $recur['BYMONTHDAY'] ) || isset( $recur['BYDAY'] ))
978
+ $step = array( 'day' => 1 );
979
+ $intervalarr = array();
980
+ if( 1 < $recur['INTERVAL'] ) {
981
+ $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst );
982
+ $intervalarr = array( $intervalix => 0 );
983
+ }
984
+ if( isset( $recur['BYSETPOS'] )) { // save start date + weekno
985
+ $bysetposymd1 = $bysetposymd2 = $bysetposw1 = $bysetposw2 = array();
986
+ // echo "bysetposXold_start=$bysetposYold $bysetposMold $bysetposDold<br>\n"; // test ###
987
+ if( is_array( $recur['BYSETPOS'] )) {
988
+ foreach( $recur['BYSETPOS'] as $bix => $bval )
989
+ $recur['BYSETPOS'][$bix] = (int) $bval;
990
+ }
991
+ else
992
+ $recur['BYSETPOS'] = array( (int) $recur['BYSETPOS'] );
993
+ if( 'YEARLY' == $recur['FREQ'] ) {
994
+ $wdate['month'] = $wdate['day'] = 1; // start from beginning of year
995
+ $wdateYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $wdate['year'], $wdate['month'], $wdate['day'] );
996
+ iCalUtilityFunctions::_stepdate( $fcnEnd, $fcnEndYMD, array( 'year' => 1 )); // make sure to count whole last year
997
+ }
998
+ elseif( 'MONTHLY' == $recur['FREQ'] ) {
999
+ $wdate['day'] = 1; // start from beginning of month
1000
+ $wdateYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], $wdate['year'], $wdate['month'], $wdate['day'] );
1001
+ iCalUtilityFunctions::_stepdate( $fcnEnd, $fcnEndYMD, array( 'month' => 1 )); // make sure to count whole last month
1002
+ }
1003
+ else
1004
+ iCalUtilityFunctions::_stepdate( $fcnEnd, $fcnEndYMD, $step); // make sure to count whole last period
1005
+ // echo "BYSETPOS endDat =".implode('-',$fcnEnd).' step='.var_export($step,TRUE)."<br>\n";//test###
1006
+ $bysetposWold = (int) date( 'W', mktime( 0, 0, $wkst, $wdate['month'], $wdate['day'], $wdate['year'] ));
1007
+ $bysetposYold = $wdate['year'];
1008
+ $bysetposMold = $wdate['month'];
1009
+ $bysetposDold = $wdate['day'];
1010
+ }
1011
+ else
1012
+ iCalUtilityFunctions::_stepdate( $wdate, $wdateYMD, $step);
1013
+ $year_old = null;
1014
+ static $daynames = array( 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA' );
1015
+ /* MAIN LOOP */
1016
+ // echo "recur start:$wdateYMD, end:$fcnEndYMD<br>\n";//test
1017
+ while( TRUE ) {
1018
+ // echo "recur while:$wdateYMD, end:$fcnEndYMD<br>\n";//test
1019
+ if( $wdateYMD.$wdateHis > $fcnEndYMD.$untilHis )
1020
+ break;
1021
+ if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] ))
1022
+ break;
1023
+ if( $year_old != $wdate['year'] ) {
1024
+ $year_old = $wdate['year'];
1025
+ $daycnts = array();
1026
+ $yeardays = $weekno = 0;
1027
+ $yeardaycnt = array();
1028
+ foreach( $daynames as $dn )
1029
+ $yeardaycnt[$dn] = 0;
1030
+ for( $m = 1; $m <= 12; $m++ ) { // count up and update up-counters
1031
+ $daycnts[$m] = array();
1032
+ $weekdaycnt = array();
1033
+ foreach( $daynames as $dn )
1034
+ $weekdaycnt[$dn] = 0;
1035
+ $mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] ));
1036
+ for( $d = 1; $d <= $mcnt; $d++ ) {
1037
+ $daycnts[$m][$d] = array();
1038
+ if( isset( $recur['BYYEARDAY'] )) {
1039
+ $yeardays++;
1040
+ $daycnts[$m][$d]['yearcnt_up'] = $yeardays;
1041
+ }
1042
+ if( isset( $recur['BYDAY'] )) {
1043
+ $day = date( 'w', mktime( 0, 0, 0, $m, $d, $wdate['year'] ));
1044
+ $day = $daynames[$day];
1045
+ $daycnts[$m][$d]['DAY'] = $day;
1046
+ $weekdaycnt[$day]++;
1047
+ $daycnts[$m][$d]['monthdayno_up'] = $weekdaycnt[$day];
1048
+ $yeardaycnt[$day]++;
1049
+ $daycnts[$m][$d]['yeardayno_up'] = $yeardaycnt[$day];
1050
+ }
1051
+ if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' ))
1052
+ $daycnts[$m][$d]['weekno_up'] =(int)date('W',mktime(0,0,$wkst,$m,$d,$wdate['year']));
1053
+ } // end for( $d = 1; $d <= $mcnt; $d++ )
1054
+ } // end for( $m = 1; $m <= 12; $m++ )
1055
+ $daycnt = 0;
1056
+ $yeardaycnt = array();
1057
+ if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) {
1058
+ $weekno = null;
1059
+ for( $d=31; $d > 25; $d-- ) { // get last weekno for year
1060
+ if( !$weekno )
1061
+ $weekno = $daycnts[12][$d]['weekno_up'];
1062
+ elseif( $weekno < $daycnts[12][$d]['weekno_up'] ) {
1063
+ $weekno = $daycnts[12][$d]['weekno_up'];
1064
+ break;
1065
+ }
1066
+ }
1067
+ }
1068
+ for( $m = 12; $m > 0; $m-- ) { // count down and update down-counters
1069
+ $weekdaycnt = array();
1070
+ foreach( $daynames as $dn )
1071
+ $yeardaycnt[$dn] = $weekdaycnt[$dn] = 0;
1072
+ $monthcnt = 0;
1073
+ $mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] ));
1074
+ for( $d = $mcnt; $d > 0; $d-- ) {
1075
+ if( isset( $recur['BYYEARDAY'] )) {
1076
+ $daycnt -= 1;
1077
+ $daycnts[$m][$d]['yearcnt_down'] = $daycnt;
1078
+ }
1079
+ if( isset( $recur['BYMONTHDAY'] )) {
1080
+ $monthcnt -= 1;
1081
+ $daycnts[$m][$d]['monthcnt_down'] = $monthcnt;
1082
+ }
1083
+ if( isset( $recur['BYDAY'] )) {
1084
+ $day = $daycnts[$m][$d]['DAY'];
1085
+ $weekdaycnt[$day] -= 1;
1086
+ $daycnts[$m][$d]['monthdayno_down'] = $weekdaycnt[$day];
1087
+ $yeardaycnt[$day] -= 1;
1088
+ $daycnts[$m][$d]['yeardayno_down'] = $yeardaycnt[$day];
1089
+ }
1090
+ if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' ))
1091
+ $daycnts[$m][$d]['weekno_down'] = ($daycnts[$m][$d]['weekno_up'] - $weekno - 1);
1092
+ }
1093
+ } // end for( $m = 12; $m > 0; $m-- )
1094
+ } // end if( $year_old != $wdate['year'] )
1095
+ /* check interval */
1096
+ if( 1 < $recur['INTERVAL'] ) {
1097
+ /* create interval index */
1098
+ $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst );
1099
+ /* check interval */
1100
+ $currentKey = array_keys( $intervalarr );
1101
+ $currentKey = end( $currentKey ); // get last index
1102
+ if( $currentKey != $intervalix )
1103
+ $intervalarr = array( $intervalix => ( $intervalarr[$currentKey] + 1 ));
1104
+ if(( $recur['INTERVAL'] != $intervalarr[$intervalix] ) &&
1105
+ ( 0 != $intervalarr[$intervalix] )) {
1106
+ /* step up date */
1107
+ // echo "skip: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br>\n";//test
1108
+ iCalUtilityFunctions::_stepdate( $wdate, $wdateYMD, $step);
1109
+ continue;
1110
+ }
1111
+ else // continue within the selected interval
1112
+ $intervalarr[$intervalix] = 0;
1113
+ // echo "cont: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br>\n";//test
1114
+ } // endif( 1 < $recur['INTERVAL'] )
1115
+ $updateOK = TRUE;
1116
+ if( $updateOK && isset( $recur['BYMONTH'] ))
1117
+ $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTH']
1118
+ , $wdate['month']
1119
+ ,($wdate['month'] - 13));
1120
+ if( $updateOK && isset( $recur['BYWEEKNO'] ))
1121
+ $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYWEEKNO']
1122
+ , $daycnts[$wdate['month']][$wdate['day']]['weekno_up']
1123
+ , $daycnts[$wdate['month']][$wdate['day']]['weekno_down'] );
1124
+ if( $updateOK && isset( $recur['BYYEARDAY'] ))
1125
+ $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYYEARDAY']
1126
+ , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_up']
1127
+ , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_down'] );
1128
+ if( $updateOK && isset( $recur['BYMONTHDAY'] ))
1129
+ $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTHDAY']
1130
+ , $wdate['day']
1131
+ , $daycnts[$wdate['month']][$wdate['day']]['monthcnt_down'] );
1132
+ // echo "efter BYMONTHDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br>\n";//test###
1133
+ if( $updateOK && isset( $recur['BYDAY'] )) {
1134
+ $updateOK = FALSE;
1135
+ $m = $wdate['month'];
1136
+ $d = $wdate['day'];
1137
+ if( isset( $recur['BYDAY']['DAY'] )) { // single day, opt with year/month day order no
1138
+ $daynoexists = $daynosw = $daynamesw = FALSE;
1139
+ if( $recur['BYDAY']['DAY'] == $daycnts[$m][$d]['DAY'] )
1140
+ $daynamesw = TRUE;
1141
+ if( isset( $recur['BYDAY'][0] )) {
1142
+ $daynoexists = TRUE;
1143
+ if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) || isset( $recur['BYMONTH'] ))
1144
+ $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0]
1145
+ , $daycnts[$m][$d]['monthdayno_up']
1146
+ , $daycnts[$m][$d]['monthdayno_down'] );
1147
+ elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' ))
1148
+ $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0]
1149
+ , $daycnts[$m][$d]['yeardayno_up']
1150
+ , $daycnts[$m][$d]['yeardayno_down'] );
1151
+ }
1152
+ if(( $daynoexists && $daynosw && $daynamesw ) ||
1153
+ ( !$daynoexists && !$daynosw && $daynamesw )) {
1154
+ $updateOK = TRUE;
1155
+ // echo "m=$m d=$d day=".$daycnts[$m][$d]['DAY']." yeardayno_up=".$daycnts[$m][$d]['yeardayno_up']." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK<br>\n"; // test ###
1156
+ }
1157
+ // echo "m=$m d=$d day=".$daycnts[$m][$d]['DAY']." yeardayno_up=".$daycnts[$m][$d]['yeardayno_up']." daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw updateOK:$updateOK<br>\n"; // test ###
1158
+ }
1159
+ else {
1160
+ foreach( $recur['BYDAY'] as $bydayvalue ) {
1161
+ $daynoexists = $daynosw = $daynamesw = FALSE;
1162
+ if( isset( $bydayvalue['DAY'] ) &&
1163
+ ( $bydayvalue['DAY'] == $daycnts[$m][$d]['DAY'] ))
1164
+ $daynamesw = TRUE;
1165
+ if( isset( $bydayvalue[0] )) {
1166
+ $daynoexists = TRUE;
1167
+ if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) ||
1168
+ isset( $recur['BYMONTH'] ))
1169
+ $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0']
1170
+ , $daycnts[$m][$d]['monthdayno_up']
1171
+ , $daycnts[$m][$d]['monthdayno_down'] );
1172
+ elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' ))
1173
+ $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0']
1174
+ , $daycnts[$m][$d]['yeardayno_up']
1175
+ , $daycnts[$m][$d]['yeardayno_down'] );
1176
+ }
1177
+ // echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw<br>\n"; // test ###
1178
+ if(( $daynoexists && $daynosw && $daynamesw ) ||
1179
+ ( !$daynoexists && !$daynosw && $daynamesw )) {
1180
+ $updateOK = TRUE;
1181
+ break;
1182
+ }
1183
+ }
1184
+ }
1185
+ }
1186
+ // echo "efter BYDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br>\n"; // test ###
1187
+ /* check BYSETPOS */
1188
+ if( $updateOK ) {
1189
+ if( isset( $recur['BYSETPOS'] ) &&
1190
+ ( in_array( $recur['FREQ'], array( 'YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY' )))) {
1191
+ if( isset( $recur['WEEKLY'] )) {
1192
+ if( $bysetposWold == $daycnts[$wdate['month']][$wdate['day']]['weekno_up'] )
1193
+ $bysetposw1[] = $wdateYMD;
1194
+ else
1195
+ $bysetposw2[] = $wdateYMD;
1196
+ }
1197
+ else {
1198
+ if(( isset( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) &&
1199
+ ( $bysetposYold == $wdate['year'] )) ||
1200
+ ( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] ) &&
1201
+ (( $bysetposYold == $wdate['year'] ) &&
1202
+ ( $bysetposMold == $wdate['month'] ))) ||
1203
+ ( isset( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) &&
1204
+ (( $bysetposYold == $wdate['year'] ) &&
1205
+ ( $bysetposMold == $wdate['month']) &&
1206
+ ( $bysetposDold == $wdate['day'] )))) {
1207
+ // echo "bysetposymd1[]=".date('Y-m-d H:i:s',$wdatets)."<br>\n";//test
1208
+ $bysetposymd1[] = $wdateYMD;
1209
+ }
1210
+ else {
1211
+ // echo "bysetposymd2[]=".date('Y-m-d H:i:s',$wdatets)."<br>\n";//test
1212
+ $bysetposymd2[] = $wdateYMD;
1213
+ }
1214
+ }
1215
+ }
1216
+ else {
1217
+ if( checkdate($wdate['month'], $wdate['day'], $wdate['year'] )) {
1218
+ /* update result array if BYSETPOS is not set */
1219
+ $countcnt++;
1220
+ if( $fcnStartYMD <= $wdateYMD ) { // only output within period
1221
+ $result[$wdateYMD] = TRUE;
1222
+ // echo "recur $wdateYMD<br>\n";//test
1223
+ }
1224
+ }
1225
+ // else echo "recur, no date $wdateYMD<br>\n";//test
1226
+ $updateOK = FALSE;
1227
+ }
1228
+ }
1229
+ /* step up date */
1230
+ iCalUtilityFunctions::_stepdate( $wdate, $wdateYMD, $step);
1231
+ /* check if BYSETPOS is set for updating result array */
1232
+ if( $updateOK && isset( $recur['BYSETPOS'] )) {
1233
+ $bysetpos = FALSE;
1234
+ if( isset( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) &&
1235
+ ( $bysetposYold != $wdate['year'] )) {
1236
+ $bysetpos = TRUE;
1237
+ $bysetposYold = $wdate['year'];
1238
+ }
1239
+ elseif( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] &&
1240
+ (( $bysetposYold != $wdate['year'] ) || ( $bysetposMold != $wdate['month'] )))) {
1241
+ $bysetpos = TRUE;
1242
+ $bysetposYold = $wdate['year'];
1243
+ $bysetposMold = $wdate['month'];
1244
+ }
1245
+ elseif( isset( $recur['FREQ'] ) && ( 'WEEKLY' == $recur['FREQ'] )) {
1246
+ $weekno = (int) date( 'W', mktime( 0, 0, $wkst, $wdate['month'], $wdate['day'], $wdate['year']));
1247
+ if( $bysetposWold != $weekno ) {
1248
+ $bysetposWold = $weekno;
1249
+ $bysetpos = TRUE;
1250
+ }
1251
+ }
1252
+ elseif( isset( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) &&
1253
+ (( $bysetposYold != $wdate['year'] ) ||
1254
+ ( $bysetposMold != $wdate['month'] ) ||
1255
+ ( $bysetposDold != $wdate['day'] ))) {
1256
+ $bysetpos = TRUE;
1257
+ $bysetposYold = $wdate['year'];
1258
+ $bysetposMold = $wdate['month'];
1259
+ $bysetposDold = $wdate['day'];
1260
+ }
1261
+ if( $bysetpos ) {
1262
+ if( isset( $recur['BYWEEKNO'] )) {
1263
+ $bysetposarr1 = & $bysetposw1;
1264
+ $bysetposarr2 = & $bysetposw2;
1265
+ }
1266
+ else {
1267
+ $bysetposarr1 = & $bysetposymd1;
1268
+ $bysetposarr2 = & $bysetposymd2;
1269
+ }
1270
+
1271
+ foreach( $recur['BYSETPOS'] as $ix ) {
1272
+ if( 0 > $ix ) // both positive and negative BYSETPOS allowed
1273
+ $ix = ( count( $bysetposarr1 ) + $ix + 1);
1274
+ $ix--;
1275
+ if( isset( $bysetposarr1[$ix] )) {
1276
+ if( $fcnStartYMD <= $bysetposarr1[$ix] ) { // only output within period
1277
+ // $testweekno = (int) date( 'W', mktime( 0, 0, $wkst, (int) substr( $bysetposarr1[$ix], 4, 2 ), (int) substr( $bysetposarr1[$ix], 6, 2 ), (int) substr( $bysetposarr1[$ix], 0, 3 ))); // test ###
1278
+ // echo " testYMD (weekno)=$bysetposarr1[$ix] ($testweekno)"; // test ###
1279
+ $result[$bysetposarr1[$ix]] = TRUE;
1280
+ }
1281
+ $countcnt++;
1282
+ }
1283
+ if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] ))
1284
+ break;
1285
+ }
1286
+ // echo "<br>\n"; // test ###
1287
+ $bysetposarr1 = $bysetposarr2;
1288
+ $bysetposarr2 = array();
1289
+ } // end if( $bysetpos )
1290
+ } // end if( $updateOK && isset( $recur['BYSETPOS'] ))
1291
+ } // end while( TRUE )
1292
+ // echo 'output='.str_replace( array( PHP_EOL, ' ' ), '', var_export( $result, TRUE ))."<br> \n"; // test ###
1293
+ }
1294
+ /**
1295
+ * _recur2date help function, checking BYDAY (etc) hits
1296
+ *
1297
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1298
+ * @since 2.6.12 - 2011-01-03
1299
+ * @param array $BYvalue
1300
+ * @param int $upValue
1301
+ * @param int $downValue
1302
+ * @return bool
1303
+ */
1304
+ public static function _recurBYcntcheck( $BYvalue, $upValue, $downValue ) {
1305
+ if( is_array( $BYvalue ) &&
1306
+ ( in_array( $upValue, $BYvalue ) || in_array( $downValue, $BYvalue )))
1307
+ return TRUE;
1308
+ elseif(( $BYvalue == $upValue ) || ( $BYvalue == $downValue ))
1309
+ return TRUE;
1310
+ else
1311
+ return FALSE;
1312
+ }
1313
+ /**
1314
+ * _recur2date help function, (re-)calculate internal index
1315
+ *
1316
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1317
+ * @since 2.6.12 - 2011-01-03
1318
+ * @param string $freq
1319
+ * @param array $date
1320
+ * @param int $wkst
1321
+ * @return bool
1322
+ */
1323
+ public static function _recurIntervalIx( $freq, $date, $wkst ) {
1324
+ /* create interval index */
1325
+ switch( $freq ) {
1326
+ case 'YEARLY':
1327
+ $intervalix = $date['year'];
1328
+ break;
1329
+ case 'MONTHLY':
1330
+ $intervalix = $date['year'].'-'.$date['month'];
1331
+ break;
1332
+ case 'WEEKLY':
1333
+ $intervalix = (int) date( 'W', mktime( 0, 0, $wkst, (int) $date['month'], (int) $date['day'], (int) $date['year'] ));
1334
+ break;
1335
+ case 'DAILY':
1336
+ default:
1337
+ $intervalix = $date['year'].'-'.$date['month'].'-'.$date['day'];
1338
+ break;
1339
+ }
1340
+ return $intervalix;
1341
+ }
1342
+ /**
1343
+ * sort recur dates
1344
+ *
1345
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1346
+ * @since 2.6.12 - 2011-01-03
1347
+ * @param array $bydaya
1348
+ * @param array $bydayb
1349
+ * @return int
1350
+ */
1351
+ public static function _recurBydaySort( $bydaya, $bydayb ) {
1352
+ static $days = array( 'SU' => 0, 'MO' => 1, 'TU' => 2, 'WE' => 3, 'TH' => 4, 'FR' => 5, 'SA' => 6 );
1353
+ return ( $days[substr( $bydaya, -2 )] < $days[substr( $bydayb, -2 )] ) ? -1 : 1;
1354
+ }
1355
+ /**
1356
+ * convert input format for exrule and rrule to internal format
1357
+ *
1358
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1359
+ * @since 2.21.11 - 2015-03-10
1360
+ * @param array $rexrule
1361
+ * @uses iCalUtilityFunctions::_strDate2arr()
1362
+ * @uses iCalUtilityFunctions::_isArrayTimestampDate()
1363
+ * @uses iCalUtilityFunctions::_timestamp2date()
1364
+ * @uses iCalUtilityFunctions::_chkDateArr()
1365
+ * @uses iCalUtilityFunctions::_isOffset()
1366
+ * @uses iCalUtilityFunctions::$fmt
1367
+ * @uses iCalUtilityFunctions::_strdate2date()
1368
+ * @return array
1369
+ */
1370
+ public static function _setRexrule( $rexrule ) {
1371
+ $input = array();
1372
+ if( empty( $rexrule ))
1373
+ return $input;
1374
+ $rexrule = array_change_key_case( $rexrule, CASE_UPPER );
1375
+ foreach( $rexrule as $rexrulelabel => $rexrulevalue ) {
1376
+ if( 'UNTIL' != $rexrulelabel )
1377
+ $input[$rexrulelabel] = $rexrulevalue;
1378
+ else {
1379
+ iCalUtilityFunctions::_strDate2arr( $rexrulevalue );
1380
+ if( iCalUtilityFunctions::_isArrayTimestampDate( $rexrulevalue )) // timestamp, always date-time UTC
1381
+ $input[$rexrulelabel] = iCalUtilityFunctions::_timestamp2date( $rexrulevalue, 7, 'UTC' );
1382
+ elseif( iCalUtilityFunctions::_isArrayDate( $rexrulevalue )) { // date or UTC date-time
1383
+ $parno = ( isset( $rexrulevalue['hour'] ) || isset( $rexrulevalue[4] )) ? 7 : 3;
1384
+ $d = iCalUtilityFunctions::_chkDateArr( $rexrulevalue, $parno );
1385
+ if(( 3 < $parno ) && isset( $d['tz'] ) && ( 'Z' != $d['tz'] ) && iCalUtilityFunctions::_isOffset( $d['tz'] )) {
1386
+ $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $d['tz'] );
1387
+ $input[$rexrulelabel] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
1388
+ unset( $input[$rexrulelabel]['unparsedtext'] );
1389
+ }
1390
+ else
1391
+ $input[$rexrulelabel] = $d;
1392
+ }
1393
+ elseif( 8 <= strlen( trim( $rexrulevalue ))) { // ex. textual date-time 2006-08-03 10:12:18 => UTC
1394
+ $input[$rexrulelabel] = iCalUtilityFunctions::_strdate2date( $rexrulevalue );
1395
+ unset( $input['$rexrulelabel']['unparsedtext'] );
1396
+ }
1397
+ if(( 3 < count( $input[$rexrulelabel] )) && !isset( $input[$rexrulelabel]['tz'] ))
1398
+ $input[$rexrulelabel]['tz'] = 'Z';
1399
+ }
1400
+ }
1401
+ /* set recurrence rule specification in rfc2445 order */
1402
+ $input2 = array();
1403
+ if( isset( $input['FREQ'] ))
1404
+ $input2['FREQ'] = $input['FREQ'];
1405
+ if( isset( $input['UNTIL'] ))
1406
+ $input2['UNTIL'] = $input['UNTIL'];
1407
+ elseif( isset( $input['COUNT'] ))
1408
+ $input2['COUNT'] = $input['COUNT'];
1409
+ if( isset( $input['INTERVAL'] ))
1410
+ $input2['INTERVAL'] = $input['INTERVAL'];
1411
+ if( isset( $input['BYSECOND'] ))
1412
+ $input2['BYSECOND'] = $input['BYSECOND'];
1413
+ if( isset( $input['BYMINUTE'] ))
1414
+ $input2['BYMINUTE'] = $input['BYMINUTE'];
1415
+ if( isset( $input['BYHOUR'] ))
1416
+ $input2['BYHOUR'] = $input['BYHOUR'];
1417
+ if( isset( $input['BYDAY'] )) {
1418
+ if( !is_array( $input['BYDAY'] )) // ensure upper case.. .
1419
+ $input2['BYDAY'] = strtoupper( $input['BYDAY'] );
1420
+ else {
1421
+ foreach( $input['BYDAY'] as $BYDAYx => $BYDAYv ) {
1422
+ if( 'DAY' == strtoupper( $BYDAYx ))
1423
+ $input2['BYDAY']['DAY'] = strtoupper( $BYDAYv );
1424
+ elseif( !is_array( $BYDAYv )) {
1425
+ $input2['BYDAY'][$BYDAYx] = $BYDAYv;
1426
+ }
1427
+ else {
1428
+ foreach( $BYDAYv as $BYDAYx2 => $BYDAYv2 ) {
1429
+ if( 'DAY' == strtoupper( $BYDAYx2 ))
1430
+ $input2['BYDAY'][$BYDAYx]['DAY'] = strtoupper( $BYDAYv2 );
1431
+ else
1432
+ $input2['BYDAY'][$BYDAYx][$BYDAYx2] = $BYDAYv2;
1433
+ }
1434
+ }
1435
+ }
1436
+ }
1437
+ }
1438
+ if( isset( $input['BYMONTHDAY'] ))
1439
+ $input2['BYMONTHDAY'] = $input['BYMONTHDAY'];
1440
+ if( isset( $input['BYYEARDAY'] ))
1441
+ $input2['BYYEARDAY'] = $input['BYYEARDAY'];
1442
+ if( isset( $input['BYWEEKNO'] ))
1443
+ $input2['BYWEEKNO'] = $input['BYWEEKNO'];
1444
+ if( isset( $input['BYMONTH'] ))
1445
+ $input2['BYMONTH'] = $input['BYMONTH'];
1446
+ if( isset( $input['BYSETPOS'] ))
1447
+ $input2['BYSETPOS'] = $input['BYSETPOS'];
1448
+ if( isset( $input['WKST'] ))
1449
+ $input2['WKST'] = $input['WKST'];
1450
+ return $input2;
1451
+ }
1452
+ /**
1453
+ * convert format for input date to internal date with parameters
1454
+ *
1455
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1456
+ * @since 2.21.11 - 2015-03-21
1457
+ * @param mixed $year
1458
+ * @param mixed $month optional
1459
+ * @param int $day optional
1460
+ * @param int $hour optional
1461
+ * @param int $min optional
1462
+ * @param int $sec optional
1463
+ * @param string $tz optional
1464
+ * @param array $params optional
1465
+ * @param string $caller optional
1466
+ * @param string $objName optional
1467
+ * @param string $tzid optional
1468
+ * @uses iCalUtilityFunctions::$tzComps
1469
+ * @uses iCalUtilityFunctions::_strDate2arr()
1470
+ * @uses iCalUtilityFunctions::_isArrayDate()
1471
+ * @uses iCalUtilityFunctions::_chkDateArr()
1472
+ * @uses iCalUtilityFunctions::_isOffset()
1473
+ * @uses iCalUtilityFunctions::_setParams()
1474
+ * @uses iCalUtilityFunctions::_existRem()
1475
+ * @uses iCalUtilityFunctions::$fmt
1476
+ * @uses iCalUtilityFunctions::_isArrayTimestampDate()
1477
+ * @uses iCalUtilityFunctions::_timestamp2date()
1478
+ * @uses iCalUtilityFunctions::_strdate2date()
1479
+ * @return array
1480
+ */
1481
+ public static function _setDate( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE, $caller=null, $objName=null, $tzid=FALSE ) {
1482
+ $input = $parno = null;
1483
+ $localtime = (( 'dtstart' == $caller ) && in_array( $objName, iCalUtilityFunctions::$tzComps )) ? TRUE : FALSE;
1484
+ iCalUtilityFunctions::_strDate2arr( $year );
1485
+ if( iCalUtilityFunctions::_isArrayDate( $year )) {
1486
+ $input['value'] = iCalUtilityFunctions::_chkDateArr( $year, FALSE ); //$parno );
1487
+ if( 100 > $input['value']['year'] )
1488
+ $input['value']['year'] += 2000;
1489
+ if( $localtime )
1490
+ unset( $month['VALUE'], $month['TZID'] );
1491
+ elseif( !isset( $month['TZID'] ) && isset( $tzid ))
1492
+ $month['TZID'] = $tzid;
1493
+ if( isset( $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] ))
1494
+ unset( $month['TZID'] );
1495
+ elseif( !isset( $input['value']['tz'] ) && isset( $month['TZID'] ) && iCalUtilityFunctions::_isOffset( $month['TZID'] )) {
1496
+ $input['value']['tz'] = $month['TZID'];
1497
+ unset( $month['TZID'] );
1498
+ }
1499
+ $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
1500
+ $hitval = ( isset( $input['value']['tz'] )) ? 7 : 6;
1501
+ $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval );
1502
+ $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, count( $input['value'] ), $parno );
1503
+ if( 6 > $parno )
1504
+ unset( $input['value']['tz'], $input['params']['TZID'], $tzid );
1505
+ if(( 6 <= $parno ) && isset( $input['value']['tz'] ) && ( 'Z' != $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) {
1506
+ $d = $input['value'];
1507
+ $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $d['tz'] );
1508
+ $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, $parno );
1509
+ unset( $input['value']['unparsedtext'], $input['params']['TZID'] );
1510
+ }
1511
+ if( isset( $input['value']['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) {
1512
+ $input['params']['TZID'] = $input['value']['tz'];
1513
+ unset( $input['value']['tz'] );
1514
+ }
1515
+ } // end if( iCalUtilityFunctions::_isArrayDate( $year ))
1516
+ elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) {
1517
+ if( $localtime ) unset ( $month['VALUE'], $month['TZID'] );
1518
+ $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
1519
+ $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 );
1520
+ $hitval = 7;
1521
+ $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno );
1522
+ if( isset( $year['tz'] ) && !empty( $year['tz'] )) {
1523
+ if( !iCalUtilityFunctions::_isOffset( $year['tz'] )) {
1524
+ $input['params']['TZID'] = $year['tz'];
1525
+ unset( $year['tz'], $tzid );
1526
+ }
1527
+ else {
1528
+ if( isset( $input['params']['TZID'] ) && !empty( $input['params']['TZID'] )) {
1529
+ if( !iCalUtilityFunctions::_isOffset( $input['params']['TZID'] ))
1530
+ unset( $tzid );
1531
+ else
1532
+ unset( $input['params']['TZID']);
1533
+ }
1534
+ elseif( isset( $tzid ) && !iCalUtilityFunctions::_isOffset( $tzid ))
1535
+ $input['params']['TZID'] = $tzid;
1536
+ }
1537
+ }
1538
+ elseif( isset( $input['params']['TZID'] ) && !empty( $input['params']['TZID'] )) {
1539
+ if( iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) {
1540
+ $year['tz'] = $input['params']['TZID'];
1541
+ unset( $input['params']['TZID']);
1542
+ if( isset( $tzid ) && !empty( $tzid ) && !iCalUtilityFunctions::_isOffset( $tzid ))
1543
+ $input['params']['TZID'] = $tzid;
1544
+ }
1545
+ }
1546
+ elseif( isset( $tzid ) && !empty( $tzid )) {
1547
+ if( iCalUtilityFunctions::_isOffset( $tzid )) {
1548
+ $year['tz'] = $tzid;
1549
+ unset( $input['params']['TZID']);
1550
+ }
1551
+ else
1552
+ $input['params']['TZID'] = $tzid;
1553
+ }
1554
+ $input['value'] = iCalUtilityFunctions::_timestamp2date( $year, $parno );
1555
+ } // end elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year ))
1556
+ elseif( 8 <= strlen( trim((string) $year ))) { // ex. 2006-08-03 10:12:18 [[[+/-]1234[56]] / timezone]
1557
+ if( $localtime )
1558
+ unset( $month['VALUE'], $month['TZID'] );
1559
+ elseif( !isset( $month['TZID'] ) && !empty( $tzid ))
1560
+ $month['TZID'] = $tzid;
1561
+ $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
1562
+ $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7, $parno );
1563
+ $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, $parno, $parno );
1564
+ $input['value'] = iCalUtilityFunctions::_strdate2date( $year, $parno );
1565
+ if( 3 == $parno )
1566
+ unset( $input['value']['tz'], $input['params']['TZID'] );
1567
+ unset( $input['value']['unparsedtext'] );
1568
+ if( isset( $input['value']['tz'] )) {
1569
+ if( iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) {
1570
+ $d = $input['value'];
1571
+ $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $d['tz'] );
1572
+ $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
1573
+ unset( $input['value']['unparsedtext'], $input['params']['TZID'] );
1574
+ }
1575
+ else {
1576
+ $input['params']['TZID'] = $input['value']['tz'];
1577
+ unset( $input['value']['tz'] );
1578
+ }
1579
+ }
1580
+ elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) {
1581
+ $d = $input['value'];
1582
+ $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $input['params']['TZID'] );
1583
+ $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
1584
+ unset( $input['value']['unparsedtext'], $input['params']['TZID'] );
1585
+ }
1586
+ } // end elseif( 8 <= strlen( trim((string) $year )))
1587
+ else {
1588
+ if( is_array( $params ))
1589
+ $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ));
1590
+ elseif( is_array( $tz )) {
1591
+ $input['params'] = iCalUtilityFunctions::_setParams( $tz, array( 'VALUE' => 'DATE-TIME' ));
1592
+ $tz = FALSE;
1593
+ }
1594
+ elseif( is_array( $hour )) {
1595
+ $input['params'] = iCalUtilityFunctions::_setParams( $hour, array( 'VALUE' => 'DATE-TIME' ));
1596
+ $hour = $min = $sec = $tz = FALSE;
1597
+ }
1598
+ if( $localtime )
1599
+ unset ( $input['params']['VALUE'], $input['params']['TZID'] );
1600
+ elseif( !isset( $tz ) && !isset( $input['params']['TZID'] ) && !empty( $tzid ))
1601
+ $input['params']['TZID'] = $tzid;
1602
+ elseif( isset( $tz ) && iCalUtilityFunctions::_isOffset( $tz ))
1603
+ unset( $input['params']['TZID'] );
1604
+ elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) {
1605
+ $tz = $input['params']['TZID'];
1606
+ unset( $input['params']['TZID'] );
1607
+ }
1608
+ $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 );
1609
+ $hitval = ( iCalUtilityFunctions::_isOffset( $tz )) ? 7 : 6;
1610
+ $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno, $parno );
1611
+ $input['value'] = array( 'year' => $year, 'month' => $month, 'day' => $day );
1612
+ if( 3 != $parno ) {
1613
+ $input['value']['hour'] = ( $hour ) ? $hour : '0';
1614
+ $input['value']['min'] = ( $min ) ? $min : '0';
1615
+ $input['value']['sec'] = ( $sec ) ? $sec : '0';
1616
+ if( !empty( $tz ))
1617
+ $input['value']['tz'] = $tz;
1618
+ $strdate = iCalUtilityFunctions::_date2strdate( $input['value'], $parno );
1619
+ if( !empty( $tz ) && !iCalUtilityFunctions::_isOffset( $tz ))
1620
+ $strdate .= ( 'Z' == $tz ) ? $tz : ' '.$tz;
1621
+ $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, $parno );
1622
+ unset( $input['value']['unparsedtext'] );
1623
+ if( isset( $input['value']['tz'] )) {
1624
+ if( iCalUtilityFunctions::_isOffset( $input['value']['tz'] )) {
1625
+ $d = $input['value'];
1626
+ $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $d['tz'] );
1627
+ $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
1628
+ unset( $input['value']['unparsedtext'], $input['params']['TZID'] );
1629
+ }
1630
+ else {
1631
+ $input['params']['TZID'] = $input['value']['tz'];
1632
+ unset( $input['value']['tz'] );
1633
+ }
1634
+ }
1635
+ elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) {
1636
+ $d = $input['value'];
1637
+ $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $input['params']['TZID'] );
1638
+ $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
1639
+ unset( $input['value']['unparsedtext'], $input['params']['TZID'] );
1640
+ }
1641
+ }
1642
+ } // end else (i.e. using all arguments)
1643
+ if(( 3 == $parno ) || ( isset( $input['params']['VALUE'] ) && ( 'DATE' == $input['params']['VALUE'] ))) {
1644
+ $input['params']['VALUE'] = 'DATE';
1645
+ unset( $input['value']['hour'], $input['value']['min'], $input['value']['sec'], $input['value']['tz'], $input['params']['TZID'] );
1646
+ }
1647
+ elseif( isset( $input['params']['TZID'] )) {
1648
+ if(( 'UTC' == strtoupper( $input['params']['TZID'] )) || ( 'GMT' == strtoupper( $input['params']['TZID'] ))) {
1649
+ $input['value']['tz'] = 'Z';
1650
+ unset( $input['params']['TZID'] );
1651
+ }
1652
+ else
1653
+ unset( $input['value']['tz'] );
1654
+ }
1655
+ elseif( isset( $input['value']['tz'] )) {
1656
+ if(( 'UTC' == strtoupper( $input['value']['tz'] )) || ( 'GMT' == strtoupper( $input['value']['tz'] )))
1657
+ $input['value']['tz'] = 'Z';
1658
+ if( 'Z' != $input['value']['tz'] ) {
1659
+ $input['params']['TZID'] = $input['value']['tz'];
1660
+ unset( $input['value']['tz'] );
1661
+ }
1662
+ else
1663
+ unset( $input['params']['TZID'] );
1664
+ }
1665
+ if( $localtime )
1666
+ unset( $input['value']['tz'], $input['params']['TZID'] );
1667
+ return $input;
1668
+ }
1669
+ /**
1670
+ * convert format for input date (UTC) to internal date with parameters
1671
+ *
1672
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1673
+ * @since 2.21.11 - 2015-03-10
1674
+ * @param mixed $year
1675
+ * @param mixed $month optional
1676
+ * @param int $day optional
1677
+ * @param int $hour optional
1678
+ * @param int $min optional
1679
+ * @param int $sec optional
1680
+ * @param array $params optional
1681
+ * @uses iCalUtilityFunctions::_strDate2arr()
1682
+ * @uses iCalUtilityFunctions::_isArrayDate()
1683
+ * @uses iCalUtilityFunctions::_chkDateArr()
1684
+ * @uses iCalUtilityFunctions::_setParams()
1685
+ * @uses iCalUtilityFunctions::_isOffset()
1686
+ * @uses iCalUtilityFunctions::$fmt
1687
+ * @uses iCalUtilityFunctions::_strdate2date()
1688
+ * @uses iCalUtilityFunctions::_isArrayTimestampDate()
1689
+ * @uses iCalUtilityFunctions::_timestamp2date()
1690
+ * @uses iCalUtilityFunctions::_date2strdate()
1691
+ * @uses iCalUtilityFunctions::_existRem()
1692
+ * @return array
1693
+ */
1694
+ public static function _setDate2( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
1695
+ $input = null;
1696
+ iCalUtilityFunctions::_strDate2arr( $year );
1697
+ if( iCalUtilityFunctions::_isArrayDate( $year )) {
1698
+ $input['value'] = iCalUtilityFunctions::_chkDateArr( $year, 7 );
1699
+ if( isset( $input['value']['year'] ) && ( 100 > $input['value']['year'] ))
1700
+ $input['value']['year'] += 2000;
1701
+ $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
1702
+ unset( $input['params']['VALUE'] );
1703
+ if( isset( $input['value']['tz'] ) && iCalUtilityFunctions::_isOffset( $input['value']['tz'] ))
1704
+ $tzid = $input['value']['tz'];
1705
+ elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] ))
1706
+ $tzid = $input['params']['TZID'];
1707
+ else
1708
+ $tzid = '';
1709
+ unset( $input['params']['VALUE'], $input['params']['TZID'] );
1710
+ if( !empty( $tzid ) && ( 'Z' != $tzid ) && iCalUtilityFunctions::_isOffset( $tzid )) {
1711
+ $d = $input['value'];
1712
+ $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $tzid );
1713
+ $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
1714
+ unset( $input['value']['unparsedtext'] );
1715
+ }
1716
+ }
1717
+ elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) {
1718
+ if( isset( $year['tz'] ) && ! iCalUtilityFunctions::_isOffset( $year['tz'] ))
1719
+ $year['tz'] = 'UTC';
1720
+ elseif( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] ))
1721
+ $year['tz'] = $input['params']['TZID'];
1722
+ else
1723
+ $year['tz'] = 'UTC';
1724
+ $input['value'] = iCalUtilityFunctions::_timestamp2date( $year, 7 );
1725
+ $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
1726
+ unset( $input['params']['VALUE'], $input['params']['TZID'] );
1727
+ }
1728
+ elseif( 8 <= strlen( trim((string) $year ))) { // ex. 2006-08-03 10:12:18
1729
+ $input['value'] = iCalUtilityFunctions::_strdate2date( $year, 7 );
1730
+ unset( $input['value']['unparsedtext'] );
1731
+ $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
1732
+ if(( !isset( $input['value']['tz'] ) || empty( $input['value']['tz'] )) && isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] )) {
1733
+ $d = $input['value'];
1734
+ $strdate = sprintf( iCalUtilityFunctions::$fmt['YmdHise'], (int) $d['year'], (int) $d['month'], (int) $d['day'], (int) $d['hour'], (int) $d['min'], (int) $d['sec'], $input['params']['TZID'] );
1735
+ $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
1736
+ unset( $input['value']['unparsedtext'] );
1737
+ }
1738
+ unset( $input['params']['VALUE'], $input['params']['TZID'] );
1739
+ }
1740
+ else {
1741
+ $input['value'] = array( 'year' => $year
1742
+ , 'month' => $month
1743
+ , 'day' => $day
1744
+ , 'hour' => $hour
1745
+ , 'min' => $min
1746
+ , 'sec' => $sec );
1747
+ if( isset( $tz )) $input['value']['tz'] = $tz;
1748
+ if(( isset( $tz ) && iCalUtilityFunctions::_isOffset( $tz )) ||
1749
+ ( isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] ))) {
1750
+ if( !isset( $tz ) && isset( $input['params']['TZID'] ) && iCalUtilityFunctions::_isOffset( $input['params']['TZID'] ))
1751
+ $input['value']['tz'] = $input['params']['TZID'];
1752
+ unset( $input['params']['TZID'] );
1753
+ $strdate = iCalUtilityFunctions::_date2strdate( $input['value'], 7 );
1754
+ $input['value'] = iCalUtilityFunctions::_strdate2date( $strdate, 7 );
1755
+ unset( $input['value']['unparsedtext'] );
1756
+ }
1757
+ $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ));
1758
+ unset( $input['params']['VALUE'] );
1759
+ }
1760
+ $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7 ); // remove default
1761
+ if( !isset( $input['value']['hour'] )) $input['value']['hour'] = 0;
1762
+ if( !isset( $input['value']['min'] )) $input['value']['min'] = 0;
1763
+ if( !isset( $input['value']['sec'] )) $input['value']['sec'] = 0;
1764
+ $input['value']['tz'] = 'Z';
1765
+ return $input;
1766
+ }
1767
+ /**
1768
+ * check index and set (an indexed) content in multiple value array
1769
+ *
1770
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1771
+ * @since 2.6.12 - 2011-01-03
1772
+ * @param array $valArr
1773
+ * @param mixed $value
1774
+ * @param array $params
1775
+ * @param array $defaults
1776
+ * @param int $index
1777
+ * @uses iCalUtilityFunctions::_setParams()
1778
+ * @return void
1779
+ */
1780
+ public static function _setMval( & $valArr, $value, $params=FALSE, $defaults=FALSE, $index=FALSE ) {
1781
+ if( !is_array( $valArr )) $valArr = array();
1782
+ if( $index )
1783
+ $index = $index - 1;
1784
+ elseif( 0 < count( $valArr )) {
1785
+ $keys = array_keys( $valArr );
1786
+ $index = end( $keys ) + 1;
1787
+ }
1788
+ else
1789
+ $index = 0;
1790
+ $valArr[$index] = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params, $defaults ));
1791
+ ksort( $valArr );
1792
+ }
1793
+ /**
1794
+ * set input (formatted) parameters- component property attributes
1795
+ *
1796
+ * default parameters can be set, if missing
1797
+ *
1798
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1799
+ * @since 2.18.10 - 2013-09-04
1800
+ * @param array $params
1801
+ * @param array $defaults
1802
+ * @return array
1803
+ */
1804
+ public static function _setParams( $params, $defaults=FALSE ) {
1805
+ if( !is_array( $params))
1806
+ $params = array();
1807
+ $input = array();
1808
+ $params = array_change_key_case( $params, CASE_UPPER );
1809
+ foreach( $params as $paramKey => $paramValue ) {
1810
+ if( is_array( $paramValue )) {
1811
+ foreach( $paramValue as $pkey => $pValue ) {
1812
+ if(( '"' == substr( $pValue, 0, 1 )) && ( '"' == substr( $pValue, -1 )))
1813
+ $paramValue[$pkey] = substr( $pValue, 1, ( strlen( $pValue ) - 2 ));
1814
+ }
1815
+ }
1816
+ elseif(( '"' == substr( $paramValue, 0, 1 )) && ( '"' == substr( $paramValue, -1 )))
1817
+ $paramValue = substr( $paramValue, 1, ( strlen( $paramValue ) - 2 ));
1818
+ if( 'VALUE' == $paramKey )
1819
+ $input['VALUE'] = strtoupper( $paramValue );
1820
+ else
1821
+ $input[$paramKey] = $paramValue;
1822
+ }
1823
+ if( is_array( $defaults )) {
1824
+ foreach( $defaults as $paramKey => $paramValue ) {
1825
+ if( !isset( $input[$paramKey] ))
1826
+ $input[$paramKey] = $paramValue;
1827
+ }
1828
+ }
1829
+ return (0 < count( $input )) ? $input : null;
1830
+ }
1831
+ /**
1832
+ * set sort arguments/parameters in component
1833
+ *
1834
+ *
1835
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1836
+ * @since 2.21.11 - 2015-03-21
1837
+ * @param object $c valendar component
1838
+ * @param string $sortArg
1839
+ * @uses calendarComponent::$srtk
1840
+ * @uses calendarComponent::$objName
1841
+ * @uses calendarComponent::$getProperty()
1842
+ * @uses iCalUtilityFunctions::$mProps1
1843
+ * @uses calendarComponent::_getProperties()
1844
+ * @uses iCalUtilityFunctions::_date2strdate()
1845
+ * @return void
1846
+ */
1847
+ public static function _setSortArgs( $c, $sortArg=FALSE ) {
1848
+ $c->srtk = array( '0', '0', '0', '0' );
1849
+ if( 'vtimezone' == $c->objName ) {
1850
+ if( FALSE === ( $c->srtk[0] = $c->getProperty( 'tzid' )))
1851
+ $c->srtk[0] = 0;
1852
+ return;
1853
+ }
1854
+ elseif( $sortArg ) {
1855
+ if( in_array( $sortArg, iCalUtilityFunctions::$mProps1 )) {
1856
+ $propValues = array();
1857
+ $c->_getProperties( $sortArg, $propValues );
1858
+ if( !empty( $propValues )) {
1859
+ $sk = array_keys( $propValues );
1860
+ $c->srtk[0] = $sk[0];
1861
+ if( 'RELATED-TO' == $sortArg )
1862
+ $c->srtk[0] .= $c->getProperty( 'uid' );
1863
+ }
1864
+ elseif( 'RELATED-TO' == $sortArg )
1865
+ $c->srtk[0] = $c->getProperty( 'uid' );
1866
+ }
1867
+ elseif( FALSE !== ( $d = $c->getProperty( $sortArg ))) {
1868
+ $c->srtk[0] = $d;
1869
+ if( 'UID' == $sortArg ) {
1870
+ if( FALSE !== ( $d = $c->getProperty( 'recurrence-id' ))) {
1871
+ $c->srtk[1] = iCalUtilityFunctions::_date2strdate( $d );
1872
+ if( FALSE === ( $c->srtk[2] = $c->getProperty( 'sequence' )))
1873
+ $c->srtk[2] = PHP_INT_MAX;
1874
+ }
1875
+ else
1876
+ $c->srtk[1] = $c->srtk[2] = PHP_INT_MAX;
1877
+ }
1878
+ }
1879
+ return;
1880
+ } // end elseif( $sortArg )
1881
+ if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTSTART' ))) {
1882
+ $c->srtk[0] = iCalUtilityFunctions::_strdate2date( $d[1] );
1883
+ unset( $c->srtk[0]['unparsedtext'] );
1884
+ }
1885
+ elseif( FALSE === ( $c->srtk[0] = $c->getProperty( 'dtstart' )))
1886
+ $c->srtk[0] = 0; // sortkey 0 : dtstart
1887
+ if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTEND' ))) {
1888
+ $c->srtk[1] = iCalUtilityFunctions::_strdate2date( $d[1] ); // sortkey 1 : dtend/due(/duration)
1889
+ unset( $c->srtk[1]['unparsedtext'] );
1890
+ }
1891
+ elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'dtend' ))) {
1892
+ if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DUE' ))) {
1893
+ $c->srtk[1] = iCalUtilityFunctions::_strdate2date( $d[1] );
1894
+ unset( $c->srtk[1]['unparsedtext'] );
1895
+ }
1896
+ elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'due' )))
1897
+ if( FALSE === ( $c->srtk[1] = $c->getProperty( 'duration', FALSE, FALSE, TRUE )))
1898
+ $c->srtk[1] = 0;
1899
+ }
1900
+ if( FALSE === ( $c->srtk[2] = $c->getProperty( 'created' ))) // sortkey 2 : created/dtstamp
1901
+ if( FALSE === ( $c->srtk[2] = $c->getProperty( 'dtstamp' )))
1902
+ $c->srtk[2] = 0;
1903
+ if( FALSE === ( $c->srtk[3] = $c->getProperty( 'uid' ))) // sortkey 3 : uid
1904
+ $c->srtk[3] = 0;
1905
+ }
1906
+ /**
1907
+ * break lines at pos 75
1908
+ *
1909
+ * Lines of text SHOULD NOT be longer than 75 octets, excluding the line
1910
+ * break. Long content lines SHOULD be split into a multiple line
1911
+ * representations using a line "folding" technique. That is, a long
1912
+ * line can be split between any two characters by inserting a CRLF
1913
+ * immediately followed by a single linear white space character (i.e.,
1914
+ * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
1915
+ * of CRLF followed immediately by a single linear white space character
1916
+ * is ignored (i.e., removed) when processing the content type.
1917
+ *
1918
+ * Edited 2007-08-26 by Anders Litzell, anders@litzell.se to fix bug where
1919
+ * the reserved expression "\n" in the arg $string could be broken up by the
1920
+ * folding of lines, causing ambiguity in the return string.
1921
+ *
1922
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1923
+ * @since 2.16.2 - 2012-12-18
1924
+ * @param string $string
1925
+ * @param string $nl
1926
+ * @return string
1927
+ */
1928
+ public static function _size75( $string, $nl ) {
1929
+ $tmp = $string;
1930
+ $string = '';
1931
+ $cCnt = $x = 0;
1932
+ while( TRUE ) {
1933
+ if( !isset( $tmp[$x] )) {
1934
+ $string .= $nl; // loop breakes here
1935
+ break;
1936
+ }
1937
+ elseif(( 74 <= $cCnt ) && ( '\\' == $tmp[$x] ) && ( 'n' == $tmp[$x+1] )) {
1938
+ $string .= $nl.' \n'; // don't break lines inside '\n'
1939
+ $x += 2;
1940
+ if( !isset( $tmp[$x] )) {
1941
+ $string .= $nl;
1942
+ break;
1943
+ }
1944
+ $cCnt = 3;
1945
+ }
1946
+ elseif( 75 <= $cCnt ) {
1947
+ $string .= $nl.' ';
1948
+ $cCnt = 1;
1949
+ }
1950
+ $byte = ord( $tmp[$x] );
1951
+ $string .= $tmp[$x];
1952
+ switch( TRUE ) { // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
1953
+ case(( $byte >= 0x20 ) && ( $byte <= 0x7F )): // characters U-00000000 - U-0000007F (same as ASCII)
1954
+ $cCnt += 1;
1955
+ break; // add a one byte character
1956
+ case(( $byte & 0xE0) == 0xC0 ): // characters U-00000080 - U-000007FF, mask 110XXXXX
1957
+ if( isset( $tmp[$x+1] )) {
1958
+ $cCnt += 1;
1959
+ $string .= $tmp[$x+1];
1960
+ $x += 1; // add a two bytes character
1961
+ }
1962
+ break;
1963
+ case(( $byte & 0xF0 ) == 0xE0 ): // characters U-00000800 - U-0000FFFF, mask 1110XXXX
1964
+ if( isset( $tmp[$x+2] )) {
1965
+ $cCnt += 1;
1966
+ $string .= $tmp[$x+1].$tmp[$x+2];
1967
+ $x += 2; // add a three bytes character
1968
+ }
1969
+ break;
1970
+ case(( $byte & 0xF8 ) == 0xF0 ): // characters U-00010000 - U-001FFFFF, mask 11110XXX
1971
+ if( isset( $tmp[$x+3] )) {
1972
+ $cCnt += 1;
1973
+ $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3];
1974
+ $x += 3; // add a four bytes character
1975
+ }
1976
+ break;
1977
+ case(( $byte & 0xFC ) == 0xF8 ): // characters U-00200000 - U-03FFFFFF, mask 111110XX
1978
+ if( isset( $tmp[$x+4] )) {
1979
+ $cCnt += 1;
1980
+ $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3].$tmp[$x+4];
1981
+ $x += 4; // add a five bytes character
1982
+ }
1983
+ break;
1984
+ case(( $byte & 0xFE ) == 0xFC ): // characters U-04000000 - U-7FFFFFFF, mask 1111110X
1985
+ if( isset( $tmp[$x+5] )) {
1986
+ $cCnt += 1;
1987
+ $string .= $tmp[$x+1].$tmp[$x+2].$tmp[$x+3].$tmp[$x+4].$tmp[$x+5];
1988
+ $x += 5; // add a six bytes character
1989
+ }
1990
+ default: // add any other byte without counting up $cCnt
1991
+ break;
1992
+ } // end switch( TRUE )
1993
+ $x += 1; // next 'byte' to test
1994
+ } // end while( TRUE ) {
1995
+ return $string;
1996
+ }
1997
+ /**
1998
+ * sort callback function for exdate
1999
+ *
2000
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2001
+ * @since 2.21.11 - 2015-03-07
2002
+ * @param array $a
2003
+ * @param array $b
2004
+ * @uses iCalUtilityFunctions::$fmt
2005
+ * @return int
2006
+ */
2007
+ public static function _sortExdate1( $a, $b ) {
2008
+ $as = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $a['year'], (int) $a['month'], (int) $a['day'] );
2009
+ $as .= ( isset( $a['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $a['hour'], (int) $a['min'], (int) $a['sec'] ) : '';
2010
+ $bs = sprintf( iCalUtilityFunctions::$fmt['His'], (int) $b['year'], (int) $b['month'], (int) $b['day'] );
2011
+ $bs .= ( isset( $b['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $b['hour'], (int) $b['min'], (int) $b['sec'] ) : '';
2012
+ return strcmp( $as, $bs );
2013
+ }
2014
+ /**
2015
+ * sort callback function for exdate
2016
+ *
2017
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2018
+ * @since 2.21.11 - 2015-03-07
2019
+ * @param array $a
2020
+ * @param array $b
2021
+ * @uses iCalUtilityFunctions::$fmt
2022
+ * @return int
2023
+ */
2024
+ public static function _sortExdate2( $a, $b ) {
2025
+ $val = reset( $a['value'] );
2026
+ $as = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $val['year'], (int) $val['month'], (int) $val['day'] );
2027
+ $as .= ( isset( $val['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $val['hour'], (int) $val['min'], (int) $val['sec'] ) : '';
2028
+ $val = reset( $b['value'] );
2029
+ $bs = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $val['year'], (int) $val['month'], (int) $val['day'] );
2030
+ $bs .= ( isset( $val['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $val['hour'], (int) $val['min'], (int) $val['sec'] ) : '';
2031
+ return strcmp( $as, $bs );
2032
+ }
2033
+ /**
2034
+ * sort callback function for rdate
2035
+ *
2036
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2037
+ * @since 2.21.11 - 2015-03-07
2038
+ * @param array $a
2039
+ * @param array $b
2040
+ * @uses iCalUtilityFunctions::$fmt
2041
+ * @return int
2042
+ */
2043
+ public static function _sortRdate1( $a, $b ) {
2044
+ $val = isset( $a['year'] ) ? $a : $a[0];
2045
+ $as = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $val['year'], (int) $val['month'], (int) $val['day'] );
2046
+ $as .= ( isset( $val['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $val['hour'], (int) $val['min'], (int) $val['sec'] ) : '';
2047
+ $val = isset( $b['year'] ) ? $b : $b[0];
2048
+ $bs = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $val['year'], (int) $val['month'], (int) $val['day'] );
2049
+ $bs .= ( isset( $val['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $val['hour'], (int) $val['min'], (int) $val['sec'] ) : '';
2050
+ return strcmp( $as, $bs );
2051
+ }
2052
+ /**
2053
+ * sort callback function for rdate
2054
+ *
2055
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2056
+ * @since 2.21.11 - 2015-03-07
2057
+ * @param array $a
2058
+ * @param array $b
2059
+ * @uses iCalUtilityFunctions::$fmt
2060
+ * @return int
2061
+ */
2062
+ public static function _sortRdate2( $a, $b ) {
2063
+ $val = isset( $a['value'][0]['year'] ) ? $a['value'][0] : $a['value'][0][0];
2064
+ if( empty( $val ))
2065
+ $as = '';
2066
+ else {
2067
+ $as = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $val['year'], (int) $val['month'], (int) $val['day'] );
2068
+ $as .= ( isset( $val['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $val['hour'], (int) $val['min'], (int) $val['sec'] ) : '';
2069
+ }
2070
+ $val = isset( $b['value'][0]['year'] ) ? $b['value'][0] : $b['value'][0][0];
2071
+ if( empty( $val ))
2072
+ $bs = '';
2073
+ else {
2074
+ $bs = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $val['year'], (int) $val['month'], (int) $val['day'] );
2075
+ $bs .= ( isset( $val['hour'] )) ? sprintf( iCalUtilityFunctions::$fmt['His'], (int) $val['hour'], (int) $val['min'], (int) $val['sec'] ) : '';
2076
+ }
2077
+ return strcmp( $as, $bs );
2078
+ }
2079
+ /**
2080
+ * separate property attributes from property value
2081
+ *
2082
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2083
+ * @since 2.18.6 - 2013-08-29
2084
+ * @param string $line property content
2085
+ * @param array $propAttr property parameters
2086
+ * @uses iCalUtilityFunctions::$parValPrefix
2087
+ * @return void
2088
+ */
2089
+ public static function _splitContent( & $line, & $propAttr=null ) {
2090
+ $attr = array();
2091
+ $attrix = -1;
2092
+ $clen = strlen( $line );
2093
+ $WithinQuotes = FALSE;
2094
+ $cix = 0;
2095
+ while( FALSE !== substr( $line, $cix, 1 )) {
2096
+ if( ! $WithinQuotes && ( ':' == $line[$cix] ) &&
2097
+ ( substr( $line,$cix, 3 ) != '://' ) &&
2098
+ ( ! in_array( strtolower( substr( $line,$cix - 6, 4 )), iCalUtilityFunctions::$parValPrefix['MStz'] )) &&
2099
+ ( ! in_array( strtolower( substr( $line,$cix - 3, 4 )), iCalUtilityFunctions::$parValPrefix['Proto3'] )) &&
2100
+ ( ! in_array( strtolower( substr( $line,$cix - 4, 5 )), iCalUtilityFunctions::$parValPrefix['Proto4'] )) &&
2101
+ ( ! in_array( strtolower( substr( $line,$cix - 6, 7 )), iCalUtilityFunctions::$parValPrefix['Proto6'] ))) {
2102
+ $attrEnd = TRUE;
2103
+ if(( $cix < ( $clen - 4 )) &&
2104
+ ctype_digit( substr( $line, $cix+1, 4 ))) { // an URI with a (4pos) portnr??
2105
+ for( $c2ix = $cix; 3 < $c2ix; $c2ix-- ) {
2106
+ if( '://' == substr( $line, $c2ix - 2, 3 )) {
2107
+ $attrEnd = FALSE;
2108
+ break; // an URI with a portnr!!
2109
+ }
2110
+ }
2111
+ }
2112
+ if( $attrEnd) {
2113
+ $line = substr( $line, ( $cix + 1 ));
2114
+ break;
2115
+ }
2116
+ $cix++;
2117
+ }
2118
+ if( '"' == $line[$cix] )
2119
+ $WithinQuotes = ! $WithinQuotes;
2120
+ if( ';' == $line[$cix] )
2121
+ $attr[++$attrix] = null;
2122
+ else
2123
+ $attr[$attrix] .= $line[$cix];
2124
+ $cix++;
2125
+ }
2126
+ /* make attributes in array format */
2127
+ $propAttr = array();
2128
+ foreach( $attr as $attribute ) {
2129
+ $attrsplit = explode( '=', $attribute, 2 );
2130
+ if( 1 < count( $attrsplit ))
2131
+ $propAttr[$attrsplit[0]] = $attrsplit[1];
2132
+ }
2133
+ }
2134
+ /**
2135
+ * step date, return updated date, array and timpstamp
2136
+ *
2137
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2138
+ * @since 2.21.11 - 2015-03-10
2139
+ * @param array $date date to step
2140
+ * @param string $dateYMD date YMD
2141
+ * @param array $step default array( 'day' => 1 )
2142
+ * @uses iCalUtilityFunctions::$fmt
2143
+ * @return void
2144
+ */
2145
+ public static function _stepdate( & $date, & $dateYMD, $step=array( 'day' => 1 )) {
2146
+ if( !isset( $date['hour'] )) $date['hour'] = 0;
2147
+ if( !isset( $date['min'] )) $date['min'] = 0;
2148
+ if( !isset( $date['sec'] )) $date['sec'] = 0;
2149
+ if( isset( $step['day'] ))
2150
+ $mcnt = date( 't', mktime( (int) $date['hour'], (int) $date['min'], (int) $date['sec'], (int) $date['month'], (int) $date['day'], (int) $date['year'] ));
2151
+ foreach( $step as $stepix => $stepvalue )
2152
+ $date[$stepix] += $stepvalue;
2153
+ if( isset( $step['month'] )) {
2154
+ if( 12 < $date['month'] ) {
2155
+ $date['year'] += 1;
2156
+ $date['month'] -= 12;
2157
+ }
2158
+ }
2159
+ elseif( isset( $step['day'] )) {
2160
+ if( $mcnt < $date['day'] ) {
2161
+ $date['day'] -= $mcnt;
2162
+ $date['month'] += 1;
2163
+ if( 12 < $date['month'] ) {
2164
+ $date['year'] += 1;
2165
+ $date['month'] -= 12;
2166
+ }
2167
+ }
2168
+ }
2169
+ $dateYMD = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $date['year'], (int) $date['month'], (int) $date['day'] );
2170
+ unset( $mcnt );
2171
+ }
2172
+ /**
2173
+ * convert a date from specific string to array format
2174
+ *
2175
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2176
+ * @since 2.11.8 - 2012-01-27
2177
+ * @param mixed $input
2178
+ * @return bool, TRUE on success
2179
+ */
2180
+ public static function _strDate2arr( & $input ) {
2181
+ if( is_array( $input ))
2182
+ return FALSE;
2183
+ if( 5 > strlen( (string) $input ))
2184
+ return FALSE;
2185
+ $work = $input;
2186
+ if( 2 == substr_count( $work, '-' ))
2187
+ $work = str_replace( '-', '', $work );
2188
+ if( 2 == substr_count( $work, '/' ))
2189
+ $work = str_replace( '/', '', $work );
2190
+ if( !ctype_digit( substr( $work, 0, 8 )))
2191
+ return FALSE;
2192
+ $temp = array( 'year' => (int) substr( $work, 0, 4 )
2193
+ , 'month' => (int) substr( $work, 4, 2 )
2194
+ , 'day' => (int) substr( $work, 6, 2 ));
2195
+ if( !checkdate( $temp['month'], $temp['day'], $temp['year'] ))
2196
+ return FALSE;
2197
+ if( 8 == strlen( $work )) {
2198
+ $input = $temp;
2199
+ return TRUE;
2200
+ }
2201
+ if(( ' ' == substr( $work, 8, 1 )) || ( 'T' == substr( $work, 8, 1 )) || ( 't' == substr( $work, 8, 1 )))
2202
+ $work = substr( $work, 9 );
2203
+ elseif( ctype_digit( substr( $work, 8, 1 )))
2204
+ $work = substr( $work, 8 );
2205
+ else
2206
+ return FALSE;
2207
+ if( 2 == substr_count( $work, ':' ))
2208
+ $work = str_replace( ':', '', $work );
2209
+ if( !ctype_digit( substr( $work, 0, 4 )))
2210
+ return FALSE;
2211
+ $temp['hour'] = substr( $work, 0, 2 );
2212
+ $temp['min'] = substr( $work, 2, 2 );
2213
+ if((( 0 > $temp['hour'] ) || ( $temp['hour'] > 23 )) ||
2214
+ (( 0 > $temp['min'] ) || ( $temp['min'] > 59 )))
2215
+ return FALSE;
2216
+ if( ctype_digit( substr( $work, 4, 2 ))) {
2217
+ $temp['sec'] = substr( $work, 4, 2 );
2218
+ if(( 0 > $temp['sec'] ) || ( $temp['sec'] > 59 ))
2219
+ return FALSE;
2220
+ $len = 6;
2221
+ }
2222
+ else {
2223
+ $temp['sec'] = 0;
2224
+ $len = 4;
2225
+ }
2226
+ if( $len < strlen( $work))
2227
+ $temp['tz'] = trim( substr( $work, 6 ));
2228
+ $input = $temp;
2229
+ return TRUE;
2230
+ }
2231
+ /**
2232
+ * ensures internal date-time/date format for input date-time/date in string fromat
2233
+ *
2234
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2235
+ * @since 2.21.11 - 2015-03-15
2236
+ * Modified to also return original string value by Yitzchok Lavi <icalcreator@onebigsystem.com>
2237
+ * @param array $datetime
2238
+ * @param int $parno optional, default FALSE
2239
+ * @param moxed $wtz optional, default null
2240
+ * @uses iCalUtilityFunctions::_isOffset()
2241
+ * @uses iCalUtilityFunctions::_strDate2arr()
2242
+ * @uses iCalUtilityFunctions::_isOffset()
2243
+ * @uses iCalUtilityFunctions::_tz2offset()
2244
+ * @uses iCalUtilityFunctions::$fmt
2245
+ * @return array
2246
+ */
2247
+ public static function _strdate2date( $datetime, $parno=FALSE, $wtz=null ) {
2248
+ $unparseddatetime = $datetime;
2249
+ $datetime = (string) trim( $datetime );
2250
+ $tz = null;
2251
+ $offset = 0;
2252
+ $tzSts = FALSE;
2253
+ $len = strlen( $datetime );
2254
+ if( 'Z' == substr( $datetime, -1 )) {
2255
+ $tz = 'Z';
2256
+ $datetime = trim( substr( $datetime, 0, ( $len - 1 )));
2257
+ $tzSts = TRUE;
2258
+ }
2259
+ if( iCalUtilityFunctions::_isOffset( substr( $datetime, -5, 5 ))) { // [+/-]NNNN offset
2260
+ $tz = substr( $datetime, -5, 5 );
2261
+ $datetime = trim( substr( $datetime, 0, ($len - 5)));
2262
+ }
2263
+ elseif( iCalUtilityFunctions::_isOffset( substr( $datetime, -7, 7 ))) { // [+/-]NNNNNN offset
2264
+ $tz = substr( $datetime, -7, 7 );
2265
+ $datetime = trim( substr( $datetime, 0, ($len - 7)));
2266
+ }
2267
+ elseif( empty( $wtz ) && ctype_digit( substr( $datetime, 0, 4 )) && ctype_digit( substr( $datetime, -2, 2 )) && iCalUtilityFunctions::_strDate2arr( $datetime )) {
2268
+ $output = $datetime;
2269
+ if( !empty( $tz ))
2270
+ $output['tz'] = 'Z';
2271
+ $output['unparsedtext'] = $unparseddatetime;
2272
+ return $output;
2273
+ }
2274
+ else {
2275
+ $cx = $tx = 0; // find any trailing timezone or offset
2276
+ $len = strlen( $datetime );
2277
+ for( $cx = -1; $cx > ( 9 - $len ); $cx-- ) {
2278
+ $char = substr( $datetime, $cx, 1 );
2279
+ if(( ' ' == $char ) || ctype_digit( $char ))
2280
+ break; // if exists, tz ends here.. . ?
2281
+ else
2282
+ $tx--; // tz length counter
2283
+ }
2284
+ if( 0 > $tx ) { // if any
2285
+ $tz = substr( $datetime, $tx );
2286
+ $datetime = trim( substr( $datetime, 0, $len + $tx ));
2287
+ }
2288
+ if(( ctype_digit( substr( $datetime, 0, 8 )) && ( 'T' == substr( $datetime, 8, 1 )) && ctype_digit( substr( $datetime, -6, 6 ))) ||
2289
+ ( ctype_digit( substr( $datetime, 0, 14 ))))
2290
+ $tzSts = TRUE;
2291
+ }
2292
+ if( empty( $tz ) && !empty( $wtz ))
2293
+ $tz = $wtz;
2294
+ if( 3 == $parno )
2295
+ $tz = null;
2296
+ if( !empty( $tz )) { // tz set
2297
+ if(( 'Z' != $tz ) && ( iCalUtilityFunctions::_isOffset( $tz ))) {
2298
+ $offset = (string) iCalUtilityFunctions::_tz2offset( $tz ) * -1;
2299
+ $tz = 'UTC';
2300
+ $tzSts = TRUE;
2301
+ }
2302
+ elseif( !empty( $wtz ))
2303
+ $tzSts = TRUE;
2304
+ $tz = trim( $tz );
2305
+ if(( 'Z' == $tz ) || ( 'GMT' == strtoupper( $tz )))
2306
+ $tz = 'UTC';
2307
+ if( 0 < substr_count( $datetime, '-' ))
2308
+ $datetime = str_replace( '-', '/', $datetime );
2309
+ try {
2310
+ $d = new DateTime( $datetime, new DateTimeZone( $tz ));
2311
+ if( 0 != $offset ) // adjust for offset
2312
+ $d->modify( $offset.' seconds' );
2313
+ $datestring = $d->format( iCalUtilityFunctions::$fmt['YmdHis3'] );
2314
+ unset( $d );
2315
+ }
2316
+ catch( Exception $e ) {
2317
+ $datestring = date( iCalUtilityFunctions::$fmt['YmdHis3'], strtotime( $datetime ));
2318
+ }
2319
+ } // end if( !empty( $tz ))
2320
+ else
2321
+ $datestring = date( iCalUtilityFunctions::$fmt['YmdHis3'], strtotime( $datetime ));
2322
+ if( 'UTC' == $tz )
2323
+ $tz = 'Z';
2324
+ $d = explode( '-', $datestring );
2325
+ $output = array( 'year' => $d[0], 'month' => $d[1], 'day' => $d[2] );
2326
+ if( !$parno || ( 3 != $parno )) { // parno is set to 6 or 7
2327
+ $output['hour'] = $d[3];
2328
+ $output['min'] = $d[4];
2329
+ $output['sec'] = $d[5];
2330
+ if(( $tzSts || ( 7 == $parno )) && !empty( $tz ))
2331
+ $output['tz'] = $tz;
2332
+ }
2333
+ // return original string in the array in case strtotime failed to make sense of it
2334
+ $output['unparsedtext'] = $unparseddatetime;
2335
+ return $output;
2336
+ }
2337
+ /********************************************************************************/
2338
+ /**
2339
+ * special characters management output
2340
+ *
2341
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2342
+ * @since 2.16.2 - 2012-12-18
2343
+ * @param string $string
2344
+ * @param string $format
2345
+ * @param string $nl
2346
+ * @return string
2347
+ */
2348
+ public static function _strrep( $string, $format, $nl ) {
2349
+ switch( $format ) {
2350
+ case 'xcal':
2351
+ $string = str_replace( '\n', $nl, $string);
2352
+ $string = htmlspecialchars( strip_tags( stripslashes( urldecode ( $string ))));
2353
+ break;
2354
+ default:
2355
+ $pos = 0;
2356
+ $specChars = array( 'n', 'N', 'r', ',', ';' );
2357
+ while( isset( $string[$pos] )) {
2358
+ if( FALSE === ( $pos = strpos( $string, "\\", $pos )))
2359
+ break;
2360
+ if( !in_array( substr( $string, $pos, 1 ), $specChars )) {
2361
+ $string = substr( $string, 0, $pos )."\\".substr( $string, ( $pos + 1 ));
2362
+ $pos += 1;
2363
+ }
2364
+ $pos += 1;
2365
+ }
2366
+ if( FALSE !== strpos( $string, '"' ))
2367
+ $string = str_replace('"', "'", $string);
2368
+ if( FALSE !== strpos( $string, ',' ))
2369
+ $string = str_replace(',', '\,', $string);
2370
+ if( FALSE !== strpos( $string, ';' ))
2371
+ $string = str_replace(';', '\;', $string);
2372
+ if( FALSE !== strpos( $string, "\r\n" ))
2373
+ $string = str_replace( "\r\n", '\n', $string);
2374
+ elseif( FALSE !== strpos( $string, "\r" ))
2375
+ $string = str_replace( "\r", '\n', $string);
2376
+ elseif( FALSE !== strpos( $string, "\n" ))
2377
+ $string = str_replace( "\n", '\n', $string);
2378
+ if( FALSE !== strpos( $string, '\N' ))
2379
+ $string = str_replace( '\N', '\n', $string);
2380
+ // if( FALSE !== strpos( $string, $nl ))
2381
+ $string = str_replace( $nl, '\n', $string);
2382
+ break;
2383
+ }
2384
+ return $string;
2385
+ }
2386
+ /**
2387
+ * special characters management input (from iCal file)
2388
+ *
2389
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2390
+ * @since 2.16.2 - 2012-12-18
2391
+ * @param string $string
2392
+ * @return string
2393
+ */
2394
+ public static function _strunrep( $string ) {
2395
+ $string = str_replace( '\\\\', '\\', $string);
2396
+ $string = str_replace( '\,', ',', $string);
2397
+ $string = str_replace( '\;', ';', $string);
2398
+ // $string = str_replace( '\n', $nl, $string); // ??
2399
+ return $string;
2400
+ }
2401
+ /**
2402
+ * convert timestamp to date array, default UTC or adjusted for offset/timezone
2403
+ *
2404
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2405
+ * @since 2.21.11 - 2015-03-07
2406
+ * @param mixed $timestamp
2407
+ * @param int $parno
2408
+ * @param string $wtz
2409
+ * @uses iCalUtilityFunctions::_isOffset()
2410
+ * @uses iCalUtilityFunctions::_tz2offset()
2411
+ * @uses iCalUtilityFunctions::$fmt
2412
+ * @return array
2413
+ */
2414
+ public static function _timestamp2date( $timestamp, $parno=6, $wtz=null ) {
2415
+ if( is_array( $timestamp )) {
2416
+ $tz = ( isset( $timestamp['tz'] )) ? $timestamp['tz'] : $wtz;
2417
+ $timestamp = $timestamp['timestamp'];
2418
+ }
2419
+ $tz = ( isset( $tz )) ? $tz : $wtz;
2420
+ $offset = 0;
2421
+ if( empty( $tz ) || ( 'Z' == $tz ) || ( 'GMT' == strtoupper( $tz )))
2422
+ $tz = 'UTC';
2423
+ elseif( iCalUtilityFunctions::_isOffset( $tz )) {
2424
+ $offset = iCalUtilityFunctions::_tz2offset( $tz );
2425
+ }
2426
+ try {
2427
+ $d = new DateTime( "@$timestamp" ); // set UTC date
2428
+ if( 0 != $offset ) // adjust for offset
2429
+ $d->modify( $offset.' seconds' );
2430
+ elseif( 'UTC' != $tz )
2431
+ $d->setTimezone( new DateTimeZone( $tz )); // convert to local date
2432
+ $date = $d->format( iCalUtilityFunctions::$fmt['YmdHis3'] );
2433
+ unset( $d );
2434
+ }
2435
+ catch( Exception $e ) {
2436
+ $date = date( iCalUtilityFunctions::$fmt['YmdHis3'], $timestamp );
2437
+ }
2438
+ $date = explode( '-', $date );
2439
+ $output = array( 'year' => $date[0], 'month' => $date[1], 'day' => $date[2] );
2440
+ if( 3 != $parno ) {
2441
+ $output['hour'] = $date[3];
2442
+ $output['min'] = $date[4];
2443
+ $output['sec'] = $date[5];
2444
+ if(( 'UTC' == $tz ) || ( 0 == $offset ))
2445
+ $output['tz'] = 'Z';
2446
+ }
2447
+ return $output;
2448
+ }
2449
+ /**
2450
+ * transforms a dateTime from a timezone to another using PHP DateTime and DateTimeZone class (PHP >= PHP 5.2.0)
2451
+ *
2452
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2453
+ * @since 2.15.1 - 2012-10-17
2454
+ * @param mixed $date date to alter
2455
+ * @param string $tzFrom PHP valid 'from' timezone
2456
+ * @param string $tzTo PHP valid 'to' timezone, default 'UTC'
2457
+ * @param string $format date output format, default 'Ymd\THis'
2458
+ * @uses iCalUtilityFunctions::_isArrayDate()
2459
+ * @uses iCalUtilityFunctions::_date2strdate()
2460
+ * @uses iCalUtilityFunctions::_chkDateArr()
2461
+ * @return bool
2462
+ */
2463
+ public static function transformDateTime( & $date, $tzFrom, $tzTo='UTC', $format = 'Ymd\THis' ) {
2464
+ if( is_array( $date ) && isset( $date['timestamp'] )) {
2465
+ try {
2466
+ $d = new DateTime( "@{$date['timestamp']}" ); // set UTC date
2467
+ $d->setTimezone(new DateTimeZone( $tzFrom )); // convert to 'from' date
2468
+ }
2469
+ catch( Exception $e ) { return FALSE; }
2470
+ }
2471
+ else {
2472
+ if( iCalUtilityFunctions::_isArrayDate( $date )) {
2473
+ if( isset( $date['tz'] ))
2474
+ unset( $date['tz'] );
2475
+ $date = iCalUtilityFunctions::_date2strdate( iCalUtilityFunctions::_chkDateArr( $date ));
2476
+ }
2477
+ if( 'Z' == substr( $date, -1 ))
2478
+ $date = substr( $date, 0, ( strlen( $date ) - 2 ));
2479
+ try { $d = new DateTime( $date, new DateTimeZone( $tzFrom )); }
2480
+ catch( Exception $e ) { return FALSE; }
2481
+ }
2482
+ try { $d->setTimezone( new DateTimeZone( $tzTo )); }
2483
+ catch( Exception $e ) { return FALSE; }
2484
+ $date = $d->format( $format );
2485
+ return TRUE;
2486
+ }
2487
+ /**
2488
+ * convert offset, [+/-]HHmm[ss], to seconds, used when correcting UTC to localtime or v.v.
2489
+ *
2490
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2491
+ * @since 2.11.4 - 2012-01-11
2492
+ * @param string $tz
2493
+ * @return integer
2494
+ */
2495
+ public static function _tz2offset( $tz ) {
2496
+ $tz = trim( (string) $tz );
2497
+ $offset = 0;
2498
+ if((( 5 != strlen( $tz )) && ( 7 != strlen( $tz ))) ||
2499
+ (( '+' != substr( $tz, 0, 1 )) && ( '-' != substr( $tz, 0, 1 ))) ||
2500
+ (( '0000' >= substr( $tz, 1, 4 )) && ( '9999' < substr( $tz, 1, 4 ))) ||
2501
+ (( 7 == strlen( $tz )) && ( '00' > substr( $tz, 5, 2 )) && ( '99' < substr( $tz, 5, 2 ))))
2502
+ return $offset;
2503
+ $hours2sec = (int) substr( $tz, 1, 2 ) * 3600;
2504
+ $min2sec = (int) substr( $tz, 3, 2 ) * 60;
2505
+ $sec = ( 7 == strlen( $tz )) ? (int) substr( $tz, -2 ) : '00';
2506
+ $offset = $hours2sec + $min2sec + $sec;
2507
+ $offset = ('-' == substr( $tz, 0, 1 )) ? $offset * -1 : $offset;
2508
+ return $offset;
2509
+ }
2510
+ }
includes/libs/iCal/lib/iCaldateTime.class.php ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*********************************************************************************/
3
+ /**
4
+ *
5
+ * iCalcreator, a PHP rfc2445/rfc5545 solution.
6
+ *
7
+ * @copyright Copyright (c) 2007-2015 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
8
+ * @link http://kigkonsult.se/iCalcreator/index.php
9
+ * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
10
+ * @package iCalcreator
11
+ * @version 2.22
12
+ */
13
+ /**
14
+ * This library is free software; you can redistribute it and/or
15
+ * modify it under the terms of the GNU Lesser General Public
16
+ * License as published by the Free Software Foundation; either
17
+ * version 2.1 of the License, or (at your option) any later version.
18
+ *
19
+ * This library is distributed in the hope that it will be useful,
20
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
+ * Lesser General Public License for more details.
23
+ *
24
+ * You should have received a copy of the GNU Lesser General Public
25
+ * License along with this library; if not, write to the Free Software
26
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
+ */
28
+ /*********************************************************************************/
29
+ /**
30
+ * selectComponent help class
31
+ *
32
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
33
+ * @since 2.21.7 - 2015-03-10
34
+ */
35
+ class iCaldateTime extends dateTime {
36
+ /** @var string default date[-time] format */
37
+ public $dateFormat = 'Y-m-d H:i:s e';
38
+ /** @var string default object instance date[-time] 'key' */
39
+ public $key = null;
40
+ /** @var array date[-time] origin */
41
+ public $SCbools = array();
42
+ /**
43
+ * return time (His) array
44
+ *
45
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
46
+ * @since 2.21.7 - 2015-03-07
47
+ * @return array
48
+ */
49
+ public function getTime() {
50
+ return explode( ':', $this->format( 'H:i:s' ));
51
+ }
52
+ /**
53
+ * return the timezone name
54
+ *
55
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
56
+ * @since 2.21.7 - 2015-03-07
57
+ * @return string
58
+ */
59
+ public function getTimezoneName() {
60
+ $tz = $this->getTimezone();
61
+ return $tz->getName();
62
+ }
63
+ /**
64
+ * return formatted date
65
+ *
66
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
67
+ * @since 2.21.7 - 2015-03-07
68
+ * @param string $format
69
+ * @uses iCaldateTime::$dateFormat
70
+ * @return string
71
+ */
72
+ public function format( $format=null ) {
73
+ if( empty( $format ) && isset( $this->dateFormat ))
74
+ $format = $this->dateFormat;
75
+ return parent::format( $format );
76
+ }
77
+ /**
78
+ * return iCaldateTime object instance based on date array and timezone(s)
79
+ *
80
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
81
+ * @since 2.21.11 - 2015-03-28
82
+ * @param array $date
83
+ * @param array $params
84
+ * @param array $tz
85
+ * @param string $dtstartTz
86
+ * @uses iCalUtilityFunctions::$fmt
87
+ * @uses iCaldateTime::getTimezoneName()
88
+ * @uses iCaldateTime::$dateFormat
89
+ * @uses iCaldateTime::$key
90
+ * @return object instance
91
+ */
92
+ public static function factory( array $date, $params=null, $tz=null, $dtstartTz=null ) {
93
+ if( isset( $params['TZID'] ) && ! empty( $params['TZID'] ))
94
+ $tz = ( 'Z' == $params['TZID'] ) ? 'UTC' : $params['TZID'];
95
+ elseif( isset( $tz['tz'] ) && ! empty( $tz['tz'] ))
96
+ $tz = ( 'Z' == $tz['tz'] ) ? 'UTC' : $tz['tz'];
97
+ else
98
+ $tz = ini_get( 'date_default_timezone_set' );
99
+ $strdate = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $date['year'], (int) $date['month'], (int) $date['day'] );
100
+ if( isset( $date['hour'] ))
101
+ $strdate .= 'T'.sprintf( iCalUtilityFunctions::$fmt['His'], (int) $date['hour'], (int) $date['min'], (int) $date['sec'] );
102
+ try {
103
+ $timezone = new DateTimeZone( $tz );
104
+ $d = new iCaldateTime( $strdate, $timezone );
105
+ }
106
+ catch( Exception $e ) {
107
+ $d = new iCaldateTime( $strdate );
108
+ }
109
+ if( ! empty( $dtstartTz )) {
110
+ if( 'Z' == $dtstartTz )
111
+ $dtstartTz = 'UTC';
112
+ if( $dtstartTz != $d->getTimezoneName()) { // set the same timezone as dtstart
113
+ try {
114
+ $timezone = new DateTimeZone( $dtstartTz );
115
+ $d->setTimezone( $timezone );
116
+ }
117
+ catch( Exception $e ) {}
118
+ }
119
+ }
120
+ unset( $timezone, $strdate );
121
+ if( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] )) {
122
+ $d->dateFormat = 'Y-m-d';
123
+ $d->key = $d->format( 'Ymd' );
124
+ }
125
+ else
126
+ $d->key = $d->format( 'YmdHis' );
127
+ return $d;
128
+ }
129
+ }
includes/libs/iCal/lib/valarm.class.php ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*********************************************************************************/
3
+ /**
4
+ *
5
+ * iCalcreator, a PHP rfc2445/rfc5545 solution.
6
+ *
7
+ * @copyright Copyright (c) 2007-2015 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
8
+ * @link http://kigkonsult.se/iCalcreator/index.php
9
+ * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
10
+ * @package iCalcreator
11
+ * @version 2.22
12
+ */
13
+ /**
14
+ * This library is free software; you can redistribute it and/or
15
+ * modify it under the terms of the GNU Lesser General Public
16
+ * License as published by the Free Software Foundation; either
17
+ * version 2.1 of the License, or (at your option) any later version.
18
+ *
19
+ * This library is distributed in the hope that it will be useful,
20
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
+ * Lesser General Public License for more details.
23
+ *
24
+ * You should have received a copy of the GNU Lesser General Public
25
+ * License along with this library; if not, write to the Free Software
26
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
+ */
28
+ /*********************************************************************************/
29
+ /**
30
+ * class for calendar component VALARM
31
+ *
32
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
33
+ * @since 2.5.1 - 2008-10-12
34
+ */
35
+ class valarm extends calendarComponent {
36
+ /**
37
+ * @var array $action component property value
38
+ * @var array $attach component property value
39
+ * @var array $attendee component property value
40
+ * @var array $description component property value
41
+ * @var array $duration component property value
42
+ * @var array $repeat component property value
43
+ * @var array $summary component property value
44
+ * @var array $trigger component property value
45
+ * @access protected
46
+ */
47
+ protected $action;
48
+ protected $attach;
49
+ protected $attendee;
50
+ protected $description;
51
+ protected $duration;
52
+ protected $repeat;
53
+ protected $summary;
54
+ protected $trigger;
55
+ /**
56
+ * constructor for calendar component VALARM object
57
+ *
58
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
59
+ * @since 2.8.2 - 2011-05-01
60
+ * @param array $config
61
+ * @uses valarm::calendarComponent()
62
+ * @uses valarm::$action
63
+ * @uses valarm::$attach
64
+ * @uses valarm::$attendee
65
+ * @uses valarm::$description
66
+ * @uses valarm::$duration
67
+ * @uses valarm::$repeat
68
+ * @uses valarm::$summary
69
+ * @uses valarm::$trigger
70
+ * @uses valarm::$xprop
71
+ * @uses calendarComponent::setConfig()
72
+ */
73
+ function __construct( $config = array()) {
74
+ $this->calendarComponent();
75
+ $this->action = '';
76
+ $this->attach = '';
77
+ $this->attendee = '';
78
+ $this->description = '';
79
+ $this->duration = '';
80
+ $this->repeat = '';
81
+ $this->summary = '';
82
+ $this->trigger = '';
83
+ $this->xprop = '';
84
+ if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
85
+ $config['language'] = ICAL_LANG;
86
+ if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
87
+ if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
88
+ if( !isset( $config['format'] )) $config['format'] = 'iCal';
89
+ if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
90
+ $this->setConfig( $config );
91
+ }
92
+ /**
93
+ * create formatted output for calendar component VALARM object instance
94
+ *
95
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
96
+ * @since 2.5.1 - 2008-10-22
97
+ * @param array $xcaldecl
98
+ * @uses calendarComponent::_createFormat()
99
+ * @uses calendarComponent::$componentStart1
100
+ * @uses calendarComponent::$componentStart2
101
+ * @uses calendarComponent::$nl
102
+ * @uses calendarComponent::createAction()
103
+ * @uses calendarComponent::createAttach()
104
+ * @uses calendarComponent::createAttendee()
105
+ * @uses calendarComponent::createDescription()
106
+ * @uses calendarComponent::createDuration()
107
+ * @uses calendarComponent::createRepeat()
108
+ * @uses calendarComponent::createSummary()
109
+ * @uses calendarComponent::createTrigger()
110
+ * @uses calendarComponent::createXprop()
111
+ * @uses calendarComponent::$componentEnd1
112
+ * @uses calendarComponent::$componentEnd2
113
+ * @uses calendarComponent::$xcaldecl
114
+ * @return string
115
+ */
116
+ function createComponent( & $xcaldecl ) {
117
+ $objectname = $this->_createFormat();
118
+ $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
119
+ $component .= $this->createAction();
120
+ $component .= $this->createAttach();
121
+ $component .= $this->createAttendee();
122
+ $component .= $this->createDescription();
123
+ $component .= $this->createDuration();
124
+ $component .= $this->createRepeat();
125
+ $component .= $this->createSummary();
126
+ $component .= $this->createTrigger();
127
+ $component .= $this->createXprop();
128
+ $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
129
+ if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
130
+ foreach( $this->xcaldecl as $localxcaldecl )
131
+ $xcaldecl[] = $localxcaldecl;
132
+ }
133
+ return $component;
134
+ }
135
+ }
includes/libs/iCal/lib/vcalendar.class.php ADDED
@@ -0,0 +1,2132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*********************************************************************************/
3
+ /**
4
+ *
5
+ * iCalcreator, a PHP rfc2445/rfc5545 solution.
6
+ *
7
+ * @copyright Copyright (c) 2007-2015 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
8
+ * @link http://kigkonsult.se/iCalcreator/index.php
9
+ * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
10
+ * @package iCalcreator
11
+ * @version 2.22
12
+ */
13
+ /**
14
+ * This library is free software; you can redistribute it and/or
15
+ * modify it under the terms of the GNU Lesser General Public
16
+ * License as published by the Free Software Foundation; either
17
+ * version 2.1 of the License, or (at your option) any later version.
18
+ *
19
+ * This library is distributed in the hope that it will be useful,
20
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
+ * Lesser General Public License for more details.
23
+ *
24
+ * You should have received a copy of the GNU Lesser General Public
25
+ * License along with this library; if not, write to the Free Software
26
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
+ */
28
+ /*********************************************************************************/
29
+ /**
30
+ * vcalendar class
31
+ *
32
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
33
+ * @since 2.21.11 - 2015-03-31
34
+ */
35
+ class vcalendar extends iCalBase {
36
+ /**
37
+ * @var array $calscale calendar property variable
38
+ * @var array $method calendar property variable
39
+ * @var array $prodid calendar property variable
40
+ * @var array $version calendar property variable
41
+ * @access private
42
+ */
43
+ private $calscale;
44
+ private $method;
45
+ private $prodid;
46
+ private $version;
47
+ /**
48
+ * @var array $directory calendar config variable
49
+ * @var array $filename calendar config variable
50
+ * @var array $url calendar config variable
51
+ * @access private
52
+ */
53
+ private $directory;
54
+ private $filename;
55
+ private $url;
56
+ /**
57
+ * redirect headers
58
+ *
59
+ * @var array $headers
60
+ * @access private
61
+ * @static
62
+ */
63
+ private static $headers = array( 'Content-Encoding: gzip',
64
+ 'Vary: *',
65
+ 'Content-Length: %s',
66
+ 'Content-Type: application/calendar+xml; charset=utf-8',
67
+ 'Content-Type: text/calendar; charset=utf-8',
68
+ 'Content-Disposition: attachment; filename="%s"',
69
+ 'Content-Disposition: inline; filename="%s"',
70
+ 'Cache-Control: max-age=10',
71
+ );
72
+ /**
73
+ * constructor for calendar object
74
+ *
75
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
76
+ * @since 2.21.15 - 2015-04-04
77
+ * @param array $config
78
+ * @uses vcalendar::_makeVersion()
79
+ * @uses vcalendar::$calscale
80
+ * @uses vcalendar::$method
81
+ * @uses vcalendar::_makeUnique_id()
82
+ * @uses vcalendar::$prodid
83
+ * @uses vcalendar::$xprop
84
+ * @uses vcalendar::$language
85
+ * @uses vcalendar::$directory
86
+ * @uses vcalendar::$filename
87
+ * @uses vcalendar::$url
88
+ * @uses vcalendar::$dtzid
89
+ * @uses vcalendar::setConfig()
90
+ * @uses vcalendar::$xcaldecl
91
+ * @uses vcalendar::$components
92
+ */
93
+ function __construct( $config = array()) {
94
+ $this->_makeVersion();
95
+ $this->calscale = null;
96
+ $this->method = null;
97
+ $this->_makeUnique_id();
98
+ $this->prodid = null;
99
+ $this->xprop = array();
100
+ $this->language = null;
101
+ $this->directory = '.';
102
+ $this->filename = null;
103
+ $this->url = null;
104
+ $this->dtzid = null;
105
+ /**
106
+ * language = <Text identifying a language, as defined in [RFC 1766]>
107
+ */
108
+ if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
109
+ $config['language'] = ICAL_LANG;
110
+ if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
111
+ if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
112
+ if( !isset( $config['format'] )) $config['format'] = 'iCal';
113
+ if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
114
+ $this->setConfig( $config );
115
+ $this->xcaldecl = array();
116
+ $this->components = array();
117
+ }
118
+ /**
119
+ * return iCalcreator version number
120
+ *
121
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
122
+ * @since 2.18.5 - 2013-08-29
123
+ * @uses ICALCREATOR_VERSION
124
+ * @return string
125
+ */
126
+ public static function iCalcreatorVersion() {
127
+ return trim( substr( ICALCREATOR_VERSION, strpos( ICALCREATOR_VERSION, ' ' )));
128
+ }
129
+ /*********************************************************************************/
130
+ /**
131
+ * Property Name: CALSCALE
132
+ */
133
+ /**
134
+ * creates formatted output for calendar property calscale
135
+ *
136
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
137
+ * @since 2.10.16 - 2011-10-28
138
+ * @uses vcalendar::$calscale
139
+ * @uses vcalendar::$format
140
+ * @uses vcalendar::$nl
141
+ * @return string
142
+ */
143
+ function createCalscale() {
144
+ if( empty( $this->calscale )) return FALSE;
145
+ switch( $this->format ) {
146
+ case 'xcal':
147
+ return $this->nl.' calscale="'.$this->calscale.'"';
148
+ break;
149
+ default:
150
+ return 'CALSCALE:'.$this->calscale.$this->nl;
151
+ break;
152
+ }
153
+ }
154
+ /**
155
+ * set calendar property calscale
156
+ *
157
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
158
+ * @since 2.4.8 - 2008-10-21
159
+ * @param string $value
160
+ * @uses vcalendar::$calscale
161
+ * @return void
162
+ */
163
+ function setCalscale( $value ) {
164
+ if( empty( $value )) return FALSE;
165
+ $this->calscale = $value;
166
+ }
167
+ /*********************************************************************************/
168
+ /**
169
+ * Property Name: METHOD
170
+ */
171
+ /**
172
+ * creates formatted output for calendar property method
173
+ *
174
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
175
+ * @since 2.10.16 - 2011-10-28
176
+ * @uses vcalendar::$method
177
+ * @uses vcalendar::$format
178
+ * @uses vcalendar::$nl
179
+ * @return string
180
+ */
181
+ function createMethod() {
182
+ if( empty( $this->method )) return FALSE;
183
+ switch( $this->format ) {
184
+ case 'xcal':
185
+ return $this->nl.' method="'.$this->method.'"';
186
+ break;
187
+ default:
188
+ return 'METHOD:'.$this->method.$this->nl;
189
+ break;
190
+ }
191
+ }
192
+ /**
193
+ * set calendar property method
194
+ *
195
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
196
+ * @since 2.4.8 - 2008-20-23
197
+ * @param string $value
198
+ * @uses vcalendar::$method
199
+ * @return bool
200
+ */
201
+ function setMethod( $value ) {
202
+ if( empty( $value )) return FALSE;
203
+ $this->method = $value;
204
+ return TRUE;
205
+ }
206
+ /*********************************************************************************/
207
+ /**
208
+ * Property Name: PRODID
209
+ *
210
+ */
211
+ /**
212
+ * creates formatted output for calendar property prodid
213
+ *
214
+ * @copyright copyright (c) 2007-2013 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
215
+ * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
216
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
217
+ * @since 2.21.11 - 2015-03-31
218
+ * @uses vcalendar::$prodid
219
+ * @uses vcalendar::_makeProdid()
220
+ * @uses vcalendar::$format
221
+ * @uses vcalendar::$nl
222
+ * @uses vcalendar::_createElement()
223
+ * @return string
224
+ */
225
+ function createProdid() {
226
+ if( !isset( $this->prodid ))
227
+ $this->_makeProdid();
228
+ switch( $this->format ) {
229
+ case 'xcal':
230
+ return $this->nl.' prodid="'.$this->prodid.'"';
231
+ break;
232
+ default:
233
+ return $this->_createElement( 'PRODID', '', $this->prodid );
234
+ break;
235
+ }
236
+ }
237
+ /**
238
+ * make default value for calendar prodid, do NOT alter or remove this method or invoke of this method
239
+ *
240
+ * @copyright copyright (c) 2007-2013 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
241
+ * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
242
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
243
+ * @since 2.6.8 - 2009-12-30
244
+ * @uses vcalendar::$prodid
245
+ * @uses vcalendar::$unique_id
246
+ * @uses ICALCREATOR_VERSION
247
+ * @uses vcalendar::$language
248
+ * @return void
249
+ */
250
+ function _makeProdid() {
251
+ $this->prodid = $this->unique_id.strtoupper( $this->language );
252
+ }
253
+ /**
254
+ * Conformance: The property MUST be specified once in an iCalendar object.
255
+ * Description: The vendor of the implementation SHOULD assure that this
256
+ * is a globally unique identifier; using some technique such as an FPI
257
+ * value, as defined in [ISO 9070].
258
+ */
259
+ /**
260
+ * make default unique_id for calendar prodid
261
+ *
262
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
263
+ * @since 0.3.0 - 2006-08-10
264
+ * @uses vcalendar::$unique_id
265
+ * @return void
266
+ */
267
+ function _makeUnique_id() {
268
+ $this->unique_id = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost';
269
+ }
270
+ /*********************************************************************************/
271
+ /**
272
+ * Property Name: VERSION
273
+ *
274
+ * Description: A value of "2.0" corresponds to this memo.
275
+ */
276
+ /**
277
+ * creates formatted output for calendar property version
278
+
279
+ *
280
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
281
+ * @since 2.10.16 - 2011-10-28
282
+ * @uses vcalendar::$version
283
+ * @uses vcalendar::$format
284
+ * @uses vcalendar::$nl
285
+ * @return string
286
+ */
287
+ function createVersion() {
288
+ if( empty( $this->version ))
289
+ $this->_makeVersion();
290
+ switch( $this->format ) {
291
+ case 'xcal':
292
+ return $this->nl.' version="'.$this->version.'"';
293
+ break;
294
+ default:
295
+ return 'VERSION:'.$this->version.$this->nl;
296
+ break;
297
+ }
298
+ }
299
+ /**
300
+ * set default calendar version
301
+ *
302
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
303
+ * @since 0.3.0 - 2006-08-10
304
+ * @uses vcalendar::$version
305
+ * @return void
306
+ */
307
+ function _makeVersion() {
308
+ $this->version = '2.0';
309
+ }
310
+ /**
311
+ * set calendar version
312
+ *
313
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
314
+ * @since 2.4.8 - 2008-10-23
315
+ * @param string $value
316
+ * @uses vcalendar::$version
317
+ * @return void
318
+ */
319
+ function setVersion( $value ) {
320
+ if( empty( $value )) return FALSE;
321
+ $this->version = $value;
322
+ return TRUE;
323
+ }
324
+ /*********************************************************************************/
325
+ /*********************************************************************************/
326
+ /**
327
+ * delete calendar property value
328
+ *
329
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
330
+ * @since 2.8.8 - 2011-03-15
331
+ * @param mixed $propName bool FALSE => X-property
332
+ * @param int $propix specific property in case of multiply occurences
333
+ * @uses vcalendar::$propdelix
334
+ * @uses vcalendar::$calscale
335
+ * @uses vcalendar::$method
336
+ * @uses vcalendar::$xprop
337
+ * @return bool, if successfull delete
338
+ */
339
+ function deleteProperty( $propName=FALSE, $propix=FALSE ) {
340
+ $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
341
+ if( !$propix )
342
+ $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1;
343
+ $this->propdelix[$propName] = --$propix;
344
+ $return = FALSE;
345
+ switch( $propName ) {
346
+ case 'CALSCALE':
347
+ if( isset( $this->calscale )) {
348
+ $this->calscale = null;
349
+ $return = TRUE;
350
+ }
351
+ break;
352
+ case 'METHOD':
353
+ if( isset( $this->method )) {
354
+ $this->method = null;
355
+ $return = TRUE;
356
+ }
357
+ break;
358
+ default:
359
+ $reduced = array();
360
+ if( $propName != 'X-PROP' ) {
361
+ if( !isset( $this->xprop[$propName] )) { unset( $this->propdelix[$propName] ); return FALSE; }
362
+ foreach( $this->xprop as $k => $a ) {
363
+ if(( $k != $propName ) && !empty( $a ))
364
+ $reduced[$k] = $a;
365
+ }
366
+ }
367
+ else {
368
+ if( count( $this->xprop ) <= $propix ) return FALSE;
369
+ $xpropno = 0;
370
+ foreach( $this->xprop as $xpropkey => $xpropvalue ) {
371
+ if( $propix != $xpropno )
372
+ $reduced[$xpropkey] = $xpropvalue;
373
+ $xpropno++;
374
+ }
375
+ }
376
+ $this->xprop = $reduced;
377
+ if( empty( $this->xprop )) {
378
+ unset( $this->propdelix[$propName] );
379
+ return FALSE;
380
+ }
381
+ return TRUE;
382
+ }
383
+ return $return;
384
+ }
385
+ /**
386
+ * get calendar property value/params
387
+ *
388
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
389
+ * @since 2.21.09 - 2015-03-29
390
+ * @param string $propName
391
+ * @param int $propix specific property in case of multiply occurences
392
+ * @param bool $inclParam
393
+ * @uses vcalendar::$propix
394
+ * @uses vcalendar::$components
395
+ * @uses calendarComponent::$objName
396
+ * @uses iCalUtilityFunctions::$vComps
397
+ * @uses iCalUtilityFunctions::$mProps1
398
+ * @uses calendarComponent::_getProperties()
399
+ * @uses calendarComponent::getProperty()
400
+ * @uses iCalUtilityFunctions::_geo2str2()
401
+ * @uses iCalUtilityFunctions::$geoLatFmt
402
+ * @uses iCalUtilityFunctions::$geoLongFmt
403
+ * @uses iCalUtilityFunctions::$fmt
404
+ * @uses vcalendar::$calscale
405
+ * @uses vcalendar::$method
406
+ * @uses vcalendar::$prodid
407
+ * @uses vcalendar::_makeProdid()
408
+ * @uses vcalendar::$version
409
+ * @uses vcalendar::$xprop
410
+ * @return mixed
411
+ */
412
+ function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE ) {
413
+ $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
414
+ if( 'X-PROP' == $propName ) {
415
+ if( empty( $propix ))
416
+ $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1;
417
+ $this->propix[$propName] = --$propix;
418
+ }
419
+ switch( $propName ) {
420
+ case 'ATTENDEE':
421
+ case 'CATEGORIES':
422
+ case 'CONTACT':
423
+ case 'DTSTART':
424
+ case 'GEOLOCATION':
425
+ case 'LOCATION':
426
+ case 'ORGANIZER':
427
+ case 'PRIORITY':
428
+ case 'RESOURCES':
429
+ case 'STATUS':
430
+ case 'SUMMARY':
431
+ case 'RECURRENCE-ID-UID':
432
+ case 'RELATED-TO':
433
+ case 'R-UID':
434
+ case 'UID':
435
+ case 'URL':
436
+ $output = array();
437
+ foreach ( $this->components as $cix => $component) {
438
+ if( !in_array( $component->objName, iCalUtilityFunctions::$vComps ))
439
+ continue;
440
+ if( in_array( $propName, iCalUtilityFunctions::$mProps1 )) {
441
+ $component->_getProperties( $propName, $output );
442
+ continue;
443
+ }
444
+ elseif(( 3 < strlen( $propName )) && ( 'UID' == substr( $propName, -3 ))) {
445
+ if( FALSE !== ( $content = $component->getProperty( 'RECURRENCE-ID' )))
446
+ $content = $component->getProperty( 'UID' );
447
+ }
448
+ elseif( 'GEOLOCATION' == $propName ) {
449
+ $content = ( FALSE === ( $loc = $component->getProperty( 'LOCATION' ))) ? '' : $loc.' ';
450
+ if( FALSE === ( $geo = $component->getProperty( 'GEO' )))
451
+ continue;
452
+ $content .= iCalUtilityFunctions::_geo2str2( $geo['latitude'], iCalUtilityFunctions::$geoLatFmt ).
453
+ iCalUtilityFunctions::_geo2str2( $geo['longitude'], iCalUtilityFunctions::$geoLongFmt ).'/';
454
+ }
455
+ elseif( FALSE === ( $content = $component->getProperty( $propName )))
456
+ continue;
457
+ if(( FALSE === $content ) || empty( $content ))
458
+ continue;
459
+ elseif( is_array( $content )) {
460
+ if( isset( $content['year'] )) {
461
+ $key = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $content['year'], (int) $content['month'], (int) $content['day'] );
462
+ if( !isset( $output[$key] ))
463
+ $output[$key] = 1;
464
+ else
465
+ $output[$key] += 1;
466
+ }
467
+ else {
468
+ foreach( $content as $partValue => $partCount ) {
469
+ if( !isset( $output[$partValue] ))
470
+ $output[$partValue] = $partCount;
471
+ else
472
+ $output[$partValue] += $partCount;
473
+ }
474
+ }
475
+ } // end elseif( is_array( $content )) {
476
+ elseif( !isset( $output[$content] ))
477
+ $output[$content] = 1;
478
+ else
479
+ $output[$content] += 1;
480
+ } // end foreach ( $this->components as $cix => $component)
481
+ if( !empty( $output ))
482
+ ksort( $output );
483
+ return $output;
484
+ break;
485
+ case 'CALSCALE':
486
+ return ( !empty( $this->calscale )) ? $this->calscale : FALSE;
487
+ break;
488
+ case 'METHOD':
489
+ return ( !empty( $this->method )) ? $this->method : FALSE;
490
+ break;
491
+ case 'PRODID':
492
+ if( empty( $this->prodid ))
493
+ $this->_makeProdid();
494
+ return $this->prodid;
495
+ break;
496
+ case 'VERSION':
497
+ return ( !empty( $this->version )) ? $this->version : FALSE;
498
+ break;
499
+ default:
500
+ if( $propName != 'X-PROP' ) {
501
+ if( !isset( $this->xprop[$propName] )) return FALSE;
502
+ return ( $inclParam ) ? array( $propName, $this->xprop[$propName] )
503
+ : array( $propName, $this->xprop[$propName]['value'] );
504
+ }
505
+ else {
506
+ if( empty( $this->xprop )) return FALSE;
507
+ $xpropno = 0;
508
+ foreach( $this->xprop as $xpropkey => $xpropvalue ) {
509
+ if( $propix == $xpropno )
510
+ return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] )
511
+ : array( $xpropkey, $this->xprop[$xpropkey]['value'] );
512
+ else
513
+ $xpropno++;
514
+ }
515
+ unset( $this->propix[$propName] );
516
+ return FALSE; // not found ??
517
+ }
518
+ }
519
+ return FALSE;
520
+ }
521
+ /**
522
+ * general vcalendar property setting
523
+ *
524
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
525
+ * @since 2.2.13 - 2007-11-04
526
+ * @param mixed $args variable number of function arguments,
527
+ * first argument is ALWAYS component name,
528
+ * second ALWAYS component value!
529
+ * @uses vcalendar::setCalscale()
530
+ * @uses vcalendar::setMethod()
531
+ * @uses vcalendar::setVersion()
532
+ * @uses vcalendar::setXprop()
533
+ * @return bool
534
+ */
535
+ function setProperty () {
536
+ $numargs = func_num_args();
537
+ if( 1 > $numargs )
538
+ return FALSE;
539
+ $arglist = func_get_args();
540
+ $arglist[0] = strtoupper( $arglist[0] );
541
+ switch( $arglist[0] ) {
542
+ case 'CALSCALE':
543
+ return $this->setCalscale( $arglist[1] );
544
+ case 'METHOD':
545
+ return $this->setMethod( $arglist[1] );
546
+ case 'VERSION':
547
+ return $this->setVersion( $arglist[1] );
548
+ default:
549
+ if( !isset( $arglist[1] )) $arglist[1] = null;
550
+ if( !isset( $arglist[2] )) $arglist[2] = null;
551
+ return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] );
552
+ }
553
+ return FALSE;
554
+ }
555
+ /*********************************************************************************/
556
+ /**
557
+ * get vcalendar config values or * calendar components
558
+ *
559
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
560
+ * @since 2.11.7 - 2012-01-12
561
+ * @param mixed $config
562
+ * @uses vcalendar::getConfig()
563
+ * @uses vcalendar::$allowEmpty
564
+ * @uses vcalendar::$components
565
+ * @uses calendarComponent::_getProperties()
566
+ * @uses calendarComponent::$objName
567
+ * @uses calendarComponent::getProperty()
568
+ * @uses calendarComponent::_getConfig()
569
+ * @uses vcalendar::$url
570
+ * @uses vcalendar::$delimiter
571
+ * @uses vcalendar::$directory
572
+ * @uses vcalendar::$filename
573
+ * @uses vcalendar::$format
574
+ * @uses vcalendar::$language
575
+ * @uses vcalendar::$nl
576
+ * @uses vcalendar::$dtzid
577
+ * @uses vcalendar::$unique_id
578
+ * @return value
579
+ */
580
+ function getConfig( $config = FALSE ) {
581
+ if( !$config ) {
582
+ $return = array();
583
+ $return['ALLOWEMPTY'] = $this->getConfig( 'ALLOWEMPTY' );
584
+ $return['DELIMITER'] = $this->getConfig( 'DELIMITER' );
585
+ $return['DIRECTORY'] = $this->getConfig( 'DIRECTORY' );
586
+ $return['FILENAME'] = $this->getConfig( 'FILENAME' );
587
+ $return['DIRFILE'] = $this->getConfig( 'DIRFILE' );
588
+ $return['FILESIZE'] = $this->getConfig( 'FILESIZE' );
589
+ $return['FORMAT'] = $this->getConfig( 'FORMAT' );
590
+ if( FALSE !== ( $lang = $this->getConfig( 'LANGUAGE' )))
591
+ $return['LANGUAGE'] = $lang;
592
+ $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' );
593
+ $return['UNIQUE_ID'] = $this->getConfig( 'UNIQUE_ID' );
594
+ if( FALSE !== ( $url = $this->getConfig( 'URL' )))
595
+ $return['URL'] = $url;
596
+ $return['TZID'] = $this->getConfig( 'TZID' );
597
+ return $return;
598
+ }
599
+ switch( strtoupper( $config )) {
600
+ case 'ALLOWEMPTY':
601
+ return $this->allowEmpty;
602
+ break;
603
+ case 'COMPSINFO':
604
+ unset( $this->compix );
605
+ $info = array();
606
+ foreach( $this->components as $cix => $component ) {
607
+ if( empty( $component )) continue;
608
+ $info[$cix]['ordno'] = $cix + 1;
609
+ $info[$cix]['type'] = $component->objName;
610
+ $info[$cix]['uid'] = $component->getProperty( 'uid' );
611
+ $info[$cix]['props'] = $component->getConfig( 'propinfo' );
612
+ $info[$cix]['sub'] = $component->getConfig( 'compsinfo' );
613
+ }
614
+ return $info;
615
+ break;
616
+ case 'DELIMITER':
617
+ return $this->delimiter;
618
+ break;
619
+ case 'DIRECTORY':
620
+ if( empty( $this->directory ) && ( '0' != $this->directory ))
621
+ $this->directory = '.';
622
+ return $this->directory;
623
+ break;
624
+ case 'DIRFILE':
625
+ return $this->getConfig( 'directory' ).$this->getConfig( 'delimiter' ).$this->getConfig( 'filename' );
626
+ break;
627
+ case 'FILEINFO':
628
+ return array( $this->getConfig( 'directory' )
629
+ , $this->getConfig( 'filename' )
630
+ , $this->getConfig( 'filesize' ));
631
+ break;
632
+ case 'FILENAME':
633
+ if( empty( $this->filename ) && ( '0' != $this->filename )) {
634
+ if( 'xcal' == $this->format )
635
+ $this->filename = date( 'YmdHis' ).'.xml'; // recommended xcs.. .
636
+ else
637
+ $this->filename = date( 'YmdHis' ).'.ics';
638
+ }
639
+ return $this->filename;
640
+ break;
641
+ case 'FILESIZE':
642
+ $size = 0;
643
+ if( empty( $this->url )) {
644
+ $dirfile = $this->getConfig( 'dirfile' );
645
+ if( !is_file( $dirfile ) || ( FALSE === ( $size = filesize( $dirfile ))))
646
+ $size = 0;
647
+ clearstatcache();
648
+ }
649
+ return $size;
650
+ break;
651
+ case 'FORMAT':
652
+ return ( $this->format == 'xcal' ) ? 'xCal' : 'iCal';
653
+ break;
654
+ case 'LANGUAGE':
655
+ /* get language for calendar component as defined in [RFC 1766] */
656
+ return $this->language;
657
+ break;
658
+ case 'NL':
659
+ case 'NEWLINECHAR':
660
+ return $this->nl;
661
+ break;
662
+ case 'TZID':
663
+ return $this->dtzid;
664
+ break;
665
+ case 'UNIQUE_ID':
666
+ return $this->unique_id;
667
+ break;
668
+ case 'URL':
669
+ if( !empty( $this->url ))
670
+ return $this->url;
671
+ else
672
+ return FALSE;
673
+ break;
674
+ }
675
+ }
676
+ /**
677
+ * general vcalendar config setting
678
+ *
679
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
680
+ * @since 2.20.2 - 2014-05-09
681
+ * @param mixed $config
682
+ * @param string $value
683
+ * @uses vcalendar::setConfig()
684
+ * @uses vcalendar::$allowEmpty
685
+ * @uses vcalendar::$components
686
+ * @uses vcalendar::$url
687
+ * @uses vcalendar::$delimiter
688
+ * @uses vcalendar::$directory
689
+ * @uses vcalendar::$filename
690
+ * @uses vcalendar::$format
691
+ * @uses vcalendar::$language
692
+ * @uses vcalendar::$nl
693
+ * @uses vcalendar::$dtzid
694
+ * @uses vcalendar::$unique_id
695
+ * @uses vcalendar::_makeProdid()
696
+ * @uses vcalendar::$components
697
+ * @uses calendarComponent::setConfig()
698
+ * @uses calendarComponent::copy()
699
+ * @return void
700
+ */
701
+ function setConfig( $config, $value = FALSE) {
702
+ if( is_array( $config )) {
703
+ $config = array_change_key_case( $config, CASE_UPPER );
704
+ if( isset( $config['DELIMITER'] )) {
705
+ if( FALSE === $this->setConfig( 'DELIMITER', $config['DELIMITER'] ))
706
+ return FALSE;
707
+ unset( $config['DELIMITER'] );
708
+ }
709
+ if( isset( $config['DIRECTORY'] )) {
710
+ if( FALSE === $this->setConfig( 'DIRECTORY', $config['DIRECTORY'] ))
711
+ return FALSE;
712
+ unset( $config['DIRECTORY'] );
713
+ }
714
+ foreach( $config as $cKey => $cValue ) {
715
+ if( FALSE === $this->setConfig( $cKey, $cValue ))
716
+ return FALSE;
717
+ }
718
+ return TRUE;
719
+ }
720
+ else
721
+ $res = FALSE;
722
+ $config = strtoupper( $config );
723
+ switch( $config ) {
724
+ case 'ALLOWEMPTY':
725
+ $this->allowEmpty = $value;
726
+ $subcfg = array( 'ALLOWEMPTY' => $value );
727
+ $res = TRUE;
728
+ break;
729
+ case 'DELIMITER':
730
+ $this->delimiter = $value;
731
+ return TRUE;
732
+ break;
733
+ case 'DIRECTORY':
734
+ if( FALSE === ( $value = realpath( rtrim( trim( $value ), $this->delimiter ))))
735
+ return FALSE;
736
+ else {
737
+ /* local directory */
738
+ $this->directory = $value;
739
+ $this->url = null;
740
+ return TRUE;
741
+ }
742
+ break;
743
+ case 'FILENAME':
744
+ $value = trim( $value );
745
+ $dirfile = $this->directory.$this->delimiter.$value;
746
+ if( file_exists( $dirfile )) {
747
+ /* local file exists */
748
+ if( is_readable( $dirfile ) || is_writable( $dirfile )) {
749
+ clearstatcache();
750
+ $this->filename = $value;
751
+ return TRUE;
752
+ }
753
+ else
754
+ return FALSE;
755
+ }
756
+ elseif( is_readable( $this->directory ) || is_writable( $this->directory )) {
757
+ /* read- or writable directory */
758
+ clearstatcache();
759
+ $this->filename = $value;
760
+ return TRUE;
761
+ }
762
+ else
763
+ return FALSE;
764
+ break;
765
+ case 'FORMAT':
766
+ $value = trim( strtolower( $value ));
767
+ if( 'xcal' == $value ) {
768
+ $this->format = 'xcal';
769
+ $this->attributeDelimiter = $this->nl;
770
+ $this->valueInit = null;
771
+ }
772
+ else {
773
+ $this->format = null;
774
+ $this->attributeDelimiter = ';';
775
+ $this->valueInit = ':';
776
+ }
777
+ $subcfg = array( 'FORMAT' => $value );
778
+ $res = TRUE;
779
+ break;
780
+ case 'LANGUAGE': // set language for calendar component as defined in [RFC 1766]
781
+ $value = trim( $value );
782
+ $this->language = $value;
783
+ $this->_makeProdid();
784
+ $subcfg = array( 'LANGUAGE' => $value );
785
+ $res = TRUE;
786
+ break;
787
+ case 'NL':
788
+ case 'NEWLINECHAR':
789
+ $this->nl = $value;
790
+ if( 'xcal' == $value ) {
791
+ $this->attributeDelimiter = $this->nl;
792
+ $this->valueInit = null;
793
+ }
794
+ else {
795
+ $this->attributeDelimiter = ';';
796
+ $this->valueInit = ':';
797
+ }
798
+ $subcfg = array( 'NL' => $value );
799
+ $res = TRUE;
800
+ break;
801
+ case 'TZID':
802
+ $this->dtzid = $value;
803
+ $subcfg = array( 'TZID' => $value );
804
+ $res = TRUE;
805
+ break;
806
+ case 'UNIQUE_ID':
807
+ $value = trim( $value );
808
+ $this->unique_id = $value;
809
+ $this->_makeProdid();
810
+ $subcfg = array( 'UNIQUE_ID' => $value );
811
+ $res = TRUE;
812
+ break;
813
+ case 'URL':
814
+ /* remote file - URL */
815
+ $value = str_replace( array( 'HTTP://', 'WEBCAL://', 'webcal://' ), 'http://', trim( $value ));
816
+ $value = str_replace( 'HTTPS://', 'https://', trim( $value ));
817
+ if(( 'http://' != substr( $value, 0, 7 )) && ( 'https://' != substr( $value, 0, 8 )))
818
+ return FALSE;
819
+ $this->directory = '.';
820
+ $this->url = $value;
821
+ if( '.ics' != strtolower( substr( $value, -4 )))
822
+ unset( $this->filename );
823
+ else
824
+ $this->filename = basename( $value );
825
+ return TRUE;
826
+ break;
827
+ default: // any unvalid config key.. .
828
+ return TRUE;
829
+ }
830
+ if( !$res ) return FALSE;
831
+ if( isset( $subcfg ) && !empty( $this->components )) {
832
+ foreach( $subcfg as $cfgkey => $cfgvalue ) {
833
+ foreach( $this->components as $cix => $component ) {
834
+ $res = $component->setConfig( $cfgkey, $cfgvalue, TRUE );
835
+ if( !$res )
836
+ break 2;
837
+ $this->components[$cix] = $component->copy(); // PHP4 compliant
838
+ }
839
+ }
840
+ }
841
+ return $res;
842
+ }
843
+ /*********************************************************************************/
844
+ /**
845
+ * add calendar component to container
846
+ *
847
+ * alias to setComponent
848
+ *
849
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
850
+ * @since 1.x.x - 2007-04-24
851
+ * @param object $component calendar component
852
+ * @uses vcalendar::setComponent()
853
+ * @return void
854
+ */
855
+ function addComponent( $component ) {
856
+ $this->setComponent( $component );
857
+ }
858
+ /**
859
+ * delete calendar component from container
860
+ *
861
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
862
+ * @since 2.8.8 - 2011-03-15
863
+ * @param mixed $arg1 ordno / component type / component uid
864
+ * @param mixed $arg2 optional, ordno if arg1 = component type
865
+ * @uses vcalendar::$components
866
+ * @uses calendarComponent::$objName
867
+ * @uses calendarComponent::getProperty()
868
+ * @return void
869
+ */
870
+ function deleteComponent( $arg1, $arg2=FALSE ) {
871
+ $argType = $index = null;
872
+ if ( ctype_digit( (string) $arg1 )) {
873
+ $argType = 'INDEX';
874
+ $index = (int) $arg1 - 1;
875
+ }
876
+ elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
877
+ $argType = strtolower( $arg1 );
878
+ $index = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0;
879
+ }
880
+ $cix1dC = 0;
881
+ foreach ( $this->components as $cix => $component) {
882
+ if( empty( $component )) continue;
883
+ if(( 'INDEX' == $argType ) && ( $index == $cix )) {
884
+ unset( $this->components[$cix] );
885
+ return TRUE;
886
+ }
887
+ elseif( $argType == $component->objName ) {
888
+ if( $index == $cix1dC ) {
889
+ unset( $this->components[$cix] );
890
+ return TRUE;
891
+ }
892
+ $cix1dC++;
893
+ }
894
+ elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) {
895
+ unset( $this->components[$cix] );
896
+ return TRUE;
897
+ }
898
+ }
899
+ return FALSE;
900
+ }
901
+ /**
902
+ * get calendar component from container
903
+ *
904
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
905
+ * @since 2.21.11 - 2015-03-21
906
+ * @param mixed $arg1 optional, ordno/component type/ component uid
907
+ * @param mixed $arg2 optional, ordno if arg1 = component type
908
+ * @uses vcalendar::$compix
909
+ * @uses vcalendar::$components
910
+ * @uses iCalUtilityFunctions::$dateProps
911
+ * @uses iCalUtilityFunctions::$otherProps
912
+ * @uses iCalUtilityFunctions::$mProps1
913
+ * @uses calendarComponent::copy()
914
+ * @uses calendarComponent::$objName
915
+ * @uses calendarComponent::_getProperties()
916
+ * @uses calendarComponent::getProperty()
917
+ * @uses iCalUtilityFunctions::$fmt
918
+ * @return object
919
+ */
920
+ function getComponent( $arg1=FALSE, $arg2=FALSE ) {
921
+ $index = $argType = null;
922
+ if ( !$arg1 ) { // first or next in component chain
923
+ $argType = 'INDEX';
924
+ $index = $this->compix['INDEX'] = ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1;
925
+ }
926
+ elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] )
927
+ $arg2 = implode( '-', array_keys( $arg1 ));
928
+ $index = $this->compix[$arg2] = ( isset( $this->compix[$arg2] )) ? $this->compix[$arg2] + 1 : 1;
929
+ }
930
+ elseif ( ctype_digit( (string) $arg1 )) { // specific component in chain
931
+ $argType = 'INDEX';
932
+ $index = (int) $arg1;
933
+ unset( $this->compix );
934
+ }
935
+ elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { // object class name
936
+ unset( $this->compix['INDEX'] );
937
+ $argType = strtolower( $arg1 );
938
+ if( !$arg2 )
939
+ $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1;
940
+ elseif( isset( $arg2 ) && ctype_digit( (string) $arg2 ))
941
+ $index = (int) $arg2;
942
+ }
943
+ elseif(( strlen( $arg1 ) > strlen( 'vfreebusy' )) && ( FALSE !== strpos( $arg1, '@' ))) { // UID as 1st argument
944
+ if( !$arg2 )
945
+ $index = $this->compix[$arg1] = ( isset( $this->compix[$arg1] )) ? $this->compix[$arg1] + 1 : 1;
946
+ elseif( isset( $arg2 ) && ctype_digit( (string) $arg2 ))
947
+ $index = (int) $arg2;
948
+ }
949
+ if( isset( $index ))
950
+ $index -= 1;
951
+ $ckeys = array_keys( $this->components );
952
+ if( !empty( $index) && ( $index > end( $ckeys )))
953
+ return FALSE;
954
+ $cix1gC = 0;
955
+ foreach ( $this->components as $cix => $component) {
956
+ if( empty( $component )) continue;
957
+ if(( 'INDEX' == $argType ) && ( $index == $cix ))
958
+ return $component->copy();
959
+ elseif( $argType == $component->objName ) {
960
+ if( $index == $cix1gC )
961
+ return $component->copy();
962
+ $cix1gC++;
963
+ }
964
+ elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] )
965
+ $hit = array();
966
+ $arg1 = array_change_key_case( $arg1, CASE_UPPER );
967
+ foreach( $arg1 as $pName => $pValue ) {
968
+ if( !in_array( $pName, iCalUtilityFunctions::$dateProps ) && !in_array( $pName, iCalUtilityFunctions::$otherProps ))
969
+ continue;
970
+ if( in_array( $pName, iCalUtilityFunctions::$mProps1 )) { // multiple occurrence
971
+ $propValues = array();
972
+ $component->_getProperties( $pName, $propValues );
973
+ $propValues = array_keys( $propValues );
974
+ $hit[] = ( in_array( $pValue, $propValues )) ? TRUE : FALSE;
975
+ continue;
976
+ } // end if(.. .// multiple occurrence
977
+ if( FALSE === ( $value = $component->getProperty( $pName ))) { // single occurrence
978
+ $hit[] = FALSE; // missing property
979
+ continue;
980
+ }
981
+ if( 'SUMMARY' == $pName ) { // exists within (any case)
982
+ $hit[] = ( FALSE !== stripos( $value, $pValue )) ? TRUE : FALSE;
983
+ continue;
984
+ }
985
+ if( in_array( $pName, iCalUtilityFunctions::$dateProps )) {
986
+ $valuedate = sprintf( iCalUtilityFunctions::$fmt['Ymd'], (int) $value['year'], (int) $value['month'], (int) $value['day'] );
987
+ if( 8 < strlen( $pValue )) {
988
+ if( isset( $value['hour'] )) {
989
+ if( 'T' == substr( $pValue, 8, 1 ))
990
+ $pValue = str_replace( 'T', '', $pValue );
991
+ $valuedate .= sprintf( iCalUtilityFunctions::$fmt['His'], (int) $value['hour'], (int) $value['min'], (int) $value['sec'] );
992
+ }
993
+ else
994
+ $pValue = substr( $pValue, 0, 8 );
995
+ }
996
+ $hit[] = ( $pValue == $valuedate ) ? TRUE : FALSE;
997
+ continue;
998
+ }
999
+ elseif( !is_array( $value ))
1000
+ $value = array( $value );
1001
+ foreach( $value as $part ) {
1002
+ $part = ( FALSE !== strpos( $part, ',' )) ? explode( ',', $part ) : array( $part );
1003
+ foreach( $part as $subPart ) {
1004
+ if( $pValue == $subPart ) {
1005
+ $hit[] = TRUE;
1006
+ continue 3;
1007
+ }
1008
+ }
1009
+ } // end foreach( $value as $part )
1010
+ $hit[] = FALSE; // no hit in property
1011
+ } // end foreach( $arg1 as $pName => $pValue )
1012
+ if( in_array( TRUE, $hit )) {
1013
+ if( $index == $cix1gC )
1014
+ return $component->copy();
1015
+ $cix1gC++;
1016
+ }
1017
+ } // end elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] )
1018
+ elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) { // UID
1019
+ if( $index == $cix1gC )
1020
+ return $component->copy();
1021
+ $cix1gC++;
1022
+ }
1023
+ } // end foreach ( $this->components.. .
1024
+ /* not found.. . */
1025
+ unset( $this->compix );
1026
+ return FALSE;
1027
+ }
1028
+ /**
1029
+ * create new calendar component, already included within calendar
1030
+ *
1031
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1032
+ * @since 2.20.4 - 2014-08-24
1033
+ * @param string $compType component type
1034
+ * @uses vcalendar::$components
1035
+ * @return object
1036
+ */
1037
+ function & newComponent( $compType ) {
1038
+ $config = $this->getConfig();
1039
+ $keys = array_keys( $this->components );
1040
+ $ix = ( empty( $keys )) ? 0 : end( $keys) + 1;
1041
+ switch( strtoupper( $compType )) {
1042
+ case 'EVENT':
1043
+ case 'VEVENT':
1044
+ $this->components[$ix] = new vevent( $config );
1045
+ break;
1046
+ case 'TODO':
1047
+ case 'VTODO':
1048
+ $this->components[$ix] = new vtodo( $config );
1049
+ break;
1050
+ case 'JOURNAL':
1051
+ case 'VJOURNAL':
1052
+ $this->components[$ix] = new vjournal( $config );
1053
+ break;
1054
+ case 'FREEBUSY':
1055
+ case 'VFREEBUSY':
1056
+ $this->components[$ix] = new vfreebusy( $config );
1057
+ break;
1058
+ case 'TIMEZONE':
1059
+ case 'VTIMEZONE':
1060
+ array_unshift( $this->components, new vtimezone( $config ));
1061
+ $ix = 0;
1062
+ break;
1063
+ default:
1064
+ return FALSE;
1065
+ }
1066
+ return $this->components[$ix];
1067
+ }
1068
+ /**
1069
+ * select components from calendar on date or selectOption basis
1070
+ *
1071
+ * Ensure DTSTART is set for every component.
1072
+ * No date controls occurs.
1073
+ *
1074
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1075
+ * @since 2.21.11 - 2015-03-31
1076
+ * @param mixed $startY optional, (int) start Year, default current Year
1077
+ * ALT. (obj) start date (datetime)
1078
+ * ALT. array selecOptions ( *[ <propName> => <uniqueValue> ] )
1079
+ * @param mixed $startM optional, (int) start Month, default current Month
1080
+ * ALT. (obj) end date (datetime)
1081
+ * @param int $startD optional, start Day, default current Day
1082
+ * @param int $endY optional, end Year, default $startY
1083
+ * @param int $endM optional, end Month, default $startM
1084
+ * @param int $endD optional, end Day, default $startD
1085
+ * @param mixed $cType optional, calendar component type(-s), default FALSE=all else string/array type(-s)
1086
+ * @param bool $flat optional, FALSE (default) => output : array[Year][Month][Day][]
1087
+ * TRUE => output : array[] (ignores split)
1088
+ * @param bool $any optional, TRUE (default) - select component(-s) that occurs within period
1089
+ * FALSE - only component(-s) that starts within period
1090
+ * @param bool $split optional, TRUE (default) - one component copy every DAY it occurs during the
1091
+ * period (implies flat=FALSE)
1092
+ * FALSE - one occurance of component only in output array
1093
+ * @uses vcalendar::$components
1094
+ * @uses vcalendar::selectComponents2()
1095
+ * @uses iCalUtilityFunctions::$vComps
1096
+ * @uses calendarComponent::$objName
1097
+ * @uses calendarComponent::getProperty()
1098
+ * @uses iCaldateTime::factory()
1099
+ * @uses iCaldateTime::getTimezoneName()
1100
+ * @uses iCaldateTime::getTime()
1101
+ * @uses iCalUtilityFunctions::$fmt
1102
+ * @uses iCaldateTime::$SCbools
1103
+ * @uses iCaldateTime::format()
1104
+ * @uses iCalUtilityFunctions::_strDate2arr()
1105
+ * @uses iCalUtilityFunctions::_recur2date()
1106
+ * @uses iCalUtilityFunctions::_inScope()
1107
+ * @uses calendarComponent::copy()
1108
+ * @uses calendarComponent::setProperty()
1109
+ * @uses iCalUtilityFunctions::$fmt
1110
+ * @uses calendarComponent::deleteProperty()
1111
+ * @uses iCalUtilityFunctions::_setSortArgs()
1112
+ * @uses iCalUtilityFunctions::_cmpfcn()
1113
+ * @return array or FALSE
1114
+ */
1115
+ function selectComponents( $startY=FALSE, $startM=FALSE, $startD=FALSE, $endY=FALSE, $endM=FALSE, $endD=FALSE, $cType=FALSE, $flat=FALSE, $any=TRUE, $split=TRUE ) {
1116
+ /* check if empty calendar */
1117
+ if( 0 >= count( $this->components )) return FALSE;
1118
+ if( is_array( $startY ))
1119
+ return $this->selectComponents2( $startY );
1120
+ /* check default dates */
1121
+ if( is_a( $startY, 'DateTime' ) && is_a( $startM, 'DateTime' )) {
1122
+ $endY = $startM->format( 'Y' );
1123
+ $endM = $startM->format( 'm' );
1124
+ $endD = $startM->format( 'd' );
1125
+ $startD = $startY->format( 'd' );
1126
+ $startM = $startY->format( 'm' );
1127
+ $startY = $startY->format( 'Y' );
1128
+ }
1129
+ else {
1130
+ if( ! $startY ) $startY = date( 'Y' );
1131
+ if( ! $startM ) $startM = date( 'm' );
1132
+ if( ! $startD ) $startD = date( 'd' );
1133
+ if( ! $endY ) $endY = $startY;
1134
+ if( ! $endM ) $endM = $startM;
1135
+ if( ! $endD ) $endD = $startD;
1136
+ }
1137
+ // echo "selectComp args={$startY}-{$startM}-{$startD} - {$endY}-{$endM}-{$endD}<br>\n"; $tcnt = 0;// test ###
1138
+ /* check component types */
1139
+ if( empty( $cType ))
1140
+ $cType = iCalUtilityFunctions::$vComps;
1141
+ else {
1142
+ if( ! is_array( $cType ))
1143
+ $cType = array( $cType );
1144
+ $cType = array_map( 'strtolower', $cType );
1145
+ foreach( $cType as $cix => $theType ) {
1146
+ if( !in_array( $theType, iCalUtilityFunctions::$vComps ))
1147
+ $cType[$cix] = 'vevent';
1148
+ }
1149
+ $cType = array_unique( $cType );
1150
+ }
1151
+ if(( FALSE === $flat ) && ( FALSE === $any )) // invalid combination
1152
+ $split = FALSE;
1153
+ if(( TRUE === $flat ) && ( TRUE === $split )) // invalid combination
1154
+ $split = FALSE;
1155
+ /* iterate components */
1156
+ $result = array();
1157
+ $this->sort( 'UID' );
1158
+ $compUIDcmp = null;
1159
+ $exdatelist = $recurridList = array();
1160
+ $intervalP1D = new DateInterval( 'P1D' );
1161
+ foreach ( $this->components as $cix => $component ) {
1162
+ if( empty( $component )) continue;
1163
+ /* deselect unvalid type components */
1164
+ if( !in_array( $component->objName, $cType ))
1165
+ continue;
1166
+ unset( $compStart, $compEnd );
1167
+ /* select start from dtstart or due if dtstart is missing */
1168
+ $prop = $component->getProperty( 'dtstart', FALSE, TRUE );
1169
+ if( empty( $prop ) && ( $component->objName == 'vtodo' ) && ( FALSE === ( $prop = $component->getProperty( 'due', FALSE, TRUE ))))
1170
+ continue;
1171
+ if( empty( $prop ))
1172
+ continue;
1173
+ /* get UID */
1174
+ $compUID = $component->getProperty( 'UID' );
1175
+ if( $compUIDcmp != $compUID ) {
1176
+ $compUIDcmp = $compUID;
1177
+ $exdatelist = $recurridList = array();
1178
+ }
1179
+ $recurrid = FALSE;
1180
+ // file_put_contents( '/opt/work/iCal/iCalcreator/iCalcreator-2.20.x/log/log.txt', "#$cix".PHP_EOL.var_export( $component, TRUE ).PHP_EOL.PHP_EOL, FILE_APPEND ); // test ###
1181
+ $compStart = iCaldateTime::factory( $prop['value'], $prop['params'], $prop['value'] );
1182
+ $dtstartTz = $compStart->getTimezoneName();
1183
+ if( isset( $prop['params']['VALUE'] ) && ( 'DATE' == $prop['params']['VALUE'] ))
1184
+ $compStartHis = '';
1185
+ else {
1186
+ $his = $compStart->getTime();
1187
+ $compStartHis = sprintf( iCalUtilityFunctions::$fmt['His'], (int) $his[0], (int) $his[1], (int) $his[2] );
1188
+ }
1189
+ /* get end date from dtend/due/duration properties */
1190
+ if( FALSE !== ( $prop = $component->getProperty( 'dtend', FALSE, TRUE ))) {
1191
+ $compEnd = iCaldateTime::factory( $prop['value'], $prop['params'], $prop['value'], $dtstartTz );
1192
+ $compEnd->SCbools[ 'dtendExist'] = TRUE;
1193
+ }
1194
+ if( empty( $prop ) && ( $component->objName == 'vtodo' ) && ( FALSE !== ( $prop = $component->getProperty( 'due', FALSE, TRUE )))) {
1195
+ $compEnd = iCaldateTime::factory( $prop['value'], $prop['params'], $prop['value'], $dtstartTz );
1196
+ $compEnd->SCbools[ 'dueExist'] = TRUE;
1197
+ }
1198
+ if( empty( $prop ) && ( FALSE !== ( $prop = $component->getProperty( 'duration', FALSE, TRUE, TRUE )))) { // in dtend (array) format
1199
+ $compEnd = iCaldateTime::factory( $prop['value'], $prop['params'], $prop['value'], $dtstartTz );
1200
+ $compEnd->SCbools[ 'durationExist'] = TRUE;
1201
+ }
1202
+ if( ! empty( $prop ) && ! isset( $prop['value']['hour'] )) {
1203
+ /* a DTEND without time part denotes an end of an event that actually ends the day before,
1204
+ for an all-day event DTSTART=20071201 DTEND=20071202, taking place 20071201!!! */
1205
+ $compEnd->SCbools[ 'endAllDayEvent'] = TRUE;
1206
+ $compEnd->modify( '-1 day' );
1207
+ $compEnd->setTime( 23, 59, 59 );
1208
+ }
1209
+ unset( $prop );
1210
+ if( empty( $compEnd )) {
1211
+ $compDuration = FALSE;
1212
+ $compEnd = clone $compStart;
1213
+ $compEnd->setTime( 23, 59, 59 ); // 23:59:59 the same day as start
1214
+ }
1215
+ else {
1216
+ if( $compEnd->format( 'Ymd' ) < $compStart->format( 'Ymd' )) { // MUST be after start date!!
1217
+ $compEnd = clone $compStart;
1218
+ $compEnd->setTime( 23, 59, 59 ); // 23:59:59 the same day as start or ???
1219
+ }
1220
+ $compDuration = $compStart->diff( $compEnd ); // DateInterval
1221
+ }
1222
+ /* check recurrence-id (note, a missing sequence is the same as sequence=0 so don't test for sequence), to alter when hit dtstart/recurlist */
1223
+ if( FALSE !== ( $prop = $component->getProperty( 'recurrence-id', FALSE, TRUE ))) {
1224
+ $recurrid = iCaldateTime::factory( $prop['value'], $prop['params'], $prop['value'], $dtstartTz );
1225
+ $rangeSet = ( isset( $prop['params']['RANGE'] ) && ( 'THISANDFUTURE' == $prop['params']['RANGE'] )) ? TRUE : FALSE;
1226
+ $recurridList[$recurrid->key] = array( clone $compStart, clone $compEnd, $compDuration, $rangeSet ); // change recur this day to new YmdHis/duration/range
1227
+ // echo "adding comp no:$cix with date=".$compStart->format(iCalUtilityFunctions::$fmt['YmdHis2e'])." to recurridList id={$recurrid->key}, newDate={$compStart->key}<br>\n"; // test ###
1228
+ unset( $prop );
1229
+ continue; // ignore any other props in the component
1230
+ } // end recurrence-id/sequence test
1231
+ // else echo "comp no:$cix with date=".$compStart->format().", NO recurrence-id<br>\n"; // test ###
1232
+ ksort( $recurridList, SORT_STRING );
1233
+ // echo 'recurridList='.implode(', ', array_keys( $recurridList ))."<br>\n"; // test ###
1234
+ $fcnStart = clone $compStart;
1235
+ $fcnStart->setDate((int) $startY, (int) $startM, (int) $startD );
1236
+ $fcnStart->setTime( 0, 0, 0 );
1237
+ $fcnEnd = clone $compEnd;
1238
+ $fcnEnd->setDate((int) $endY, (int) $endM, (int) $endD );
1239
+ $fcnEnd->setTime( 23, 59, 59 );
1240
+ // echo 'compStart='.$compStart->format().', compEnd'.$compEnd->format(); if($compDuration)echo ', interval='.$compDuration->format( iCalUtilityFunctions::$fmt['durDHis'] ); echo "<br>\n"; $tcnt = 0;// test ###
1241
+ /* *************************************************************
1242
+ make a list of optional exclude dates for component occurence from exrule and exdate
1243
+ *********************************************************** */
1244
+ $workStart = clone $compStart;
1245
+ $workStart->sub( $compDuration ? $compDuration : $intervalP1D );
1246
+ $workEnd = clone $fcnEnd;
1247
+ $workEnd->add( $compDuration ? $compDuration : $intervalP1D );
1248
+ while( FALSE !== ( $prop = $component->getProperty( 'EXRULE' ))) {
1249
+ $exdatelist2 = array();
1250
+ if( isset( $prop['UNTIL']['hour'] )) { // convert until date to dtstart timezone
1251
+ $until = iCaldateTime::factory( $prop['UNTIL'], array( 'TZID' => 'UTC' ), null, $dtstartTz );
1252
+ $until = $until->format();
1253
+ iCalUtilityFunctions::_strDate2arr( $until );
1254
+ $prop['UNTIL'] = $until;
1255
+ }
1256
+ iCalUtilityFunctions::_recur2date( $exdatelist2, $prop, $compStart, $workStart, $workEnd );
1257
+ foreach( $exdatelist2 as $k => $v )
1258
+ $exdatelist[$k.$compStartHis] = $v; // point out exact every excluded ocurrence (incl. opt. His)
1259
+ unset( $until, $exdatelist2 );
1260
+ }
1261
+ while( FALSE !== ( $prop = $component->getProperty( 'EXDATE', FALSE, TRUE ))) { // - start check exdate
1262
+ foreach( $prop['value'] as $exdate ) {
1263
+ $exdate = iCaldateTime::factory( $exdate, $prop['params'], $exdate, $dtstartTz );
1264
+ $exdatelist[$exdate->key] = TRUE;
1265
+ } // end - foreach( $exdate as $exdate )
1266
+ } // end - check exdate
1267
+ unset( $prop, $exdate );
1268
+ // echo 'exdatelist=' .implode(', ', array_keys( $exdatelist )) ."<br>\n"; // test ###
1269
+ /* *************************************************************
1270
+ select only components within.. .
1271
+ *********************************************************** */
1272
+ $xRecurrence = 1;
1273
+ if(( ! $any && iCalUtilityFunctions::_inScope( $compStart, $fcnStart, $compStart, $fcnEnd, $compStart->dateFormat )) ||
1274
+ ( $any && iCalUtilityFunctions::_inScope( $fcnEnd, $compStart, $fcnStart, $compEnd, $compStart->dateFormat ))) {
1275
+ /* add the selected component (WITHIN valid dates) to output array */
1276
+ if( $flat ) { // any=true/false, ignores split
1277
+ if( !$recurrid )
1278
+ $result[$compUID] = $component->copy(); // copy original to output (but not anyone with recurrence-id)
1279
+ }
1280
+ elseif( $split ) { // split the original component
1281
+ // echo 'split org.:'.$compStart->format().' < '.$fcnStart->format( 'Ymd His e' )."<br>\n"; // test ###
1282
+ if( $compStart->format( iCalUtilityFunctions::$fmt['YmdHis2'] ) < $fcnStart->format( iCalUtilityFunctions::$fmt['YmdHis2'] ))
1283
+ $rstart = clone $fcnStart;
1284
+ else
1285
+ $rstart = clone $compStart;
1286
+ if( $compEnd->format( iCalUtilityFunctions::$fmt['YmdHis2'] ) > $fcnEnd->format( iCalUtilityFunctions::$fmt['YmdHis2'] ))
1287
+ $rend = clone $fcnEnd;
1288
+ else
1289
+ $rend = clone $compEnd;
1290
+ // echo "going to test comp no:$cix, rstart=".$rstart->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )." (key={$rstart->key}), end=".$rend->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )."<br>\n"; // test ###
1291
+ if( ! isset( $exdatelist[$rstart->key] )) { // not excluded in exrule/exdate
1292
+ if( isset( $recurridList[$rstart->key] )) { // change start day to new YmdHis/duration
1293
+ $k = $rstart->key;
1294
+ // echo "recurridList HIT, key={$k}, recur Date=".$recurridList[$k][0]->key."<br>\n"; // test ###
1295
+ $rstart = clone $recurridList[$k][0];
1296
+ $startHis = $rstart->getTime();
1297
+ $rend = clone $rstart;
1298
+ if( FALSE !== $recurridList[$k][2] )
1299
+ $rend->add( $recurridList[$k][2] );
1300
+ elseif( FALSE !== $compDuration )
1301
+ $rend->add( $compDuration );
1302
+ $endHis = $rend->getTime();
1303
+ unset( $recurridList[$k] );
1304
+ }
1305
+ else {
1306
+ $startHis = $compStart->getTime();
1307
+ $endHis = $compEnd->getTime();
1308
+ }
1309
+ // echo "_____testing comp no:$cix, rstart=".$rstart->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )." (key={$rstart->key}), end=".$rend->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )."<br>\n"; // test ###
1310
+ $cnt = 0; // exclude any recurrence START date, found in exdatelist or recurridList but accept the reccurence-id comp itself
1311
+ $occurenceDays = 1 + (int) $rstart->diff( $rend )->format( '%a' ); // count the days (incl start day)
1312
+ while( $rstart->format( iCalUtilityFunctions::$fmt['Ymd2'] ) <= $rend->format( iCalUtilityFunctions::$fmt['Ymd2'] )) {
1313
+ $cnt += 1;
1314
+ if( 1 < $occurenceDays )
1315
+ $component->setProperty( 'X-OCCURENCE', sprintf( iCalUtilityFunctions::$fmt['dayOfDays'], $cnt, $occurenceDays ));
1316
+ if( 1 < $cnt )
1317
+ $rstart->setTime( 0, 0, 0 );
1318
+ else {
1319
+ $rstart->setTime( $startHis[0], $startHis[1], $startHis[2] );
1320
+ $exdatelist[$rstart->key] = $compDuration; // make sure to exclude start day from the recurrence pattern
1321
+ }
1322
+ $component->setProperty( 'X-CURRENT-DTSTART', $rstart->format( $compStart->dateFormat ));
1323
+ if( FALSE !== $compDuration ) {
1324
+ $propName = ( isset( $compEnd->SCbools[ 'dueExist'] )) ? 'X-CURRENT-DUE' : 'X-CURRENT-DTEND';
1325
+ if( $cnt < $occurenceDays )
1326
+ $rstart->setTime( 23, 59, 59 );
1327
+ else
1328
+ $rstart->setTime( $endHis[0], $endHis[1], $endHis[2] );
1329
+ $component->setProperty( $propName, $rstart->format( $compEnd->dateFormat ));
1330
+ }
1331
+ $result[(int)$rstart->format( 'Y' )][(int)$rstart->format( 'm' )][(int)$rstart->format( 'd' )][$compUID] = $component->copy(); // copy to output
1332
+ $rstart->add( $intervalP1D );
1333
+ } // end while(( $rstart->format( 'Ymd' ) < $rend->format( 'Ymd' ))
1334
+ unset( $cnt, $occurenceDays );
1335
+ } // end if( ! isset( $exdatelist[$rstart->key] ))
1336
+ // else echo "skip no:$cix with date=".$compStart->format()."<br>\n"; // test ###
1337
+ unset( $rstart, $rend );
1338
+ } // end elseif( $split ) - else use component date
1339
+ else { // !$flat && !$split, i.e. no flat array and DTSTART within period
1340
+ $tstart = ( isset( $recurridList[$compStart->key] )) ? clone $recurridList[$k][0] : clone $compStart;
1341
+ // echo "going to test comp no:$cix with checkDate={$compStart->key} with recurridList=".implode(',',array_keys($recurridList)); // test ###
1342
+ if( ! $any || ! isset( $exdatelist[$tstart->key] )) { // exclude any recurrence date, found in exdatelist
1343
+ // echo " and copied to output<br>\n"; // test ###
1344
+ $result[(int)$tstart->format( 'Y' )][(int)$tstart->format( 'm' )][(int)$tstart->format( 'd' )][$compUID] = $component->copy(); // copy to output
1345
+ }
1346
+ unset( $tstart );
1347
+ }
1348
+ } // end (dt)start within the period OR occurs within the period
1349
+ /* *************************************************************
1350
+ if 'any' components, check components with reccurrence rules, removing all excluding dates
1351
+ *********************************************************** */
1352
+ if( TRUE === $any ) {
1353
+ $recurlist = array();
1354
+ /* make a list of optional repeating dates for component occurence, rrule, rdate */
1355
+ while( FALSE !== ( $prop = $component->getProperty( 'RRULE' ))) { // get all rrule dates (multiple values allowed)
1356
+ $recurlist2 = array();
1357
+ if( isset( $prop['UNTIL']['hour'] )) { // convert $rrule['UNTIL'] to the same timezone as DTSTART !!
1358
+ $until = iCaldateTime::factory( $prop['UNTIL'], array( 'TZID' => 'UTC' ), null, $dtstartTz );
1359
+ $until = $until->format();
1360
+ iCalUtilityFunctions::_strDate2arr( $until );
1361
+ $prop['UNTIL'] = $until;
1362
+ }
1363
+ iCalUtilityFunctions::_recur2date( $recurlist2, $prop, $compStart, $workStart, $workEnd );
1364
+ foreach( $recurlist2 as $recurkey => $recurvalue ) { // recurkey=Ymd
1365
+ $recurkey .= $compStartHis; // add opt His
1366
+ if( ! isset( $exdatelist[$recurkey] ))
1367
+ $recurlist[$recurkey] = $compDuration; // DateInterval or FALSE
1368
+ }
1369
+ unset( $prop, $until, $recurlist2 );
1370
+ }
1371
+ $workStart = clone $fcnStart;
1372
+ $workStart->sub( $compDuration ? $compDuration : $intervalP1D );
1373
+ $format = $compStart->dateFormat;
1374
+ while( FALSE !== ( $prop = $component->getProperty( 'RDATE', FALSE, TRUE ))) {
1375
+ $rdateFmt = ( isset( $prop['params']['VALUE'] )) ? $prop['params']['VALUE'] : 'DATETIME';
1376
+ $params = $prop['params'];
1377
+ $prop = $prop['value'];
1378
+ foreach( $prop as $theRdate ) {
1379
+ if( 'PERIOD' == $rdateFmt ) { // all days within PERIOD
1380
+ $rdate = iCaldateTime::factory( $theRdate[0], $params, $theRdate[0], $dtstartTz );
1381
+ if( ! iCalUtilityFunctions::_inScope( $rdate, $workStart, $rdate, $fcnEnd, $format ) || isset( $exdatelist[$rdate->key] ))
1382
+ continue;
1383
+ if( isset( $theRdate[1]['year'] )) // date-date period end
1384
+ $recurlist[$rdate->key] = $rdate->diff( iCaldateTime::factory( $theRdate[1], $params, $theRdate[1], $dtstartTz ));
1385
+ else // period duration
1386
+ $recurlist[$rdate->key] = new DateInterval( iCalUtilityFunctions::_duration2str( $theRdate[1] ));
1387
+ } // end if( 'PERIOD' == $rdateFmt )
1388
+ elseif( 'DATE' == $rdateFmt ) { // single recurrence, date
1389
+ $rdate = iCaldateTime::factory( $theRdate, array_merge( $params, array( 'TZID' => $dtstartTz )), null, $dtstartTz );
1390
+ if( iCalUtilityFunctions::_inScope( $rdate, $workStart, $rdate, $fcnEnd, $format ) && ! isset( $exdatelist[$rdate->key] ))
1391
+ $recurlist[$rdate->key.$compStartHis] = $compDuration; // set start date for recurrence + DateInterval/FALSE (+opt His)
1392
+ } // end DATE
1393
+ else { // start DATETIME
1394
+ $rdate = iCaldateTime::factory( $theRdate, $params, $theRdate, $dtstartTz );
1395
+ if( iCalUtilityFunctions::_inScope( $rdate, $workStart, $rdate, $fcnEnd, $format ) && ! isset( $exdatelist[$rdate->key] ))
1396
+ $recurlist[$rdate->key] = $compDuration; // set start datetime for recurrence DateInterval/FALSE
1397
+ } // end DATETIME
1398
+ } // end foreach( $prop as $theRdate )
1399
+ } // end while( FALSE !== ( $prop = $component->getProperty( 'rdate', FALSE, TRUE )))
1400
+ unset( $prop, $workStart, $format, $theRdate, $rdate, $rend );
1401
+ foreach( $recurridList as $rKey => $rVal ) { // check for recurrence-id, i.e. alter recur Ymd[His] and duration
1402
+ if( isset( $recurlist[$rKey] )) {
1403
+ unset( $recurlist[$rKey] );
1404
+ $recurlist[$rVal[0]->key] = ( FALSE !== $rVal[2] ) ? $rVal[2] : $compDuration;
1405
+ // echo "alter recurfrom {$rKey} to {$rVal[0]->key} ";if(FALSE!==$dur)echo " ({$dur->format( '%a days, %h-%i-%s' )})";echo "<br>\n"; // test ###
1406
+ }
1407
+ }
1408
+ ksort( $recurlist, SORT_STRING );
1409
+ // echo 'recurlist=' .implode(', ', array_keys( $recurlist )) ."<br>\n"; // test ###
1410
+ // echo 'recurridList=' .implode(', ', array_keys( $recurridList )) ."<br>\n"; // test ###
1411
+ /* *************************************************************
1412
+ output all remaining components in recurlist
1413
+ *********************************************************** */
1414
+ if( 0 < count( $recurlist )) {
1415
+ $component2 = $component->copy();
1416
+ $compUID = $component2->getProperty( 'UID' );
1417
+ $workStart = clone $fcnStart;
1418
+ $workStart->sub( $compDuration ? $compDuration : $intervalP1D );
1419
+ $YmdOld = null;
1420
+ foreach( $recurlist as $recurkey => $durvalue ) {
1421
+ if( $YmdOld == substr( $recurkey, 0, 8 )) // skip overlapping recur the same day, i.e. RDATE before RRULE
1422
+ continue;
1423
+ $YmdOld = substr( $recurkey, 0, 8 );
1424
+ $rstart = clone $compStart;
1425
+ $rstart->setDate((int) substr( $recurkey, 0, 4 ), (int) substr( $recurkey, 4, 2 ), (int) substr( $recurkey, 6, 2 ));
1426
+ $rstart->setTime((int) substr( $recurkey, 8, 2 ), (int) substr( $recurkey, 10, 2 ), (int) substr( $recurkey, 12, 2 ));
1427
+ // echo "recur start=".$rstart->format( iCalUtilityFunctions::$fmt['YmdHis2e'] )."<br>\n"; // test ###;
1428
+ /* add recurring components within valid dates to output array, only start date set */
1429
+ if( $flat ) {
1430
+ if( !isset( $result[$compUID] )) // only one comp
1431
+ $result[$compUID] = $component2->copy(); // copy to output
1432
+ }
1433
+ /* add recurring components within valid dates to output array, split for each day */
1434
+ elseif( $split ) {
1435
+ $rend = clone $rstart;
1436
+ if( FALSE !== $durvalue )
1437
+ $rend->add( $durvalue );
1438
+ if( $rend->format( iCalUtilityFunctions::$fmt['Ymd2'] ) > $fcnEnd->format( iCalUtilityFunctions::$fmt['Ymd2'] ))
1439
+ $rend = clone $fcnEnd;
1440
+ // echo "recur 1={$recurkey}, start=".$rstart->format( iCalUtilityFunctions::$fmt['YmdHis2e'] ).", end=".$rend->format( iCalUtilityFunctions::$fmt['YmdHis2e'] );if($durvalue) echo ", duration=".$durvalue->format( iCalUtilityFunctions::$fmt['durDHis'] );echo "<br>\n"; // test ###
1441
+ $xRecurrence += 1;
1442
+ $cnt = 0;
1443
+ $occurenceDays = 1 + (int) $rstart->diff( $rend )->format( '%a' ); // count the days (incl start day)
1444
+ while( $rstart->format( iCalUtilityFunctions::$fmt['Ymd2'] ) <= $rend->format( iCalUtilityFunctions::$fmt['Ymd2'] )) { // iterate.. .
1445
+ $cnt += 1;
1446
+ if( $rstart->format( iCalUtilityFunctions::$fmt['Ymd2'] ) < $fcnStart->format( iCalUtilityFunctions::$fmt['Ymd2'] )) { // date before dtstart
1447
+ // echo "recur 3, start=".$rstart->format( 'Y-m-d H:i:s' )." &gt;= fcnStart=".$fcnStart->format( 'Y-m-d H:i:s' )."<br>\n"; // test ###
1448
+ $rstart->add( $intervalP1D );
1449
+ $rstart->setTime( 0, 0, 0 );
1450
+ continue;
1451
+ }
1452
+ elseif( 2 == $cnt )
1453
+ $rstart->setTime( 0, 0, 0 );
1454
+ $component2->setProperty( 'X-RECURRENCE', $xRecurrence );
1455
+ if( 1 < $occurenceDays )
1456
+ $component2->setProperty( 'X-OCCURENCE', sprintf( iCalUtilityFunctions::$fmt['dayOfDays'], $cnt, $occurenceDays ));
1457
+ else
1458
+ $component2->deleteProperty( 'X-OCCURENCE' );
1459
+ $component2->setProperty( 'X-CURRENT-DTSTART', $rstart->format( $compStart->dateFormat ));
1460
+ $propName = ( isset( $compEnd->SCbools[ 'dueExist'] )) ? 'X-CURRENT-DUE' : 'X-CURRENT-DTEND';
1461
+ if( FALSE !== $durvalue ) {
1462
+ if( $cnt < $occurenceDays )
1463
+ $rstart->setTime( 23, 59, 59 );
1464
+ else {
1465
+ $His = $rend->getTime(); // set end time
1466
+ $rstart->setTime( $His[0], $His[1], $His[2] );
1467
+ }
1468
+ $component2->setProperty( $propName, $rstart->format( $compEnd->dateFormat ));
1469
+ // echo "checking date, (day {$cnt} of {$occurenceDays}), _end_=".$rstart->format( 'Y-m-d H:i:s e' )."<br>"; // test ###;
1470
+ }
1471
+ else
1472
+ $component2->deleteProperty( $propName );
1473
+ $result[(int)$rstart->format( 'Y' )][(int)$rstart->format( 'm' )][(int)$rstart->format( 'd' )][$compUID] = $component2->copy(); // copy to output
1474
+ $rstart->add( $intervalP1D );
1475
+ } // end while( $rstart->format( 'Ymd' ) <= $rend->format( 'Ymd' ))
1476
+ unset( $rstart, $rend );
1477
+ } // end elseif( $split )
1478
+ elseif( $rstart->format( iCalUtilityFunctions::$fmt['Ymd2'] ) >= $fcnStart->format( iCalUtilityFunctions::$fmt['Ymd2'] )) {
1479
+ $xRecurrence += 1; // date within period //* flat=FALSE && split=FALSE => one comp every recur startdate *//
1480
+ $component2->setProperty( 'X-RECURRENCE', $xRecurrence );
1481
+ $component2->setProperty( 'X-CURRENT-DTSTART', $rstart->format( $compStart->dateFormat ));
1482
+ $propName = ( isset( $compEnd->SCbools[ 'dueExist'] )) ? 'X-CURRENT-DUE' : 'X-CURRENT-DTEND';
1483
+ if( FALSE !== $durvalue ) {
1484
+ $rstart->add( $durvalue );
1485
+ $component2->setProperty( $propName, $rstart->format( $compEnd->dateFormat ));
1486
+ }
1487
+ else
1488
+ $component2->deleteProperty( $propName );
1489
+ $result[(int)$rstart->format( 'Y' )][(int)$rstart->format( 'm' )][(int)$rstart->format( 'd' )][$compUID] = $component2->copy(); // copy to output
1490
+ } // end elseif( $rstart >= $fcnStart )
1491
+ unset( $rstart );
1492
+ } // end foreach( $recurlist as $recurkey => $durvalue )
1493
+ unset( $component2, $xRecurrence, $compUID, $workStart, $rstart );
1494
+ } // end if( 0 < count( $recurlist ))
1495
+ } // end if( TRUE === $any )
1496
+ unset( $component );
1497
+ } // end foreach ( $this->components as $cix => $component )
1498
+ unset( $recurrid, $recurridList, $fcnStart, $fcnEnd, $compStart, $compEnd, $exdatelist, $recurlist ); // clean up
1499
+ if( 0 >= count( $result ))
1500
+ return FALSE;
1501
+ elseif( !$flat ) {
1502
+ foreach( $result as $y => $yeararr ) {
1503
+ foreach( $yeararr as $m => $montharr ) {
1504
+ foreach( $montharr as $d => $dayarr ) {
1505
+ if( empty( $result[$y][$m][$d] ))
1506
+ unset( $result[$y][$m][$d] );
1507
+ else {
1508
+ $result[$y][$m][$d] = array_values( $dayarr ); // skip tricky UID-index
1509
+ if( 1 < count( $result[$y][$m][$d] )) {
1510
+ foreach( $result[$y][$m][$d] as & $c ) // sort
1511
+ iCalUtilityFunctions::_setSortArgs( $c );
1512
+ usort( $result[$y][$m][$d], array( 'iCalUtilityFunctions', '_cmpfcn' ));
1513
+ }
1514
+ }
1515
+ } // end foreach( $montharr as $d => $dayarr )
1516
+ if( empty( $result[$y][$m] ))
1517
+ unset( $result[$y][$m] );
1518
+ else
1519
+ ksort( $result[$y][$m] );
1520
+ } // end foreach( $yeararr as $m => $montharr )
1521
+ if( empty( $result[$y] ))
1522
+ unset( $result[$y] );
1523
+ else
1524
+ ksort( $result[$y] );
1525
+ }// end foreach( $result as $y => $yeararr )
1526
+ if( empty( $result ))
1527
+ unset( $result );
1528
+ else
1529
+ ksort( $result );
1530
+ } // end elseif( !$flat )
1531
+ if( 0 >= count( $result ))
1532
+ return FALSE;
1533
+ return $result;
1534
+ }
1535
+ /**
1536
+ * select components from calendar on based on specific property value(-s)
1537
+ *
1538
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1539
+ * @since 2.21.11 - 2015-03-21
1540
+ * @param array $selectOptions (string) key => (mixed) value, (key=propertyName)
1541
+ * @uses vcalendar::$components
1542
+ * @uses calendarComponent::$objName
1543
+ * @uses iCalUtilityFunctions::$vComps
1544
+ * @uses calendarComponent::getProperty()
1545
+ * @uses iCalUtilityFunctions::$otherProps
1546
+ * @uses calendarComponent::copy()
1547
+ * @uses iCalUtilityFunctions::$mProps1
1548
+ * @uses calendarComponent::_getProperties()
1549
+ * @return array
1550
+ */
1551
+ function selectComponents2( $selectOptions ) {
1552
+ // $output = array();
1553
+ $selectOptions = array_change_key_case( $selectOptions, CASE_UPPER );
1554
+ foreach( $this->components as $cix => $component3 ) {
1555
+ if( !in_array( $component3->objName, iCalUtilityFunctions::$vComps ))
1556
+ continue;
1557
+ $uid = $component3->getProperty( 'UID' );
1558
+ foreach( $selectOptions as $propName => $pvalue ) {
1559
+ if( !in_array( $propName, iCalUtilityFunctions::$otherProps ))
1560
+ continue;
1561
+ if( !is_array( $pvalue ))
1562
+ $pvalue = array( $pvalue );
1563
+ if(( 'UID' == $propName ) && in_array( $uid, $pvalue )) {
1564
+ $output[$uid][] = $component3->copy();
1565
+ continue;
1566
+ }
1567
+ elseif( in_array( $propName, iCalUtilityFunctions::$mProps1 )) {
1568
+ $propValues = array();
1569
+ $component3->_getProperties( $propName, $propValues );
1570
+ $propValues = array_keys( $propValues );
1571
+ foreach( $pvalue as $theValue ) {
1572
+ if( in_array( $theValue, $propValues )) { // && !isset( $output[$uid] )) {
1573
+ $output[$uid][] = $component3->copy();
1574
+ break;
1575
+ }
1576
+ }
1577
+ continue;
1578
+ } // end elseif( // multiple occurrence?
1579
+ elseif( FALSE === ( $d = $component3->getProperty( $propName ))) // single occurrence
1580
+ continue;
1581
+ if( is_array( $d )) {
1582
+ foreach( $d as $part ) {
1583
+ if( in_array( $part, $pvalue ) && !isset( $output[$uid] ))
1584
+ $output[$uid][] = $component3->copy();
1585
+ }
1586
+ }
1587
+ elseif(( 'SUMMARY' == $propName ) && !isset( $output[$uid] )) {
1588
+ foreach( $pvalue as $pval ) {
1589
+ if( FALSE !== stripos( $d, $pval )) {
1590
+ $output[$uid][] = $component3->copy();
1591
+ break;
1592
+ }
1593
+ }
1594
+ }
1595
+ elseif( in_array( $d, $pvalue ) && !isset( $output[$uid] ))
1596
+ $output[$uid][] = $component3->copy();
1597
+ } // end foreach( $selectOptions as $propName => $pvalue ) {
1598
+ } // end foreach( $this->components as $cix => $component3 ) {
1599
+ if( !empty( $output )) {
1600
+ ksort( $output ); // uid order
1601
+ $output2 = array();
1602
+ foreach( $output as $uid => $components ) {
1603
+ foreach( $components as $component )
1604
+ $output2[] = $component;
1605
+ }
1606
+ $output = $output2;
1607
+ }
1608
+ return $output;
1609
+ }
1610
+ /**
1611
+ * replace calendar component in vcalendar
1612
+ *
1613
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1614
+ * @since 2.21.11 - 2015-03-21
1615
+ * @param object $component calendar component
1616
+ * @uses calendarComponent::$objName
1617
+ * @uses iCalUtilityFunctions::$vComps
1618
+ * @uses vcalendar::setComponent()
1619
+ * @uses calendarComponent::getProperty()
1620
+ * @return bool
1621
+ */
1622
+ function replaceComponent( $component ) {
1623
+ if( in_array( $component->objName, iCalUtilityFunctions::$vComps ))
1624
+ return $this->setComponent( $component, $component->getProperty( 'UID' ));
1625
+ if(( 'vtimezone' != $component->objName ) || ( FALSE === ( $tzid = $component->getProperty( 'TZID' ))))
1626
+ return FALSE;
1627
+ foreach( $this->components as $cix => $comp ) {
1628
+ if( 'vtimezone' != $component->objName )
1629
+ continue;
1630
+ if( $tzid == $comp->getComponent( 'TZID' )) {
1631
+ unset( $component->propix, $component->compix );
1632
+ $this->components[$cix] = $component;
1633
+ return TRUE;
1634
+ }
1635
+ }
1636
+ return FALSE;
1637
+ }
1638
+ /**
1639
+ * add calendar component to calendar
1640
+ *
1641
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1642
+ * @since 2.21.11 - 2015-03-21
1643
+ * @param object $component calendar component
1644
+ * @param mixed $arg1 optional, ordno/component type/ component uid
1645
+ * @param mixed $arg2 optional, ordno if arg1 = component type
1646
+ * @uses calendarComponent::setConfig()
1647
+ * @uses vcalendar::getConfig()
1648
+ * @uses calendarComponent::$objName
1649
+ * @uses calendarComponent::getProperty()
1650
+ * @uses vcalendar::$components
1651
+ * @uses iCalUtilityFunctions::$mComps
1652
+ * @uses calendarComponent::copy()
1653
+ * @return bool
1654
+ */
1655
+ function setComponent( $component, $arg1=FALSE, $arg2=FALSE ) {
1656
+ $component->setConfig( $this->getConfig(), FALSE, TRUE );
1657
+ if( !in_array( $component->objName, array( 'valarm', 'vtimezone' ))) {
1658
+ /* make sure dtstamp and uid is set */
1659
+ $dummy1 = $component->getProperty( 'dtstamp' );
1660
+ $dummy2 = $component->getProperty( 'uid' );
1661
+ }
1662
+ unset( $component->propix, $component->compix );
1663
+ if( !$arg1 ) { // plain insert, last in chain
1664
+ $this->components[] = $component->copy();
1665
+ return TRUE;
1666
+ }
1667
+ $argType = $index = null;
1668
+ if ( ctype_digit( (string) $arg1 )) { // index insert/replace
1669
+ $argType = 'INDEX';
1670
+ $index = (int) $arg1 - 1;
1671
+ }
1672
+ elseif( in_array( strtolower( $arg1 ), iCalUtilityFunctions::$mComps )) {
1673
+ $argType = strtolower( $arg1 );
1674
+ $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0;
1675
+ }
1676
+ // else if arg1 is set, arg1 must be an UID
1677
+ $cix1sC = 0;
1678
+ foreach ( $this->components as $cix => $component2) {
1679
+ if( empty( $component2 )) continue;
1680
+ if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace
1681
+ $this->components[$cix] = $component->copy();
1682
+ return TRUE;
1683
+ }
1684
+ elseif( $argType == $component2->objName ) { // component Type index insert/replace
1685
+ if( $index == $cix1sC ) {
1686
+ $this->components[$cix] = $component->copy();
1687
+ return TRUE;
1688
+ }
1689
+ $cix1sC++;
1690
+ }
1691
+ elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace
1692
+ $this->components[$cix] = $component->copy();
1693
+ return TRUE;
1694
+ }
1695
+ }
1696
+ /* arg1=index and not found.. . insert at index .. .*/
1697
+ if( 'INDEX' == $argType ) {
1698
+ $this->components[$index] = $component->copy();
1699
+ ksort( $this->components, SORT_NUMERIC );
1700
+ }
1701
+ else /* not found.. . insert last in chain anyway .. .*/
1702
+ $this->components[] = $component->copy();
1703
+ return TRUE;
1704
+ }
1705
+ /**
1706
+ * sort iCal compoments
1707
+ *
1708
+ * ascending sort on properties (if exist) x-current-dtstart, dtstart,
1709
+ * x-current-dtend, dtend, x-current-due, due, duration, created, dtstamp, uid if called without arguments,
1710
+ * otherwise sorting on specific (argument) property values
1711
+ *
1712
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1713
+ * @since 2.21.11 - 2015-03-21
1714
+ * @param string $sortArg
1715
+ * @uses vcalendar::$components
1716
+ * @uses iCalUtilityFunctions::$otherProps
1717
+ * @uses iCalUtilityFunctions::_setSortArgs()
1718
+ * @uses iCalUtilityFunctions::_cmpfcn()
1719
+ * @return void
1720
+ */
1721
+ function sort( $sortArg=FALSE ) {
1722
+ if( ! is_array( $this->components ) || ( 2 > count( $this->components )))
1723
+ return;
1724
+ if( $sortArg ) {
1725
+ $sortArg = strtoupper( $sortArg );
1726
+ if( ! in_array( $sortArg, iCalUtilityFunctions::$otherProps ) && ( 'DTSTAMP' != $sortArg ))
1727
+ $sortArg = FALSE;
1728
+ }
1729
+ foreach( $this->components as & $c )
1730
+ iCalUtilityFunctions::_setSortArgs( $c, $sortArg );
1731
+ usort( $this->components, array( 'iCalUtilityFunctions', '_cmpfcn' ));
1732
+ }
1733
+ /**
1734
+ * parse iCal text/file into vcalendar, components, properties and parameters
1735
+ *
1736
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1737
+ * @since 2.21.12 - 2014-03-10
1738
+ * @param mixed $unparsedtext strict rfc2445 formatted, single property string or array of property strings
1739
+ * @param resource $context PHP resource context
1740
+ * @uses iCalUtilityFunctions::convEolChar()
1741
+ * @uses vcalendar::getConfig()
1742
+ * @uses vcalendar::$components
1743
+ * @uses calendarComponent::copy()
1744
+ * @uses vevent::construct()
1745
+ * @uses vfreebusy::construct()
1746
+ * @uses vjournal::construct()
1747
+ * @uses vtodo::construct()
1748
+ * @uses vtimezone::construct()
1749
+ * @uses vcalendar::$unparsed
1750
+ * @uses iCalUtilityFunctions::_splitContent()
1751
+ * @uses iCalUtilityFunctions::_strunrep()
1752
+ * @uses vcalendar::setProperty()
1753
+ * @uses calendarComponent::$unparsed
1754
+ * @uses calendarComponent::parse()
1755
+ * @return bool FALSE if error occurs during parsing
1756
+ */
1757
+ function parse( $unparsedtext=FALSE, $context=null ) {
1758
+ $nl = $this->getConfig( 'nl' );
1759
+ if(( FALSE === $unparsedtext ) || empty( $unparsedtext )) {
1760
+ /* directory+filename is set previously via setConfig url or directory+filename */
1761
+ if( FALSE === ( $file = $this->getConfig( 'url' ))) {
1762
+ if( FALSE === ( $file = $this->getConfig( 'dirfile' )))
1763
+ return FALSE; /* err 1 */
1764
+ if( ! is_file( $file ))
1765
+ return FALSE; /* err 2 */
1766
+ if( ! is_readable( $file ))
1767
+ return FALSE; /* err 3 */
1768
+ }
1769
+ /* READ FILE */
1770
+ if( ! empty( $context ) && filter_var( $file, FILTER_VALIDATE_URL ) &&
1771
+ ( FALSE === ( $rows = file_get_contents( $file, FALSE, $context ))))
1772
+ return FALSE; /* err 6 */
1773
+ elseif( FALSE === ( $rows = file_get_contents( $file )))
1774
+ return FALSE; /* err 5 */
1775
+ } // end if(( FALSE === $unparsedtext ) || empty( $unparsedtext ))
1776
+ elseif( is_array( $unparsedtext ))
1777
+ $rows = implode( '\n'.$nl, $unparsedtext );
1778
+ else
1779
+ $rows = & $unparsedtext;
1780
+ /* fix line folding */
1781
+ $rows = iCalUtilityFunctions::convEolChar( $rows, $nl );
1782
+ /* skip leading (empty/invalid) lines (and remove leading BOM chars etc) */
1783
+ foreach( $rows as $lix => $line ) {
1784
+ if( FALSE !== stripos( $line, 'BEGIN:VCALENDAR' )) {
1785
+ $rows[$lix] = 'BEGIN:VCALENDAR';
1786
+ break;
1787
+ }
1788
+ unset( $rows[$lix] );
1789
+ }
1790
+ $rcnt = count( $rows );
1791
+ if( 3 > $rcnt ) /* err 10 */
1792
+ return FALSE;
1793
+ /* skip trailing empty lines and ensure an end row */
1794
+ $lix = array_keys( $rows );
1795
+ $lix = end( $lix );
1796
+ while( 3 < $lix ) {
1797
+ $tst = trim( $rows[$lix] );
1798
+ if(( '\n' == $tst ) || empty( $tst )) {
1799
+ unset( $rows[$lix] );
1800
+ $lix--;
1801
+ continue;
1802
+ }
1803
+ if( FALSE === stripos( $rows[$lix], 'END:VCALENDAR' ))
1804
+ $rows[] = 'END:VCALENDAR';
1805
+ else
1806
+ $rows[$lix] = 'END:VCALENDAR';
1807
+ break;
1808
+ }
1809
+ $comp = & $this;
1810
+ $calsync = $compsync = 0;
1811
+ /* identify components and update unparsed data within component */
1812
+ $config = $this->getConfig();
1813
+ $endtxt = array( 'END:VE', 'END:VF', 'END:VJ', 'END:VT' );
1814
+ foreach( $rows as $lix => $line ) {
1815
+ if( 'BEGIN:VCALENDAR' == strtoupper( substr( $line, 0, 15 ))) {
1816
+ $calsync++;
1817
+ continue;
1818
+ }
1819
+ elseif( 'END:VCALENDAR' == strtoupper( substr( $line, 0, 13 ))) {
1820
+ if( 0 < $compsync )
1821
+ $this->components[] = $comp->copy();
1822
+ $compsync--;
1823
+ $calsync--;
1824
+ break;
1825
+ }
1826
+ elseif( 1 != $calsync )
1827
+ return FALSE; /* err 20 */
1828
+ elseif( in_array( strtoupper( substr( $line, 0, 6 )), $endtxt )) {
1829
+ $this->components[] = $comp->copy();
1830
+ $compsync--;
1831
+ continue;
1832
+ }
1833
+ if( 'BEGIN:VEVENT' == strtoupper( substr( $line, 0, 12 ))) {
1834
+ $comp = new vevent( $config );
1835
+ $compsync++;
1836
+ }
1837
+ elseif( 'BEGIN:VFREEBUSY' == strtoupper( substr( $line, 0, 15 ))) {
1838
+ $comp = new vfreebusy( $config );
1839
+ $compsync++;
1840
+ }
1841
+ elseif( 'BEGIN:VJOURNAL' == strtoupper( substr( $line, 0, 14 ))) {
1842
+ $comp = new vjournal( $config );
1843
+ $compsync++;
1844
+ }
1845
+ elseif( 'BEGIN:VTODO' == strtoupper( substr( $line, 0, 11 ))) {
1846
+ $comp = new vtodo( $config );
1847
+ $compsync++;
1848
+ }
1849
+ elseif( 'BEGIN:VTIMEZONE' == strtoupper( substr( $line, 0, 15 ))) {
1850
+ $comp = new vtimezone( $config );
1851
+ $compsync++;
1852
+ }
1853
+ else { /* update component with unparsed data */
1854
+ $comp->unparsed[] = $line;
1855
+ }
1856
+ } // end foreach( $rows as $line )
1857
+ unset( $config, $endtxt );
1858
+ /* parse data for calendar (this) object */
1859
+ if( isset( $this->unparsed ) && is_array( $this->unparsed ) && ( 0 < count( $this->unparsed ))) {
1860
+ /* concatenate property values spread over several lines */
1861
+ $propnames = array( 'calscale','method','prodid','version','x-' );
1862
+ $proprows = array();
1863
+ for( $i = 0; $i < count( $this->unparsed ); $i++ ) { // concatenate lines
1864
+ $line = rtrim( $this->unparsed[$i], $nl );
1865
+ while( isset( $this->unparsed[$i+1] ) && !empty( $this->unparsed[$i+1] ) && ( ' ' == $this->unparsed[$i+1]{0} ))
1866
+ $line .= rtrim( substr( $this->unparsed[++$i], 1 ), $nl );
1867
+ $proprows[] = $line;
1868
+ }
1869
+ foreach( $proprows as $line ) {
1870
+ if( '\n' == substr( $line, -2 ))
1871
+ $line = substr( $line, 0, -2 );
1872
+ /* get property name */
1873
+ $propname = '';
1874
+ $cix = 0;
1875
+ while( FALSE !== ( $char = substr( $line, $cix, 1 ))) {
1876
+ if( in_array( $char, array( ':', ';' )))
1877
+ break;
1878
+ else
1879
+ $propname .= $char;
1880
+ $cix++;
1881
+ }
1882
+ /* skip non standard property names */
1883
+ if(( 'x-' != strtolower( substr( $propname, 0, 2 ))) && !in_array( strtolower( $propname ), $propnames ))
1884
+ continue;
1885
+ /* ignore version/prodid properties */
1886
+ if( in_array( strtolower( $propname ), array( 'version', 'prodid' )))
1887
+ continue;
1888
+ /* rest of the line is opt.params and value */
1889
+ $line = substr( $line, $cix);
1890
+ /* separate attributes from value */
1891
+ iCalUtilityFunctions::_splitContent( $line, $propAttr );
1892
+ /* update Property */
1893
+ if( FALSE !== strpos( $line, ',' )) {
1894
+ $content = array( 0 => '' );
1895
+ $cix = $lix = 0;
1896
+ while( FALSE !== substr( $line, $lix, 1 )) {
1897
+ if(( 0 < $lix ) && ( ',' == $line[$lix] ) && ( "\\" != $line[( $lix - 1 )])) {
1898
+ $cix++;
1899
+ $content[$cix] = '';
1900
+ }
1901
+ else
1902
+ $content[$cix] .= $line[$lix];
1903
+ $lix++;
1904
+ }
1905
+ if( 1 < count( $content )) {
1906
+ foreach( $content as $cix => $contentPart )
1907
+ $content[$cix] = iCalUtilityFunctions::_strunrep( $contentPart );
1908
+ $this->setProperty( $propname, $content, $propAttr );
1909
+ continue;
1910
+ }
1911
+ else
1912
+ $line = reset( $content );
1913
+ $line = iCalUtilityFunctions::_strunrep( $line );
1914
+ }
1915
+ $this->setProperty( $propname, rtrim( $line, "\x00..\x1F" ), $propAttr );
1916
+ } // end - foreach( $this->unparsed.. .
1917
+ } // end - if( is_array( $this->unparsed.. .
1918
+ unset( $unparsedtext, $rows, $this->unparsed, $proprows );
1919
+ /* parse Components */
1920
+ if( is_array( $this->components ) && ( 0 < count( $this->components ))) {
1921
+ $ckeys = array_keys( $this->components );
1922
+ foreach( $ckeys as $ckey ) {
1923
+ if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) {
1924
+ $this->components[$ckey]->parse();
1925
+ }
1926
+ }
1927
+ }
1928
+ else
1929
+ return FALSE; /* err 91 or something.. . */
1930
+ return TRUE;
1931
+ }
1932
+ /*********************************************************************************/
1933
+ /**
1934
+ * creates formatted output for calendar object instance
1935
+ *
1936
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1937
+ * @since 2.21.07 - 2015-03-31
1938
+ * @uses vcalendar::$format
1939
+ * @uses vcalendar::$nl
1940
+ * @uses vcalendar::createVersion()
1941
+ * @uses vcalendar::createProdid()
1942
+ * @uses vcalendar::createCalscale()
1943
+ * @uses vcalendar::createMethod()
1944
+ * @uses vcalendar::createXprop()
1945
+ * @uses vcalendar::$components
1946
+ * @uses calendarComponent::setConfig()
1947
+ * @uses vcalendar::getConfig()
1948
+ * @uses calendarComponent::createComponent()
1949
+ * @uses vcalendar::$xcaldecl
1950
+ * @return string
1951
+ */
1952
+ function createCalendar() {
1953
+ parent::_createFormat();
1954
+ $calendarInit = $calendarxCaldecl = $calendarStart = $calendar = '';
1955
+ switch( $this->format ) {
1956
+ case 'xcal':
1957
+ $calendarInit = '<?xml version="1.0" encoding="UTF-8"?>'.$this->nl.
1958
+ '<!DOCTYPE vcalendar PUBLIC "-//IETF//DTD XCAL/iCalendar XML//EN"'.$this->nl.
1959
+ '"http://www.ietf.org/internet-drafts/draft-ietf-calsch-many-xcal-01.txt"';
1960
+ $calendarStart = '>'.$this->nl.'<vcalendar';
1961
+ break;
1962
+ default:
1963
+ $calendarStart = 'BEGIN:VCALENDAR'.$this->nl;
1964
+ break;
1965
+ }
1966
+ $calendarStart .= $this->createVersion();
1967
+ $calendarStart .= $this->createProdid();
1968
+ $calendarStart .= $this->createCalscale();
1969
+ $calendarStart .= $this->createMethod();
1970
+ if( 'xcal' == $this->format )
1971
+ $calendarStart .= '>'.$this->nl;
1972
+ $calendar .= $this->createXprop();
1973
+ foreach( $this->components as $component ) {
1974
+ if( empty( $component )) continue;
1975
+ $component->setConfig( $this->getConfig(), FALSE, TRUE );
1976
+ $calendar .= $component->createComponent( $this->xcaldecl );
1977
+ }
1978
+ if(( 'xcal' == $this->format ) && ( 0 < count( $this->xcaldecl ))) { // xCal only
1979
+ $calendarInit .= ' [';
1980
+ $old_xcaldecl = array();
1981
+ foreach( $this->xcaldecl as $declix => $declPart ) {
1982
+ if(( 0 < count( $old_xcaldecl)) &&
1983
+ isset( $declPart['uri'] ) && isset( $declPart['external'] ) &&
1984
+ isset( $old_xcaldecl['uri'] ) && isset( $old_xcaldecl['external'] ) &&
1985
+ ( in_array( $declPart['uri'], $old_xcaldecl['uri'] )) &&
1986
+ ( in_array( $declPart['external'], $old_xcaldecl['external'] )))
1987
+ continue; // no duplicate uri and ext. references
1988
+ if(( 0 < count( $old_xcaldecl)) &&
1989
+ !isset( $declPart['uri'] ) && !isset( $declPart['uri'] ) &&
1990
+ isset( $declPart['ref'] ) && isset( $old_xcaldecl['ref'] ) &&
1991
+ ( in_array( $declPart['ref'], $old_xcaldecl['ref'] )))
1992
+ continue; // no duplicate element declarations
1993
+ $calendarxCaldecl .= $this->nl.'<!';
1994
+ foreach( $declPart as $declKey => $declValue ) {
1995
+ switch( $declKey ) { // index
1996
+ case 'xmldecl': // no 1
1997
+ $calendarxCaldecl .= $declValue.' ';
1998
+ break;
1999
+ case 'uri': // no 2
2000
+ $calendarxCaldecl .= $declValue.' ';
2001
+ $old_xcaldecl['uri'][] = $declValue;
2002
+ break;
2003
+ case 'ref': // no 3
2004
+ $calendarxCaldecl .= $declValue.' ';
2005
+ $old_xcaldecl['ref'][] = $declValue;
2006
+ break;
2007
+ case 'external': // no 4
2008
+ $calendarxCaldecl .= '"'.$declValue.'" ';
2009
+ $old_xcaldecl['external'][] = $declValue;
2010
+ break;
2011
+ case 'type': // no 5
2012
+ $calendarxCaldecl .= $declValue.' ';
2013
+ break;
2014
+ case 'type2': // no 6
2015
+ $calendarxCaldecl .= $declValue;
2016
+ break;
2017
+ }
2018
+ }
2019
+ $calendarxCaldecl .= '>';
2020
+ }
2021
+ $calendarxCaldecl .= $this->nl.']';
2022
+ } // end if(( 'xcal'...
2023
+ switch( $this->format ) {
2024
+ case 'xcal':
2025
+ $calendar .= '</vcalendar>'.$this->nl;
2026
+ break;
2027
+ default:
2028
+ $calendar .= 'END:VCALENDAR'.$this->nl;
2029
+ break;
2030
+ }
2031
+ return $calendarInit.$calendarxCaldecl.$calendarStart.$calendar;
2032
+ }
2033
+ /**
2034
+ * a HTTP redirect header is sent with created, updated and/or parsed calendar
2035
+ *
2036
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2037
+ * @since 2.21.5 - 2015-03-29
2038
+ * @param bool $utf8Encode
2039
+ * @param bool $gzip
2040
+ * @param bool $cdType TRUE : Content-Disposition: attachment... (default), FALSE : ...inline...
2041
+ * @uses vcalendar::getConfig()
2042
+ * @uses vcalendar::createCalendar()
2043
+ * @uses vcalendar::$headers
2044
+ * @uses vcalendar::$format
2045
+ * @return bool TRUE on success, FALSE on error
2046
+ */
2047
+ function returnCalendar( $utf8Encode=FALSE, $gzip=FALSE, $cdType=TRUE ) {
2048
+ $filename = $this->getConfig( 'filename' );
2049
+ $output = $this->createCalendar();
2050
+ if( $utf8Encode )
2051
+ $output = utf8_encode( $output );
2052
+ $fsize = null;
2053
+ if( $gzip ) {
2054
+ $output = gzencode( $output, 9 );
2055
+ $fsize = strlen( $output );
2056
+ header( self::$headers[0] );
2057
+ header( self::$headers[1] );
2058
+ }
2059
+ // else {
2060
+ // if( FALSE !== ( $temp = tempnam( sys_get_temp_dir(), 'iCr' ))) {
2061
+ // if( FALSE !== file_put_contents( $temp, $output ))
2062
+ // $fsize = @filesize( $temp );
2063
+ // unlink( $temp );
2064
+ // }
2065
+ // }
2066
+ if( ! empty( $fsize ))
2067
+ header( sprintf( self::$headers[2], $fsize ));
2068
+ if( 'xcal' == $this->format )
2069
+ header( self::$headers[3] );
2070
+ else
2071
+ header( self::$headers[4] );
2072
+ $cdType = ( $cdType ) ? 5 : 6;
2073
+ header( sprintf( self::$headers[$cdType], $filename ));
2074
+ header( self::$headers[7] );
2075
+ echo trim($output);
2076
+ return TRUE;
2077
+ }
2078
+ /**
2079
+ * save content in a file
2080
+ *
2081
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2082
+ * @since 2.21.5 - 2015-03-29
2083
+ * @uses vcalendar::createComponent()
2084
+ * @uses vcalendar::getConfig()
2085
+ * @return bool TRUE on success, FALSE on error
2086
+ */
2087
+ function saveCalendar() {
2088
+ $output = $this->createCalendar();
2089
+ if( FALSE === ( $dirfile = $this->getConfig( 'url' )))
2090
+ $dirfile = $this->getConfig( 'dirfile' );
2091
+ return ( FALSE === file_put_contents( $dirfile, $output, LOCK_EX )) ? FALSE : TRUE;
2092
+ }
2093
+ /**
2094
+ * if recent version of calendar file exists (default one hour), an HTTP redirect header is sent
2095
+ * else FALSE is returned
2096
+ *
2097
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2098
+ * @since 2.21.5 - 2015-03-29
2099
+ * @param int $timeout default 3600 sec
2100
+ * @param bool $cdType TRUE : Content-Disposition: attachment... (default), FALSE : ...inline...
2101
+ * @uses vcalendar::getConfig()
2102
+ * @uses vcalendar::$headers
2103
+ * @uses vcalendar::$format
2104
+ * @return bool TRUE on success, FALSE on error
2105
+ */
2106
+ function useCachedCalendar( $timeout=3600, $cdType=TRUE ) {
2107
+ if( FALSE === ( $dirfile = $this->getConfig( 'url' )))
2108
+ $dirfile = $this->getConfig( 'dirfile' );
2109
+ if( ! is_file( $dirfile ) || ! is_readable( $dirfile ))
2110
+ return FALSE;
2111
+ if( time() - filemtime( $dirfile ) > $timeout )
2112
+ return FALSE;
2113
+ clearstatcache();
2114
+ $dirfile = $this->getConfig( 'dirfile' );
2115
+ $fsize = @filesize( $dirfile );
2116
+ $filename = $this->getConfig( 'filename' );
2117
+ if( 'xcal' == $this->format )
2118
+ header( self::$headers[3] );
2119
+ else
2120
+ header( self::$headers[4] );
2121
+ if( ! empty( $fsize ))
2122
+ header( sprintf( self::$headers[2], $fsize ));
2123
+ $cdType = ( $cdType ) ? 5 : 6;
2124
+ header( sprintf( self::$headers[$cdType], $filename ));
2125
+ header( self::$headers[7] );
2126
+ if( FALSE === ( $fp = @fopen( $dirfile, 'r' )))
2127
+ return FALSE;
2128
+ fpassthru( $fp );
2129
+ fclose( $fp );
2130
+ return TRUE;
2131
+ }
2132
+ }
includes/libs/iCal/lib/vevent.class.php ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*********************************************************************************/
3
+ /**
4
+ *
5
+ * iCalcreator, a PHP rfc2445/rfc5545 solution.
6
+ *
7
+ * @copyright Copyright (c) 2007-2015 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
8
+ * @link http://kigkonsult.se/iCalcreator/index.php
9
+ * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
10
+ * @package iCalcreator
11
+ * @version 2.22
12
+ */
13
+ /**
14
+ * This library is free software; you can redistribute it and/or
15
+ * modify it under the terms of the GNU Lesser General Public
16
+ * License as published by the Free Software Foundation; either
17
+ * version 2.1 of the License, or (at your option) any later version.
18
+ *
19
+ * This library is distributed in the hope that it will be useful,
20
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
+ * Lesser General Public License for more details.
23
+ *
24
+ * You should have received a copy of the GNU Lesser General Public
25
+ * License along with this library; if not, write to the Free Software
26
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
+ */
28
+ /*********************************************************************************/
29
+ /**
30
+ * class for calendar component VEVENT
31
+ *
32
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
33
+ * @since 2.5.1 - 2008-10-12
34
+ */
35
+ class vevent extends calendarComponent {
36
+ /**
37
+ * @var array $attach component property value
38
+ * @var array $attendee component property value
39
+ * @var array $categories component property value
40
+ * @var array $comment component property value
41
+ * @var array $contact component property value
42
+ * @var array $class component property value
43
+ * @var array $created component property value
44
+ * @var array $description component property value
45
+ * @var array $dtend component property value
46
+ * @var array $dtstart component property value
47
+ * @var array $duration component property value
48
+ * @var array $exdate component property value
49
+ * @var array $exrule component property value
50
+ * @var array $geo component property value
51
+ * @var array $lastmodified component property value
52
+ * @var array $location component property value
53
+ * @var array $organizer component property value
54
+ * @var array $priority component property value
55
+ * @var array $rdate component property value
56
+ * @var array $recurrenceid component property value
57
+ * @var array $relatedto component property value
58
+ * @var array $requeststatus component property value
59
+ * @var array $resources component property value
60
+ * @var array $rrule component property value
61
+ * @var array $sequence component property value
62
+ * @var array $status component property value
63
+ * @var array $summary component property value
64
+ * @var array $transp component property value
65
+ * @var array $url component property value
66
+ * @access protected
67
+ */
68
+ protected $attach;
69
+ protected $attendee;
70
+ protected $categories;
71
+ protected $comment;
72
+ protected $contact;
73
+ protected $class;
74
+ protected $created;
75
+ protected $description;
76
+ protected $dtend;
77
+ protected $dtstart;
78
+ protected $duration;
79
+ protected $exdate;
80
+ protected $exrule;
81
+ protected $geo;
82
+ protected $lastmodified;
83
+ protected $location;
84
+ protected $organizer;
85
+ protected $priority;
86
+ protected $rdate;
87
+ protected $recurrenceid;
88
+ protected $relatedto;
89
+ protected $requeststatus;
90
+ protected $resources;
91
+ protected $rrule;
92
+ protected $sequence;
93
+ protected $status;
94
+ protected $summary;
95
+ protected $transp;
96
+ protected $url;
97
+ /**
98
+ * constructor for calendar component VEVENT object
99
+ *
100
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
101
+ * @since 2.8.2 - 2011-05-01
102
+ * @param array $config
103
+ * @uses vevent::calendarComponent()
104
+ * @uses vevent::$attach
105
+ * @uses vevent::$attendee
106
+ * @uses vevent::$categories
107
+ * @uses vevent::$class
108
+ * @uses vevent::$comment
109
+ * @uses vevent::$contact
110
+ * @uses vevent::$created
111
+ * @uses vevent::$description
112
+ * @uses vevent::$dtstart
113
+ * @uses vevent::$dtend
114
+ * @uses vevent::$duration
115
+ * @uses vevent::$exdate
116
+ * @uses vevent::$exrule
117
+ * @uses vevent::$geo
118
+ * @uses vevent::$lastmodified
119
+ * @uses vevent::$location
120
+ * @uses vevent::$organizer
121
+ * @uses vevent::$priority
122
+ * @uses vevent::$rdate
123
+ * @uses vevent::$recurrenceid
124
+ * @uses vevent::$relatedto
125
+ * @uses vevent::$requeststatus
126
+ * @uses vevent::$resources
127
+ * @uses vevent::$rrule
128
+ * @uses vevent::$sequence
129
+ * @uses vevent::$status
130
+ * @uses vevent::$summary
131
+ * @uses vevent::$transp
132
+ * @uses vevent::$url
133
+ * @uses vevent::$xprop
134
+ * @uses vevent::$components
135
+ * @uses calendarComponent::setConfig()
136
+ */
137
+ function __construct( $config = array()) {
138
+ $this->component();
139
+ $this->attach = '';
140
+ $this->attendee = '';
141
+ $this->categories = '';
142
+ $this->class = '';
143
+ $this->comment = '';
144
+ $this->contact = '';
145
+ $this->created = '';
146
+ $this->description = '';
147
+ $this->dtstart = '';
148
+ $this->dtend = '';
149
+ $this->duration = '';
150
+ $this->exdate = '';
151
+ $this->exrule = '';
152
+ $this->geo = '';
153
+ $this->lastmodified = '';
154
+ $this->location = '';
155
+ $this->organizer = '';
156
+ $this->priority = '';
157
+ $this->rdate = '';
158
+ $this->recurrenceid = '';
159
+ $this->relatedto = '';
160
+ $this->requeststatus = '';
161
+ $this->resources = '';
162
+ $this->rrule = '';
163
+ $this->sequence = '';
164
+ $this->status = '';
165
+ $this->summary = '';
166
+ $this->transp = '';
167
+ $this->url = '';
168
+ $this->xprop = '';
169
+ $this->components = array();
170
+ if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
171
+ $config['language'] = ICAL_LANG;
172
+ if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
173
+ if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
174
+ if( !isset( $config['format'] )) $config['format'] = 'iCal';
175
+ if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
176
+ $this->setConfig( $config );
177
+ }
178
+ /**
179
+ * create formatted output for calendar component VEVENT object instance
180
+ *
181
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
182
+ * @since 2.10.16 - 2011-10-28
183
+ * @param array $xcaldecl
184
+ * @uses calendarComponent::_createFormat()
185
+ * @uses calendarComponent::$componentStart1
186
+ * @uses calendarComponent::$componentStart2
187
+ * @uses calendarComponent::$nl;
188
+ * @uses calendarComponent::createUid()
189
+ * @uses calendarComponent::createDtstamp()
190
+ * @uses calendarComponent::createAttach()
191
+ * @uses calendarComponent::createAttendee()
192
+ * @uses calendarComponent::createCategories()
193
+ * @uses calendarComponent::createComment()
194
+ * @uses calendarComponent::createContact()
195
+ * @uses calendarComponent::createClass()
196
+ * @uses calendarComponent::createCreated()
197
+ * @uses calendarComponent::createDescription()
198
+ * @uses calendarComponent::createDtstart()
199
+ * @uses calendarComponent::createDtend()
200
+ * @uses calendarComponent::createDuration()
201
+ * @uses calendarComponent::createExdate()
202
+ * @uses calendarComponent::createExrule()
203
+ * @uses calendarComponent::createGeo()
204
+ * @uses calendarComponent::createLastModified()
205
+ * @uses calendarComponent::createLocation()
206
+ * @uses calendarComponent::createOrganizer()
207
+ * @uses calendarComponent::createPriority()
208
+ * @uses calendarComponent::createRdate()
209
+ * @uses calendarComponent::createRrule()
210
+ * @uses calendarComponent::createRelatedTo()
211
+ * @uses calendarComponent::createRequestStatus()
212
+ * @uses calendarComponent::createRecurrenceid()
213
+ * @uses calendarComponent::createResources()
214
+ * @uses calendarComponent::createSequence()
215
+ * @uses calendarComponent::createStatus()
216
+ * @uses calendarComponent::createSummary()
217
+ * @uses calendarComponent::createTransp()
218
+ * @uses calendarComponent::createUrl()
219
+ * @uses calendarComponent::createXprop()
220
+ * @uses calendarComponent::createSubComponent()
221
+ * @uses calendarComponent::$componentEnd1
222
+ * @uses calendarComponent::$componentEnd2
223
+ * @uses calendarComponent::$xcaldecl
224
+ * @return string
225
+ */
226
+ function createComponent( & $xcaldecl ) {
227
+ $objectname = $this->_createFormat();
228
+ $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
229
+ $component .= $this->createUid();
230
+ $component .= $this->createDtstamp();
231
+ $component .= $this->createAttach();
232
+ $component .= $this->createAttendee();
233
+ $component .= $this->createCategories();
234
+ $component .= $this->createComment();
235
+ $component .= $this->createContact();
236
+ $component .= $this->createClass();
237
+ $component .= $this->createCreated();
238
+ $component .= $this->createDescription();
239
+ $component .= $this->createDtstart();
240
+ $component .= $this->createDtend();
241
+ $component .= $this->createDuration();
242
+ $component .= $this->createExdate();
243
+ $component .= $this->createExrule();
244
+ $component .= $this->createGeo();
245
+ $component .= $this->createLastModified();
246
+ $component .= $this->createLocation();
247
+ $component .= $this->createOrganizer();
248
+ $component .= $this->createPriority();
249
+ $component .= $this->createRdate();
250
+ $component .= $this->createRrule();
251
+ $component .= $this->createRelatedTo();
252
+ $component .= $this->createRequestStatus();
253
+ $component .= $this->createRecurrenceid();
254
+ $component .= $this->createResources();
255
+ $component .= $this->createSequence();
256
+ $component .= $this->createStatus();
257
+ $component .= $this->createSummary();
258
+ $component .= $this->createTransp();
259
+ $component .= $this->createUrl();
260
+ $component .= $this->createXprop();
261
+ $component .= $this->createSubComponent();
262
+ $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
263
+ if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
264
+ foreach( $this->xcaldecl as $localxcaldecl )
265
+ $xcaldecl[] = $localxcaldecl;
266
+ }
267
+ return $component;
268
+ }
269
+ }
includes/libs/iCal/lib/vfreebusy.class.php ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*********************************************************************************/
3
+ /**
4
+ *
5
+ * iCalcreator, a PHP rfc2445/rfc5545 solution.
6
+ *
7
+ * @copyright Copyright (c) 2007-2015 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
8
+ * @link http://kigkonsult.se/iCalcreator/index.php
9
+ * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
10
+ * @package iCalcreator
11
+ * @version 2.22
12
+ */
13
+ /**
14
+ * This library is free software; you can redistribute it and/or
15
+ * modify it under the terms of the GNU Lesser General Public
16
+ * License as published by the Free Software Foundation; either
17
+ * version 2.1 of the License, or (at your option) any later version.
18
+ *
19
+ * This library is distributed in the hope that it will be useful,
20
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
+ * Lesser General Public License for more details.
23
+ *
24
+ * You should have received a copy of the GNU Lesser General Public
25
+ * License along with this library; if not, write to the Free Software
26
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
+ */
28
+ /*********************************************************************************/
29
+ /**
30
+ * class for calendar component VFREEBUSY
31
+ *
32
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
33
+ * @since 2.5.1 - 2008-10-12
34
+ */
35
+ class vfreebusy extends calendarComponent {
36
+ /**
37
+ * @var array $attendee component property value
38
+ * @var array $comment component property value
39
+ * @var array $contact component property value
40
+ * @var array $dtend component property value
41
+ * @var array $dtstart component property value
42
+ * @var array $duration component property value
43
+ * @var array $freebusy component property value
44
+ * @var array $organizer component property value
45
+ * @var array $requeststatus component property value
46
+ * @var array $url component property value
47
+ * @access protected
48
+ */
49
+ protected $attendee;
50
+ protected $comment;
51
+ protected $contact;
52
+ protected $dtend;
53
+ protected $dtstart;
54
+ protected $duration;
55
+ protected $freebusy;
56
+ protected $organizer;
57
+ protected $requeststatus;
58
+ protected $url;
59
+ /**
60
+ * constructor for calendar component VFREEBUSY object
61
+ *
62
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
63
+ * @since 2.8.2 - 2011-05-01
64
+ * @param array $config
65
+ * @uses vjournal::calendarComponent()
66
+ * @uses vjournal::$attendee
67
+ * @uses vjournal::$comment
68
+ * @uses vjournal::$contact
69
+ * @uses vjournal::$dtend
70
+ * @uses vjournal::$dtstart
71
+ * @uses vjournal::$dtduration
72
+ * @uses vjournal::$organizer
73
+ * @uses vjournal::$requeststatus
74
+ * @uses vjournal::$url
75
+ * @uses vjournal::$xprop
76
+ * @uses calendarComponent::setConfig()
77
+ */
78
+ function __construct( $config = array()) {
79
+ $this->calendarComponent();
80
+ $this->attendee = '';
81
+ $this->comment = '';
82
+ $this->contact = '';
83
+ $this->dtend = '';
84
+ $this->dtstart = '';
85
+ $this->duration = '';
86
+ $this->freebusy = '';
87
+ $this->organizer = '';
88
+ $this->requeststatus = '';
89
+ $this->url = '';
90
+ $this->xprop = '';
91
+ if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
92
+ $config['language'] = ICAL_LANG;
93
+ if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
94
+ if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
95
+ if( !isset( $config['format'] )) $config['format'] = 'iCal';
96
+ if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
97
+ $this->setConfig( $config );
98
+ }
99
+ /**
100
+ * create formatted output for calendar component VFREEBUSY object instance
101
+ *
102
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
103
+ * @since 2.3.1 - 2007-11-19
104
+ * @param array $xcaldecl
105
+ * @uses calendarComponent::_createFormat()
106
+ * @uses calendarComponent::createUid()
107
+ * @uses calendarComponent::createDtstamp()
108
+ * @uses calendarComponent::createAttendee()
109
+ * @uses calendarComponent::createComment()
110
+ * @uses calendarComponent::createContact()
111
+ * @uses calendarComponent::createDtstart()
112
+ * @uses calendarComponent::createDtend()
113
+ * @uses calendarComponent::createDuration()
114
+ * @uses calendarComponent::createFreebusy()
115
+ * @uses calendarComponent::createOrganizer()
116
+ * @uses calendarComponent::createRequestStatus()
117
+ * @uses calendarComponent::createUrl()
118
+ * @uses calendarComponent::createXprop()
119
+ * @uses calendarComponent::createUrl()
120
+ * @uses calendarComponent::createXprop()
121
+ * @uses calendarComponent::$componentEnd1
122
+ * @uses calendarComponent::$componentEnd2
123
+ * @uses calendarComponent::$xcaldecl
124
+ * @return string
125
+ */
126
+ function createComponent( &$xcaldecl ) {
127
+ $objectname = $this->_createFormat();
128
+ $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
129
+ $component .= $this->createUid();
130
+ $component .= $this->createDtstamp();
131
+ $component .= $this->createAttendee();
132
+ $component .= $this->createComment();
133
+ $component .= $this->createContact();
134
+ $component .= $this->createDtstart();
135
+ $component .= $this->createDtend();
136
+ $component .= $this->createDuration();
137
+ $component .= $this->createFreebusy();
138
+ $component .= $this->createOrganizer();
139
+ $component .= $this->createRequestStatus();
140
+ $component .= $this->createUrl();
141
+ $component .= $this->createXprop();
142
+ $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
143
+ if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
144
+ foreach( $this->xcaldecl as $localxcaldecl )
145
+ $xcaldecl[] = $localxcaldecl;
146
+ }
147
+ return $component;
148
+ }
149
+ }
includes/libs/iCal/lib/vjournal.class.php ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*********************************************************************************/
3
+ /**
4
+ *
5
+ * iCalcreator, a PHP rfc2445/rfc5545 solution.
6
+ *
7
+ * @copyright Copyright (c) 2007-2015 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
8
+ * @link http://kigkonsult.se/iCalcreator/index.php
9
+ * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
10
+ * @package iCalcreator
11
+ * @version 2.22
12
+ */
13
+ /**
14
+ * This library is free software; you can redistribute it and/or
15
+ * modify it under the terms of the GNU Lesser General Public
16
+ * License as published by the Free Software Foundation; either
17
+ * version 2.1 of the License, or (at your option) any later version.
18
+ *
19
+ * This library is distributed in the hope that it will be useful,
20
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
+ * Lesser General Public License for more details.
23
+ *
24
+ * You should have received a copy of the GNU Lesser General Public
25
+ * License along with this library; if not, write to the Free Software
26
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
+ */
28
+ /*********************************************************************************/
29
+ /**
30
+ * class for calendar component VJOURNAL
31
+ *
32
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
33
+ * @since 2.5.1 - 2008-10-12
34
+ */
35
+ class vjournal extends calendarComponent {
36
+ /**
37
+ * @var array $attach component property value
38
+ * @var array $attendee component property value
39
+ * @var array $categories component property value
40
+ * @var array $comment component property value
41
+ * @var array $contact component property value
42
+ * @var array $class component property value
43
+ * @var array $created component property value
44
+ * @var array $description component property value
45
+ * @var array $dtstart component property value
46
+ * @var array $exdate component property value
47
+ * @var array $exrule component property value
48
+ * @var array $lastmodified component property value
49
+ * @var array $organizer component property value
50
+ * @var array $rdate component property value
51
+ * @var array $recurrenceid component property value
52
+ * @var array $relatedto component property value
53
+ * @var array $requeststatus component property value
54
+ * @var array $rrule component property value
55
+ * @var array $sequence component property value
56
+ * @var array $status component property value
57
+ * @var array $summary component property value
58
+ * @var array $url component property value
59
+ * @access protected
60
+ */
61
+ protected $attach;
62
+ protected $attendee;
63
+ protected $categories;
64
+ protected $comment;
65
+ protected $contact;
66
+ protected $class;
67
+ protected $created;
68
+ protected $description;
69
+ protected $dtstart;
70
+ protected $exdate;
71
+ protected $exrule;
72
+ protected $lastmodified;
73
+ protected $organizer;
74
+ protected $rdate;
75
+ protected $recurrenceid;
76
+ protected $relatedto;
77
+ protected $requeststatus;
78
+ protected $rrule;
79
+ protected $sequence;
80
+ protected $status;
81
+ protected $summary;
82
+ protected $url;
83
+ /**
84
+ * constructor for calendar component VJOURNAL object
85
+ *
86
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
87
+ * @since 2.8.2 - 2011-05-01
88
+ * @param array $config
89
+ * @uses vjournal::calendarComponent()
90
+ * @uses vjournal::$attach
91
+ * @uses vjournal::$attendee
92
+ * @uses vjournal::$categories
93
+ * @uses vjournal::$class
94
+ * @uses vjournal::$comment
95
+ * @uses vjournal::$contact
96
+ * @uses vjournal::$created
97
+ * @uses vjournal::$description
98
+ * @uses vjournal::$dtstart
99
+ * @uses vjournal::$exdate
100
+ * @uses vjournal::$exrule
101
+ * @uses vjournal::$lastmodified
102
+ * @uses vjournal::$organizer
103
+ * @uses vjournal::$rdate
104
+ * @uses vjournal::$recurrenceid
105
+ * @uses vjournal::$relatedto
106
+ * @uses vjournal::$requeststatus
107
+ * @uses vjournal::$rrule
108
+ * @uses vjournal::$sequence
109
+ * @uses vjournal::$status
110
+ * @uses vjournal::$summary
111
+ * @uses vjournal::$url
112
+ * @uses vjournal::$xprop
113
+ * @uses calendarComponent::setConfig()
114
+ */
115
+ function __construct( $config = array()) {
116
+ $this->calendarComponent();
117
+ $this->attach = '';
118
+ $this->attendee = '';
119
+ $this->categories = '';
120
+ $this->class = '';
121
+ $this->comment = '';
122
+ $this->contact = '';
123
+ $this->created = '';
124
+ $this->description = '';
125
+ $this->dtstart = '';
126
+ $this->exdate = '';
127
+ $this->exrule = '';
128
+ $this->lastmodified = '';
129
+ $this->organizer = '';
130
+ $this->rdate = '';
131
+ $this->recurrenceid = '';
132
+ $this->relatedto = '';
133
+ $this->requeststatus = '';
134
+ $this->rrule = '';
135
+ $this->sequence = '';
136
+ $this->status = '';
137
+ $this->summary = '';
138
+ $this->url = '';
139
+ $this->xprop = '';
140
+ if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
141
+ $config['language'] = ICAL_LANG;
142
+ if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
143
+ if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
144
+ if( !isset( $config['format'] )) $config['format'] = 'iCal';
145
+ if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
146
+ $this->setConfig( $config );
147
+ }
148
+ /**
149
+ * create formatted output for calendar component VJOURNAL object instance
150
+ *
151
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
152
+ * @since 2.5.1 - 2008-10-12
153
+ * @param array $xcaldecl
154
+ * @uses calendarComponent::_createFormat()
155
+ * @uses calendarComponent::$componentStart1
156
+ * @uses calendarComponent::$componentStart2
157
+ * @uses calendarComponent::$nl
158
+ * @uses calendarComponent::createUid()
159
+ * @uses calendarComponent::createDtstamp()
160
+ * @uses calendarComponent::createAttach()
161
+ * @uses calendarComponent::createAttendee()
162
+ * @uses calendarComponent::createCategories()
163
+ * @uses calendarComponent::createClass()
164
+ * @uses calendarComponent::createComment()
165
+ * @uses calendarComponent::createContact()
166
+ * @uses calendarComponent::createDescription()
167
+ * @uses calendarComponent::createDtstart()
168
+ * @uses calendarComponent::createExdate()
169
+ * @uses calendarComponent::createExrule()
170
+ * @uses calendarComponent::createLastModified()
171
+ * @uses calendarComponent::createOrganizer()
172
+ * @uses calendarComponent::createRdate()
173
+ * @uses calendarComponent::createRelatedTo()
174
+ * @uses calendarComponent::createRequestStatus()
175
+ * @uses calendarComponent::createRecurrenceid()
176
+ * @uses calendarComponent::createRrule()
177
+ * @uses calendarComponent::createSequence()
178
+ * @uses calendarComponent::createStatus()
179
+ * @uses calendarComponent::createSummary()
180
+ * @uses calendarComponent::createUrl()
181
+ * @uses calendarComponent::createXprop()
182
+ * @uses calendarComponent::$componentEnd1
183
+ * @uses calendarComponent::$componentEnd2
184
+ * @uses calendarComponent::$xcaldecl
185
+ * @return string
186
+ */
187
+ function createComponent( &$xcaldecl ) {
188
+ $objectname = $this->_createFormat();
189
+ $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
190
+ $component .= $this->createUid();
191
+ $component .= $this->createDtstamp();
192
+ $component .= $this->createAttach();
193
+ $component .= $this->createAttendee();
194
+ $component .= $this->createCategories();
195
+ $component .= $this->createClass();
196
+ $component .= $this->createComment();
197
+ $component .= $this->createContact();
198
+ $component .= $this->createCreated();
199
+ $component .= $this->createDescription();
200
+ $component .= $this->createDtstart();
201
+ $component .= $this->createExdate();
202
+ $component .= $this->createExrule();
203
+ $component .= $this->createLastModified();
204
+ $component .= $this->createOrganizer();
205
+ $component .= $this->createRdate();
206
+ $component .= $this->createRequestStatus();
207
+ $component .= $this->createRecurrenceid();
208
+ $component .= $this->createRelatedTo();
209
+ $component .= $this->createRrule();
210
+ $component .= $this->createSequence();
211
+ $component .= $this->createStatus();
212
+ $component .= $this->createSummary();
213
+ $component .= $this->createUrl();
214
+ $component .= $this->createXprop();
215
+ $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
216
+ if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
217
+ foreach( $this->xcaldecl as $localxcaldecl )
218
+ $xcaldecl[] = $localxcaldecl;
219
+ }
220
+ return $component;
221
+ }
222
+ }
includes/libs/iCal/lib/vtimezone.class.php ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*********************************************************************************/
3
+ /**
4
+ *
5
+ * iCalcreator, a PHP rfc2445/rfc5545 solution.
6
+ *
7
+ * @copyright Copyright (c) 2007-2015 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
8
+ * @link http://kigkonsult.se/iCalcreator/index.php
9
+ * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
10
+ * @package iCalcreator
11
+ * @version 2.22
12
+ */
13
+ /**
14
+ * This library is free software; you can redistribute it and/or
15
+ * modify it under the terms of the GNU Lesser General Public
16
+ * License as published by the Free Software Foundation; either
17
+ * version 2.1 of the License, or (at your option) any later version.
18
+ *
19
+ * This library is distributed in the hope that it will be useful,
20
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
+ * Lesser General Public License for more details.
23
+ *
24
+ * You should have received a copy of the GNU Lesser General Public
25
+ * License along with this library; if not, write to the Free Software
26
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
+ */
28
+ /*********************************************************************************/
29
+ /**
30
+ * class for calendar component VTIMEZONE
31
+ *
32
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
33
+ * @since 2.5.1 - 2008-10-12
34
+ */
35
+ class vtimezone extends calendarComponent {
36
+ /** @var array $timezonetype component property value */
37
+ public $timezonetype;
38
+ /**
39
+ * @var array $comment component property value
40
+ * @var array $dtstart component property value
41
+ * @var array $lastmodified component property value
42
+ * @var array $rdate component property value
43
+ * @var array $rrule component property value
44
+ * @var array $tzid component property value
45
+ * @var array $tzname component property value
46
+ * @var array $tzoffsetfrom component property value
47
+ * @var array $tzoffsetto component property value
48
+ * @var array $tzurl component property value
49
+ * @access protected
50
+ */
51
+ protected $comment;
52
+ protected $dtstart;
53
+ protected $lastmodified;
54
+ protected $rdate;
55
+ protected $rrule;
56
+ protected $tzid;
57
+ protected $tzname;
58
+ protected $tzoffsetfrom;
59
+ protected $tzoffsetto;
60
+ protected $tzurl;
61
+ /**
62
+ * constructor for calendar component VTIMEZONE object
63
+ *
64
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
65
+ * @since 2.8.2 - 2011-05-01
66
+ * @param mixed $timezonetype default FALSE ( STANDARD / DAYLIGHT )
67
+ * @param array $config
68
+ * @uses vtimezone::$timezonetype
69
+ * @uses vtimezone::calendarComponent()
70
+ * @uses vtimezone::$comment
71
+ * @uses vtimezone::$dtstart
72
+ * @uses vtimezone::$lastmodified
73
+ * @uses vtimezone::$rdate
74
+ * @uses vtimezone::$rrule
75
+ * @uses vtimezone::$tzid
76
+ * @uses vtimezone::$tzname
77
+ * @uses vtimezone::$tzoffsetfrom
78
+ * @uses vtimezone::$tzoffsetto
79
+ * @uses vtimezone::$tzurl
80
+ * @uses vtimezone::$xprop
81
+ * @uses vtimezone::$components
82
+ * @uses calendarComponent::setConfig()
83
+ */
84
+ function __construct( $timezonetype=FALSE, $config = array()) {
85
+ if( is_array( $timezonetype )) {
86
+ $config = $timezonetype;
87
+ $timezonetype = FALSE;
88
+ }
89
+ if( !$timezonetype )
90
+ $this->timezonetype = 'VTIMEZONE';
91
+ else
92
+ $this->timezonetype = strtoupper( $timezonetype );
93
+ $this->component();
94
+ $this->comment = '';
95
+ $this->dtstart = '';
96
+ $this->lastmodified = '';
97
+ $this->rdate = '';
98
+ $this->rrule = '';
99
+ $this->tzid = '';
100
+ $this->tzname = '';
101
+ $this->tzoffsetfrom = '';
102
+ $this->tzoffsetto = '';
103
+ $this->tzurl = '';
104
+ $this->xprop = '';
105
+ $this->components = array();
106
+ if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
107
+ $config['language'] = ICAL_LANG;
108
+ if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
109
+ if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
110
+ if( !isset( $config['format'] )) $config['format'] = 'iCal';
111
+ if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
112
+ $this->setConfig( $config );
113
+ }
114
+ /**
115
+ * create formatted output for calendar component VTIMEZONE object instance
116
+ *
117
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
118
+ * @since 2.5.1 - 2008-10-25
119
+ * @param array $xcaldecl
120
+ * @uses calendarComponent::_createFormat()
121
+ * @uses calendarComponent::$componentStart1
122
+ * @uses calendarComponent::$componentStart2
123
+ * @uses calendarComponent::$nl
124
+ * @uses calendarComponent::createTzid()
125
+ * @uses calendarComponent::createLastModified()
126
+ * @uses calendarComponent::createTzurl()
127
+ * @uses calendarComponent::createDtstart()
128
+ * @uses calendarComponent::createTzoffsetfrom()
129
+ * @uses calendarComponent::createTzoffsetto()
130
+ * @uses calendarComponent::createComment()
131
+ * @uses calendarComponent::createRdate()
132
+ * @uses calendarComponent::createRrule()
133
+ * @uses calendarComponent::createTzname()
134
+ * @uses calendarComponent::createXprop()
135
+ * @uses calendarComponent::createSubComponent()
136
+ * @uses calendarComponent::createXprop()
137
+ * @uses calendarComponent::$componentEnd1
138
+ * @uses calendarComponent::$componentEnd2
139
+ * @uses calendarComponent::$xcaldecl
140
+ * @return string
141
+ */
142
+ function createComponent( &$xcaldecl )
143
+ {
144
+ $objectname = $this->_createFormat();
145
+ $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
146
+ $component .= $this->createTzid();
147
+ $component .= $this->createLastModified();
148
+ $component .= $this->createTzurl();
149
+ $component .= $this->createDtstart();
150
+ $component .= $this->createTzoffsetfrom();
151
+ $component .= $this->createTzoffsetto();
152
+ $component .= $this->createComment();
153
+ $component .= $this->createRdate();
154
+ $component .= $this->createRrule();
155
+ $component .= $this->createTzname();
156
+ $component .= $this->createXprop();
157
+ $component .= $this->createSubComponent();
158
+ $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
159
+ if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
160
+ foreach( $this->xcaldecl as $localxcaldecl )
161
+ $xcaldecl[] = $localxcaldecl;
162
+ }
163
+ return $component;
164
+ }
165
+ }
includes/libs/iCal/lib/vtodo.class.php ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*********************************************************************************/
3
+ /**
4
+ *
5
+ * A PHP implementation of rfc2445/rfc5545.
6
+ *
7
+ * @copyright Copyright (c) 2007-2015 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
8
+ * @link http://kigkonsult.se/iCalcreator/index.php
9
+ * @license http://kigkonsult.se/downloads/dl.php?f=LGPL
10
+ * @package iCalcreator
11
+ * @version 2.22
12
+ */
13
+ /**
14
+ * This library is free software; you can redistribute it and/or
15
+ * modify it under the terms of the GNU Lesser General Public
16
+ * License as published by the Free Software Foundation; either
17
+ * version 2.1 of the License, or (at your option) any later version.
18
+ *
19
+ * This library is distributed in the hope that it will be useful,
20
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
+ * Lesser General Public License for more details.
23
+ *
24
+ * You should have received a copy of the GNU Lesser General Public
25
+ * License along with this library; if not, write to the Free Software
26
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
+ */
28
+ /*********************************************************************************/
29
+ /**
30
+ * class for calendar component VTODO
31
+ *
32
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
33
+ * @since 2.5.1 - 2008-10-12
34
+ */
35
+ class vtodo extends calendarComponent {
36
+ /**
37
+ * @var array $attach component property value
38
+ * @var array $attendee component property value
39
+ * @var array $categories component property value
40
+ * @var array $comment component property value
41
+ * @var array $completed component property value
42
+ * @var array $contact component property value
43
+ * @var array $class component property value
44
+ * @var array $created component property value
45
+ * @var array $description component property value
46
+ * @var array $dtstart component property value
47
+ * @var array $due component property value
48
+ * @var array $duration component property value
49
+ * @var array $exdate component property value
50
+ * @var array $exrule component property value
51
+ * @var array $geo component property value
52
+ * @var array $lastmodified component property value
53
+ * @var array $location component property value
54
+ * @var array $organizer component property value
55
+ * @var array $percentcomplete component property value
56
+ * @var array $priority component property value
57
+ * @var array $rdate component property value
58
+ * @var array $recurrenceid component property value
59
+ * @var array $relatedto component property value
60
+ * @var array $requeststatus component property value
61
+ * @var array $resources component property value
62
+ * @var array $rrule component property value
63
+ * @var array $sequence component property value
64
+ * @var array $status component property value
65
+ * @var array $summary component property value
66
+ * @var arrayr $url;
67
+ * @access protected
68
+ */
69
+ protected $attach;
70
+ protected $attendee;
71
+ protected $categories;
72
+ protected $comment;
73
+ protected $completed;
74
+ protected $contact;
75
+ protected $class;
76
+ protected $created;
77
+ protected $description;
78
+ protected $dtstart;
79
+ protected $due;
80
+ protected $duration;
81
+ protected $exdate;
82
+ protected $exrule;
83
+ protected $geo;
84
+ protected $lastmodified;
85
+ protected $location;
86
+ protected $organizer;
87
+ protected $percentcomplete;
88
+ protected $priority;
89
+ protected $rdate;
90
+ protected $recurrenceid;
91
+ protected $relatedto;
92
+ protected $requeststatus;
93
+ protected $resources;
94
+ protected $rrule;
95
+ protected $sequence;
96
+ protected $status;
97
+ protected $summary;
98
+ protected $url;
99
+ /**
100
+ * constructor for calendar component VTODO object
101
+ *
102
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
103
+ * @since 2.8.2 - 2011-05-01
104
+ * @param array $config
105
+ * @uses vtodo::calendarComponent()
106
+ * @uses vtodo::$attach
107
+ * @uses vtodo::$attendee
108
+ * @uses vtodo::$categories
109
+ * @uses vtodo::$class
110
+ * @uses vtodo::$comment
111
+ * @uses vtodo::$completed
112
+ * @uses vtodo::$contact
113
+ * @uses vtodo::$created
114
+ * @uses vtodo::$description
115
+ * @uses vtodo::$dtstart
116
+ * @uses vtodo::$due
117
+ * @uses vtodo::$duration
118
+ * @uses vtodo::$exdate
119
+ * @uses vtodo::$exrule
120
+ * @uses vtodo::$geo
121
+ * @uses vtodo::$lastmodified
122
+ * @uses vtodo::$location
123
+ * @uses vtodo::$organizer
124
+ * @uses vtodo::$percentcomplete
125
+ * @uses vtodo::$priority
126
+ * @uses vtodo::$rdate
127
+ * @uses vtodo::$recurrenceid
128
+ * @uses vtodo::$relatedto
129
+ * @uses vtodo::$requeststatus
130
+ * @uses vtodo::$resources
131
+ * @uses vtodo::$rrule
132
+ * @uses vtodo::$sequence
133
+ * @uses vtodo::$status
134
+ * @uses vtodo::$summary
135
+ * @uses vtodo::$url
136
+ * @uses vtodo::$xprop
137
+ * @uses vtodo::$components
138
+ * @uses calendarComponent::setConfig()
139
+ */
140
+ function __construct( $config = array()) {
141
+ $this->calendarComponent();
142
+ $this->attach = '';
143
+ $this->attendee = '';
144
+ $this->categories = '';
145
+ $this->class = '';
146
+ $this->comment = '';
147
+ $this->completed = '';
148
+ $this->contact = '';
149
+ $this->created = '';
150
+ $this->description = '';
151
+ $this->dtstart = '';
152
+ $this->due = '';
153
+ $this->duration = '';
154
+ $this->exdate = '';
155
+ $this->exrule = '';
156
+ $this->geo = '';
157
+ $this->lastmodified = '';
158
+ $this->location = '';
159
+ $this->organizer = '';
160
+ $this->percentcomplete = '';
161
+ $this->priority = '';
162
+ $this->rdate = '';
163
+ $this->recurrenceid = '';
164
+ $this->relatedto = '';
165
+ $this->requeststatus = '';
166
+ $this->resources = '';
167
+ $this->rrule = '';
168
+ $this->sequence = '';
169
+ $this->status = '';
170
+ $this->summary = '';
171
+ $this->url = '';
172
+ $this->xprop = '';
173
+ $this->components = array();
174
+ if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
175
+ $config['language'] = ICAL_LANG;
176
+ if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
177
+ if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
178
+ if( !isset( $config['format'] )) $config['format'] = 'iCal';
179
+ if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
180
+ $this->setConfig( $config );
181
+ }
182
+ /**
183
+ * create formatted output for calendar component VTODO object instance
184
+ *
185
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
186
+ * @since 2.5.1 - 2008-11-07
187
+ * @param array $xcaldecl
188
+ * @uses calendarComponent::_createFormat()
189
+ * @uses calendarComponent::$componentStart1
190
+ * @uses calendarComponent::$componentStart2
191
+ * @uses calendarComponent::$nl;
192
+ * @uses calendarComponent::createUid()
193
+ * @uses calendarComponent::createDtstamp()
194
+ * @uses calendarComponent::createAttach()
195
+ * @uses calendarComponent::createAttendee()
196
+ * @uses calendarComponent::createCategories()
197
+ * @uses calendarComponent::createClass()
198
+ * @uses calendarComponent::createComment()
199
+ * @uses calendarComponent::createCompleted()
200
+ * @uses calendarComponent::createContact()
201
+ * @uses calendarComponent::createDescription()
202
+ * @uses calendarComponent::createDtstart()
203
+ * @uses calendarComponent::createDtend()
204
+ * @uses calendarComponent::createDuration()
205
+ * @uses calendarComponent::createExdate()
206
+ * @uses calendarComponent::createExrule()
207
+ * @uses calendarComponent::crea