All-in-One Event Calendar - Version 1.0

Version Description

Download this release

Release Info

Developer theseed
Plugin Icon 128x128 All-in-One Event Calendar
Version 1.0
Comparing to
See all releases

Version 1.0

Files changed (123) hide show
  1. all-in-one-events-calendar.php +215 -0
  2. app/controller/class-ai1ec-app-controller.php +385 -0
  3. app/controller/class-ai1ec-calendar-controller.php +484 -0
  4. app/controller/class-ai1ec-events-controller.php +790 -0
  5. app/controller/class-ai1ec-exporter-controller.php +82 -0
  6. app/controller/class-ai1ec-importer-controller.php +201 -0
  7. app/controller/class-ai1ec-settings-controller.php +325 -0
  8. app/exception/class-ai1ec-event-not-found.php +18 -0
  9. app/exception/class-ai1ec-file-not-found.php +18 -0
  10. app/exception/class-ai1ec-file-not-provided.php +18 -0
  11. app/exception/class-ai1ec-invalid-argument.php +18 -0
  12. app/helper/class-ai1ec-app-helper.php +636 -0
  13. app/helper/class-ai1ec-calendar-helper.php +542 -0
  14. app/helper/class-ai1ec-events-helper.php +895 -0
  15. app/helper/class-ai1ec-exporter-helper.php +130 -0
  16. app/helper/class-ai1ec-importer-helper.php +233 -0
  17. app/helper/class-ai1ec-settings-helper.php +329 -0
  18. app/helper/class-ai1ec-view-helper.php +182 -0
  19. app/model/class-ai1ec-event.php +794 -0
  20. app/model/class-ai1ec-exporter.php +29 -0
  21. app/model/class-ai1ec-importer.php +30 -0
  22. app/model/class-ai1ec-settings.php +288 -0
  23. app/view/admin_notices.php +3 -0
  24. app/view/agenda.php +119 -0
  25. app/view/box_event_contact.php +35 -0
  26. app/view/box_event_cost.php +15 -0
  27. app/view/box_event_location.php +42 -0
  28. app/view/box_eventbrite.php +116 -0
  29. app/view/box_general_settings.php +37 -0
  30. app/view/box_ics_import_settings.php +26 -0
  31. app/view/box_publish_button.php +5 -0
  32. app/view/box_the_seed_studio.php +41 -0
  33. app/view/box_time_and_date.php +83 -0
  34. app/view/calendar.php +109 -0
  35. app/view/donate_button.php +6 -0
  36. app/view/event-excerpt.php +9 -0
  37. app/view/event-map.php +8 -0
  38. app/view/event-multi.php +56 -0
  39. app/view/event-single-footer.php +13 -0
  40. app/view/event-single.php +63 -0
  41. app/view/event_categories-color_picker.php +27 -0
  42. app/view/feed_row.php +23 -0
  43. app/view/import.php +14 -0
  44. app/view/month.php +84 -0
  45. app/view/save_successful.php +7 -0
  46. app/view/settings.php +24 -0
  47. app/view/subscription_button.php +6 -0
  48. css/add_new_event.css +1 -0
  49. css/calendar.css +1 -0
  50. css/colorpicker.css +1 -0
  51. css/event.css +1 -0
  52. css/general.css +1 -0
  53. css/jquery.autocomplete.css +1 -0
  54. css/selector.css +1 -0
  55. css/settings.css +1 -0
  56. img/agenda-view.png +0 -0
  57. img/ajax-loader.gif +0 -0
  58. img/blank.gif +0 -0
  59. img/color-picker.png +0 -0
  60. img/colorpicker_background.png +0 -0
  61. img/colorpicker_hex.png +0 -0
  62. img/colorpicker_hsb_b.png +0 -0
  63. img/colorpicker_hsb_h.png +0 -0
  64. img/colorpicker_hsb_s.png +0 -0
  65. img/colorpicker_indic.gif +0 -0
  66. img/colorpicker_overlay.png +0 -0
  67. img/colorpicker_rgb_b.png +0 -0
  68. img/colorpicker_rgb_g.png +0 -0
  69. img/colorpicker_rgb_r.png +0 -0
  70. img/colorpicker_select.gif +0 -0
  71. img/colorpicker_submit.png +0 -0
  72. img/custom_background.png +0 -0
  73. img/custom_hex.png +0 -0
  74. img/custom_hsb_b.png +0 -0
  75. img/custom_hsb_h.png +0 -0
  76. img/custom_hsb_s.png +0 -0
  77. img/custom_indic.gif +0 -0
  78. img/custom_rgb_b.png +0 -0
  79. img/custom_rgb_g.png +0 -0
  80. img/custom_rgb_r.png +0 -0
  81. img/custom_submit.png +0 -0
  82. img/google-calendar.png +0 -0
  83. img/ics-icon.png +0 -0
  84. img/indicator.gif +0 -0
  85. img/month-view.png +0 -0
  86. img/slider.png +0 -0
  87. js/add_new_event.js +1 -0
  88. js/calendar.js +1 -0
  89. js/colorpicker.js +1 -0
  90. js/element-selector.js +1 -0
  91. js/event.js +1 -0
  92. js/geo_autocomplete.js +1 -0
  93. js/jquery.autocomplete_geomod.js +1 -0
  94. js/jquery.calendrical.js +1 -0
  95. js/jquery.inputdate.js +1 -0
  96. js/jquery.scrollTo-min.js +1 -0
  97. js/jquery.timespan.js +1 -0
  98. js/settings.js +1 -0
  99. lib/SG_iCal.php +126 -0
  100. lib/blocks/SG_iCal_VCalendar.php +64 -0
  101. lib/blocks/SG_iCal_VEvent.php +292 -0
  102. lib/blocks/SG_iCal_VTimeZone.php +95 -0
  103. lib/helpers/SG_iCal_Duration.php +56 -0
  104. lib/helpers/SG_iCal_Factory.php +42 -0
  105. lib/helpers/SG_iCal_Freq.php +554 -0
  106. lib/helpers/SG_iCal_Line.php +165 -0
  107. lib/helpers/SG_iCal_Parser.php +197 -0
  108. lib/helpers/SG_iCal_Query.php +84 -0
  109. lib/helpers/SG_iCal_Recurrence.php +221 -0
  110. lib/iCalUtilityFunctions.class.php +1505 -0
  111. lib/iCalcreator.class.php +6897 -0
  112. lib/lgpl.txt +504 -0
  113. readme.txt +69 -0
  114. screenshot-1.png +0 -0
  115. screenshot-10.png +0 -0
  116. screenshot-2.png +0 -0
  117. screenshot-3.png +0 -0
  118. screenshot-4.png +0 -0
  119. screenshot-5.png +0 -0
  120. screenshot-6.png +0 -0
  121. screenshot-7.png +0 -0
  122. screenshot-8.png +0 -0
  123. screenshot-9.png +0 -0
all-in-one-events-calendar.php ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin Name: All-in-One Events Calendar Plugin
4
+ * Plugin URI: http://theseedstudio.com/software/all-in-one-events-calendar-wordpress/
5
+ * Description: An events calendar system with month and agenda calendar views, color-coded categories, recurring events, and import/export of iCalendar (.ics) feeds.
6
+ * Version: 1.0
7
+ * Author: The Seed Studio
8
+ * Author URI: http://theseedstudio.com/
9
+ */
10
+ @set_time_limit( 0 );
11
+ @ini_set( "memory_limit", "256M" );
12
+ @ini_set( "max_input_time", "-1" );
13
+
14
+ // ===============
15
+ // = Plugin Name =
16
+ // ===============
17
+ define( 'AI1EC_PLUGIN_NAME', basename( dirname( __FILE__ ) ) );
18
+
19
+ // ====================
20
+ // = Database Version =
21
+ // ====================
22
+ define( 'AI1EC_DB_VERSION', 107 );
23
+
24
+ // ================
25
+ // = Cron Version =
26
+ // ================
27
+ define( 'AI1EC_CRON_VERSION', 102 );
28
+
29
+ // ===============
30
+ // = Plugin Path =
31
+ // ===============
32
+ define( 'AI1EC_PATH', dirname( __FILE__ ) );
33
+
34
+ // ===============
35
+ // = Images Path =
36
+ // ===============
37
+ define( 'AI1EC_IMAGE_PATH', AI1EC_PATH . '/img' );
38
+
39
+ // ============
40
+ // = CSS Path =
41
+ // ============
42
+ define( 'AI1EC_CSS_PATH', AI1EC_PATH . '/css' );
43
+
44
+ // ===========
45
+ // = JS Path =
46
+ // ===========
47
+ define( 'AI1EC_JS_PATH', AI1EC_PATH . '/js' );
48
+
49
+ // ============
50
+ // = Lib Path =
51
+ // ============
52
+ define( 'AI1EC_LIB_PATH', AI1EC_PATH . '/lib' );
53
+
54
+ // ============
55
+ // = App Path =
56
+ // ============
57
+ define( 'AI1EC_APP_PATH', AI1EC_PATH . '/app' );
58
+
59
+ // ===================
60
+ // = Controller Path =
61
+ // ===================
62
+ define( 'AI1EC_CONTROLLER_PATH', AI1EC_APP_PATH . '/controller' );
63
+
64
+ // ==============
65
+ // = Model Path =
66
+ // ==============
67
+ define( 'AI1EC_MODEL_PATH', AI1EC_APP_PATH . '/model' );
68
+
69
+ // =============
70
+ // = View Path =
71
+ // =============
72
+ define( 'AI1EC_VIEW_PATH', AI1EC_APP_PATH . '/view' );
73
+
74
+ // ===============
75
+ // = Helper Path =
76
+ // ===============
77
+ define( 'AI1EC_HELPER_PATH', AI1EC_APP_PATH . '/helper' );
78
+
79
+ // ==================
80
+ // = Exception Path =
81
+ // ==================
82
+ define( 'AI1EC_EXCEPTION_PATH', AI1EC_APP_PATH . '/exception' );
83
+
84
+ // ==============
85
+ // = Plugin Url =
86
+ // ==============
87
+ define( 'AI1EC_URL', plugins_url( '', __FILE__ ) );
88
+
89
+ // ==============
90
+ // = Images URL =
91
+ // ==============
92
+ define( 'AI1EC_IMAGE_URL', AI1EC_URL . '/img' );
93
+
94
+ // ===========
95
+ // = CSS URL =
96
+ // ===========
97
+ define( 'AI1EC_CSS_URL', AI1EC_URL . '/css' );
98
+
99
+ // ==========
100
+ // = JS URL =
101
+ // ==========
102
+ define( 'AI1EC_JS_URL', AI1EC_URL . '/js' );
103
+
104
+ // =============
105
+ // = POST TYPE =
106
+ // =============
107
+ define( 'AI1EC_POST_TYPE', 'ai1ec_event' );
108
+
109
+ // ======================================
110
+ // = FAKE CATEGORY ID FOR CALENDAR PAGE =
111
+ // ======================================
112
+ define( 'AI1EC_FAKE_CATEGORY_ID', -4113473042 ); // Numeric-only 1337-speak of AI1EC_CALENDAR - ID must be numeric
113
+
114
+ // ==============
115
+ // = SCRIPT URL =
116
+ // ==============
117
+ $ai1ec_script_url = get_option( 'home' ) . '/?plugin=' . AI1EC_PLUGIN_NAME;
118
+ define( 'AI1EC_SCRIPT_URL', $ai1ec_script_url );
119
+
120
+ // ==================================================
121
+ // = Convert https/http to ical in AI1EC_SCRIPT_URL =
122
+ // ==================================================
123
+ $tmp = '';
124
+ if( strpos( AI1EC_SCRIPT_URL, 'https' ) === false ) {
125
+ $tmp = str_replace( 'http', 'webcal', AI1EC_SCRIPT_URL );
126
+ } else {
127
+ $tmp = str_replace( 'https', 'webcal', AI1EC_SCRIPT_URL );
128
+ }
129
+
130
+ // ==============
131
+ // = EXPORT URL =
132
+ // ==============
133
+ define( 'AI1EC_EXPORT_URL', $tmp . '&controller=ai1ec_exporter_controller&action=export_events' );
134
+
135
+ // ====================================
136
+ // = Include iCal parsers and helpers =
137
+ // ====================================
138
+ require_once( AI1EC_LIB_PATH . '/iCalcreator.class.php' );
139
+ require_once( AI1EC_LIB_PATH . '/iCalUtilityFunctions.class.php' );
140
+ require_once( AI1EC_LIB_PATH . '/SG_iCal.php' );
141
+ require_once( AI1EC_LIB_PATH . '/helpers/SG_iCal_Line.php' );
142
+ require_once( AI1EC_LIB_PATH . '/helpers/SG_iCal_Duration.php' );
143
+ require_once( AI1EC_LIB_PATH . '/helpers/SG_iCal_Freq.php' );
144
+ require_once( AI1EC_LIB_PATH . '/helpers/SG_iCal_Recurrence.php' );
145
+ require_once( AI1EC_LIB_PATH . '/helpers/SG_iCal_Parser.php' );
146
+ require_once( AI1EC_LIB_PATH . '/helpers/SG_iCal_Query.php' );
147
+ require_once( AI1EC_LIB_PATH . '/helpers/SG_iCal_Factory.php' );
148
+
149
+ // ===============================
150
+ // = The autoload function =
151
+ // ===============================
152
+ function ai1ec_autoload( $class_name )
153
+ {
154
+ // Convert class name to filename format.
155
+ $class_name = strtr( strtolower( $class_name ), '_', '-' );
156
+
157
+ // Search each path for the class.
158
+ foreach( array( AI1EC_CONTROLLER_PATH, AI1EC_MODEL_PATH, AI1EC_HELPER_PATH, AI1EC_EXCEPTION_PATH, AI1EC_LIB_PATH )
159
+ as $path )
160
+ {
161
+ if( file_exists( "$path/class-$class_name.php" ) )
162
+ require_once( "$path/class-$class_name.php" );
163
+ }
164
+ }
165
+ spl_autoload_register( 'ai1ec_autoload' );
166
+
167
+ // ===============================
168
+ // = Initialize and setup MODELS =
169
+ // ===============================
170
+ global $ai1ec_settings;
171
+
172
+ $ai1ec_settings = Ai1ec_Settings::get_instance();
173
+
174
+
175
+ // ================================
176
+ // = Initialize and setup HELPERS =
177
+ // ================================
178
+ global $ai1ec_view_helper,
179
+ $ai1ec_settings_helper,
180
+ $ai1ec_calendar_helper,
181
+ $ai1ec_app_helper,
182
+ $ai1ec_events_helper,
183
+ $ai1ec_importer_helper,
184
+ $ai1ec_exporter_helper;
185
+
186
+ $ai1ec_view_helper = Ai1ec_View_Helper::get_instance();
187
+ $ai1ec_settings_helper = Ai1ec_Settings_Helper::get_instance();
188
+ $ai1ec_calendar_helper = Ai1ec_Calendar_Helper::get_instance();
189
+ $ai1ec_app_helper = Ai1ec_App_Helper::get_instance();
190
+ $ai1ec_events_helper = Ai1ec_Events_Helper::get_instance();
191
+ $ai1ec_importer_helper = Ai1ec_Importer_Helper::get_instance();
192
+ $ai1ec_exporter_helper = Ai1ec_Exporter_Helper::get_instance();
193
+
194
+
195
+ // ====================================
196
+ // = Initialize and setup CONTROLLERS =
197
+ // ====================================
198
+ global $ai1ec_app_controller,
199
+ $ai1ec_settings_controller,
200
+ $ai1ec_events_controller,
201
+ $ai1ec_calendar_controller,
202
+ $ai1ec_importer_controller,
203
+ $ai1ec_exporter_controller;
204
+
205
+ $ai1ec_app_controller = Ai1ec_App_Controller::get_instance();
206
+ $ai1ec_settings_controller = Ai1ec_Settings_Controller::get_instance();
207
+ $ai1ec_events_controller = Ai1ec_Events_Controller::get_instance();
208
+ $ai1ec_calendar_controller = Ai1ec_Calendar_Controller::get_instance();
209
+ $ai1ec_importer_controller = Ai1ec_Importer_Controller::get_instance();
210
+ $ai1ec_exporter_controller = Ai1ec_Exporter_Controller::get_instance();
211
+
212
+ // ===================
213
+ // = Call admin menu =
214
+ // ===================
215
+ $ai1ec_app_controller->setup_menus();
app/controller/class-ai1ec-app-controller.php ADDED
@@ -0,0 +1,385 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-app-controller.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+
10
+ /**
11
+ * Ai1ec_App_Controller class
12
+ *
13
+ * @package Controllers
14
+ * @author The Seed Studio
15
+ **/
16
+ class Ai1ec_App_Controller {
17
+ /**
18
+ * _instance class variable
19
+ *
20
+ * Class instance
21
+ *
22
+ * @var null | object
23
+ **/
24
+ private static $_instance = NULL;
25
+
26
+ /**
27
+ * page_content class variable
28
+ *
29
+ * String storing page content for output by the_content()
30
+ *
31
+ * @var null | string
32
+ **/
33
+ private $page_content = NULL;
34
+
35
+ /**
36
+ * get_instance function
37
+ *
38
+ * Return singleton instance
39
+ *
40
+ * @return object
41
+ **/
42
+ static function get_instance() {
43
+ if( self::$_instance === NULL ) {
44
+ self::$_instance = new self();
45
+ }
46
+
47
+ return self::$_instance;
48
+ }
49
+
50
+ /**
51
+ * Constructor
52
+ *
53
+ * Default constructor - application initialization
54
+ **/
55
+ private function __construct()
56
+ {
57
+ global $wpdb,
58
+ $ai1ec_app_helper,
59
+ $ai1ec_events_controller,
60
+ $ai1ec_importer_controller,
61
+ $ai1ec_settings_controller,
62
+ $ai1ec_settings;
63
+
64
+ // register_activation_hook
65
+ register_activation_hook( AI1EC_PLUGIN_NAME . '/' . AI1EC_PLUGIN_NAME . '.php', array( &$this, 'rewrite_flush' ) );
66
+
67
+ // Configure MySQL to operate in GMT time
68
+ $wpdb->query( "SET time_zone = '+0:00'" );
69
+
70
+ // Install/update database schema as necessary
71
+ $this->install_schema();
72
+
73
+ // Install/update cron as necessary
74
+ $this->install_cron();
75
+
76
+ // ===========
77
+ // = ACTIONS =
78
+ // ===========
79
+ // Create custom post type
80
+ add_action( 'init', array( &$ai1ec_app_helper, 'create_post_type' ) );
81
+ // Handle ICS export requests
82
+ add_action( 'init', array( &$this, 'parse_standalone_request' ) );
83
+ // General initialization
84
+ add_action( 'init', array( &$ai1ec_events_controller, 'init' ) );
85
+ // Register The Events Calendar importer
86
+ add_action( 'admin_init', array( &$ai1ec_importer_controller, 'register_importer' ) );
87
+ // add content for our custom columns
88
+ add_action( "manage_posts_custom_column", array( &$ai1ec_app_helper, 'custom_columns' ), 10, 2 );
89
+ // Add filtering dropdowns for event categories and tags
90
+ add_action( 'restrict_manage_posts', array( &$ai1ec_app_helper, 'taxonomy_filter_restrict_manage_posts' ) );
91
+ // Trigger display of page in front-end depending on request
92
+ add_action( 'template_redirect', array( &$this, 'route_request' ) );
93
+ // Add meta boxes to event creation/edit form
94
+ add_action( 'add_meta_boxes', array( &$ai1ec_app_helper, 'add_meta_boxes' ) );
95
+ // Save event data when post is saved
96
+ add_action( 'save_post', array( &$ai1ec_events_controller, 'save_post' ) );
97
+ // Delete event data when post is deleted
98
+ add_action( 'delete_post', array( &$ai1ec_events_controller, 'delete_post' ) );
99
+ // cron job hook
100
+ add_action( 'ai1ec_cron', array( &$ai1ec_importer_controller, 'cron' ) );
101
+ add_action( 'events_categories_add_form_fields', array( &$ai1ec_events_controller, 'events_categories_add_form_fields' ) );
102
+ add_action( 'events_categories_edit_form_fields', array( &$ai1ec_events_controller, 'events_categories_edit_form_fields' ) );
103
+ add_action( 'created_events_categories', array( &$ai1ec_events_controller, 'created_events_categories' ) );
104
+ add_action( 'edited_events_categories', array( &$ai1ec_events_controller, 'edited_events_categories' ) );
105
+ add_action( 'admin_notices', array( &$ai1ec_app_helper, 'admin_notices' ) );
106
+ add_action( 'admin_enqueue_scripts', array( &$ai1ec_settings_controller, 'admin_enqueue_scripts' ) );
107
+
108
+ // ===========
109
+ // = FILTERS =
110
+ // ===========
111
+ add_filter( 'posts_orderby', array( &$ai1ec_app_helper, 'orderby' ), 10, 2 );
112
+ // add custom column names and change existing columns
113
+ add_filter( 'manage_ai1ec_event_posts_columns', array( &$ai1ec_app_helper, 'change_columns' ) );
114
+ // filter the post lists by custom filters
115
+ add_filter( 'parse_query', array( &$ai1ec_app_helper, 'taxonomy_filter_post_type_request' ) );
116
+ // Filter event post content, in single- and multi-post views
117
+ add_filter( 'the_content', array( &$ai1ec_events_controller, 'event_content' ) );
118
+ // Override excerpt filters for proper event display in excerpt form
119
+ add_filter( 'get_the_excerpt', array( &$ai1ec_events_controller, 'event_excerpt' ), 11 );
120
+ add_filter( 'the_excerpt', array( &$ai1ec_events_controller, 'event_excerpt_noautop' ), 11 );
121
+ remove_filter( 'the_excerpt', 'wpautop', 10 );
122
+ // Update event post update messages
123
+ add_filter( 'post_updated_messages', array( &$ai1ec_events_controller, 'post_updated_messages' ) );
124
+ // Sort the custom columns
125
+ add_filter( 'manage_edit-ai1ec_event_sortable_columns', array( &$ai1ec_app_helper, 'sortable_columns' ) );
126
+ add_filter( 'map_meta_cap', array( &$ai1ec_app_helper, 'map_meta_cap' ), 10, 4 );
127
+ // Inject event categories, only in front-end, depending on setting
128
+ if( $ai1ec_settings->inject_categories && ! is_admin() ) {
129
+ add_filter( 'get_terms', array( &$ai1ec_app_helper, 'inject_categories' ), 10, 3 );
130
+ add_filter( 'wp_list_categories', array( &$ai1ec_app_helper, 'selected_category_link' ), 10, 2 );
131
+ }
132
+ // Rewrite event category URLs to point to calendar page
133
+ add_filter( 'term_link', array( &$ai1ec_app_helper, 'calendar_term_link' ), 10, 3 );
134
+
135
+ // ========
136
+ // = AJAX =
137
+ // ========
138
+ // Add iCalendar feed
139
+ add_action( 'wp_ajax_ai1ec_add_ics', array( &$ai1ec_settings_controller, 'add_ics_feed' ) );
140
+ // Delete iCalendar feed
141
+ add_action( 'wp_ajax_ai1ec_delete_ics', array( &$ai1ec_settings_controller, 'delete_ics_feed' ) );
142
+ // Flush iCalendar feed
143
+ add_action( 'wp_ajax_ai1ec_flush_ics', array( &$ai1ec_settings_controller, 'flush_ics_feed' ) );
144
+ // Update iCalendar feed
145
+ add_action( 'wp_ajax_ai1ec_update_ics', array( &$ai1ec_settings_controller, 'update_ics_feed' ) );
146
+ }
147
+
148
+ /**
149
+ * rewrite_flush function
150
+ *
151
+ * Get permalinks to work when activating the plugin
152
+ *
153
+ * @return void
154
+ **/
155
+ function rewrite_flush() {
156
+ global $ai1ec_app_helper;
157
+ $ai1ec_app_helper->create_post_type();
158
+ flush_rewrite_rules( true );
159
+ }
160
+
161
+ /**
162
+ * install_schema function
163
+ *
164
+ * This function sets up the database, and upgrades it if it is out of date.
165
+ *
166
+ * @return void
167
+ **/
168
+ function install_schema() {
169
+ global $wpdb;
170
+
171
+ // If existing DB version is not consistent with current plugin's version,
172
+ // or does not exist, then create/update table structure using dbDelta().
173
+ if( get_option( 'ai1ec_db_version' ) != AI1EC_DB_VERSION )
174
+ {
175
+ // =======================
176
+ // = Create table events =
177
+ // =======================
178
+ $table_name = $wpdb->prefix . 'ai1ec_events';
179
+ $sql = "CREATE TABLE $table_name (
180
+ post_id bigint(20) NOT NULL,
181
+ start datetime NOT NULL,
182
+ end datetime,
183
+ allday tinyint(1) NOT NULL,
184
+ recurrence_rules longtext,
185
+ exception_rules longtext,
186
+ recurrence_dates longtext,
187
+ exception_dates longtext,
188
+ venue varchar(255),
189
+ country varchar(255),
190
+ address varchar(255),
191
+ city varchar(255),
192
+ province varchar(255),
193
+ postal_code varchar(32),
194
+ show_map tinyint(1),
195
+ contact_name varchar(255),
196
+ contact_phone varchar(32),
197
+ contact_email varchar(128),
198
+ cost varchar(255),
199
+ ical_feed_url varchar(255),
200
+ ical_source_url varchar(255),
201
+ ical_organizer varchar(255),
202
+ ical_contact varchar(255),
203
+ ical_uid varchar(255),
204
+ PRIMARY KEY (post_id)
205
+ ) CHARACTER SET utf8;";
206
+
207
+ // ==========================
208
+ // = Create table instances =
209
+ // ==========================
210
+ $table_name = $wpdb->prefix . 'ai1ec_event_instances';
211
+ $sql .= "CREATE TABLE $table_name (
212
+ id bigint(20) NOT NULL AUTO_INCREMENT,
213
+ post_id bigint(20) NOT NULL,
214
+ start datetime NOT NULL,
215
+ end datetime NOT NULL,
216
+ PRIMARY KEY (id)
217
+ ) CHARACTER SET utf8;";
218
+
219
+ // ======================
220
+ // = Create table feeds =
221
+ // ======================
222
+ $table_name = $wpdb->prefix . 'ai1ec_event_feeds';
223
+ $sql .= "CREATE TABLE $table_name (
224
+ feed_id bigint(20) NOT NULL AUTO_INCREMENT,
225
+ feed_url varchar(255) NOT NULL,
226
+ feed_category bigint(20) NOT NULL,
227
+ feed_tags varchar(255) NOT NULL,
228
+ PRIMARY KEY (feed_id)
229
+ ) CHARACTER SET utf8;";
230
+
231
+ // ================================
232
+ // = Create table category colors =
233
+ // ================================
234
+ $table_name = $wpdb->prefix . 'ai1ec_event_category_colors';
235
+ $sql .= "CREATE TABLE $table_name (
236
+ term_id bigint(20) NOT NULL,
237
+ term_color varchar(255) NOT NULL,
238
+ PRIMARY KEY (term_id)
239
+ ) CHARACTER SET utf8;";
240
+
241
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
242
+ dbDelta( $sql );
243
+
244
+ update_option( 'ai1ec_db_version', AI1EC_DB_VERSION );
245
+ }
246
+ }
247
+
248
+ /**
249
+ * install_cron function
250
+ *
251
+ * This function sets up the cron job for updating the events, and upgrades it if it is out of date.
252
+ *
253
+ * @return void
254
+ **/
255
+ function install_cron() {
256
+ // If existing CRON version is not consistent with current plugin's version,
257
+ // or does not exist, then create/update cron using
258
+ if( get_option( 'ai1ec_cron_version' ) != AI1EC_CRON_VERSION ) {
259
+ global $ai1ec_settings;
260
+ // delete our scheduled crons
261
+ wp_clear_scheduled_hook( 'ai1ec_cron' );
262
+ // set the new cron
263
+ wp_schedule_event( time(), $ai1ec_settings->cron_freq, 'ai1ec_cron' );
264
+ // update the cron version
265
+ update_option( 'ai1ec_cron_version', AI1EC_CRON_VERSION );
266
+ }
267
+ }
268
+
269
+ /**
270
+ * setup_menus function
271
+ * Adds the hook to admin_menu that is pointing to menu member function
272
+ *
273
+ * @return void
274
+ **/
275
+ function setup_menus() {
276
+ add_action( "admin_menu", array( &$this, "menu" ) );
277
+ }
278
+
279
+ /**
280
+ * menu function
281
+ * Display the admin menu items using the add_menu_page WP function.
282
+ *
283
+ * @return void
284
+ **/
285
+ function menu() {
286
+ global $ai1ec_settings_controller,
287
+ $ai1ec_settings_helper,
288
+ $ai1ec_settings;
289
+
290
+ // =================
291
+ // = Settings Page =
292
+ // =================
293
+ $ai1ec_settings->settings_page = add_submenu_page(
294
+ 'edit.php?post_type=' . AI1EC_POST_TYPE,
295
+ __( 'Settings', AI1EC_PLUGIN_NAME ),
296
+ __( 'Settings', AI1EC_PLUGIN_NAME ),
297
+ 'manage_options',
298
+ AI1EC_PLUGIN_NAME . "-settings",
299
+ array( &$ai1ec_settings_controller, "view" )
300
+ );
301
+ // Create a hook for adding meta boxes
302
+ add_action( "load-{$ai1ec_settings->settings_page}", array( &$ai1ec_settings_helper, 'add_meta_boxes') );
303
+ // Load the meta boxes
304
+ add_action( "load-{$ai1ec_settings->settings_page}", array( &$ai1ec_settings_controller, 'add_meta_boxes' ) );
305
+ // Use registered handlers to hook loading of styles/scripts
306
+ add_action( 'admin_print_styles-' . $ai1ec_settings->settings_page, array( &$ai1ec_settings_controller, 'admin_print_styles' ) );
307
+ add_action( 'admin_print_scripts-' . $ai1ec_settings->settings_page, array( &$ai1ec_settings_controller, 'admin_print_scripts' ) );
308
+ }
309
+
310
+
311
+ /**
312
+ * route_request function
313
+ *
314
+ * Determines if the page viewed should be handled by this plugin, and if so
315
+ * schedule new content to be displayed.
316
+ *
317
+ * @return void
318
+ **/
319
+ function route_request() {
320
+ global $ai1ec_settings,
321
+ $ai1ec_calendar_controller,
322
+ $ai1ec_events_controller;
323
+
324
+ // Find out if we're on the calendar page
325
+ if( is_page( $ai1ec_settings->calendar_page_id ) )
326
+ {
327
+ ob_start();
328
+ // Render view
329
+ $ai1ec_calendar_controller->view();
330
+ // Save page content to local variable
331
+ $this->page_content = ob_get_contents();
332
+ ob_end_clean();
333
+
334
+ // Replace page content
335
+ add_filter( 'the_content', array( &$this, 'append_content' ) );
336
+ }
337
+ }
338
+
339
+ /**
340
+ * parse_standalone_request function
341
+ *
342
+ * @return void
343
+ **/
344
+ function parse_standalone_request() {
345
+ global $ai1ec_exporter_controller,
346
+ $ai1ec_app_helper;
347
+
348
+ $plugin = $ai1ec_app_helper->get_param('plugin');
349
+ $action = $ai1ec_app_helper->get_param('action');
350
+ $controller = $ai1ec_app_helper->get_param('controller');
351
+
352
+ if( ! empty( $plugin ) && $plugin == AI1EC_PLUGIN_NAME && ! empty( $controller ) && ! empty( $action ) ) {
353
+ if( $controller == "ai1ec_exporter_controller" ) :
354
+ switch( $action ) :
355
+ case 'export_events':
356
+ $ai1ec_exporter_controller->export_events();
357
+ break;
358
+ endswitch;
359
+ endif; // ai1ec_exporter_controller
360
+ }
361
+ }
362
+
363
+ /**
364
+ * append_content function
365
+ *
366
+ * Append locally generated content to normal page content (if in the loop;
367
+ * don't want to do it for all instances of the_content() on the page!)
368
+ *
369
+ * @param string $content Post/Page content
370
+ * @return string Post/Page content
371
+ **/
372
+ function append_content( $content )
373
+ {
374
+ // Enclose entire content (including any admin-provided page content) in
375
+ // the calendar container div
376
+ if( in_the_loop() )
377
+ $content =
378
+ '<div id="ai1ec-container" class="ai1ec-container">' .
379
+ $content . $this->page_content .
380
+ '</div>';
381
+
382
+ return $content;
383
+ }
384
+ }
385
+ // END class
app/controller/class-ai1ec-calendar-controller.php ADDED
@@ -0,0 +1,484 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-calendar-controller.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_Calendar_Controller class
11
+ *
12
+ * @package Controllers
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_Calendar_Controller {
16
+ /**
17
+ * _instance class variable
18
+ *
19
+ * Class instance
20
+ *
21
+ * @var null | object
22
+ **/
23
+ static $_instance = NULL;
24
+
25
+ /**
26
+ * request class variable
27
+ *
28
+ * Stores a custom $_REQUEST array for all calendar requests
29
+ *
30
+ * @var array
31
+ **/
32
+ private $request = array();
33
+
34
+ /**
35
+ * __construct function
36
+ *
37
+ * Default constructor - calendar initialization
38
+ **/
39
+ private function __construct() {
40
+ // ===========
41
+ // = ACTIONS =
42
+ // ===========
43
+ // Handle AJAX requests
44
+ // Strange! Now regular WordPress requests will respond to the below AJAX
45
+ // hooks! Thus we need to check to make sure we are being called by the
46
+ // AJAX script before returning AJAX responses.
47
+ if( basename( $_SERVER['SCRIPT_NAME'] ) == 'admin-ajax.php' )
48
+ {
49
+ add_action( 'wp_ajax_ai1ec_month', array( &$this, 'ajax_month' ) );
50
+ add_action( 'wp_ajax_ai1ec_agenda', array( &$this, 'ajax_agenda' ) );
51
+ add_action( 'wp_ajax_nopriv_ai1ec_month', array( &$this, 'ajax_month' ) );
52
+ add_action( 'wp_ajax_nopriv_ai1ec_agenda', array( &$this, 'ajax_agenda' ) );
53
+ add_action( 'wp_ajax_ai1ec_term_filter', array( &$this, 'ajax_term_filter' ) );
54
+ add_action( 'wp_ajax_nopriv_ai1ec_term_filter', array( &$this, 'ajax_term_filter' ) );
55
+ }
56
+ }
57
+
58
+ /**
59
+ * process_request function
60
+ *
61
+ * Initialize/validate custom request array, based on contents of $_REQUEST,
62
+ * to keep track of this component's request variables.
63
+ *
64
+ * @return void
65
+ **/
66
+ function process_request()
67
+ {
68
+ global $ai1ec_settings;
69
+
70
+ // Find out which view of the calendar page was requested, then validate
71
+ // request parameters accordingly and save them to our custom request
72
+ // object
73
+ $this->request['action'] = $_REQUEST['action'];
74
+ if( ! in_array( $this->request['action'],
75
+ array( 'ai1ec_month', 'ai1ec_agenda', 'ai1ec_term_filter' ) ) )
76
+ $this->request['action'] = 'ai1ec_' . $ai1ec_settings->default_calendar_view;
77
+
78
+ switch( $this->request['action'] )
79
+ {
80
+ case 'ai1ec_month':
81
+ $this->request['ai1ec_month_offset'] =
82
+ $_REQUEST['ai1ec_month_offset'] ?
83
+ intval( $_REQUEST['ai1ec_month_offset'] ) : 0;
84
+ // Parse active event parameter as an integer ID
85
+ $this->request['ai1ec_active_event'] = intval( $_REQUEST['ai1ec_active_event'] );
86
+ // Category/tag filter parameters
87
+ $this->request['ai1ec_cat_ids'] = $_REQUEST['ai1ec_cat_ids'];
88
+ $this->request['ai1ec_tag_ids'] = $_REQUEST['ai1ec_tag_ids'];
89
+ break;
90
+
91
+ case 'ai1ec_agenda':
92
+ $this->request['ai1ec_page_offset'] =
93
+ $_REQUEST['ai1ec_page_offset'] ?
94
+ intval( $_REQUEST['ai1ec_page_offset'] ) : 0;
95
+ // Parse active event parameter as an integer ID
96
+ $this->request['ai1ec_active_event'] = intval( $_REQUEST['ai1ec_active_event'] );
97
+ // Category/tag filter parameters
98
+ $this->request['ai1ec_cat_ids'] = $_REQUEST['ai1ec_cat_ids'];
99
+ $this->request['ai1ec_tag_ids'] = $_REQUEST['ai1ec_tag_ids'];
100
+ break;
101
+
102
+ case 'ai1ec_term_filter':
103
+ $this->request['ai1ec_post_ids'] = $_REQUEST['ai1ec_post_ids'];
104
+ $this->request['ai1ec_term_ids'] = $_REQUEST['ai1ec_term_ids'];
105
+ break;
106
+ }
107
+ }
108
+
109
+ /**
110
+ * get_instance function
111
+ *
112
+ * Return singleton instance
113
+ *
114
+ * @return object
115
+ **/
116
+ static function get_instance() {
117
+ if( self::$_instance === NULL ) {
118
+ self::$_instance = new self();
119
+ }
120
+
121
+ return self::$_instance;
122
+ }
123
+
124
+ /**
125
+ * view function
126
+ *
127
+ * Display requested calendar page.
128
+ *
129
+ * @return void
130
+ **/
131
+ function view()
132
+ {
133
+ global $ai1ec_view_helper,
134
+ $ai1ec_settings,
135
+ $ai1ec_events_helper;
136
+
137
+ $this->process_request();
138
+
139
+ // Set body class
140
+ add_filter( 'body_class', array( &$this, 'body_class' ) );
141
+ // Queue any styles, scripts
142
+ $this->load_css();
143
+ $this->load_js();
144
+
145
+ // Define arguments for specific calendar sub-view (month, agenda, etc.)
146
+ $args = array(
147
+ 'active_event' => $this->request['ai1ec_active_event'],
148
+ );
149
+
150
+ // Find out which view of the calendar page was requested
151
+ switch( $this->request['action'] )
152
+ {
153
+ case 'ai1ec_month':
154
+ $args['month_offset'] = $this->request['ai1ec_month_offset'];
155
+ $view = $this->get_month_view( $args );
156
+ break;
157
+
158
+ case 'ai1ec_agenda':
159
+ $args['page_offset'] = $this->request['ai1ec_page_offset'];
160
+ $view = $this->get_agenda_view( $args );
161
+ break;
162
+ }
163
+
164
+ if( $ai1ec_settings->show_create_event_button && current_user_can( 'edit_ai1ec_events' ) )
165
+ $create_event_url = admin_url( 'post-new.php?post_type=' . AI1EC_POST_TYPE );
166
+ else
167
+ $create_event_url = false;
168
+
169
+ // Validate preselected category/tag IDs
170
+ $cat_ids = join( ',', array_filter( split( ',', $this->request['ai1ec_cat_ids'] ), 'is_numeric' ) );
171
+ $tag_ids = join( ',', array_filter( split( ',', $this->request['ai1ec_tag_ids'] ), 'is_numeric' ) );
172
+
173
+ $categories = get_terms( 'events_categories', array( 'orderby' => 'name' ) );
174
+ foreach( $categories as &$cat ) {
175
+ $cat->color = $ai1ec_events_helper->get_category_color_square( $cat->term_id );
176
+ }
177
+ // Define new arguments for overall calendar view
178
+ $args = array(
179
+ 'view' => $view,
180
+ 'create_event_url' => $create_event_url,
181
+ 'categories' => $categories,
182
+ 'tags' => get_terms( 'events_tags', array( 'orderby' => 'name' ) ),
183
+ 'selected_cat_ids' => $cat_ids,
184
+ 'selected_tag_ids' => $tag_ids,
185
+ );
186
+
187
+ // Feed month view into generic calendar view
188
+ $ai1ec_view_helper->display( 'calendar.php', $args );
189
+ }
190
+
191
+ /**
192
+ * get_month_view function
193
+ *
194
+ * Return the embedded month view of the calendar, optionally filtered by
195
+ * event categories and tags.
196
+ *
197
+ * @param array $args associative array with any of these elements:
198
+ * int month_offset => specifies which month to display relative to the
199
+ * current month
200
+ * int active_event => specifies which event to make visible when
201
+ * page is loaded
202
+ * array categories => restrict events returned to the given set of
203
+ * event category slugs
204
+ * array tags => restrict events returned to the given set of
205
+ * event tag names
206
+ *
207
+ * @return string returns string of view output
208
+ **/
209
+ function get_month_view( $args )
210
+ {
211
+ global $ai1ec_view_helper,
212
+ $ai1ec_events_helper,
213
+ $ai1ec_calendar_helper;
214
+
215
+ extract( $args );
216
+
217
+ // Get components of localized time
218
+ $bits = $ai1ec_events_helper->gmgetdate( $ai1ec_events_helper->gmt_to_local( time() ) );
219
+ // Use first day of the month as reference timestamp
220
+ $timestamp = gmmktime( 0, 0, 0, $bits['mon'], 1, $bits['year'] );
221
+ // Incorporate month offset into date, so we are looking at right month
222
+ $timestamp = strtotime(
223
+ $month_offset > 0 ? "+$month_offset months" : "$month_offset months",
224
+ $timestamp );
225
+
226
+ $days_events = $ai1ec_calendar_helper->get_events_for_month( $timestamp, $categories, $tags );
227
+ $cell_array = $ai1ec_calendar_helper->get_month_cell_array( $timestamp, $days_events );
228
+ $pagination_links = $ai1ec_calendar_helper->get_month_pagination_links( $month_offset );
229
+
230
+ $view_args = array(
231
+ 'title' => strftime( '%B %Y', $timestamp ),
232
+ 'weekdays' => $ai1ec_calendar_helper->get_weekdays(),
233
+ 'cell_array' => $cell_array,
234
+ 'pagination_links' => $pagination_links,
235
+ 'active_event' => $active_event,
236
+ );
237
+ return $ai1ec_view_helper->get_view( 'month.php', $view_args );
238
+ }
239
+
240
+ /**
241
+ * get_agenda_view function
242
+ *
243
+ * Return the embedded agenda view of the calendar, optionally filtered by
244
+ * event categories and tags.
245
+ *
246
+ * @param array $args associative array with any of these elements:
247
+ * int page_offset => specifies which page to display relative to today's page
248
+ * int active_event => specifies which event to make visible when
249
+ * page is loaded
250
+ * array categories => restrict events returned to the given set of
251
+ * event category slugs
252
+ * array tags => restrict events returned to the given set of
253
+ * event tag names
254
+ *
255
+ * @return string returns string of view output
256
+ **/
257
+ function get_agenda_view( $args )
258
+ {
259
+ global $ai1ec_view_helper,
260
+ $ai1ec_events_helper,
261
+ $ai1ec_calendar_helper,
262
+ $ai1ec_settings;
263
+
264
+ extract( $args );
265
+
266
+ // Get localized time
267
+ $timestamp = $ai1ec_events_helper->gmt_to_local( time() );
268
+
269
+ // Get events, then classify into date array
270
+ $event_results = $ai1ec_calendar_helper->get_events_relative_to(
271
+ $timestamp,
272
+ $ai1ec_settings->agenda_events_per_page,
273
+ $page_offset
274
+ );
275
+ $dates = $ai1ec_calendar_helper->get_agenda_date_array( $event_results['events'] );
276
+
277
+ $pagination_links =
278
+ $ai1ec_calendar_helper->get_agenda_pagination_links(
279
+ $page_offset, $event_results['prev'], $event_results['next'] );
280
+
281
+ // Incorporate offset into date
282
+ $args = array(
283
+ 'title' => __( 'Agenda' ),
284
+ 'dates' => $dates,
285
+ 'page_offset' => $page_offset,
286
+ 'pagination_links' => $pagination_links,
287
+ 'active_event' => $active_event,
288
+ );
289
+ return $ai1ec_view_helper->get_view( 'agenda.php', $args );
290
+ }
291
+
292
+ /**
293
+ * ajax_month function
294
+ *
295
+ * AJAX request handler for month view.
296
+ *
297
+ * @return void
298
+ **/
299
+ function ajax_month() {
300
+ global $ai1ec_view_helper;
301
+
302
+ $this->process_request();
303
+
304
+ // View arguments
305
+ $args = array(
306
+ 'month_offset' => $this->request['ai1ec_month_offset'],
307
+ 'active_event' => $this->request['ai1ec_active_event'],
308
+ );
309
+
310
+ // Return this data structure to the client
311
+ $data = array(
312
+ 'body_class' => join( ' ', $this->body_class() ),
313
+ 'html' => $this->get_month_view( $args ),
314
+ );
315
+ $ai1ec_view_helper->json_response( $data );
316
+ }
317
+
318
+ /**
319
+ * ajax_agenda function
320
+ *
321
+ * AJAX request handler for agenda view.
322
+ *
323
+ * @return void
324
+ **/
325
+ function ajax_agenda() {
326
+ global $ai1ec_view_helper;
327
+
328
+ $this->process_request();
329
+
330
+ // View arguments
331
+ $args = array(
332
+ 'page_offset' => $this->request['ai1ec_page_offset'],
333
+ 'active_event' => $this->request['ai1ec_active_event'],
334
+ );
335
+
336
+ // Return this data structure to the client
337
+ $data = array(
338
+ 'body_class' => join( ' ', $this->body_class() ),
339
+ 'html' => $this->get_agenda_view( $args ),
340
+ );
341
+ $ai1ec_view_helper->json_response( $data );
342
+ }
343
+
344
+ /**
345
+ * ajax_term_filter function
346
+ *
347
+ * AJAX request handler that takes a comma-separated list of event IDs and
348
+ * comma-separated list of term IDs and returns those event IDs within the
349
+ * set that have any of the term IDs.
350
+ *
351
+ * @return void
352
+ **/
353
+ function ajax_term_filter() {
354
+ global $ai1ec_view_helper, $ai1ec_events_helper;
355
+
356
+ $this->process_request();
357
+
358
+ $post_ids = array_unique( explode( ',', $this->request['ai1ec_post_ids'] ) );
359
+
360
+ if( $this->request['ai1ec_term_ids'] ) {
361
+ $term_ids = explode( ',', $this->request['ai1ec_term_ids'] );
362
+ $matching_ids = $ai1ec_events_helper->filter_by_terms( $post_ids, $term_ids );
363
+ } else {
364
+ // If no term IDs were provided for filtering, then return all posts
365
+ $matching_ids = $post_ids;
366
+ }
367
+
368
+ $unmatching_ids = array_diff( $post_ids, $matching_ids );
369
+
370
+ $data = array(
371
+ 'matching_ids' => $matching_ids,
372
+ 'unmatching_ids' => $unmatching_ids,
373
+ );
374
+ $ai1ec_view_helper->json_response( $data );
375
+ }
376
+
377
+ /**
378
+ * body_class function
379
+ *
380
+ * Append custom classes to body element.
381
+ *
382
+ * @return void
383
+ **/
384
+ function body_class( $classes = array() ) {
385
+ $classes[] = 'ai1ec-calendar';
386
+
387
+ // Reformat action for body class
388
+ $action = $this->request['action'];
389
+ $action = strtr( $action, '_', '-' );
390
+ $action = preg_replace( '/^ai1ec-/', '', $action );
391
+
392
+ $classes[] = "ai1ec-action-$action";
393
+ if( ! $this->request['ai1ec_month_offset'] &&
394
+ ! $this->request['ai1ec_page_offset'] ) {
395
+ $classes[] = 'ai1ec-today';
396
+ }
397
+ return $classes;
398
+ }
399
+
400
+ /**
401
+ * load_css function
402
+ *
403
+ * Enqueue any CSS files required by the calendar views, as well as embeds any
404
+ * CSS rules necessary for calendar container replacement.
405
+ *
406
+ * @return void
407
+ **/
408
+ function load_css()
409
+ {
410
+ global $ai1ec_settings;
411
+
412
+ wp_enqueue_style( 'ai1ec-general', AI1EC_CSS_URL . '/general.css', array(), 1 );
413
+ wp_enqueue_style( 'ai1ec-calendar', AI1EC_CSS_URL . '/calendar.css', array(), 1 );
414
+
415
+ if( $ai1ec_settings->calendar_css_selector )
416
+ add_action( 'wp_head', array( &$this, 'selector_css' ) );
417
+ }
418
+
419
+ /**
420
+ * selector_css function
421
+ *
422
+ * Inserts dynamic CSS rules into <head> section of page to replace
423
+ * desired CSS selector with calendar.
424
+ */
425
+ function selector_css() {
426
+ global $ai1ec_view_helper, $ai1ec_settings;
427
+
428
+ $ai1ec_view_helper->display_css(
429
+ 'selector.css',
430
+ array( 'selector' => $ai1ec_settings->calendar_css_selector )
431
+ );
432
+ }
433
+
434
+ /**
435
+ * load_js function
436
+ *
437
+ * Enqueue any JavaScript files required by the calendar views.
438
+ *
439
+ * @return void
440
+ **/
441
+ function load_js()
442
+ {
443
+ global $ai1ec_settings;
444
+
445
+ // Include scrollTo jQuery plugin
446
+ wp_enqueue_script( 'jquery.scrollTo', AI1EC_JS_URL . '/jquery.scrollTo-min.js', array( 'jquery' ), 1 );
447
+ // Include element selector function
448
+ wp_enqueue_script( 'ai1ec-element-selector', AI1EC_JS_URL . '/element-selector.js', array( 'jquery', 'jquery.scrollTo' ), 1 );
449
+ // Include custom script
450
+ wp_enqueue_script( 'ai1ec-calendar', AI1EC_JS_URL . '/calendar.js', array( 'jquery', 'jquery.scrollTo' ), 1 );
451
+
452
+ $data = array(
453
+ // Point script to AJAX URL
454
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ),
455
+ // What this view defaults to, in case there is no #hash appended
456
+ 'default_hash' => '#' . http_build_query( $this->request ),
457
+ 'export_url' => AI1EC_EXPORT_URL,
458
+ // Body classes if need to be set manually
459
+ 'body_class' => join( ' ', $this->body_class() ),
460
+ );
461
+ // Replace desired CSS selector with calendar, if selector has been set
462
+ if( $ai1ec_settings->calendar_css_selector )
463
+ {
464
+ $page = get_post( $ai1ec_settings->calendar_post_id );
465
+ $data['selector'] = $ai1ec_settings->calendar_css_selector;
466
+ $data['title'] = $page->post_title;
467
+ }
468
+
469
+ wp_localize_script( 'ai1ec-calendar', 'ai1ec_calendar', $data );
470
+ }
471
+
472
+ /**
473
+ * function is_category_requested
474
+ *
475
+ * Returns the comma-separated list of category IDs that the calendar page
476
+ * was requested to be prefiltered by.
477
+ *
478
+ * @return string
479
+ */
480
+ function get_requested_categories() {
481
+ return $this->request['ai1ec_cat_ids'];
482
+ }
483
+ }
484
+ // END class
app/controller/class-ai1ec-events-controller.php ADDED
@@ -0,0 +1,790 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-events-controller.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_Events_Controller class
11
+ *
12
+ * @package Controllers
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_Events_Controller {
16
+ /**
17
+ * _instance class variable
18
+ *
19
+ * Class instance
20
+ *
21
+ * @var null | object
22
+ **/
23
+ private static $_instance = NULL;
24
+
25
+ /**
26
+ * get_instance function
27
+ *
28
+ * Return singleton instance
29
+ *
30
+ * @return object
31
+ **/
32
+ static function get_instance() {
33
+ if( self::$_instance === NULL ) {
34
+ self::$_instance = new self();
35
+ }
36
+
37
+ return self::$_instance;
38
+ }
39
+
40
+ /**
41
+ * Constructor
42
+ *
43
+ **/
44
+ private function __construct() { }
45
+
46
+ /**
47
+ * delete_hook function
48
+ *
49
+ * If the deleted post is an event
50
+ * then all entries that match the post_id are
51
+ * removed from ai1ec_events and ai1ec_event_instances tables
52
+ *
53
+ * @param int $pid Post ID
54
+ *
55
+ * @return bool | int
56
+ **/
57
+ function delete_post( $pid ) {
58
+ global $wpdb;
59
+
60
+ $sql = "SELECT
61
+ ID
62
+ FROM
63
+ $wpdb->posts
64
+ WHERE
65
+ ID = %d AND
66
+ post_type = '" . AI1EC_POST_TYPE . "'";
67
+
68
+ // is this post an event?
69
+ if( $wpdb->get_var( $wpdb->prepare( $sql, $pid ) ) ) {
70
+ $table_name = $wpdb->prefix . 'ai1ec_events';
71
+ $sql = "DELETE FROM
72
+ $table_name
73
+ WHERE
74
+ post_id = %d";
75
+ // delete from ai1ec_events
76
+ $wpdb->query( $wpdb->prepare( $sql, $pid ) );
77
+
78
+ $table_name = $wpdb->prefix . 'ai1ec_event_instances';
79
+ $sql = "DELETE FROM
80
+ $table_name
81
+ WHERE
82
+ post_id = %d";
83
+ // delete from ai1ec_event_instances
84
+ return $wpdb->query( $wpdb->prepare( $sql, $pid ) );
85
+ }
86
+ return true;
87
+ }
88
+
89
+ /**
90
+ * init function
91
+ *
92
+ * This function is executed when admin_head hook is called.
93
+ * Adds CSS and JS files.
94
+ *
95
+ * @return void
96
+ **/
97
+ function init()
98
+ {
99
+ global $ai1ec_events_helper;
100
+
101
+ // Initialize dashboard view
102
+ if( is_admin() ) {
103
+ // ======
104
+ // = JS =
105
+ // ======
106
+ // Include timespan helper functions
107
+ wp_enqueue_script( 'jquery.calendrical', AI1EC_JS_URL . '/jquery.calendrical.js', array( 'jquery' ) );
108
+ // Include timespan plugin
109
+ wp_enqueue_script( 'jquery.timespan', AI1EC_JS_URL . '/jquery.timespan.js', array( 'jquery', 'jquery.calendrical' ) );
110
+ // Include timespan plugin
111
+ wp_enqueue_script( 'jquery.inputdate', AI1EC_JS_URL . '/jquery.inputdate.js', array( 'jquery', 'jquery.calendrical' ) );
112
+ // Include Google Maps API
113
+ wp_enqueue_script( 'gmap_api', 'http://maps.google.com/maps/api/js?sensor=false' );
114
+ // Include autocomplete_geomod plugin
115
+ wp_enqueue_script( 'autocomplete_geomod', AI1EC_JS_URL . '/jquery.autocomplete_geomod.js', array( 'jquery' ) );
116
+ // Include geo_autocomplete plugin
117
+ wp_enqueue_script( 'geo_autocomplete', AI1EC_JS_URL . '/geo_autocomplete.js', array( 'jquery', 'autocomplete_geomod' ) );
118
+ // Include element selector function
119
+ wp_enqueue_script( 'ai1ec-element-selector', AI1EC_JS_URL . '/element-selector.js', array( 'jquery' ) );
120
+ // Include jQuery Tools form elements
121
+ wp_enqueue_script( 'jquery.tools-form', 'http://cdn.jquerytools.org/1.2.5/form/jquery.tools.min.js', array( 'jquery' ), '1.2.5' );
122
+ // Include add new event script
123
+ wp_enqueue_script( 'ai1ec-add_new_event', AI1EC_JS_URL . '/add_new_event.js', array( 'jquery', 'jquery.timespan', 'ai1ec-element-selector', 'jquery.tools-form' ) );
124
+ wp_enqueue_script( 'ai1ec-color-picker', AI1EC_JS_URL . '/colorpicker.js', array( 'jquery' ) );
125
+
126
+ // Supply custom value to JavaScript from PHP
127
+ wp_localize_script( 'ai1ec-add_new_event', 'ai1ec_add_new_event', array(
128
+ // Current time, used for date/time pickers
129
+ 'now' => $ai1ec_events_helper->gmt_to_local( time() ),
130
+ ) );
131
+
132
+ // =======
133
+ // = CSS =
134
+ // =======
135
+ // include autocomplete style
136
+ wp_enqueue_style( 'autocomplete', AI1EC_CSS_URL . '/jquery.autocomplete.css' );
137
+ // include colorpicker style
138
+ wp_enqueue_style( 'colorpicker', AI1EC_CSS_URL . '/colorpicker.css' );
139
+ // include add new event style
140
+ wp_enqueue_style( 'ai1ec_add_new_event', AI1EC_CSS_URL . '/add_new_event.css' );
141
+ }
142
+ // Initialize front-end view
143
+ else
144
+ {
145
+ // ======
146
+ // = JS =
147
+ // ======
148
+ wp_enqueue_script( 'ai1ec-event', AI1EC_JS_URL . '/event.js', array( 'jquery' ), 1 );
149
+
150
+ // =======
151
+ // = CSS =
152
+ // =======
153
+ wp_enqueue_style( 'ai1ec-general', AI1EC_CSS_URL . '/general.css', array(), 1 );
154
+ wp_enqueue_style( 'ai1ec-event', AI1EC_CSS_URL . '/event.css', array(), 1 );
155
+ }
156
+ }
157
+
158
+ /**
159
+ * meta_box_view function
160
+ *
161
+ * Add Events Calculator box to the Add New Event page
162
+ *
163
+ * @return void
164
+ **/
165
+ function meta_box_view() {
166
+ global $ai1ec_view_helper,
167
+ $ai1ec_events_helper,
168
+ $post,
169
+ $wpdb,
170
+ $ai1ec_settings;
171
+
172
+ try
173
+ {
174
+ $event = new Ai1ec_Event( $post->ID );
175
+
176
+ // Existing event was found. Initialize form values with values from
177
+ // event object.
178
+
179
+ $all_day_event = $event->allday ? 'checked="checked"' : '';
180
+
181
+ $start_timestamp = $ai1ec_events_helper->gmt_to_local( $event->start );
182
+ $end_timestamp = $ai1ec_events_helper->gmt_to_local( $event->end );
183
+
184
+ $show_map = $event->show_map;
185
+ $google_map = $show_map ? 'checked="checked"' : '';
186
+
187
+ $venue = $event->venue;
188
+ $country = $event->country;
189
+ $address = $event->address;
190
+ $city = $event->city;
191
+ $province = $event->province;
192
+ $postal_code = $event->postal_code;
193
+ $contact_name = $event->contact_name;
194
+ $contact_phone = $event->contact_phone;
195
+ $contact_email = $event->contact_email;
196
+ $cost = $event->cost;
197
+ }
198
+ catch( Ai1ec_Event_Not_Found $e ) {
199
+ // Event does not exist.
200
+ // Leave form fields undefined (= zero-length strings)
201
+ $event = null;
202
+ }
203
+
204
+ // Recurrence fields
205
+ $recurrence = $ai1ec_events_helper->parse_recurrence_rules( $event );
206
+ extract( $recurrence );
207
+
208
+ // Time zone
209
+ $timezone = get_option( 'gmt_offset' );
210
+ $timezone = sprintf( '(GMT%+d:%02d)', intval( $timezone ), ( abs( $timezone ) * 60 ) % 60 );
211
+
212
+ // ===============================
213
+ // = Display event time and date =
214
+ // ===============================
215
+ if( is_null( $until ) ) $until = gmmktime();
216
+ $repeating_event = is_null( $repeat ) ? false : true;
217
+ $args = array(
218
+ 'all_day_event' => $all_day_event,
219
+ 'start_timestamp' => $start_timestamp,
220
+ 'end_timestamp' => $end_timestamp,
221
+ 'repeat' => $ai1ec_events_helper->create_repeat_dropdown( $repeat ),
222
+ 'count' => $ai1ec_events_helper->create_count_input( $count ),
223
+ 'end' => $ai1ec_events_helper->create_end_dropdown( $end ),
224
+ 'until' => $ai1ec_events_helper->gmt_to_local( $until ),
225
+ 'repeating_event' => $repeating_event,
226
+ 'timezone' => $timezone,
227
+ 'ending' => $end
228
+ );
229
+ $ai1ec_view_helper->display( 'box_time_and_date.php', $args );
230
+
231
+ // =================================================
232
+ // = Display event location details and Google map =
233
+ // =================================================
234
+ $args = array(
235
+ 'venue' => $venue,
236
+ 'country' => $country,
237
+ 'address' => $address,
238
+ 'city' => $city,
239
+ 'province' => $province,
240
+ 'postal_code' => $postal_code,
241
+ 'google_map' => $google_map,
242
+ 'show_map' => $show_map,
243
+ );
244
+ $ai1ec_view_helper->display( 'box_event_location.php', $args );
245
+
246
+ // ======================
247
+ // = Display event cost =
248
+ // ======================
249
+ $args = array(
250
+ 'cost' => $cost
251
+ );
252
+ $ai1ec_view_helper->display( 'box_event_cost.php', $args );
253
+
254
+ // =========================================
255
+ // = Display organizer contact information =
256
+ // =========================================
257
+ $args = array(
258
+ 'contact_name' => $contact_name,
259
+ 'contact_phone' => $contact_phone,
260
+ 'contact_email' => $contact_email,
261
+ );
262
+ $ai1ec_view_helper->display( 'box_event_contact.php', $args );
263
+
264
+ if( $ai1ec_settings->show_publish_button ) {
265
+ $args = array();
266
+ $post_type = $post->post_type;
267
+ $post_type_object = get_post_type_object( $post_type );
268
+ if( current_user_can( $post_type_object->cap->publish_posts ) )
269
+ $args["button_value"] = is_null( $event ) ? __( 'Publish', AI1EC_PLUGIN_NAME ) : __( 'Update', AI1EC_PLUGIN_NAME );
270
+ else
271
+ $args["button_value"] = __( 'Submit for Review', AI1EC_PLUGIN_NAME );
272
+
273
+ $ai1ec_view_helper->display( 'box_publish_button.php', $args );
274
+ }
275
+
276
+ /*
277
+ TODO Display Eventbrite ticketing
278
+ $ai1ec_view_helper->display( 'box_eventbrite.php' );
279
+ */
280
+ }
281
+
282
+ /**
283
+ * save_post function
284
+ *
285
+ * Saves meta post data
286
+ *
287
+ * @param int $post_id Post ID
288
+ *
289
+ * @return void
290
+ **/
291
+ function save_post( $post_id ) {
292
+ global $wpdb, $ai1ec_events_helper;
293
+
294
+ // verify if this is not an auto save routine.
295
+ if( ! defined( 'DOING_AUTOSAVE' ) && ! DOING_AUTOSAVE ) {
296
+
297
+ // verify this came from the our screen and with proper authorization,
298
+ // because save_post can be triggered at other times
299
+ if ( !wp_verify_nonce( $_POST[AI1EC_POST_TYPE], 'ai1ec' ) ) {
300
+ return;
301
+ }
302
+ }
303
+
304
+ // verify that the post_type is that of an event
305
+ if( $_POST['post_type'] != AI1EC_POST_TYPE ) {
306
+ return;
307
+ }
308
+
309
+ $all_day = isset( $_POST['ai1ec_all_day_event'] ) ? 1 : 0;
310
+ $start_time = isset( $_POST['ai1ec_start_time'] ) ? $_POST['ai1ec_start_time'] : '';
311
+ $end_time = isset( $_POST['ai1ec_end_time'] ) ? $_POST['ai1ec_end_time'] : '';
312
+ $venue = isset( $_POST['ai1ec_venue'] ) ? stripslashes( $_POST['ai1ec_venue'] ) : '';
313
+ $address = isset( $_POST['ai1ec_address'] ) ? stripslashes( $_POST['ai1ec_address'] ) : '';
314
+ $city = isset( $_POST['ai1ec_city'] ) ? stripslashes( $_POST['ai1ec_city'] ) : '';
315
+ $province = isset( $_POST['ai1ec_province'] ) ? stripslashes( $_POST['ai1ec_province'] ) : '';
316
+ $postal_code = isset( $_POST['ai1ec_postal_code'] ) ? stripslashes( $_POST['ai1ec_postal_code'] ) : '';
317
+ $country = isset( $_POST['ai1ec_country'] ) ? stripslashes( $_POST['ai1ec_country'] ) : '';
318
+ $google_map = isset( $_POST['ai1ec_google_map'] ) ? 1 : 0;
319
+ $cost = isset( $_POST['ai1ec_cost'] ) ? stripslashes( $_POST['ai1ec_cost'] ) : '';
320
+ $contact_name = isset( $_POST['ai1ec_contact_name'] ) ? stripslashes( $_POST['ai1ec_contact_name'] ) : '';
321
+ $contact_phone = isset( $_POST['ai1ec_contact_phone'] ) ? stripslashes( $_POST['ai1ec_contact_phone'] ) : '';
322
+ $contact_email = isset( $_POST['ai1ec_contact_email'] ) ? stripslashes( $_POST['ai1ec_contact_email'] ) : '';
323
+
324
+ $rrule = null;
325
+
326
+ if( isset( $_POST['ai1ec_repeat'] ) && ! empty( $_POST['ai1ec_repeat'] ) && $_POST['ai1ec_repeat'] != ' ' ) {
327
+ // ================================
328
+ // = Repeating event, build rrule =
329
+ // ================================
330
+ $end = (int) $_POST['ai1ec_end'];
331
+ switch( $end ) :
332
+ // Never
333
+ case 0:
334
+ $end = '';
335
+ break;
336
+ // After
337
+ case 1:
338
+ $end = ';COUNT=' . (int) $_POST['ai1ec_count'];
339
+ break;
340
+ // On date
341
+ case 2:
342
+ $until = $_POST['ai1ec_until_time'];
343
+ $until = $ai1ec_events_helper->local_to_gmt( $until );
344
+ $until += 24 * 60 * 60; // Add 1 day (intuitively, the last day is included)
345
+ $until = gmdate( 'Ymd\THis\Z', $until ); // Convert to Zulu time
346
+ $end = ';UNTIL=' . $until;
347
+ break;
348
+ endswitch;
349
+
350
+ switch( $_POST['ai1ec_repeat'] ) {
351
+ // Daily
352
+ case 'DAILY':
353
+ $rrule = 'FREQ=DAILY' . $end;
354
+ break;
355
+ // Mondays
356
+ case 'MO':
357
+ $rrule = 'FREQ=DAILY;BYDAY=MO' . $end;
358
+ break;
359
+ // Tuesdays
360
+ case 'TU':
361
+ $rrule = 'FREQ=DAILY;BYDAY=TU' . $end;
362
+ break;
363
+ // Wednesdays
364
+ case 'WE':
365
+ $rrule = 'FREQ=DAILY;BYDAY=WE' . $end;
366
+ break;
367
+ // Thursdays
368
+ case 'TH':
369
+ $rrule = 'FREQ=DAILY;BYDAY=TH' . $end;
370
+ break;
371
+ // Fridays
372
+ case 'FR':
373
+ $rrule = 'FREQ=DAILY;BYDAY=FR' . $end;
374
+ break;
375
+ // Tuesdays and Thursdays
376
+ case 'TU+TH':
377
+ $rrule = 'FREQ=DAILY;BYDAY=TU,TH' . $end;
378
+ break;
379
+ // Mondays Wednesdays Fridays
380
+ case 'MO+WE+FR':
381
+ $rrule = 'FREQ=DAILY;BYDAY=MO,WE,FR' . $end;
382
+ break;
383
+ // Weekends
384
+ case 'WEEKDAYS':
385
+ $rrule = 'FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR' . $end;
386
+ break;
387
+ // Saturdays
388
+ case 'SA':
389
+ $rrule = 'FREQ=DAILY;BYDAY=SA' . $end;
390
+ break;
391
+ // Sundays
392
+ case 'SU':
393
+ $rrule = 'FREQ=DAILY;BYDAY=SU' . $end;
394
+ break;
395
+ // Weekends
396
+ case 'WEEKENDS':
397
+ $rrule = 'FREQ=DAILY;BYDAY=SA+SU' . $end;
398
+ break;
399
+ // Weekly
400
+ case 'WEEKLY':
401
+ $rrule = 'FREQ=WEEKLY' . $end;
402
+ break;
403
+ // Monthly
404
+ case 'MONTHLY':
405
+ $rrule = 'FREQ=MONTHLY' . $end;
406
+ break;
407
+ // Yearly
408
+ case 'YEARLY':
409
+ $rrule = 'FREQ=YEARLY' . $end;
410
+ break;
411
+ }
412
+ }
413
+
414
+ $is_new = false;
415
+ $event = null;
416
+ try {
417
+ $event = new Ai1ec_Event( $post_id ? $post_id : null );
418
+ } catch( Ai1ec_Event_Not_Found $e ) {
419
+ // Post exists, but event data hasn't been saved yet. Create new event
420
+ // object.
421
+ $is_new = true;
422
+ $event = new Ai1ec_Event();
423
+ $event->post_id = $post_id;
424
+ }
425
+
426
+ $event->start = $ai1ec_events_helper->local_to_gmt( $start_time );
427
+ $event->end = $ai1ec_events_helper->local_to_gmt( $end_time );
428
+ $event->allday = $all_day;
429
+ $event->venue = $venue;
430
+ $event->address = $address;
431
+ $event->city = $city;
432
+ $event->province = $province;
433
+ $event->postal_code = $postal_code;
434
+ $event->country = $country;
435
+ $event->show_map = $google_map;
436
+ $event->cost = $cost;
437
+ $event->contact_name = $contact_name;
438
+ $event->contact_phone = $contact_phone;
439
+ $event->contact_email = $contact_email;
440
+ $event->recurrence_rules = $rrule;
441
+ $event->save( ! $is_new );
442
+
443
+ $ai1ec_events_helper->delete_event_cache( $post_id );
444
+ $ai1ec_events_helper->cache_event( $event );
445
+ return;
446
+ }
447
+
448
+ /**
449
+ * post_updated_messages function
450
+ *
451
+ * Filter success messages returned by WordPress when an event post is
452
+ * updated/saved.
453
+ */
454
+ function post_updated_messages( $messages )
455
+ {
456
+ global $post, $post_ID;
457
+
458
+ $messages[AI1EC_POST_TYPE] = array(
459
+ 0 => '', // Unused. Messages start at index 1.
460
+ 1 => sprintf( __( 'Event updated. <a href="%s">View event</a>', AI1EC_PLUGIN_NAME ), esc_url( get_permalink( $post_ID ) ) ),
461
+ 2 => __( 'Custom field updated.', AI1EC_PLUGIN_NAME ),
462
+ 3 => __( 'Custom field deleted.', AI1EC_PLUGIN_NAME ),
463
+ 4 => __( 'Event updated.', AI1EC_PLUGIN_NAME ),
464
+ /* translators: %s: date and time of the revision */
465
+ 5 => isset( $_GET['revision'] ) ? sprintf( __( 'Event restored to revision from %s', AI1EC_PLUGIN_NAME ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
466
+ 6 => sprintf( __( 'Event published. <a href="%s">View event</a>', AI1EC_PLUGIN_NAME ), esc_url( get_permalink($post_ID) ) ),
467
+ 7 => __( 'Event saved.' ),
468
+ 8 => sprintf( __( 'Event submitted. <a target="_blank" href="%s">Preview event</a>', AI1EC_PLUGIN_NAME ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post_ID ) ) ) ),
469
+ 9 => sprintf( __( 'Event scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview event</a>', AI1EC_PLUGIN_NAME ),
470
+ // translators: Publish box date format, see http://php.net/date
471
+ date_i18n( __( 'M j, Y @ G:i', AI1EC_PLUGIN_NAME ), strtotime( $post->post_date ) ), esc_url( get_permalink($post_ID) ) ),
472
+ 10 => sprintf( __( 'Event draft updated. <a target="_blank" href="%s">Preview event</a>', AI1EC_PLUGIN_NAME ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post_ID ) ) ) ),
473
+ );
474
+
475
+ return $messages;
476
+ }
477
+
478
+ /**
479
+ * event_content function
480
+ *
481
+ * Filter event post content by inserting relevant details of the event
482
+ * alongside the regular post content.
483
+ *
484
+ * @param string $content Post/Page content
485
+ *
486
+ * @return string Post/Page content
487
+ **/
488
+ function event_content( $content )
489
+ {
490
+ global $ai1ec_events_helper;
491
+
492
+ if( get_post_type() == AI1EC_POST_TYPE ) {
493
+ $event = $ai1ec_events_helper->get_event( get_the_ID() );
494
+ $content = $this->get_view( $event, $content );
495
+ }
496
+ return $content;
497
+ }
498
+
499
+ /**
500
+ * event_excerpt function
501
+ *
502
+ * Overrides what wp_trim_excerpt() returned if the post is an event,
503
+ * and outputs better rich-text (but not too rich) excerpt instead.
504
+ *
505
+ * @return void
506
+ **/
507
+ function event_excerpt( $text )
508
+ {
509
+ global $ai1ec_view_helper,
510
+ $ai1ec_events_helper;
511
+
512
+ if( get_post_type() != AI1EC_POST_TYPE )
513
+ return $text;
514
+
515
+ $event = new Ai1ec_Event( get_the_ID() );
516
+
517
+ ob_start();
518
+
519
+ $this->excerpt_view( $event );
520
+
521
+ // Re-apply any filters to the post content that normally would have been
522
+ // applied if it weren't for our interference (below).
523
+ echo
524
+ shortcode_unautop( wpautop(
525
+ $ai1ec_events_helper->trim_excerpt( $event->post->post_content )
526
+ ) );
527
+
528
+ $page_content = ob_get_contents();
529
+ ob_end_clean();
530
+
531
+ return $page_content;
532
+ }
533
+
534
+ /**
535
+ * event_excerpt_noautop function
536
+ *
537
+ * Conditionally apply wpautop() filter to content, only if it is not an
538
+ * event.
539
+ *
540
+ * @return void
541
+ **/
542
+ function event_excerpt_noautop( $content )
543
+ {
544
+ if( get_post_type() != AI1EC_POST_TYPE )
545
+ return wpautop( $content );
546
+ return $content;
547
+ }
548
+
549
+ /**
550
+ * get_view function
551
+ *
552
+ * Returns the appropriate output to prepend to an event post, depending on
553
+ * WP loop context.
554
+ *
555
+ * @param Ai1ec_Event $event The event post being displayed
556
+ * @param string $content The post's original content
557
+ *
558
+ * @return string The event data markup to prepend to the post content
559
+ **/
560
+ function get_view( &$event, &$content )
561
+ {
562
+ global $ai1ec_view_helper;
563
+
564
+ ob_start();
565
+
566
+ if( is_single() ) {
567
+ $this->single_view( $event );
568
+ } else {
569
+ $this->multi_view( $event );
570
+ }
571
+
572
+ echo $content;
573
+
574
+ if( is_single() )
575
+ $this->single_event_footer( $event );
576
+
577
+ $page_content = ob_get_contents();
578
+ ob_end_clean();
579
+
580
+ return $page_content;
581
+ }
582
+
583
+ /**
584
+ * single_view function
585
+ *
586
+ * Outputs event-specific details as HTML to be prepended to post content
587
+ * when displayed as a single page.
588
+ *
589
+ * @param Ai1ec_Event $event The event being displayed
590
+ *
591
+ * @return void
592
+ **/
593
+ function single_view( &$event )
594
+ {
595
+ global $ai1ec_view_helper,
596
+ $ai1ec_calendar_helper;
597
+
598
+ $subscribe_url = AI1EC_EXPORT_URL . "&ai1ec_post_ids=$event->post_id";
599
+ $subscribe_url = str_replace( 'webcal://', 'http://', $subscribe_url );
600
+
601
+ $args = array(
602
+ 'event' => &$event,
603
+ 'recurrence' => $event->recurrence_html,
604
+ 'categories' => $event->categories_html,
605
+ 'tags' => $event->tags_html,
606
+ 'location' => nl2br( $event->location ),
607
+ 'map' => $this->get_map_view( $event ),
608
+ 'contact' => $event->contact_html,
609
+ 'calendar_url' => $ai1ec_calendar_helper->get_calendar_url( $event ),
610
+ 'subscribe_url' => $subscribe_url,
611
+ 'google_url' => 'http://www.google.com/calendar/render?cid=' . urlencode( $subscribe_url ),
612
+ );
613
+ $ai1ec_view_helper->display( 'event-single.php', $args );
614
+ }
615
+
616
+ /**
617
+ * multi_view function
618
+ *
619
+ * Outputs event-specific details as HTML to be prepended to post content
620
+ * when displayed in a loop alongside other posts.
621
+ *
622
+ * @param Ai1ec_Event $event The event being displayed
623
+ *
624
+ * @return void
625
+ **/
626
+ function multi_view( &$event )
627
+ {
628
+ global $ai1ec_view_helper,
629
+ $ai1ec_calendar_helper;
630
+
631
+ $location = str_replace( "\n", ', ', rtrim( $event->location ) );
632
+
633
+ $args = array(
634
+ 'event' => &$event,
635
+ 'recurrence' => $event->recurrence_html,
636
+ 'categories' => $event->categories_html,
637
+ 'tags' => $event->tags_html,
638
+ 'location' => $location,
639
+ 'contact' => $event->contact_html,
640
+ 'calendar_url' => $ai1ec_calendar_helper->get_calendar_url( $event ),
641
+ );
642
+ $ai1ec_view_helper->display( 'event-multi.php', $args );
643
+ }
644
+
645
+ /**
646
+ * excerpt_view function
647
+ *
648
+ * Outputs event-specific details as HTML to be prepended to post content
649
+ * when displayed in an excerpt format.
650
+ *
651
+ * @param Ai1ec_Event $event The event being displayed
652
+ *
653
+ * @return void
654
+ **/
655
+ function excerpt_view( &$event )
656
+ {
657
+ global $ai1ec_view_helper,
658
+ $ai1ec_calendar_helper;
659
+
660
+ $location = str_replace( "\n", ', ', rtrim( $event->location ) );
661
+
662
+ $args = array(
663
+ 'event' => &$event,
664
+ 'location' => $location,
665
+ );
666
+ $ai1ec_view_helper->display( 'event-excerpt.php', $args );
667
+ }
668
+
669
+ /**
670
+ * get_map_view function
671
+ *
672
+ * Returns HTML markup displaying a Google map of the given event, if the event
673
+ * has show_map set to true. Returns a zero-length string otherwise.
674
+ *
675
+ * @return void
676
+ **/
677
+ function get_map_view( &$event )
678
+ {
679
+ global $ai1ec_view_helper, $ai1ec_events_helper;
680
+
681
+ if( ! $event->show_map )
682
+ return '';
683
+
684
+ $args = array(
685
+ 'address' => $event->address,
686
+ 'gmap_url_link' => $ai1ec_events_helper->get_gmap_url( $event, false ),
687
+ );
688
+ return $ai1ec_view_helper->get_view( 'event-map.php', $args );
689
+ }
690
+
691
+ /**
692
+ * single_event_footer function
693
+ *
694
+ * Outputs any markup that should appear below the post's content on the
695
+ * single post page for this event.
696
+ *
697
+ * @return void
698
+ **/
699
+ function single_event_footer( &$event )
700
+ {
701
+ global $ai1ec_view_helper;
702
+
703
+ $args = array(
704
+ 'event' => &$event,
705
+ );
706
+ return $ai1ec_view_helper->display( 'event-single-footer.php', $args );
707
+ }
708
+
709
+ /**
710
+ * events_categories_add_form_fields function
711
+ *
712
+ *
713
+ *
714
+ * @return void
715
+ **/
716
+ function events_categories_add_form_fields() {
717
+ global $ai1ec_view_helper;
718
+
719
+ $args = array();
720
+ $ai1ec_view_helper->display( 'event_categories-color_picker.php' );
721
+ }
722
+
723
+ /**
724
+ * events_categories_edit_form_fields function
725
+ *
726
+ *
727
+ *
728
+ * @return void
729
+ **/
730
+ function events_categories_edit_form_fields( $term ) {
731
+ global $ai1ec_view_helper, $wpdb;
732
+
733
+ $table_name = $wpdb->prefix . 'ai1ec_event_category_colors';
734
+ $color = $wpdb->get_var( "SELECT term_color FROM {$table_name} WHERE term_id = {$term->term_id}" );
735
+
736
+ $style = '';
737
+ $clr = '';
738
+
739
+ if( ! is_null( $color ) && ! empty( $color ) ) {
740
+ $style = 'style="background-color: ' . $color . '"';
741
+ $clr = $color;
742
+ }
743
+ $args = array(
744
+ 'style' => $style,
745
+ 'color' => $clr,
746
+ 'edit' => true,
747
+ );
748
+ $ai1ec_view_helper->display( 'event_categories-color_picker.php', $args );
749
+ }
750
+
751
+ /**
752
+ * edited_events_categories function
753
+ *
754
+ *
755
+ *
756
+ * @return void
757
+ **/
758
+ function created_events_categories( $term_id ) {
759
+ global $wpdb;
760
+ $tag_color_value = '';
761
+ if( isset( $_POST["tag-color-value"] ) && ! empty( $_POST["tag-color-value"] ) ) {
762
+ $tag_color_value = $_POST["tag-color-value"];
763
+ }
764
+
765
+ $table_name = $wpdb->prefix . 'ai1ec_event_category_colors';
766
+ $wpdb->insert( $table_name, array( 'term_id' => $term_id, 'term_color' => $tag_color_value ), array( '%d', '%s' ) );
767
+ }
768
+
769
+ function edited_events_categories( $term_id ) {
770
+ global $wpdb;
771
+ $tag_color_value = '';
772
+ if( isset( $_POST["tag-color-value"] ) && ! empty( $_POST["tag-color-value"] ) ) {
773
+ $tag_color_value = $_POST["tag-color-value"];
774
+ }
775
+
776
+ $table_name = $wpdb->prefix . 'ai1ec_event_category_colors';
777
+ $term = $wpdb->get_var( "SELECT term_id FROM {$table_name} WHERE term_id = {$term_id}" );
778
+
779
+ if( is_null( $term ) ) {
780
+ // term doesn't exist, create it
781
+ $wpdb->insert( $table_name, array( 'term_id' => $term_id, 'term_color' => $tag_color_value ), array( '%d', '%s' ) );
782
+ } else {
783
+ // term exist, update it
784
+ $wpdb->update( $table_name, array( 'term_color' => $tag_color_value ), array( 'term_id' => $term_id ), array( '%s' ), array( '%d' ) );
785
+ }
786
+
787
+
788
+ }
789
+ }
790
+ // END class
app/controller/class-ai1ec-exporter-controller.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-exporter-controller.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_Exporter_Controller class
11
+ *
12
+ * @package Controllers
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_Exporter_Controller {
16
+ /**
17
+ * _instance class variable
18
+ *
19
+ * Class instance
20
+ *
21
+ * @var null | object
22
+ **/
23
+ private static $_instance = NULL;
24
+
25
+ /**
26
+ * get_instance function
27
+ *
28
+ * Return singleton instance
29
+ *
30
+ * @return object
31
+ **/
32
+ static function get_instance() {
33
+ if( self::$_instance === NULL ) {
34
+ self::$_instance = new self();
35
+ }
36
+
37
+ return self::$_instance;
38
+ }
39
+
40
+ /**
41
+ * Constructor
42
+ *
43
+ * Default constructor
44
+ **/
45
+ private function __construct() { }
46
+
47
+ /**
48
+ * export_events function
49
+ *
50
+ * Export events
51
+ *
52
+ * @return void
53
+ **/
54
+ function export_events() {
55
+ global $ai1ec_events_helper,
56
+ $ai1ec_exporter_helper;
57
+ $ai1ec_cat_ids = isset( $_REQUEST['ai1ec_cat_ids'] ) && ! empty( $_REQUEST['ai1ec_cat_ids'] ) ? $_REQUEST['ai1ec_cat_ids'] : false;
58
+ $ai1ec_tag_ids = isset( $_REQUEST['ai1ec_tag_ids'] ) && ! empty( $_REQUEST['ai1ec_tag_ids'] ) ? $_REQUEST['ai1ec_tag_ids'] : false;
59
+ $ai1ec_post_ids = isset( $_REQUEST['ai1ec_post_ids'] ) && ! empty( $_REQUEST['ai1ec_post_ids'] ) ? $_REQUEST['ai1ec_post_ids'] : false;
60
+ $start = false;
61
+ $end = gmmktime() - 24 * 60 * 60; // Include any events ending today
62
+ $events = $ai1ec_events_helper->get_matching_events( $start, $end, $ai1ec_tag_ids, $ai1ec_cat_ids, $ai1ec_post_ids );
63
+ $c = new vcalendar();
64
+ $c->setProperty( 'calscale', 'GREGORIAN' );
65
+ $c->setProperty( 'method', 'PUBLISH' );
66
+ $c->setProperty( 'X-WR-CALNAME', get_bloginfo( 'name' ) );
67
+ $c->setProperty( 'X-WR-CALDESC', get_bloginfo( 'description' ) );
68
+ // Timezone setup
69
+ $tz = get_option( 'timezone_string' );
70
+ $c->setProperty( 'X-WR-TIMEZONE', $tz );
71
+ $tz_xprops = array( 'X-LIC-LOCATION' => $tz );
72
+ iCalUtilityFunctions::createTimezone( $c, $tz, $tz_xprops );
73
+
74
+ foreach( $events as $event ) {
75
+ $ai1ec_exporter_helper->insert_event_in_calendar( $event, $c );
76
+ }
77
+ $str = $c->createCalendar();
78
+ echo $str;
79
+ exit();
80
+ }
81
+ }
82
+ // END class
app/controller/class-ai1ec-importer-controller.php ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-importer-controller.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_Importer_Controller class
11
+ *
12
+ * @package Controllers
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_Importer_Controller {
16
+ /**
17
+ * _instance class variable
18
+ *
19
+ * Class instance
20
+ *
21
+ * @var null | object
22
+ **/
23
+ private static $_instance = NULL;
24
+
25
+ /**
26
+ * get_instance function
27
+ *
28
+ * Return singleton instance
29
+ *
30
+ * @return object
31
+ **/
32
+ static function get_instance() {
33
+ if( self::$_instance === NULL ) {
34
+ self::$_instance = new self();
35
+ }
36
+
37
+ return self::$_instance;
38
+ }
39
+
40
+ /**
41
+ * Constructor
42
+ *
43
+ * Default constructor
44
+ **/
45
+ private function __construct() { }
46
+
47
+ /**
48
+ * cron function
49
+ *
50
+ * Import all ICS feeds
51
+ *
52
+ * @return void
53
+ **/
54
+ function cron()
55
+ {
56
+ global $wpdb,
57
+ $ai1ec_importer_helper,
58
+ $ai1ec_events_helper;
59
+
60
+ // ====================
61
+ // = Select all feeds =
62
+ // ====================
63
+ $table_name = $wpdb->prefix . 'ai1ec_event_feeds';
64
+ $sql = "SELECT * FROM {$table_name}";
65
+ $feeds = $wpdb->get_results( $sql );
66
+
67
+ // ===============================
68
+ // = go over each iCalendar feed =
69
+ // ===============================
70
+ foreach( $feeds as $feed ) {
71
+ $ai1ec_importer_helper->parse_ics_feed( $feed );
72
+ }
73
+ }
74
+
75
+ /**
76
+ * register_importer function
77
+ *
78
+ * Registers the events calendar importer
79
+ *
80
+ * @return void
81
+ **/
82
+ function register_importer() {
83
+ global $wp_importers;
84
+
85
+ if( ! isset( $wp_importers['ai1ec_the_events_calendar'] ) ) {
86
+ $wp_importers['ai1ec_the_events_calendar'] = array(
87
+ __( 'The Events Calendar → All-in-One Events Calendar', AI1EC_PLUGIN_NAME ),
88
+ __( 'Imports events created using The Events Calendar plugin into the All-in-One Events Calendar', AI1EC_PLUGIN_NAME ),
89
+ array( &$this, 'import_the_events_calendar' )
90
+ );
91
+ }
92
+ }
93
+
94
+ /**
95
+ * import_the_events_calendar function
96
+ *
97
+ * Import events from The Events Calendar into Ai1ec.
98
+ *
99
+ * @return void
100
+ **/
101
+ function import_the_events_calendar() {
102
+ global $ai1ec_view_helper,
103
+ $ai1ec_events_helper;
104
+
105
+ $args = array(
106
+ 'post_type' => 'post',
107
+ 'numberposts' => -1,
108
+ 'meta_key' => '_isEvent',
109
+ 'meta_value' => 'yes'
110
+ );
111
+ $posts = get_posts( $args );
112
+
113
+ $imported_events = 0;
114
+ foreach( $posts as $post )
115
+ {
116
+ $event = new Ai1ec_Event( null );
117
+ $postmeta = get_post_custom( $post->ID );
118
+
119
+ // Need this to offset dates coming from The Events Calendar
120
+ $gm_diff = mktime( 0 ) - gmmktime( 0 );
121
+
122
+ $event->allday = $postmeta['_EventAllDay'][0] == 'yes' || $postmeta['_EventAllDay'][0] == 1;
123
+ $event->start = strtotime( $postmeta['_EventStartDate'][0] ) - $gm_diff;
124
+ $event->end = strtotime( $postmeta['_EventEndDate'][0] ) - $gm_diff;
125
+ // If all-day event, align start/end to start/end of day
126
+ if( $event->allday ) {
127
+ $event->start = $ai1ec_events_helper->gmgetdate( $event->start );
128
+ $event->start = gmmktime( 0, 0, 0, $event->start['mon'], $event->start['mday'], $event->start['year'] );
129
+ $event->end = $ai1ec_events_helper->gmgetdate( $event->end );
130
+ $event->end = gmmktime( 0, 0, 0, $event->end['mon'], $event->end['mday'], $event->end['year'] );
131
+ }
132
+ // Finally, convert to GMT storage format
133
+ $event->start = $ai1ec_events_helper->local_to_gmt( $event->start );
134
+ $event->end = $ai1ec_events_helper->local_to_gmt( $event->end );
135
+ // Bug in The Events Calendar where some all-day events start and end at the same time
136
+ if( $event->allday && $event->end - $event->start < ( 24 * 60 * 60 ) )
137
+ $event->end = $event->start + 24 * 60 * 60;
138
+ $event->venue = $postmeta['_EventVenue'][0];
139
+ $event->country = $postmeta['_EventCountry'][0];
140
+ $event->city = $postmeta['_EventCity'][0];
141
+ $event->province = $postmeta['_EventState'][0];
142
+ $event->postal_code = $postmeta['_EventZip'][0];
143
+ $event->address = array();
144
+ if( $postmeta['$_EventAddress'] ) $event->address[] = $postmeta['$_EventAddress'];
145
+ if( $event->city ) $event->address[] = $event->city;
146
+ if( $event->province ) $event->address[] = $event->province;
147
+ if( $event->postal_code ) $event->address[] = $event->postal_code;
148
+ if( $event->country ) $event->address[] = $event->country;
149
+ $event->address = join( ', ', $event->address );
150
+ $event->show_map = $postmeta['_EventShowMapLink'][0] == 'true' || $postmeta['_EventShowMap'][0] == 'true';
151
+ $event->cost = $postmeta['_EventCost'][0];
152
+ $event->contact_phone = $postmeta['_EventPhone'][0];
153
+ $event->post = get_object_vars( $post );
154
+ $event->post["post_type"] = AI1EC_POST_TYPE;
155
+ unset( $event->post["ID"] );
156
+
157
+ // Transfer post categories => event categories, post tags => event tags
158
+ $terms = wp_get_post_terms( $post->ID, array( 'category', 'post_tag' ) );
159
+ $event->categories = array();
160
+ $event->tags = array();
161
+ foreach( $terms as $term )
162
+ {
163
+ switch( $term->taxonomy )
164
+ {
165
+ case 'category':
166
+ // Ignore special "Events" category by The Events Calendar
167
+ if( $term->name == 'Events' )
168
+ break;
169
+ // Need to find out the category ID, if it exists.
170
+ $event_term = get_term_by( 'name', $term->name, 'events_categories' );
171
+ // If no category exists, create it.
172
+ if( $event_term === false )
173
+ $event_term = (object) wp_insert_term(
174
+ $term->name,
175
+ 'events_categories',
176
+ array(
177
+ 'description' => $term->description,
178
+ 'slug' => $term->slug
179
+ )
180
+ );
181
+ $event->categories[] = $event_term->term_id;
182
+ break;
183
+
184
+ case 'post_tag':
185
+ // For some reason tag-like taxonomies are treated differently; term
186
+ // IDs cannot be used; instead the actual term name must be appended
187
+ $event->tags[] = $term->name;
188
+ break;
189
+ }
190
+ }
191
+
192
+ $post_id = $event->save();
193
+ $ai1ec_events_helper->cache_event( $event, $post_id );
194
+
195
+ $imported_events++;
196
+ }
197
+
198
+ $ai1ec_view_helper->display( "import.php", array( 'imported_events' => $imported_events ) );
199
+ }
200
+ }
201
+ // END class
app/controller/class-ai1ec-settings-controller.php ADDED
@@ -0,0 +1,325 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-settings-controller.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_Settings_Controller class
11
+ *
12
+ * @package Controllers
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_Settings_Controller {
16
+ /**
17
+ * _instance class variable
18
+ *
19
+ * Class instance
20
+ *
21
+ * @var null | object
22
+ **/
23
+ private static $_instance = NULL;
24
+
25
+ /**
26
+ * Constructor
27
+ *
28
+ * Default constructor
29
+ **/
30
+ private function __construct() { }
31
+
32
+ /**
33
+ * get_instance function
34
+ *
35
+ * Return singleton instance
36
+ *
37
+ * @return object
38
+ **/
39
+ static function get_instance() {
40
+ if( self::$_instance === NULL ) {
41
+ self::$_instance = new self();
42
+ }
43
+
44
+ return self::$_instance;
45
+ }
46
+
47
+ /**
48
+ * view function
49
+ *
50
+ * Display this plugin's settings page in the admin.
51
+ *
52
+ * @return void
53
+ **/
54
+ function view() {
55
+ global $ai1ec_view_helper,
56
+ $ai1ec_settings;
57
+
58
+ if( isset( $_REQUEST['ai1ec_save_settings'] ) ) {
59
+ $this->save();
60
+ }
61
+ $args = array(
62
+ 'settings_page' => $ai1ec_settings->settings_page
63
+ );
64
+ $ai1ec_view_helper->display( 'settings.php', $args );
65
+ }
66
+
67
+ /**
68
+ * save function
69
+ *
70
+ * Save the submitted settings form.
71
+ *
72
+ * @return void
73
+ **/
74
+ function save() {
75
+ global $ai1ec_settings,
76
+ $ai1ec_view_helper;
77
+
78
+ $ai1ec_settings->update( $_REQUEST );
79
+ $ai1ec_settings->save();
80
+
81
+ $args = array(
82
+ "msg" => __( "Settings Updated.", AI1EC_PLUGIN_NAME )
83
+ );
84
+
85
+ $ai1ec_view_helper->display( "save_successful.php", $args );
86
+ }
87
+
88
+ /**
89
+ * add_ics_feed function
90
+ *
91
+ * Adds submitted ics feed to the database
92
+ *
93
+ * @return string JSON output
94
+ **/
95
+ function add_ics_feed() {
96
+ global $ai1ec_view_helper,
97
+ $wpdb;
98
+
99
+ $table_name = $wpdb->prefix . 'ai1ec_event_feeds';
100
+
101
+ $wpdb->insert(
102
+ $table_name,
103
+ array(
104
+ 'feed_url' => $_REQUEST["feed_url"], // convert webcal to http
105
+ 'feed_category' => $_REQUEST["feed_category"],
106
+ 'feed_tags' => $_REQUEST["feed_tags"],
107
+ ),
108
+ array(
109
+ '%s',
110
+ '%d',
111
+ '%s'
112
+ )
113
+ );
114
+ $feed_id = $wpdb->insert_id;
115
+ ob_start();
116
+ $feed_category = get_term( $_REQUEST["feed_category"], 'events_categories' );
117
+ $args = array(
118
+ 'feed_url' => $_REQUEST["feed_url"],
119
+ 'event_category' => $feed_category->name,
120
+ 'tags' => $_REQUEST["feed_tags"],
121
+ 'feed_id' => $feed_id
122
+ );
123
+ // display added feed row
124
+ $ai1ec_view_helper->display( 'feed_row.php', $args );
125
+
126
+ $output = ob_get_contents();
127
+ ob_end_clean();
128
+
129
+ $output = array(
130
+ "error" => 0,
131
+ "message" => stripslashes( $output )
132
+ );
133
+
134
+ echo json_encode( $output );
135
+ exit();
136
+ }
137
+
138
+ /**
139
+ * flush_ics_feed function
140
+ *
141
+ * Deletes all event posts that are from that selected feed
142
+ *
143
+ * @return void
144
+ **/
145
+ function flush_ics_feed()
146
+ {
147
+ global $wpdb,
148
+ $ai1ec_view_helper;
149
+ $ics_id = (int) $_REQUEST['ics_id'];
150
+ $table_name = $wpdb->prefix . 'ai1ec_event_feeds';
151
+ $feed_url = $wpdb->get_var( $wpdb->prepare( "SELECT feed_url FROM $table_name WHERE feed_id = %d", $ics_id ) );
152
+ if( $feed_url )
153
+ {
154
+ $table_name = $wpdb->prefix . 'ai1ec_events';
155
+ $sql = "SELECT post_id FROM {$table_name} WHERE ical_feed_url = '%s'";
156
+ $events = $wpdb->get_results( $wpdb->prepare( $sql, $feed_url ) );
157
+ $total = count( $events );
158
+
159
+ foreach( $events as $event ) {
160
+ // delete post (this will trigger deletion of cached events, and remove the event from events table)
161
+ wp_delete_post( $event->post_id, 'true' );
162
+ }
163
+
164
+ $output = array(
165
+ 'error' => false,
166
+ 'message' => sprintf( __( 'Flushed %d events', AI1EC_PLUGIN_NAME ), $total ),
167
+ 'count' => $total,
168
+ );
169
+ }
170
+ else
171
+ {
172
+ $output = array(
173
+ 'error' => true,
174
+ 'message' => 'Invalid feed'
175
+ );
176
+ }
177
+
178
+ $ai1ec_view_helper->json_response( $output );
179
+ }
180
+
181
+ /**
182
+ * update_ics_feed function
183
+ *
184
+ * Imports the selected iCalendar feed
185
+ *
186
+ * @return void
187
+ **/
188
+ function update_ics_feed()
189
+ {
190
+ global $wpdb,
191
+ $ai1ec_view_helper,
192
+ $ai1ec_importer_helper;
193
+
194
+ $feed_id = (int) $_REQUEST['ics_id'];
195
+ $table_name = $wpdb->prefix . 'ai1ec_event_feeds';
196
+ $feed = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table_name WHERE feed_id = %d", $feed_id ) );
197
+
198
+ if( $feed )
199
+ {
200
+ $count = $ai1ec_importer_helper->parse_ics_feed( $feed );
201
+ $output = array(
202
+ 'error' => false,
203
+ 'message' => sprintf( __( 'Imported %d events', AI1EC_PLUGIN_NAME ), $count ),
204
+ 'flush_label' => sprintf( _n( 'Flush 1 event', 'Flush %s events', $count, AI1EC_PLUGIN_NAME ), $count ),
205
+ 'count' => $count,
206
+ );
207
+ }
208
+ else
209
+ {
210
+ $output = array(
211
+ 'error' => true,
212
+ 'message' => 'Invalid feed'
213
+ );
214
+ }
215
+
216
+ $ai1ec_view_helper->json_response( $output );
217
+ }
218
+
219
+ /**
220
+ * delete_ics_feed function
221
+ *
222
+ * Deletes submitted ics feed id from the database
223
+ *
224
+ * @return String JSON output
225
+ **/
226
+ function delete_ics_feed()
227
+ {
228
+ global $wpdb,
229
+ $ai1ec_view_helper;
230
+
231
+ $ics_id = (int) $_REQUEST['ics_id'];
232
+ $table_name = $wpdb->prefix . 'ai1ec_event_feeds';
233
+ $wpdb->query( $wpdb->prepare( "DELETE FROM {$table_name} WHERE feed_id = %d", $ics_id ) );
234
+ $output = array(
235
+ 'error' => false,
236
+ 'message' => 'Request successful.'
237
+ );
238
+
239
+ $ai1ec_view_helper->json_response( $output );
240
+ }
241
+
242
+ /**
243
+ * add_meta_boxes function
244
+ *
245
+ *
246
+ *
247
+ * @return void
248
+ **/
249
+ function add_meta_boxes() {
250
+ global $ai1ec_settings_helper,
251
+ $ai1ec_settings;
252
+
253
+ /* Add the 'General Settings' meta box. */
254
+ add_meta_box( 'general-settings',
255
+ _x( 'General Settings', 'meta box', AI1EC_PLUGIN_NAME ),
256
+ array( &$ai1ec_settings_helper, 'general_settings_meta_box' ),
257
+ $ai1ec_settings->settings_page,
258
+ 'left-side',
259
+ 'default' );
260
+ /* Add the 'The Seed Studio' meta box. */
261
+ add_meta_box( 'the-seed-studio-settings',
262
+ _x( 'The Seed Studio Support', 'meta box', AI1EC_PLUGIN_NAME ),
263
+ array( &$ai1ec_settings_helper, 'the_seed_studio_meta_box' ),
264
+ $ai1ec_settings->settings_page,
265
+ 'right-side',
266
+ 'default' );
267
+ /* Add the 'ICS Import Settings' meta box. */
268
+ add_meta_box( 'ics-import-settings',
269
+ _x( 'ICS Import Settings', 'meta box', AI1EC_PLUGIN_NAME ),
270
+ array( &$ai1ec_settings_helper, 'ics_import_settings_meta_box' ),
271
+ $ai1ec_settings->settings_page,
272
+ 'left-side',
273
+ 'default' );
274
+
275
+ }
276
+
277
+ /**
278
+ * admin_print_styles function
279
+ *
280
+ *
281
+ *
282
+ * @return void
283
+ **/
284
+ function admin_print_styles() {
285
+ global $ai1ec_view_helper;
286
+ $ai1ec_view_helper->display_css( 'settings.css' );
287
+ }
288
+
289
+ /**
290
+ * admin_print_scripts function
291
+ *
292
+ *
293
+ *
294
+ * @return void
295
+ **/
296
+ function admin_print_scripts() {
297
+ global $ai1ec_settings;
298
+ ?>
299
+ <script type="text/javascript">
300
+ //<![CDATA[
301
+ var ai1ec_settings_page = '<?php echo $ai1ec_settings->settings_page; ?>';
302
+ //]]>
303
+ </script>
304
+ <?php
305
+ }
306
+
307
+ /**
308
+ * admin_enqueue_scripts function
309
+ *
310
+ *
311
+ *
312
+ * @return void
313
+ **/
314
+ function admin_enqueue_scripts( $hook_suffix ) {
315
+ global $ai1ec_settings;
316
+
317
+ if( isset( $ai1ec_settings->settings_page ) && $hook_suffix == $ai1ec_settings->settings_page ) {
318
+ wp_enqueue_script( 'common' );
319
+ wp_enqueue_script( 'wp-lists' );
320
+ wp_enqueue_script( 'postbox' );
321
+ wp_enqueue_script( 'ai1ec-settings', AI1EC_JS_URL . '/settings.js', array( 'jquery' ) );
322
+ }
323
+ }
324
+ }
325
+ // END class
app/exception/class-ai1ec-event-not-found.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-event-not-found.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_Event_Not_Found class
11
+ *
12
+ * @package Exceptions
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_Event_Not_Found extends Exception {
16
+
17
+ }
18
+ // END class
app/exception/class-ai1ec-file-not-found.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-file-not-found.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_File_Not_Found class
11
+ *
12
+ * @package Exceptions
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_File_Not_Found extends Exception {
16
+
17
+ }
18
+ // END class
app/exception/class-ai1ec-file-not-provided.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-file-not-provided.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_File_Not_Provided class
11
+ *
12
+ * @package Exceptions
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_File_Not_Provided extends Exception {
16
+
17
+ }
18
+ // END class
app/exception/class-ai1ec-invalid-argument.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-invalid-argument.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_Invalid_Argument class
11
+ *
12
+ * @package Exceptions
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_Invalid_Argument extends Exception {
16
+
17
+ }
18
+ // END class
app/helper/class-ai1ec-app-helper.php ADDED
@@ -0,0 +1,636 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-app-helper.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_App_Helper class
11
+ *
12
+ * @package Helpers
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_App_Helper {
16
+ /**
17
+ * _instance class variable
18
+ *
19
+ * Class instance
20
+ *
21
+ * @var null | object
22
+ **/
23
+ private static $_instance = NULL;
24
+
25
+ /**
26
+ * Constructor
27
+ *
28
+ * Default constructor
29
+ **/
30
+ private function __construct() { }
31
+
32
+ /**
33
+ * get_instance function
34
+ *
35
+ * Return singleton instance
36
+ *
37
+ * @return object
38
+ **/
39
+ static function get_instance() {
40
+ if( self::$_instance === NULL ) {
41
+ self::$_instance = new self();
42
+ }
43
+
44
+ return self::$_instance;
45
+ }
46
+
47
+ /**
48
+ * map_meta_cap function
49
+ *
50
+ * Assigns proper capability
51
+ *
52
+ * @return void
53
+ **/
54
+ function map_meta_cap( $caps, $cap, $user_id, $args ) {
55
+ // If editing, deleting, or reading an event, get the post and post type object.
56
+ if( 'edit_ai1ec_event' == $cap || 'delete_ai1ec_event' == $cap || 'read_ai1ec_event' == $cap ) {
57
+ $post = get_post( $args[0] );
58
+ $post_type = get_post_type_object( $post->post_type );
59
+ /* Set an empty array for the caps. */
60
+ $caps = array();
61
+ }
62
+
63
+ /* If editing an event, assign the required capability. */
64
+ if( 'edit_ai1ec_event' == $cap ) {
65
+ if( $user_id == $post->post_author )
66
+ $caps[] = $post_type->cap->edit_posts;
67
+ else
68
+ $caps[] = $post_type->cap->edit_others_posts;
69
+ }
70
+
71
+ /* If deleting an event, assign the required capability. */
72
+ else if( 'delete_ai1ec_event' == $cap ) {
73
+ if( $user_id == $post->post_author )
74
+ $caps[] = $post_type->cap->delete_posts;
75
+ else
76
+ $caps[] = $post_type->cap->delete_others_posts;
77
+ }
78
+
79
+ /* If reading a private event, assign the required capability. */
80
+ else if( 'read_ai1ec_event' == $cap ) {
81
+ if( 'private' != $post->post_status )
82
+ $caps[] = 'read';
83
+ elseif ( $user_id == $post->post_author )
84
+ $caps[] = 'read';
85
+ else
86
+ $caps[] = $post_type->cap->read_private_posts;
87
+ }
88
+
89
+ /* Return the capabilities required by the user. */
90
+ return $caps;
91
+ }
92
+
93
+ /**
94
+ * create_post_type function
95
+ *
96
+ * Create event's custom post type
97
+ * and registers events_categories and events_tags under
98
+ * event's custom post type taxonomy
99
+ *
100
+ * @return void
101
+ **/
102
+ function create_post_type() {
103
+
104
+ // if the event contributor role is not created, create it
105
+ if( !get_role( 'ai1ec_event_assistant' ) ) {
106
+
107
+ // creating event contributor role with the same capabilities
108
+ // as subscriber role, later in this file, event contributor role will be extended
109
+ // to include more capabilities
110
+ $caps = get_role( 'subscriber' )->capabilities;
111
+ add_role( 'ai1ec_event_assistant', 'Event Contributor', $caps );
112
+
113
+ // add event managing capability to administrator, editor, author
114
+ foreach( array( 'administrator', 'editor', 'author' ) as $user ) {
115
+ $role = get_role( $user );
116
+ // read events
117
+ $role->add_cap( 'read_ai1ec_event' );
118
+ // edit events
119
+ $role->add_cap( 'edit_ai1ec_event' );
120
+ $role->add_cap( 'edit_ai1ec_events' );
121
+ $role->add_cap( 'edit_others_ai1ec_events' );
122
+ $role->add_cap( 'edit_private_ai1ec_events' );
123
+ $role->add_cap( 'edit_published_ai1ec_events' );
124
+ // delete events
125
+ $role->add_cap( 'delete_ai1ec_event' );
126
+ $role->add_cap( 'delete_ai1ec_events' );
127
+ $role->add_cap( 'delete_others_ai1ec_events' );
128
+ $role->add_cap( 'delete_published_ai1ec_events' );
129
+ $role->add_cap( 'delete_private_ai1ec_events' );
130
+ // publish events
131
+ $role->add_cap( 'publish_ai1ec_events' );
132
+ // read private events
133
+ $role->add_cap( 'read_private_ai1ec_events' );
134
+ }
135
+
136
+ // add event managing capability to contributors
137
+ $role = get_role( 'ai1ec_event_assistant' );
138
+ $role->add_cap( 'edit_ai1ec_events' );
139
+ $role->add_cap( 'delete_ai1ec_event' );
140
+ $role->add_cap( 'read' );
141
+ }
142
+ // ===============================
143
+ // = labels for custom post type =
144
+ // ===============================
145
+ $labels = array(
146
+ 'name' => _x( 'Events', AI1EC_PLUGIN_NAME ),
147
+ 'singular_name' => _x( 'Event', AI1EC_PLUGIN_NAME ),
148
+ 'add_new' => __( 'Add New', AI1EC_PLUGIN_NAME ),
149
+ 'add_new_item' => __( 'Add New Event', AI1EC_PLUGIN_NAME ),
150
+ 'edit_item' => __( 'Edit Event', AI1EC_PLUGIN_NAME ),
151
+ 'new_item' => __( 'New Event', AI1EC_PLUGIN_NAME ),
152
+ 'view_item' => __( 'View Event', AI1EC_PLUGIN_NAME ),
153
+ 'search_items' => __( 'Search Events', AI1EC_PLUGIN_NAME ),
154
+ 'not_found' => __( 'No Events found', AI1EC_PLUGIN_NAME ),
155
+ 'not_found_in_trash' => __( 'No Events found in Trash', AI1EC_PLUGIN_NAME ),
156
+ 'parent_item_colon' => __( 'Parent Event', AI1EC_PLUGIN_NAME ),
157
+ 'menu_name' => __( 'Events', AI1EC_PLUGIN_NAME ),
158
+ 'all_items' => $this->get_all_items_name()
159
+ );
160
+
161
+
162
+ // ================================
163
+ // = support for custom post type =
164
+ // ================================
165
+ $supports = array( 'title', 'editor', 'comments' );
166
+
167
+ // =============================
168
+ // = args for custom post type =
169
+ // =============================
170
+ $args = array(
171
+ 'labels' => $labels,
172
+ 'public' => true,
173
+ 'publicly_queryable' => true,
174
+ 'show_ui' => true,
175
+ 'show_in_menu' => true,
176
+ 'query_var' => true,
177
+ 'rewrite' => true,
178
+ 'capability_type' => array( 'ai1ec_event', 'ai1ec_events' ),
179
+ 'capabilities' => array(
180
+ 'read_post' => 'read_ai1ec_event',
181
+ 'edit_post' => 'edit_ai1ec_event',
182
+ 'edit_posts' => 'edit_ai1ec_events',
183
+ 'edit_others_posts' => 'edit_others_ai1ec_events',
184
+ 'edit_private_posts' => 'edit_private_ai1ec_events',
185
+ 'edit_published_posts' => 'edit_published_ai1ec_events',
186
+ 'delete_post' => 'delete_ai1ec_event',
187
+ 'delete_posts' => 'delete_ai1ec_events',
188
+ 'delete_others_posts' => 'delete_others_ai1ec_events',
189
+ 'delete_published_posts' => 'delete_published_ai1ec_events',
190
+ 'delete_private_posts' => 'delete_private_ai1ec_events',
191
+ 'publish_posts' => 'publish_ai1ec_events',
192
+ 'read_private_posts' => 'read_private_ai1ec_events' ),
193
+ 'has_archive' => true,
194
+ 'hierarchical' => false,
195
+ 'menu_position' => 5,
196
+ 'supports' => $supports
197
+ );
198
+
199
+ // ========================================
200
+ // = labels for event categories taxonomy =
201
+ // ========================================
202
+ $events_categories_labels = array(
203
+ 'name' => _x( 'Event Categories', AI1EC_PLUGIN_NAME ),
204
+ 'singular_name' => _x( 'Event Category', AI1EC_PLUGIN_NAME )
205
+ );
206
+
207
+ // ==================================
208
+ // = labels for event tags taxonomy =
209
+ // ==================================
210
+ $events_tags_labels = array(
211
+ 'name' => _x( 'Event Tags', AI1EC_PLUGIN_NAME ),
212
+ 'singular_name' => _x( 'Event Tag', AI1EC_PLUGIN_NAME )
213
+ );
214
+
215
+ // ======================================
216
+ // = args for event categories taxonomy =
217
+ // ======================================
218
+ $events_categories_args = array(
219
+ 'labels' => $events_categories_labels,
220
+ 'hierarchical' => true,
221
+ 'rewrite' => array( 'slug' => 'events_categories' ),
222
+ 'capabilities' => array(
223
+ 'manage_terms' => 'manage_categories',
224
+ 'edit_terms' => 'manage_categories',
225
+ 'delete_terms' => 'manage_categories',
226
+ 'assign_terms' => 'edit_ai1ec_events'
227
+ )
228
+ );
229
+
230
+ // ================================
231
+ // = args for event tags taxonomy =
232
+ // ================================
233
+ $events_tags_args = array(
234
+ 'labels' => $events_tags_labels,
235
+ 'hierarchical' => false,
236
+ 'rewrite' => array( 'slug' => 'events_tags' ),
237
+ 'capabilities' => array(
238
+ 'manage_terms' => 'manage_categories',
239
+ 'edit_terms' => 'manage_categories',
240
+ 'delete_terms' => 'manage_categories',
241
+ 'assign_terms' => 'edit_ai1ec_events'
242
+ )
243
+ );
244
+
245
+ // ======================================
246
+ // = register event categories taxonomy =
247
+ // ======================================
248
+ register_taxonomy( 'events_categories', array( AI1EC_POST_TYPE ), $events_categories_args );
249
+
250
+ // ================================
251
+ // = register event tags taxonomy =
252
+ // ================================
253
+ register_taxonomy( 'events_tags', array( AI1EC_POST_TYPE ), $events_tags_args );
254
+
255
+ // ========================================
256
+ // = register custom post type for events =
257
+ // ========================================
258
+ register_post_type( AI1EC_POST_TYPE, $args );
259
+ }
260
+
261
+ /**
262
+ * taxonomy_filter_restrict_manage_posts function
263
+ *
264
+ * Adds filter dropdowns for event categories and event tags
265
+ *
266
+ * @return void
267
+ **/
268
+ function taxonomy_filter_restrict_manage_posts() {
269
+ global $typenow;
270
+
271
+ // =============================================
272
+ // = add the dropdowns only on the events page =
273
+ // =============================================
274
+ if( $typenow == AI1EC_POST_TYPE ) {
275
+ $filters = get_object_taxonomies( $typenow );
276
+ foreach( $filters as $tax_slug ) {
277
+ $tax_obj = get_taxonomy( $tax_slug );
278
+ wp_dropdown_categories( array(
279
+ 'show_option_all' => __( 'Show All ' . $tax_obj->label, AI1EC_PLUGIN_NAME ),
280
+ 'taxonomy' => $tax_slug,
281
+ 'name' => $tax_obj->name,
282
+ 'orderby' => 'name',
283
+ 'selected' => $_GET[$tax_slug],
284
+ 'hierarchical' => $tax_obj->hierarchical,
285
+ 'show_count' => false,
286
+ 'hide_empty' => true
287
+ ));
288
+ }
289
+ }
290
+ }
291
+
292
+ /**
293
+ * get_all_items_name function
294
+ *
295
+ * If current user can publish events and there
296
+ * is at least 1 event pending, append the pending
297
+ * events number to the menu
298
+ *
299
+ * @return string
300
+ **/
301
+ function get_all_items_name() {
302
+
303
+ // if current user can publish events
304
+ if( current_user_can( 'publish_ai1ec_events' ) ) {
305
+ // get all pending events
306
+ $query = new WP_Query( array ( 'post_type' => 'ai1ec_event', 'post_status' => 'pending', 'posts_per_page' => -1, ) );
307
+
308
+ // at least 1 pending event?
309
+ if( $query->post_count > 0 ) {
310
+ // append the pending events number to the menu
311
+ return sprintf( __( 'All Events <span class="update-plugins count-%d" title="%d Pending Events"><span class="update-count">%d</span></span>', AI1EC_PLUGIN_NAME ),
312
+ $query->post_count, $query->post_count, $query->post_count );
313
+ }
314
+ }
315
+
316
+ // no pending events, or the user doesn't have sufficient capabilities
317
+ return __( 'All Events', AI1EC_PLUGIN_NAME );
318
+ }
319
+
320
+ /**
321
+ * taxonomy_filter_post_type_request function
322
+ *
323
+ * Adds filtering of events list by event tags and event categories
324
+ *
325
+ * @return void
326
+ **/
327
+ function taxonomy_filter_post_type_request( $query ) {
328
+ global $pagenow, $typenow;
329
+ if( 'edit.php' == $pagenow ) {
330
+ $filters = get_object_taxonomies( $typenow );
331
+ foreach( $filters as $tax_slug ) {
332
+ $var = &$query->query_vars[$tax_slug];
333
+ if( isset( $var ) ) {
334
+ $term = null;
335
+
336
+ if( is_numeric( $var ) )
337
+ $term = get_term_by( 'id', $var, $tax_slug );
338
+ else
339
+ $term = get_term_by( 'slug', $var, $tax_slug );
340
+
341
+ $var = $term->slug;
342
+ }
343
+ }
344
+ }
345
+ // ===========================
346
+ // = Order by Event date ASC =
347
+ // ===========================
348
+ if( $typenow == 'ai1ec_event' ) {
349
+ if( ! array_key_exists( 'orderby', $query->query_vars ) ) {
350
+ $query->query_vars["orderby"] = 'ai1ec_event_date';
351
+ $query->query_vars["order"] = 'desc';
352
+ }
353
+ }
354
+
355
+ }
356
+
357
+ /**
358
+ * orderby function
359
+ *
360
+ * Orders events by event date
361
+ *
362
+ * @param string $orderby Orderby sql
363
+ * @param object $wp_query
364
+ *
365
+ * @return void
366
+ **/
367
+ function orderby( $orderby, $wp_query ) {
368
+ global $typenow, $wpdb, $post;
369
+
370
+ if( $typenow == 'ai1ec_event' ) {
371
+ $wp_query->query = wp_parse_args( $wp_query->query );
372
+ $table_name = $wpdb->prefix . 'ai1ec_events';
373
+ if( 'ai1ec_event_date' == @$wp_query->query['orderby'] ) {
374
+ $orderby = "(SELECT start FROM {$table_name} WHERE post_id = $wpdb->posts.ID) " . $wp_query->get('order');
375
+ } else if( empty( $wp_query->query['orderby'] ) ) {
376
+ $orderby = "(SELECT start FROM {$table_name} WHERE post_id = $wpdb->posts.ID) " . 'desc';
377
+ }
378
+ }
379
+ return $orderby;
380
+ }
381
+
382
+ /**
383
+ * add_meta_boxes function
384
+ *
385
+ * Display event meta_box when creating or editing an event
386
+ *
387
+ * @return void
388
+ **/
389
+ function add_meta_boxes() {
390
+ global $ai1ec_events_controller;
391
+ add_meta_box(
392
+ AI1EC_POST_TYPE,
393
+ __( 'Event Details', AI1EC_PLUGIN_NAME ),
394
+ array( &$ai1ec_events_controller, 'meta_box_view' ),
395
+ AI1EC_POST_TYPE
396
+ );
397
+ }
398
+
399
+ /**
400
+ * change_columns function
401
+ *
402
+ * Adds Event date/time column to our custom post type
403
+ * and renames Date column to Post Date
404
+ *
405
+ * @param array $columns Existing columns
406
+ *
407
+ * @return array Updated columns array
408
+ **/
409
+ function change_columns( $columns ) {
410
+ $columns["date"] = __( 'Post Date', AI1EC_PLUGIN_NAME );
411
+ $columns["ai1ec_event_date"] = __( 'Event date/time', AI1EC_PLUGIN_NAME );
412
+ return $columns;
413
+ }
414
+
415
+ /**
416
+ * custom_columns function
417
+ *
418
+ * Adds content for custom columns
419
+ *
420
+ * @return void
421
+ **/
422
+ function custom_columns( $column, $post_id ) {
423
+ global $ai1ec_events_helper;
424
+ switch( $column ) {
425
+ case 'ai1ec_event_date':
426
+ $e = new Ai1ec_Event( $post_id );
427
+ echo $e->short_start_date . ' ' . $e->short_start_time . " - " . $e->short_end_date . ' ' .$e->short_end_time;
428
+ break;
429
+ }
430
+ }
431
+
432
+ /**
433
+ * sortable_columns function
434
+ *
435
+ * Enable sorting of columns
436
+ *
437
+ * @return void
438
+ **/
439
+ function sortable_columns( $columns ) {
440
+ $columns["ai1ec_event_date"] = 'ai1ec_event_date';
441
+ return $columns;
442
+ }
443
+
444
+ /**
445
+ * get_param function
446
+ *
447
+ * Tries to return the parameter from POST and GET
448
+ * incase it is missing, default value is returned
449
+ *
450
+ * @param string $param Parameter to return
451
+ * @param mixed $default Default value
452
+ *
453
+ * @return mixed
454
+ **/
455
+ function get_param( $param, $default='' ) {
456
+ return isset( $_POST[$param] )
457
+ ? $_POST[$param]
458
+ : isset( $_GET[$param] )
459
+ ? $_GET[$param]
460
+ : $default;
461
+ }
462
+
463
+ /**
464
+ * get_param_delimiter_char function
465
+ *
466
+ * Returns the delimiter character in a link
467
+ *
468
+ * @param string $link Link to parse
469
+ *
470
+ * @return string
471
+ **/
472
+ function get_param_delimiter_char( $link ) {
473
+ return strpos( $link, '?' ) === false ? '?' : '&';
474
+ }
475
+
476
+ /**
477
+ * inject_categories function
478
+ *
479
+ * Displays event categories whenever post categories are requested
480
+ *
481
+ * @param array $terms Terms to be returned by get_terms()
482
+ * @param array $taxonomies Taxonomies requested in get_terms()
483
+ * @param array $args Args passed to get_terms()
484
+ *
485
+ * @return string|array If "category" taxonomy was requested, then returns
486
+ * $terms with fake category pointing to calendar page
487
+ * with its children being the event categories
488
+ **/
489
+ function inject_categories( $terms, $taxonomies, $args )
490
+ {
491
+ global $ai1ec_settings;
492
+
493
+ if( in_array( 'category', $taxonomies ) )
494
+ {
495
+ // Create fake calendar page category
496
+ $count_args = $args;
497
+ $count_args['fields'] = 'count';
498
+ $count = get_terms( 'events_categories', $count_args );
499
+ $post = get_post( $ai1ec_settings->calendar_page_id );
500
+ switch( $args['fields'] )
501
+ {
502
+ case 'all':
503
+ $calendar = (object) array(
504
+ 'term_id' => AI1EC_FAKE_CATEGORY_ID,
505
+ 'name' => $post->post_title,
506
+ 'slug' => $post->post_name,
507
+ 'taxonomy' => 'events_categories',
508
+ 'description' => '',
509
+ 'parent' => 0,
510
+ 'count' => $count,
511
+ );
512
+ break;
513
+ case 'ids':
514
+ $calendar = 'ai1ec_calendar';
515
+ break;
516
+ case 'names':
517
+ $calendar = $post->post_title;
518
+ break;
519
+ }
520
+ $terms[] = $calendar;
521
+
522
+ if( $args['hierarchical'] ) {
523
+ $children = get_terms( 'events_categories', $args );
524
+ foreach( $children as &$child ) {
525
+ if( is_object( $child ) && $child->parent == 0 )
526
+ $child->parent = AI1EC_FAKE_CATEGORY_ID;
527
+ $terms[] = $child;
528
+ }
529
+ }
530
+ }
531
+
532
+ return $terms;
533
+ }
534
+
535
+ /**
536
+ * function calendar_term_link
537
+ *
538
+ * Corrects the URL for the calendar page when injected into the post
539
+ * categories.
540
+ *
541
+ * @param string $link The normally generated link
542
+ * @param object $term The term that we're getting the link for
543
+ * @param string $taxonomy The name of the taxonomy of interest
544
+ *
545
+ * @return string The correct link to the calendar page
546
+ */
547
+ function calendar_term_link( $link, $term, $taxonomy )
548
+ {
549
+ global $ai1ec_calendar_helper;
550
+
551
+ if( $taxonomy == 'events_categories' ) {
552
+ if( $term->term_id == AI1EC_FAKE_CATEGORY_ID )
553
+ $link = $ai1ec_calendar_helper->get_calendar_url( null );
554
+ else
555
+ $link = $ai1ec_calendar_helper->get_calendar_url( null, array( $term->term_id ) );
556
+ }
557
+
558
+ return $link;
559
+ }
560
+
561
+ /**
562
+ * function selected_category_link
563
+ *
564
+ * Corrects the output of wp_list_categories so that the currently viewed
565
+ * event category (in calendar view) has the "active" CSS class applied to it.
566
+ *
567
+ * @param string $output The normally generated output of wp_list_categories()
568
+ * @param object $args The args passed to wp_list_categories()
569
+ *
570
+ * @return string The corrected output
571
+ */
572
+ function selected_category_link( $output, $args )
573
+ {
574
+ global $ai1ec_calendar_controller, $ai1ec_settings;
575
+
576
+ // First check if current page is calendar
577
+ if( is_page( $ai1ec_settings->calendar_page_id ) )
578
+ {
579
+ $cat_ids = array_filter( split( ',', $ai1ec_calendar_controller->get_requested_categories() ), 'is_numeric' );
580
+ if( $cat_ids ) {
581
+ // Mark each filtered event category link as selected
582
+ foreach( $cat_ids as $cat_id ) {
583
+ $output = str_replace(
584
+ 'class="cat-item cat-item-' . $cat_id . '"',
585
+ 'class="cat-item cat-item-' . $cat_id . ' current-cat current_page_item"',
586
+ $output );
587
+ }
588
+ // Mark calendar page link as selected parent
589
+ $output = str_replace(
590
+ 'class="cat-item cat-item-' . AI1EC_FAKE_CATEGORY_ID . '"',
591
+ 'class="cat-item cat-item-' . AI1EC_FAKE_CATEGORY_ID . ' current-cat-parent"',
592
+ $output );
593
+ } else {
594
+ // No categories filtered, so mark calendar page link as selected
595
+ $output = str_replace(
596
+ 'class="cat-item cat-item-' . AI1EC_FAKE_CATEGORY_ID . '"',
597
+ 'class="cat-item cat-item-' . AI1EC_FAKE_CATEGORY_ID . ' current-cat current_page_item"',
598
+ $output );
599
+ }
600
+ }
601
+
602
+ return $output;
603
+ }
604
+
605
+ /**
606
+ * admin_notices function
607
+ *
608
+ * Notify the user about anything special.
609
+ *
610
+ * @return void
611
+ **/
612
+ function admin_notices() {
613
+ global $ai1ec_view_helper,
614
+ $ai1ec_settings,
615
+ $plugin_page;
616
+
617
+ // If calendar page ID has not been set, and we're not updating the settings
618
+ // page, the calendar is not properly set up yet
619
+ if( ! $ai1ec_settings->calendar_page_id && ! isset( $_REQUEST['ai1ec_save_settings'] ) )
620
+ {
621
+ $args = array();
622
+ // If not on the settings page already, direct user there with a message
623
+ if( $plugin_page == 'all-in-one-events-calendar-settings' ) {
624
+ $args['msg'] = __( 'To set up the plugin, please select an option in the <strong>Calendar page</strong> dropdown list, then click <strong>Update Settings</strong>.', AI1EC_PLUGIN_NAME );
625
+ // Else instruct user as to what to do on the settings page
626
+ } else {
627
+ $args['msg'] = sprintf(
628
+ __( 'The plugin is installed, but has not been configured. <a href="%s">Click here to set it up now »</a>', AI1EC_PLUGIN_NAME ),
629
+ admin_url( 'edit.php?post_type=ai1ec_event&page=all-in-one-events-calendar-settings' )
630
+ );
631
+ }
632
+ $ai1ec_view_helper->display( 'admin_notices.php', $args );
633
+ }
634
+ }
635
+ }
636
+ // END class
app/helper/class-ai1ec-calendar-helper.php ADDED
@@ -0,0 +1,542 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-calendar-helper.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_Calendar_Helper class
11
+ *
12
+ * @package Helpers
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_Calendar_Helper {
16
+ /**
17
+ * _instance class variable
18
+ *
19
+ * Class instance
20
+ *
21
+ * @var null | object
22
+ **/
23
+ private static $_instance = NULL;
24
+
25
+ /**
26
+ * Constructor
27
+ *
28
+ * Default constructor
29
+ **/
30
+ private function __construct() { }
31
+
32
+ /**
33
+ * get_instance function
34
+ *
35
+ * Return singleton instance
36
+ *
37
+ * @return object
38
+ **/
39
+ static function get_instance() {
40
+ if( self::$_instance === NULL ) {
41
+ self::$_instance = new self();
42
+ }
43
+
44
+ return self::$_instance;
45
+ }
46
+
47
+ /**
48
+ * get_events_for_month function
49
+ *
50
+ * Return an array of all dates for the current month as an associative
51
+ * array, with each element's value being another array of event objects
52
+ * representing the events occuring on that date.
53
+ *
54
+ * @param int $time the UNIX timestamp of a date within the desired month
55
+ * @param array $categories the categories to filter events by
56
+ * @param array $tags the tags to filter events by
57
+ *
58
+ * @return array array of arrays as per function description
59
+ **/
60
+ function get_events_for_month( $time, $categories = array(), $tags = array() )
61
+ {
62
+ global $ai1ec_events_helper;
63
+
64
+ $days_events = array();
65
+
66
+ $bits = $ai1ec_events_helper->gmgetdate( $time );
67
+ $last_day = gmdate( 't', $time );
68
+
69
+ // ==========================================
70
+ // = Iterate through each date of the month =
71
+ // ==========================================
72
+ for( $day = 1; $day <= $last_day; $day++ )
73
+ {
74
+ $start_time = gmmktime( 0, 0, 0, $bits['mon'], $day, $bits['year'] );
75
+ $end_time = gmmktime( 0, 0, 0, $bits['mon'], $day + 1, $bits['year'] );
76
+
77
+ $days_events[$day] = $this->get_events_between(
78
+ $start_time, $end_time, 'publish', $categories, $tags );
79
+ }
80
+
81
+ return $days_events;
82
+ }
83
+
84
+ /**
85
+ * get_month_cell_array function
86
+ *
87
+ * Return an array of weeks, each containing an array of days, each
88
+ * containing the date for the day ['date'] (if inside the month) and
89
+ * the events ['events'] (if any) for the day.
90
+ *
91
+ * @param int $timestamp UNIX timestamp of the 1st day of the desired
92
+ * month to display
93
+ * @param array $days_events list of events for each day of the month in
94
+ * the format returned by get_events_for_month()
95
+ *
96
+ * @return void
97
+ **/
98
+ function get_month_cell_array( $timestamp, $days_events )
99
+ {
100
+ global $ai1ec_settings, $ai1ec_events_helper;
101
+
102
+ // Decompose date into components, used for calculations below
103
+ $bits = $ai1ec_events_helper->gmgetdate( $timestamp );
104
+ $today = $ai1ec_events_helper->gmgetdate( $ai1ec_events_helper->gmt_to_local( time() ) ); // Used to flag today's cell
105
+
106
+ // Figure out index of first table cell
107
+ $first_cell_index = gmdate( 'w', $timestamp );
108
+ // Modify weekday based on start of week setting
109
+ $first_cell_index = ( 7 + $first_cell_index - $ai1ec_settings->week_start_day ) % 7;
110
+
111
+ // Get the last day of the month
112
+ $last_day = gmdate( 't', $timestamp );
113
+ $last_timestamp = gmmktime( 0, 0, 0, $bits['mon'], $last_day, $bits['year'] );
114
+ // Figure out index of last table cell
115
+ $last_cell_index = gmdate( 'w', $last_timestamp );
116
+ // Modify weekday based on start of week setting
117
+ $last_cell_index = ( 7 + $last_cell_index - $ai1ec_settings->week_start_day ) % 7;
118
+
119
+ $weeks = array();
120
+ $week = 0;
121
+ $weeks[$week] = array();
122
+
123
+ // Insert any needed blank cells into first week
124
+ for( $i = 0; $i < $first_cell_index; $i++ ) {
125
+ $weeks[$week][] = array( 'date' => null, 'events' => array() );
126
+ }
127
+
128
+ // Insert each month's day and associated events
129
+ for( $i = 1; $i <= $last_day; $i++ ) {
130
+ $weeks[$week][] = array(
131
+ 'date' => $i,
132
+ 'today' =>
133
+ $bits['year'] == $today['year'] &&
134
+ $bits['mon'] == $today['mon'] &&
135
+ $i == $today['mday'],
136
+ 'events' => $days_events[$i]
137
+ );
138
+ // If reached the end of the week, increment week
139
+ if( count( $weeks[$week] ) == 7 )
140
+ $week++;
141
+ }
142
+
143
+ // Insert any needed blank cells into last week
144
+ for( $i = $last_cell_index + 1; $i < 7; $i++ ) {
145
+ $weeks[$week][] = array( 'date' => null, 'events' => array() );
146
+ }
147
+
148
+ return $weeks;
149
+ }
150
+
151
+ /**
152
+ * get_events_between function
153
+ *
154
+ * Return all events starting after the given start time and before the
155
+ * given end time. If there are any all-day events spanning this period,
156
+ * then return those as well. All-day events are returned first.
157
+ *
158
+ * @param int $start_time limit to events starting after this (local) UNIX time
159
+ * @param int $end_time limit to events starting before this (local) UNIX time
160
+ * @param string $post_status limit to events matching this post_status
161
+ * (null for no restriction)
162
+ *
163
+ * @return array list of matching event objects
164
+ **/
165
+ function get_events_between( $start_time, $end_time, $post_status = 'publish' ) {
166
+ global $wpdb, $ai1ec_events_helper;
167
+
168
+ // Convert timestamps to MySQL format in GMT time
169
+ $start_time = $ai1ec_events_helper->local_to_gmt( $start_time );
170
+ $end_time = $ai1ec_events_helper->local_to_gmt( $end_time );
171
+
172
+ // Query arguments
173
+ $args = array(
174
+ $start_time,
175
+ $end_time,
176
+ );
177
+ if( $post_status != null ) $args[] = $post_status;
178
+
179
+ $query = $wpdb->prepare(
180
+ "SELECT p.*, e.post_id, i.id AS instance_id, " .
181
+ "UNIX_TIMESTAMP( i.start ) AS start, " .
182
+ "UNIX_TIMESTAMP( i.end ) AS end, " .
183
+ // Treat event instances that span 24 hours as all-day
184
+ "IF( e.allday, e.allday, i.end = DATE_ADD( i.start, INTERVAL 1 DAY ) ) AS allday, " .
185
+ "e.recurrence_rules, e.exception_rules, e.recurrence_dates, e.exception_dates, " .
186
+ "e.venue, e.country, e.address, e.city, e.province, e.postal_code, " .
187
+ "e.show_map, e.contact_name, e.contact_phone, e.contact_email, e.cost, " .
188
+ "e.ical_feed_url, e.ical_source_url, e.ical_organizer, e.ical_contact, e.ical_uid " .
189
+ "FROM {$wpdb->prefix}ai1ec_events e " .
190
+ "INNER JOIN $wpdb->posts p ON p.ID = e.post_id " .
191
+ "INNER JOIN {$wpdb->prefix}ai1ec_event_instances i ON e.post_id = i.post_id " .
192
+ "WHERE post_type = '" . AI1EC_POST_TYPE . "' " .
193
+ "AND i.start >= FROM_UNIXTIME( %d ) " .
194
+ "AND i.start < FROM_UNIXTIME( %d ) " .
195
+ ( $post_status == null ? '' : "AND post_status = %s " ) .
196
+ "ORDER BY allday DESC, i.start ASC, post_title ASC",
197
+ $args );
198
+ $events = $wpdb->get_results( $query, ARRAY_A );
199
+ foreach( $events as &$event ) {
200
+ $event = new Ai1ec_Event( $event );
201
+ }
202
+
203
+ return $events;
204
+ }
205
+
206
+ /**
207
+ * get_events_relative_to function
208
+ *
209
+ * Return all events starting after the given reference time, limiting the
210
+ * result set to a maximum of $limit items, offset by $page_offset. A
211
+ * negative $page_offset can be provided, which will return events *before*
212
+ * the reference time, as expected.
213
+ *
214
+ * @param int $time limit to events starting after this (local) UNIX time
215
+ * @param int $limit return a maximum of this number of items
216
+ * @param int $page_offset offset the result set by $limit times this number
217
+ * @param string $post_status limit to events matching this post_status
218
+ * (null for no restriction)
219
+ *
220
+ *
221
+ * @return array three-element array:
222
+ * ['events'] an array of matching event objects
223
+ * ['prev'] true if more previous events
224
+ * ['next'] true if more next events
225
+ **/
226
+ function get_events_relative_to( $time, $limit = 0, $page_offset = 0, $post_status = 'publish' ) {
227
+ global $wpdb, $ai1ec_events_helper;
228
+
229
+ // Figure out what the beginning of the day is to properly query all-day
230
+ // events; then convert to GMT time
231
+ $bits = $ai1ec_events_helper->gmgetdate( $time );
232
+
233
+ // Convert timestamp to GMT time
234
+ $time = $ai1ec_events_helper->local_to_gmt( $time );
235
+
236
+ // Query arguments
237
+ $args = array( $time );
238
+ if( $post_status != null ) $args[] = $post_status;
239
+
240
+ if( $page_offset >= 0 )
241
+ $first_record = $page_offset * $limit;
242
+ else
243
+ $first_record = ( -$page_offset - 1 ) * $limit;
244
+
245
+ $query = $wpdb->prepare(
246
+ "SELECT SQL_CALC_FOUND_ROWS p.*, e.post_id, i.id AS instance_id, " .
247
+ "UNIX_TIMESTAMP( i.start ) AS start, " .
248
+ "UNIX_TIMESTAMP( i.end ) AS end, " .
249
+ // Treat event instances that span 24 hours as all-day
250
+ "IF( e.allday, e.allday, i.end = DATE_ADD( i.start, INTERVAL 1 DAY ) ) AS allday, " .
251
+ "e.recurrence_rules, e.exception_rules, e.recurrence_dates, e.exception_dates, " .
252
+ "e.venue, e.country, e.address, e.city, e.province, e.postal_code, " .
253
+ "e.show_map, e.contact_name, e.contact_phone, e.contact_email, e.cost, " .
254
+ "e.ical_feed_url, e.ical_source_url, e.ical_organizer, e.ical_contact, e.ical_uid " .
255
+ "FROM {$wpdb->prefix}ai1ec_events e " .
256
+ "INNER JOIN $wpdb->posts p ON e.post_id = p.ID " .
257
+ "INNER JOIN {$wpdb->prefix}ai1ec_event_instances i ON e.post_id = i.post_id " .
258
+ "WHERE post_type = '" . AI1EC_POST_TYPE . "' " .
259
+ "AND " .
260
+ ( $page_offset >= 0 ? "i.end >= FROM_UNIXTIME( %d ) "
261
+ : "i.start < FROM_UNIXTIME( %d ) "
262
+ ) .
263
+ ( $post_status == null ? '' : "AND post_status = %s " ) .
264
+ // Reverse order when viewing negative pages, to get correct set of
265
+ // records. Then reverse results later to order them properly.
266
+ "ORDER BY i.start " . ( $page_offset >= 0 ? 'ASC' : 'DESC' ) .
267
+ ", post_title " . ( $page_offset >= 0 ? 'ASC' : 'DESC' ) .
268
+ " LIMIT $first_record, $limit",
269
+ $args );
270
+
271
+ $events = $wpdb->get_results( $query, ARRAY_A );
272
+
273
+ // Re-order records if in negative page offset
274
+ if( $page_offset < 0 ) $events = array_reverse( $events );
275
+
276
+ foreach( $events as &$event ) {
277
+ $event = new Ai1ec_Event( $event );
278
+ }
279
+
280
+ // Find out if there are more records in the current nav direction
281
+ $more = $wpdb->get_var( 'SELECT FOUND_ROWS()' ) > $first_record + $limit;
282
+
283
+ // Navigating in the future
284
+ if( $page_offset > 0 ) {
285
+ $prev = true;
286
+ $next = $more;
287
+ }
288
+ // Navigating in the past
289
+ elseif( $page_offset < 0 ) {
290
+ $prev = $more;
291
+ $next = true;
292
+ }
293
+ // Navigating from the reference time
294
+ else {
295
+ $query = $wpdb->prepare(
296
+ "SELECT COUNT(*) " .
297
+ "FROM {$wpdb->prefix}ai1ec_events e " .
298
+ "INNER JOIN {$wpdb->prefix}ai1ec_event_instances i ON e.post_id = i.post_id " .
299
+ "INNER JOIN $wpdb->posts p ON e.post_id = p.ID " .
300
+ "WHERE post_type = '" . AI1EC_POST_TYPE . "' " .
301
+ "AND i.start < FROM_UNIXTIME( %d ) " .
302
+ ( $post_status == null ? '' : "AND post_status = %s " ),
303
+ $args );
304
+ $prev = $wpdb->get_var( $query );
305
+ $next = $more;
306
+ }
307
+ return array(
308
+ 'events' => $events,
309
+ 'prev' => $prev,
310
+ 'next' => $next,
311
+ );
312
+ }
313
+
314
+ /**
315
+ * get_agenda_date_array function
316
+ *
317
+ * Breaks down the given ordered array of event objects into dates, and
318
+ * outputs an ordered array of two-element associative arrays in the
319
+ * following format:
320
+ * key: localized UNIX timestamp of date
321
+ * value:
322
+ * ['events'] => two-element associatative array broken down thus:
323
+ * ['allday'] => all-day events occurring on this day
324
+ * ['notallday'] => all other events occurring on this day
325
+ * ['today'] => whether or not this date is today
326
+ *
327
+ * @param array $events
328
+ *
329
+ * @return array
330
+ **/
331
+ function get_agenda_date_array( $events ) {
332
+ global $ai1ec_events_helper;
333
+
334
+ $dates = array();
335
+
336
+ // Classify each event into a date/allday category
337
+ foreach( $events as $event ) {
338
+ $date = $ai1ec_events_helper->gmt_to_local( $event->start );
339
+ $date = $ai1ec_events_helper->gmgetdate( $date );
340
+ $timestamp = gmmktime( 0, 0, 0, $date['mon'], $date['mday'], $date['year'] );
341
+ $category = $event->allday ? 'allday' : 'notallday';
342
+ $dates[$timestamp]['events'][$category][] = $event;
343
+ }
344
+
345
+ // Flag today
346
+ $today = $ai1ec_events_helper->gmt_to_local( time() );
347
+ $today = $ai1ec_events_helper->gmgetdate( $today );
348
+ $today = gmmktime( 0, 0, 0, $today['mon'], $today['mday'], $today['year'] );
349
+ if( isset( $dates[$today] ) )
350
+ $dates[$today]['today'] = true;
351
+
352
+ return $dates;
353
+ }
354
+
355
+ /**
356
+ * get_calendar_url function
357
+ *
358
+ * Returns the URL of the configured calendar page in the default view,
359
+ * optionally preloaded at the month containing the given event (rather than
360
+ * today's date), and optionally prefiltered by the given category IDs and/or
361
+ * tag IDs.
362
+ *
363
+ * @param object|null $event The event to focus the calendar on
364
+ * @param array $cat_ids The category IDs to filter the calendar by
365
+ * @param array $tag_ids The tag IDs to filter the calendar by
366
+ *
367
+ * @return string The URL for this calendar
368
+ **/
369
+ function get_calendar_url( $event = null, $cat_ids = array(), $tag_ids = array() ) {
370
+ global $ai1ec_settings, $ai1ec_events_helper, $ai1ec_app_helper, $wpdb;
371
+
372
+ $url = get_permalink( $ai1ec_settings->calendar_page_id );
373
+
374
+ if( $event )
375
+ {
376
+ $url .= $ai1ec_app_helper->get_param_delimiter_char( $url );
377
+
378
+ switch( $ai1ec_settings->default_calendar_view )
379
+ {
380
+ case 'month':
381
+ // Get components of localized timstamps and calculate month offset
382
+ $today = $ai1ec_events_helper->gmgetdate( $ai1ec_events_helper->gmt_to_local( time() ) );
383
+ $desired = $ai1ec_events_helper->gmgetdate( $ai1ec_events_helper->gmt_to_local( $event->start ) );
384
+ $month_offset =
385
+ ( $desired['year'] - $today['year'] ) * 12 +
386
+ $desired['mon'] - $today['mon'];
387
+
388
+ $url .= "ai1ec_month_offset=$month_offset";
389
+ break;
390
+
391
+ case 'agenda':
392
+ // Find out how many event instances are between today's first
393
+ // instance and the desired event's instance
394
+ $now = $ai1ec_events_helper->local_to_gmt( time() );
395
+ $after_today = $event->end >= $now;
396
+ $query = $wpdb->prepare(
397
+ "SELECT COUNT(*) FROM {$wpdb->prefix}ai1ec_events e " .
398
+ "INNER JOIN $wpdb->posts p ON e.post_id = p.ID " .
399
+ "INNER JOIN {$wpdb->prefix}ai1ec_event_instances i ON e.post_id = i.post_id " .
400
+ "WHERE post_type = '" . AI1EC_POST_TYPE . "' " .
401
+ "AND post_status = 'publish' " .
402
+ ( $after_today
403
+ ? "AND i.end >= FROM_UNIXTIME( %d ) AND i.end < FROM_UNIXTIME( %d ) "
404
+ : "AND i.start < FROM_UNIXTIME( %d ) AND i.start >= FROM_UNIXTIME( %d ) "
405
+ ) .
406
+ "ORDER BY i.start ASC",
407
+ array( $now, $after_today ? $event->end : $event->start ) );
408
+ $count = $wpdb->get_var( $query );
409
+ // ( $count - 1 ) below solves boundary case for first event of each agenda page
410
+ $page_offset = intval( ( $count - 1 ) / $ai1ec_settings->agenda_events_per_page );
411
+ if( ! $after_today ) $page_offset = -1 - $page_offset;
412
+
413
+ $url .= "ai1ec_page_offset=$page_offset";
414
+ break;
415
+ }
416
+
417
+ $url .= "&ai1ec_active_event=$event->post_id";
418
+ }
419
+
420
+ if( $cat_ids )
421
+ $url .= $ai1ec_app_helper->get_param_delimiter_char( $url ) .
422
+ 'ai1ec_cat_ids=' . join( ',', $cat_ids );
423
+ if( $tag_ids )
424
+ $url .= $ai1ec_app_helper->get_param_delimiter_char( $url ) .
425
+ 'ai1ec_tag_ids=' . join( ',', $tag_ids );
426
+
427
+ return $url;
428
+ }
429
+
430
+ /**
431
+ * get_weekdays function
432
+ *
433
+ * Returns a list of abbreviated weekday names starting on the configured
434
+ * week start day setting.
435
+ *
436
+ * @return array
437
+ **/
438
+ function get_weekdays() {
439
+ global $ai1ec_settings;
440
+ static $weekdays;
441
+
442
+ if( ! isset( $weekdays ) )
443
+ {
444
+ $time = strtotime( 'next Sunday' );
445
+ $time = strtotime( "+{$ai1ec_settings->week_start_day} days", $time );
446
+
447
+ $weekdays = array();
448
+ for( $i = 0; $i < 7; $i++ ) {
449
+ $weekdays[] = strftime( '%a', $time );
450
+ $time += 60 * 60 * 24; // Add a day
451
+ }
452
+ }
453
+ return $weekdays;
454
+ }
455
+
456
+ /**
457
+ * get_month_pagination_links function
458
+ *
459
+ * Returns an associative array of four links for the month view of the
460
+ * calendar:
461
+ * previous year, previous month, next month, and next year, in that order.
462
+ * Each element's key is an associative array containing the link's ID
463
+ * ['id'], text ['text'] and value to assign to link's href ['href'].
464
+ *
465
+ * @param int $cur_offset month offset of current month, needed for hrefs
466
+ *
467
+ * @return array array of link information as described above
468
+ **/
469
+ function get_month_pagination_links( $cur_offset ) {
470
+ global $ai1ec_events_helper;
471
+
472
+ $links = array();
473
+
474
+ // Base timestamp on offset month
475
+ $bits = $ai1ec_events_helper->gmgetdate( $ai1ec_events_helper->gmt_to_local( time() ) );
476
+ $bits['mon'] += $cur_offset;
477
+ // 'mon' may now be out of range (< 1 or > 12), so recreate $bits to make sane
478
+ $bits = $ai1ec_events_helper->gmgetdate( gmmktime( 0, 0, 0, $bits['mon'], 1, $bits['year'] ) );
479
+
480
+ $links[] = array(
481
+ 'id' => 'ai1ec-prev-year',
482
+ 'text' => '« ' . ( $bits['year'] - 1 ),
483
+ 'href' => '#action=ai1ec_month&ai1ec_month_offset=' . ( $cur_offset - 12 ),
484
+ );
485
+ $links[] = array(
486
+ 'id' => 'ai1ec-prev-month',
487
+ 'text' => '‹ ' . gmstrftime( '%B', gmmktime( 0, 0, 0, $bits['mon'] - 1, 1, $bits['year'] ) ),
488
+ 'href' => '#action=ai1ec_month&ai1ec_month_offset=' . ( $cur_offset - 1 ),
489
+ );
490
+ $links[] = array(
491
+ 'id' => 'ai1ec-next-month',
492
+ 'text' => gmstrftime( '%B', gmmktime( 0, 0, 0, $bits['mon'] + 1, 1, $bits['year'] ) ) . ' ›',
493
+ 'href' => '#action=ai1ec_month&ai1ec_month_offset=' . ( $cur_offset + 1 ),
494
+ );
495
+ $links[] = array(
496
+ 'id' => 'ai1ec-next-year',
497
+ 'text' => ( $bits['year'] + 1 ) . ' »',
498
+ 'href' => '#action=ai1ec_month&ai1ec_month_offset=' . ( $cur_offset + 12 ),
499
+ );
500
+
501
+ return $links;
502
+ }
503
+
504
+ /**
505
+ * get_agenda_pagination_links function
506
+ *
507
+ * Returns an associative array of two links for the agenda view of the
508
+ * calendar: previous page (if previous events exist), next page (if next
509
+ * events exist), in that order.
510
+ * Each element' is an associative array containing the link ID ['id'],
511
+ * text ['text'] and value to assign to link's href ['href'].
512
+ *
513
+ * @param int $cur_offset page offset of agenda view, needed for hrefs
514
+ * @param int $prev whether there are more events before the current page
515
+ * @param int $next whether there are more events after the current page
516
+ *
517
+ * @return array array of link information as described above
518
+ **/
519
+ function get_agenda_pagination_links( $cur_offset, $prev = false, $next = false ) {
520
+ global $ai1ec_settings;
521
+
522
+ $links = array();
523
+
524
+ if( $prev ) {
525
+ $links['prev'] = array(
526
+ 'id' => 'ai1ec-prev-page',
527
+ 'text' => sprintf( __( '« Previous Events', AI1EC_PLUGIN_NAME ), $ai1ec_settings->agenda_events_per_page ),
528
+ 'href' => '#action=ai1ec_agenda&ai1ec_page_offset=' . ( $cur_offset - 1 ),
529
+ );
530
+ }
531
+ if( $next ) {
532
+ $links['next'] = array(
533
+ 'id' => 'ai1ec-next-page',
534
+ 'text' => sprintf( __( 'Next Events »', AI1EC_PLUGIN_NAME ), $ai1ec_settings->agenda_events_per_page ),
535
+ 'href' => '#action=ai1ec_agenda&ai1ec_page_offset=' . ( $cur_offset + 1 ),
536
+ );
537
+ }
538
+
539
+ return $links;
540
+ }
541
+ }
542
+ // END class
app/helper/class-ai1ec-events-helper.php ADDED
@@ -0,0 +1,895 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-events-helper.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_Events_Helper class
11
+ *
12
+ * @package Helpers
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_Events_Helper {
16
+ /**
17
+ * _instance class variable
18
+ *
19
+ * Class instance
20
+ *
21
+ * @var null | object
22
+ **/
23
+ private static $_instance = NULL;
24
+
25
+ /**
26
+ * Constructor
27
+ *
28
+ * Default constructor
29
+ **/
30
+ private function __construct() { }
31
+
32
+ /**
33
+ * get_instance function
34
+ *
35
+ * Return singleton instance
36
+ *
37
+ * @return object
38
+ **/
39
+ static function get_instance() {
40
+ if( self::$_instance === NULL ) {
41
+ self::$_instance = new self();
42
+ }
43
+
44
+ return self::$_instance;
45
+ }
46
+
47
+ /**
48
+ * get_event function
49
+ *
50
+ * Fetches the event object with the given post ID. Uses the WP cache to
51
+ * make this more efficient if possible.
52
+ *
53
+ * @param int $post_id The ID of the post associated with the event
54
+ *
55
+ * @return Ai1ec_Event The associated event object
56
+ **/
57
+ static function get_event( $post_id )
58
+ {
59
+ $event = wp_cache_get( $post_id, AI1EC_POST_TYPE );
60
+ if( $event === false ) {
61
+ $event = new Ai1ec_Event( $post_id );
62
+
63
+ if( ! $event->post_id )
64
+ throw new Ai1ec_Event_Not_Found( "Event with ID '$post_id' could not be retrieved from the database." );
65
+
66
+ // Cache the event data
67
+ wp_cache_add( $post_id, $event, AI1EC_POST_TYPE );
68
+ }
69
+ return $event;
70
+ }
71
+
72
+ /**
73
+ * get_matching_event function
74
+ *
75
+ * Return event ID by iCalendar UID, feed url, start time and whether the
76
+ * event has recurrence rules (to differentiate between an event with a UID
77
+ * defining the recurrence pattern, and other events with with the same UID,
78
+ * which are just RECURRENCE-IDs).
79
+ *
80
+ * @param int $uid iCalendar UID property
81
+ * @param string $feed Feed URL
82
+ * @param int $start Start timestamp (GMT)
83
+ * @param bool $has_recurrence Whether the event has recurrence rules
84
+ * @param int|null $exclude_post_id Do not match against this post ID
85
+ *
86
+ * @return object|null Matching event's post ID, or null if no match
87
+ **/
88
+ function get_matching_event_id( $uid, $feed, $start, $has_recurrence = false, $exclude_post_id = null ) {
89
+ global $wpdb;
90
+
91
+ $table_name = $wpdb->prefix . 'ai1ec_events';
92
+ $query = "SELECT post_id FROM {$table_name} " .
93
+ "WHERE ical_feed_url = %s " .
94
+ "AND ical_uid = %s " .
95
+ "AND start = FROM_UNIXTIME( %d ) " .
96
+ ( $has_recurrence ? 'AND NOT ' : 'AND ' ) .
97
+ "( recurrence_rules IS NULL OR recurrence_rules = '' )";
98
+ $args = array( $feed, $uid, $start );
99
+ if( ! is_null( $exclude_post_id ) ) {
100
+ $query .= 'AND post_id <> %d';
101
+ $args[] = $exclude_post_id;
102
+ }
103
+
104
+ return $wpdb->get_var( $wpdb->prepare( $query, $args ) );
105
+ }
106
+
107
+ /**
108
+ * delete_event_cache function
109
+ *
110
+ * Delete cache of event
111
+ *
112
+ * @param int $pid Event post ID
113
+ *
114
+ * @return void
115
+ **/
116
+ function delete_event_cache( $pid ) {
117
+ global $wpdb;
118
+
119
+ $table_name = $wpdb->prefix . 'ai1ec_event_instances';
120
+ $wpdb->query( $wpdb->prepare( "DELETE FROM $table_name WHERE post_id = %d", $pid ) );
121
+ }
122
+
123
+ /**
124
+ * cache_event function
125
+ *
126
+ * Creates a new entry in the cache table for each date that the event appears
127
+ * (and does not already have an explicit RECURRENCE-ID instance, given its
128
+ * iCalendar UID).
129
+ *
130
+ * @param object $event Event to generate cache table for
131
+ *
132
+ * @return void
133
+ **/
134
+ function cache_event( &$event ) {
135
+ global $wpdb;
136
+
137
+ // Convert event's timestamps to local for correct calculations of
138
+ // recurrence.
139
+ // $gm_diff: Needed with SG_iCal_Freq, which doesn't use gm___ version of
140
+ // PHP date functions and calculates wrong recurrences sometimes
141
+ $gm_diff = mktime( 0 ) - gmmktime( 0 );
142
+ $event->start = $this->gmt_to_local( $event->start ) + $gm_diff;
143
+ $event->end = $this->gmt_to_local( $event->end ) + $gm_diff;
144
+
145
+ $evs = array();
146
+ $e = array(
147
+ 'post_id' => $event->post_id,
148
+ 'start' => $event->start,
149
+ 'end' => $event->end,
150
+ );
151
+ $duration = $event->getDuration();
152
+
153
+ // Always cache initial instance
154
+ $evs[] = $e;
155
+
156
+ if( $event->recurrence_rules )
157
+ {
158
+ $count = 0;
159
+ $start = $event->start;
160
+ $freq = $event->getFrequency();
161
+
162
+ $freq->firstOccurrence();
163
+ while( ( $next = $freq->nextOccurrence( $start ) ) > 0 &&
164
+ $count < 1000 )
165
+ {
166
+ $count++;
167
+ $start = $next;
168
+ $e['start'] = $start;
169
+ $e['end'] = $start + $duration;
170
+
171
+ $evs[] = $e;
172
+ }
173
+ }
174
+
175
+ // Make entries unique (sometimes recurrence generator creates duplicates?)
176
+ $evs_unique = array();
177
+ foreach( $evs as $ev ) {
178
+ $evs_unique[ md5( serialize( $ev ) ) ] = $ev;
179
+ }
180
+
181
+ foreach( $evs_unique as $e )
182
+ {
183
+ // Find out if this event instance is already accounted for by an
184
+ // overriding 'RECURRENCE-ID' of the same iCalendar feed (by comparing the
185
+ // UID, start date, recurrence). If so, then do not create duplicate
186
+ // instance of event.
187
+ $matching_event_id = $event->ical_uid ?
188
+ $this->get_matching_event_id(
189
+ $event->ical_uid,
190
+ $event->ical_feed_url,
191
+ $start = $this->local_to_gmt( $e['start'] ) - $gm_diff,
192
+ false, // Only search events that don't define recurrence (i.e. only search for RECURRENCE-ID events)
193
+ $event->post_id
194
+ )
195
+ : null;
196
+
197
+ // If no other instance was found
198
+ if( is_null( $matching_event_id ) )
199
+ {
200
+ $start_day = date( 'j', $e['start'] );
201
+ $end_day = date( 'j', $e['end'] );
202
+
203
+ // If event spans days, create instance for each spanning day
204
+ if( $start_day != $end_day || $e['end'] - $e['start'] > 60 * 60 * 24 )
205
+ $this->create_cache_table_entries( $e, $gm_diff );
206
+ // Else cache single instance of event
207
+ else
208
+ $this->insert_event_in_cache_table( $e, $gm_diff );
209
+ }
210
+ }
211
+ }
212
+
213
+ /**
214
+ * insert_event_in_cache_table function
215
+ *
216
+ * Inserts a new record in the cache table
217
+ *
218
+ * @param array $event Event array
219
+ * @param int $gm_diff Different between local time and gmt time
220
+ *
221
+ * @return void
222
+ **/
223
+ function insert_event_in_cache_table( $event, $gm_diff ) {
224
+ global $wpdb;
225
+
226
+ // Return the start/end times to GMT zone
227
+ $event['start'] = $this->local_to_gmt( $event['start'] ) - $gm_diff;
228
+ $event['end'] = $this->local_to_gmt( $event['end'] ) - $gm_diff;
229
+
230
+ $wpdb->query(
231
+ $wpdb->prepare(
232
+ "INSERT INTO {$wpdb->prefix}ai1ec_event_instances " .
233
+ " ( post_id, start, end ) " .
234
+ "VALUES ( %d, FROM_UNIXTIME( %d ), FROM_UNIXTIME( %d ) )",
235
+ $event
236
+ )
237
+ );
238
+ }
239
+
240
+ /**
241
+ * create_cache_table_entries function
242
+ *
243
+ * Create a new entry for each day that the event spans.
244
+ *
245
+ * @param array $e Event array
246
+ * @param int $gm_diff Different between local time and GMT time
247
+ *
248
+ * @return void
249
+ **/
250
+ function create_cache_table_entries( $e, $gm_diff )
251
+ {
252
+ // Decompose start dates into components
253
+ $start_bits = getdate( $e['start'] );
254
+
255
+ // ============================================
256
+ // = Calculate the time for event's first day =
257
+ // ============================================
258
+ // Start time is event's original start time
259
+ $event_start = $e['start'];
260
+ // End time is beginning of next day
261
+ $event_end = mktime(
262
+ 0, // hour
263
+ 0, // minute
264
+ 0, // second
265
+ $start_bits['mon'], // month
266
+ $start_bits['mday'] + 1, // day
267
+ $start_bits['year'] // year
268
+ );
269
+ // Cache first day
270
+ $this->insert_event_in_cache_table( array( 'post_id' => $e['post_id'], 'start' => $event_start, 'end' => $event_end ), $gm_diff );
271
+
272
+ // ====================================================
273
+ // = Calculate the time for event's intermediate days =
274
+ // ====================================================
275
+ // Start time is previous end time
276
+ $event_start = $event_end;
277
+ // End time one day ahead
278
+ $event_end += 60 * 60 * 24;
279
+ // Cache intermediate days
280
+ while( $event_end < $e['end'] ) {
281
+ $this->insert_event_in_cache_table( array( 'post_id' => $e['post_id'], 'start' => $event_start, 'end' => $event_end ), $gm_diff );
282
+ $event_start = $event_end; // Start time is previous end time
283
+ $event_end += 24 * 60 * 60; // Increment end time by 1 day
284
+ }
285
+
286
+ // ===========================================
287
+ // = Calculate the time for event's last day =
288
+ // ===========================================
289
+ // Start time is already correct (previous end time)
290
+ // End time is event end time
291
+ // Only insert if the last event instance if span is > 0
292
+ $event_end = $e['end'];
293
+ if( $event_end > $event_start )
294
+ // Cache last day
295
+ $this->insert_event_in_cache_table( array( 'post_id' => $e['post_id'], 'start' => $event_start, 'end' => $event_end ), $gm_diff );
296
+ }
297
+
298
+ /**
299
+ * Returns the various preset recurrence options available (e.g.,
300
+ * 'DAILY', 'WEEKENDS', etc.).
301
+ *
302
+ * @return string An associative array of pattern names to English
303
+ * equivalents
304
+ */
305
+ function get_repeat_patterns() {
306
+ static $options = array(
307
+ ' ' => 'No repeat',
308
+ 'DAILY' => 'Daily',
309
+ 'MO' => 'Mondays',
310
+ 'TU' => 'Tuesdays',
311
+ 'WE' => 'Wednesdays',
312
+ 'TH' => 'Thursdays',
313
+ 'FR' => 'Fridays',
314
+ 'SA' => 'Saturdays',
315
+ 'SU' => 'Sundays',
316
+ 'TU+TH' => 'Tuesdays & Thursdays',
317
+ 'MO+WE+FR' => 'Mondays, Wednesdays & Fridays',
318
+ 'WEEKDAYS' => 'Weekdays',
319
+ 'WEEKENDS' => 'Weekends',
320
+ 'WEEKLY' => 'Weekly',
321
+ 'MONTHLY' => 'Monthly',
322
+ 'YEARLY' => 'Yearly',
323
+ );
324
+
325
+ return $options;
326
+ }
327
+
328
+ /**
329
+ * Generates and returns repeat dropdown
330
+ *
331
+ * @param Integer|NULL $selected Selected option
332
+ *
333
+ * @return String Repeat dropdown
334
+ */
335
+ function create_repeat_dropdown( $selected = null ) {
336
+ ob_start();
337
+
338
+ $options = $this->get_repeat_patterns();
339
+
340
+ ?>
341
+ <select name="ai1ec_repeat" id="ai1ec_repeat">
342
+ <?php foreach( $options as $key => $val ): ?>
343
+ <option value="<?php echo $key ?>" <?php if( $key === $selected ) echo 'selected="selected"' ?>>
344
+ <?php _e( $val, AI1EC_PLUGIN_NAME ) ?>
345
+ </option>
346
+ <?php endforeach ?>
347
+ </select>
348
+ <?php
349
+
350
+ $output = ob_get_contents();
351
+ ob_end_clean();
352
+
353
+ return $output;
354
+ }
355
+
356
+ /**
357
+ * Returns an associative array containing the following information:
358
+ * string 'repeat' => pattern of repetition ('DAILY', 'WEEKENDS', etc.)
359
+ * int 'count' => end after 'count' times
360
+ * int 'until' => repeat until date (as UNIX timestamp)
361
+ * Elements are null if no such recurrence information is available.
362
+ *
363
+ * @param Ai1ec_Event Event object to parse recurrence rules of
364
+ * @return array Array structured as described above
365
+ **/
366
+ function parse_recurrence_rules( &$event )
367
+ {
368
+ $repeat = null;
369
+ $count = null;
370
+ $until = null;
371
+ $end = 0;
372
+ if( ! is_null( $event ) ) {
373
+ if( strlen( $event->recurrence_rules ) > 0 ) {
374
+ $line = new SG_iCal_Line( $event->recurrence_rules );
375
+ $rec = new SG_iCal_Recurrence( $line );
376
+ switch( $rec->req ) {
377
+ case 'DAILY':
378
+ $by_day = $rec->getByDay();
379
+ if( empty( $by_day ) ) {
380
+ $repeat = 'DAILY';
381
+ } elseif( $by_day[0] == 'SA+SU' ) {
382
+ $repeat = 'WEEKENDS';
383
+ } elseif( count( $by_day ) == 5 ) {
384
+ $repeat = 'WEEKDAYS';
385
+ } else {
386
+ foreach( $by_day as $d ) {
387
+ $repeat .= $d . '+';
388
+ }
389
+ $repeat = substr( $repeat, 0, -1 );
390
+ }
391
+ break;
392
+ case 'WEEKLY':
393
+ $repeat = 'WEEKLY';
394
+ break;
395
+ case 'MONTHLY':
396
+ $repeat = 'MONTHLY';
397
+ break;
398
+ case 'YEARLY':
399
+ $repeat = 'YEARLY';
400
+ break;
401
+ }
402
+ $count = $rec->getCount();
403
+ $until = $rec->getUntil();
404
+ if( $until ) {
405
+ $until = strtotime( $rec->getUntil() );
406
+ $until -= 24 * 60 * 60; // Subtract 1 day (intuitively, last date is included)
407
+ $end = 2; // set end value
408
+ } else if( $count )
409
+ $end = 1; // set end value
410
+ else
411
+ $end = 0; // set end value
412
+ }
413
+ }
414
+ return array(
415
+ 'repeat' => $repeat,
416
+ 'count' => $count,
417
+ 'until' => $until,
418
+ 'end' => $end
419
+ );
420
+ }
421
+
422
+ /**
423
+ * Generates and returns "End after X times" input
424
+ *
425
+ * @param Integer|NULL $count Initial value of range input
426
+ *
427
+ * @return String Repeat dropdown
428
+ */
429
+ function create_count_input( $count = 100 ) {
430
+ ob_start();
431
+
432
+ if( ! $count ) $count = 100;
433
+ ?>
434
+ <input type="range" name="ai1ec_count" id="ai1ec_count" min="1" max="100"
435
+ <?php if( $count ) echo 'value="' . $count . '"' ?> />
436
+ <?php _e( 'times', AI1EC_PLUGIN_NAME ) ?>
437
+ <?php
438
+ $output = ob_get_contents();
439
+ ob_end_clean();
440
+
441
+ return $output;
442
+ }
443
+
444
+ /**
445
+ * get_all_matching_posts function
446
+ *
447
+ * Gets existing event posts that are between the interval
448
+ *
449
+ * @param int $s_time Start time
450
+ * @param int $e_time End time
451
+ *
452
+ * @return Array of matching event posts
453
+ **/
454
+ function get_all_matching_posts( $s_time, $e_time ) {
455
+ global $ai1ec_calendar_helper;
456
+ return $ai1ec_calendar_helper->get_events_between( $s_time, $e_time );
457
+ }
458
+
459
+ /**
460
+ * get_matching_events function
461
+ *
462
+ * Get events that match with the arguments provided.
463
+ *
464
+ * @param int | bool $start Events start before this (GMT) time
465
+ * @param int | bool $end Events end before this (GMT) time
466
+ * @param int | array | bool $tags Tag(s) of events
467
+ * @param int | array | bool $categories Category(ies) of events
468
+ *
469
+ * @return array Matching events
470
+ **/
471
+ function get_matching_events( $start = false, $end = false, $tags = false, $categories = false, $posts = false ) {
472
+ $args = array(
473
+ 'post_type' => AI1EC_POST_TYPE,
474
+ 'posts_per_page' => -1
475
+ );
476
+
477
+ if( $categories !== false ) {
478
+ // add categories
479
+ if( strstr( $categories, ',' ) !== false ) {
480
+ $categories = explode( ',', $categories );
481
+ // prevent sql injection
482
+ foreach( $categories as $key => $val ) {
483
+ $categories[$key] = (int) $val;
484
+ }
485
+ }
486
+ $args["tax_query"][] = array(
487
+ 'taxonomy' => 'events_categories',
488
+ 'terms' => $categories,
489
+ 'field' => 'id'
490
+ );
491
+ }
492
+
493
+ if( $tags !== false ) {
494
+ // add tags
495
+ if( strstr( $tags, ',' ) !== false ) {
496
+ $tags = explode( ',', $tags );
497
+ // prevent sql injection
498
+ foreach( $tags as $key => $val ) {
499
+ $tags[$key] = (int) $val;
500
+ }
501
+ }
502
+ $args["tax_query"][] = array(
503
+ 'taxonomy' => 'events_tags',
504
+ 'terms' => $tags,
505
+ 'field' => 'id'
506
+ );
507
+ }
508
+
509
+ if( $posts !== false ) {
510
+ // add posts
511
+ if( strstr( $posts, ',' ) !== false ) {
512
+ $posts = explode( ',', $posts );
513
+ // prevent sql injection
514
+ foreach( $posts as $key => $val ) {
515
+ $posts[$key] = (int) $val;
516
+ }
517
+ }
518
+ if( is_array( $posts ) )
519
+ $args["post__in"] = $posts;
520
+ else
521
+ $args["p"] = $posts;
522
+ }
523
+
524
+ $loop = new WP_Query( $args );
525
+ $events = array();
526
+ foreach( $loop->posts as $post ) {
527
+ try{
528
+ $ev = new Ai1ec_Event( $post->ID );
529
+ } catch( Ai1ec_Event_Not_Found $n ) {
530
+ // The event is not found, continue to the next event
531
+ continue;
532
+ }
533
+ // if there are recurrence rules, include the event, else...
534
+ if( empty( $ev->recurrence_rules ) ) {
535
+ // if start time is set, and event start time is before the range
536
+ // it, continue to the next event
537
+ if( $start !== false && $ev->start < $start )
538
+ continue;
539
+ // if end time is set, and event end time is after
540
+ // it, continue to the next event
541
+ if( $end !== false && $ev->end < $end )
542
+ continue;
543
+ }
544
+ $events[] = $ev;
545
+ }
546
+
547
+ return $events;
548
+ }
549
+
550
+ /**
551
+ * fuzzy_string_compare function
552
+ *
553
+ * Compares string A to string B using fuzzy comparison algorithm
554
+ *
555
+ * @param String $a String to compare
556
+ * @param String $b String to compare
557
+ *
558
+ * @return boolean True if the two strings match, false otherwise
559
+ **/
560
+ function fuzzy_string_compare( $a, $b ) {
561
+ $percent = 0;
562
+ similar_text( $a, $b, &$percent );
563
+ return ( $percent > 50 );
564
+ }
565
+
566
+ /**
567
+ * get_short_time function
568
+ *
569
+ * Format a short-form time for use in compressed (e.g. month) views;
570
+ * this is also converted to the local timezone.
571
+ *
572
+ * @param int $timestamp
573
+ *
574
+ * @return string
575
+ **/
576
+ function get_short_time( $timestamp ) {
577
+ $timestamp = $this->gmt_to_local( $timestamp );
578
+ $ampm = gmstrftime( '%p', $timestamp );
579
+ $ampm = $ampm[0];
580
+ $ampm = strtolower( $ampm );
581
+ return gmstrftime( '%l:%M', $timestamp ) . $ampm;
582
+ }
583
+
584
+ /**
585
+ * get_short_date function
586
+ *
587
+ * Format a short-form date for use in compressed (e.g. month) views;
588
+ * this is also converted to the local timezone.
589
+ *
590
+ * @param int $timestamp
591
+ *
592
+ * @return string
593
+ **/
594
+ function get_short_date( $timestamp ) {
595
+ $timestamp = $this->gmt_to_local( $timestamp );
596
+ return gmstrftime( '%b %e', $timestamp );
597
+ }
598
+
599
+ /**
600
+ * get_medium_time function
601
+ *
602
+ * Format a medium-length time for use in other views (e.g., Agenda);
603
+ * this is also converted to the local timezone.
604
+ *
605
+ * @param int $timestamp
606
+ *
607
+ * @return string
608
+ **/
609
+ function get_medium_time( $timestamp ) {
610
+ $timestamp = $this->gmt_to_local( $timestamp );
611
+ $ampm = gmstrftime( '%p', $timestamp );
612
+ $ampm = strtolower( $ampm );
613
+ return gmstrftime( '%l:%M', $timestamp ) . $ampm;
614
+ }
615
+
616
+ /**
617
+ * get_long_time function
618
+ *
619
+ * Format a long-length time for use in other views (e.g., single event);
620
+ * this is also converted to the local timezone.
621
+ *
622
+ * @param int $timestamp
623
+ *
624
+ * @return string
625
+ **/
626
+ function get_long_time( $timestamp ) {
627
+ $timestamp = $this->gmt_to_local( $timestamp );
628
+ $ampm = gmstrftime( '%p', $timestamp );
629
+ $ampm = strtolower( $ampm );
630
+ return gmstrftime( '%a, %B %e @ %l:%M', $timestamp ) . $ampm;
631
+ }
632
+
633
+ /**
634
+ * get_long_date function
635
+ *
636
+ * Format a long-length date for use in other views (e.g., single event);
637
+ * this is also converted to the local timezone.
638
+ *
639
+ * @param int $timestamp
640
+ *
641
+ * @return string
642
+ **/
643
+ function get_long_date( $timestamp ) {
644
+ $timestamp = $this->gmt_to_local( $timestamp );
645
+ return gmstrftime( '%a, %B %e', $timestamp );
646
+ }
647
+
648
+ /**
649
+ * gmt_to_local function
650
+ *
651
+ * Returns the UNIX timestamp adjusted to the local timezone.
652
+ *
653
+ * @param int $timestamp
654
+ *
655
+ * @return int
656
+ **/
657
+ function gmt_to_local( $timestamp ) {
658
+ return $timestamp + get_option( 'gmt_offset' ) * 3600;
659
+ }
660
+
661
+ /**
662
+ * local_to_gmt function
663
+ *
664
+ * Returns the UNIX timestamp adjusted from the local timezone to GMT.
665
+ *
666
+ * @param int $timestamp
667
+ *
668
+ * @return int
669
+ **/
670
+ function local_to_gmt( $timestamp ) {
671
+ return $timestamp - get_option( 'gmt_offset' ) * 3600;
672
+ }
673
+
674
+ /**
675
+ * A GMT-version of PHP getdate().
676
+ *
677
+ * @param int $timestamp UNIX timestamp
678
+ * @return array Same result as getdate(), but based in GMT time.
679
+ **/
680
+ function gmgetdate( $timestamp = null ) {
681
+ if( ! $timestamp ) $timestamp = time();
682
+ $bits = explode( ',', gmdate( 's,i,G,j,w,n,Y,z,l,F,U', $timestamp ) );
683
+ $bits = array_combine(
684
+ array( 'seconds', 'minutes', 'hours', 'mday', 'wday', 'mon', 'year', 'yday', 'weekday', 'month', 0 ),
685
+ $bits
686
+ );
687
+ return $bits;
688
+ }
689
+
690
+ /**
691
+ * time_to_gmt function
692
+ *
693
+ * Converts time to GMT
694
+ *
695
+ * @param int $timestamp
696
+ *
697
+ * @return int
698
+ **/
699
+ function time_to_gmt( $timestamp ) {
700
+ return strtotime( gmdate( 'M d Y H:i:s', $timestamp ) );
701
+ }
702
+
703
+ /**
704
+ * get_gmap_url function
705
+ *
706
+ * Returns the URL to the Google Map for the given event object.
707
+ *
708
+ * @param Ai1ec_Event $event The event object to display a map for
709
+ *
710
+ * @return string
711
+ **/
712
+ function get_gmap_url( &$event ) {
713
+ $location_arg = urlencode( $event->address );
714
+ return "http://www.google.com/maps?f=q&hl=en&source=embed&q=$location_arg";
715
+ }
716
+
717
+ /**
718
+ * trim_excerpt function
719
+ *
720
+ * Generates an excerpt from the given content string. Adapted from
721
+ * WordPress's wp_trim_excerpt function that is not useful for applying
722
+ * to custom content.
723
+ *
724
+ * @param string $text The content to trim.
725
+ *
726
+ * @return string The excerpt.
727
+ **/
728
+ function trim_excerpt( $text )
729
+ {
730
+ $raw_excerpt = $text;
731
+
732
+ $text = strip_shortcodes( $text );
733
+
734
+ $text = str_replace(']]>', ']]&gt;', $text);
735
+ $text = strip_tags($text);
736
+ $excerpt_length = apply_filters('excerpt_length', 55);
737
+ $excerpt_more = apply_filters('excerpt_more', ' ' . '[...]');
738
+ $words = preg_split("/[\n\r\t ]+/", $text, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY);
739
+ if ( count($words) > $excerpt_length ) {
740
+ array_pop($words);
741
+ $text = implode(' ', $words);
742
+ $text = $text . $excerpt_more;
743
+ } else {
744
+ $text = implode(' ', $words);
745
+ }
746
+ return apply_filters('wp_trim_excerpt', $text, $raw_excerpt);
747
+ }
748
+
749
+ /**
750
+ * filter_by_terms function
751
+ *
752
+ * Returns a subset of post IDs from the given set of post IDs that have any
753
+ * of the given taxonomy term IDs. This is actually useful for all posts and
754
+ * taxonomies in general, not just event posts and event-specific taxonomies.
755
+ *
756
+ * @param array|string $post_ids Post IDs as an array of ints or
757
+ * comma-separated string
758
+ * @param array|string $term_ids Term IDs as an array of ints or
759
+ * comma-separated string
760
+ *
761
+ * @return array Filtered post IDs as an array of ints
762
+ **/
763
+ function filter_by_terms( $post_ids, $term_ids )
764
+ {
765
+ global $wpdb;
766
+
767
+ // ===============================================
768
+ // = Sanitize provided IDs against SQL injection =
769
+ // ===============================================
770
+ if( ! is_array( $post_ids ) )
771
+ $post_ids = explode( ',', $post_ids );
772
+ foreach( $post_ids as &$post_id ) {
773
+ $post_id = intval( $post_id );
774
+ }
775
+ $post_ids = join( ',', $post_ids );
776
+
777
+ if( ! is_array( $term_ids ) )
778
+ $term_ids = explode( ',', $term_ids );
779
+ foreach( $term_ids as &$term_id ) {
780
+ $term_id = intval( $term_id );
781
+ }
782
+ $term_ids = join( ',', $term_ids );
783
+
784
+ $query =
785
+ "SELECT DISTINCT p.ID " .
786
+ "FROM $wpdb->posts p " .
787
+ "INNER JOIN $wpdb->term_relationships tr ON p.ID = tr.object_id " .
788
+ "INNER JOIN $wpdb->term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id " .
789
+ "WHERE p.ID IN ( " . $post_ids . " ) " .
790
+ "AND tt.term_id IN ( " . $term_ids . " )";
791
+
792
+ return $wpdb->get_col( $query );
793
+ }
794
+
795
+ /**
796
+ * get_category_color function
797
+ *
798
+ *
799
+ *
800
+ * @return void
801
+ **/
802
+ function get_category_color( $term_id = 0 ) {
803
+ global $wpdb;
804
+
805
+ $term_id = (int) $term_id;
806
+ $table_name = $wpdb->prefix . 'ai1ec_event_category_colors';
807
+ $color = $wpdb->get_var( "SELECT term_color FROM {$table_name} WHERE term_id = {$term_id}" );
808
+ return $color;
809
+ }
810
+
811
+ /**
812
+ * get_category_color_square function
813
+ *
814
+ *
815
+ *
816
+ * @return void
817
+ **/
818
+ function get_category_color_square( $term_id ) {
819
+ $color = $this->get_category_color( $term_id );
820
+ if( ! is_null( $color ) && ! empty( $color ) )
821
+ return '<div class="ai1ec-category-color" style="background:' . $color . '"></div>';
822
+
823
+ return '';
824
+ }
825
+
826
+ /**
827
+ * get_event_category_color_style function
828
+ *
829
+ * Returns the style attribute assigning the category color style to an event.
830
+ *
831
+ * @return string
832
+ **/
833
+ function get_event_category_color_style( $term_id, $allday = false ) {
834
+ $color = $this->get_category_color( $term_id );
835
+ if( ! is_null( $color ) && ! empty( $color ) ) {
836
+ if( $allday )
837
+ return ' style="background:' . $color . '"';
838
+ else
839
+ return ' style="color:' . $color . ' !important"';
840
+ }
841
+
842
+ return '';
843
+ }
844
+
845
+ /**
846
+ * get_event_category_colors function
847
+ *
848
+ * Returns category color boxes
849
+ *
850
+ * @return string
851
+ **/
852
+ function get_event_category_colors( $cats ) {
853
+ $sqrs = '';
854
+ foreach( $cats as $cat ) :
855
+ $tmp = $this->get_category_color_square( $cat->term_id );
856
+ if( ! empty( $tmp ) )
857
+ $sqrs .= $tmp;
858
+ endforeach;
859
+
860
+ return $sqrs;
861
+ }
862
+
863
+ /**
864
+ * create_end_dropdown function
865
+ *
866
+ *
867
+ *
868
+ * @return void
869
+ **/
870
+ function create_end_dropdown( $selected = null ) {
871
+ ob_start();
872
+
873
+ $options = array(
874
+ 0 => 'Never',
875
+ 1 => 'After',
876
+ 2 => 'On date'
877
+ );
878
+
879
+ ?>
880
+ <select name="ai1ec_end" id="ai1ec_end">
881
+ <?php foreach( $options as $key => $val ): ?>
882
+ <option value="<?php echo $key ?>" <?php if( $key === $selected ) echo 'selected="selected"' ?>>
883
+ <?php _e( $val, AI1EC_PLUGIN_NAME ) ?>
884
+ </option>
885
+ <?php endforeach ?>
886
+ </select>
887
+ <?php
888
+
889
+ $output = ob_get_contents();
890
+ ob_end_clean();
891
+
892
+ return $output;
893
+ }
894
+ }
895
+ // END class
app/helper/class-ai1ec-exporter-helper.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-exporter-helper.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_Exporter_Helper class
11
+ *
12
+ * @package Helpers
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_Exporter_Helper {
16
+ /**
17
+ * _instance class variable
18
+ *
19
+ * Class instance
20
+ *
21
+ * @var null | object
22
+ **/
23
+ private static $_instance = NULL;
24
+
25
+ /**
26
+ * Constructor
27
+ *
28
+ * Default constructor
29
+ **/
30
+ private function __construct() { }
31
+
32
+ /**
33
+ * get_instance function
34
+ *
35
+ * Return singleton instance
36
+ *
37
+ * @return object
38
+ **/
39
+ static function get_instance() {
40
+ if( self::$_instance === NULL ) {
41
+ self::$_instance = new self();
42
+ }
43
+
44
+ return self::$_instance;
45
+ }
46
+
47
+ /**
48
+ * insert_event_in_calendar function
49
+ *
50
+ * Add event to the calendar
51
+ *
52
+ * @param object $event Event object
53
+ * @param object $c Calendar object
54
+ *
55
+ * @return void
56
+ **/
57
+ function insert_event_in_calendar( $event, &$c )
58
+ {
59
+ global $ai1ec_events_helper;
60
+
61
+ $tz = get_option( 'timezone_string' );
62
+
63
+ $e = & $c->newComponent( 'vevent' );
64
+ $uid = $event->ical_uid ? $event->ical_uid : $event->post->guid;
65
+ $e->setProperty( 'uid', $uid );
66
+ $e->setProperty( 'url', get_permalink( $event->post_id ) );
67
+ $e->setProperty( 'summary', html_entity_decode( $event->post->post_title, ENT_QUOTES ) );
68
+ $content = apply_filters( 'the_content', $event->post->post_content );
69
+ $content = str_replace(']]>', ']]&gt;', $content);
70
+ $e->setProperty( 'description', $content );
71
+ if( $event->allday ) {
72
+ $e->setProperty( 'dtstart',
73
+ gmdate( "Ymd\T", $ai1ec_events_helper->gmt_to_local( $event->start ) ),
74
+ array( 'VALUE' => 'DATE', 'TZID' => $tz ) );
75
+ $e->setProperty( 'dtend',
76
+ gmdate( "Ymd\T", $ai1ec_events_helper->gmt_to_local( $event->end ) ),
77
+ array( 'VALUE' => 'DATE', 'TZID' => $tz ) );
78
+ } else {
79
+ $e->setProperty( 'dtstart',
80
+ gmdate( "Ymd\THis\Z", $ai1ec_events_helper->gmt_to_local( $event->start ) ),
81
+ array( 'TZID' => $tz ) );
82
+ $e->setProperty( 'dtend',
83
+ gmdate( "Ymd\THis\Z", $ai1ec_events_helper->gmt_to_local( $event->end ) ),
84
+ array( 'TZID' => $tz ) );
85
+ }
86
+ $e->setProperty( 'location', $event->address );
87
+
88
+ $contact = ! empty( $event->contact_name ) ? $event->contact_name : '';
89
+ $contact .= ! empty( $event->contact_phone ) ? " ($event->contact_phone)" : '';
90
+ $contact .= ! empty( $event->contact_email ) ? " <$event->contact_email>" : '';
91
+ $e->setProperty( 'contact', $contact );
92
+
93
+ $rrule = array();
94
+ if( ! empty( $event->recurrence_rules ) ) {
95
+ $rules = array();
96
+ foreach( explode( ';', $event->recurrence_rules ) AS $v) {
97
+ list($k, $v) = explode( '=', $v );
98
+ // If $v is a comma-separated list, turn it into array for iCalcreator
99
+ switch( $k ) {
100
+ case 'BYSECOND':
101
+ case 'BYMINUTE':
102
+ case 'BYHOUR':
103
+ case 'BYDAY':
104
+ case 'BYMONTHDAY':
105
+ case 'BYYEARDAY':
106
+ case 'BYWEEKNO':
107
+ case 'BYMONTH':
108
+ case 'BYSETPOS':
109
+ $exploded = explode( ',', $v );
110
+ break;
111
+ default:
112
+ $exploded = $v;
113
+ break;
114
+ }
115
+ // iCalcreator requires a more complex array structure for BYDAY...
116
+ if( $k == 'BYDAY' ) {
117
+ $v = array();
118
+ foreach( $exploded as $day ) {
119
+ $v[] = array( 'DAY' => $day );
120
+ }
121
+ } else {
122
+ $v = $exploded;
123
+ }
124
+ $rrule[ $k ] = $v;
125
+ }
126
+ }
127
+
128
+ if( ! empty( $rrule ) ) $e->setProperty( 'rrule', $rrule );
129
+ }
130
+ }
app/helper/class-ai1ec-importer-helper.php ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-importer-helper.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_Importer_Helper class
11
+ *
12
+ * @package Helpers
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_Importer_Helper {
16
+ /**
17
+ * _instance class variable
18
+ *
19
+ * Class instance
20
+ *
21
+ * @var null | object
22
+ **/
23
+ private static $_instance = NULL;
24
+
25
+ /**
26
+ * Constructor
27
+ *
28
+ * Default constructor
29
+ **/
30
+ private function __construct() { }
31
+
32
+ /**
33
+ * get_instance function
34
+ *
35
+ * Return singleton instance
36
+ *
37
+ * @return object
38
+ **/
39
+ static function get_instance() {
40
+ if( self::$_instance === NULL ) {
41
+ self::$_instance = new self();
42
+ }
43
+
44
+ return self::$_instance;
45
+ }
46
+
47
+ /**
48
+ * time_array_to_timestamp function
49
+ *
50
+ * Converts time array to time string.
51
+ * Passed array: Array( 'year', 'month', 'day', ['hour', 'min', 'sec', ['tz']] )
52
+ * Return int: UNIX timestamp in GMT
53
+ *
54
+ * @param array $t Time array
55
+ * @param string $def_timezone Default time zone in case not defined in $t
56
+ *
57
+ * @return int UNIX timestamp
58
+ **/
59
+ function time_array_to_timestamp( $t, $def_timezone ) {
60
+ $ret = $t['year'] . '-' . $t['month'] . '-' . $t['day'];
61
+ if( isset( $t['hour'] ) )
62
+ $ret .= ' ' . $t['hour'] . ':' . $t['min'] . ':' . $t['sec'];
63
+ $timezone = $t['tz'];
64
+ if( ! $timezone ) $timezone = $def_timezone;
65
+ if( $timezone )
66
+ $ret .= ' ' . $timezone;
67
+ return strtotime( $ret );
68
+ }
69
+
70
+ /**
71
+ * Gets and parses an iCalendar feed into an array of Ai1ec_Event objects
72
+ *
73
+ * @param object $feed Row from the ai1ec_event_feeds table
74
+ *
75
+ * @return int Number of events imported
76
+ */
77
+ function parse_ics_feed( &$feed )
78
+ {
79
+ global $ai1ec_events_helper;
80
+
81
+ $count = 0;
82
+
83
+ // include ical parser
84
+ require_once( AI1EC_LIB_PATH . '/iCalcreator.class.php' );
85
+
86
+ // set unique id, required if any component UID is missing
87
+ $config = array( 'unique_id' => 'ai1ec' );
88
+
89
+ // create new instance
90
+ $v = new vcalendar( array(
91
+ 'unique_id' => $feed->feed_url,
92
+ 'url' => str_replace( 'webcal', 'http', $feed->feed_url ),
93
+ ) );
94
+
95
+ // actual parse of the feed
96
+ if( $v->parse() )
97
+ {
98
+ $v->sort();
99
+ // Reverse the sort order, so that RECURRENCE-IDs are listed before the
100
+ // defining recurrence events, and therefore take precedence during
101
+ // caching.
102
+ $v->components = array_reverse( $v->components );
103
+
104
+ // TODO: select only VEVENT components that occur after, say, 1 month ago.
105
+ // Maybe use $v->selectComponents(), which takes into account recurrence
106
+
107
+ // Fetch default timezone in case individual properties don't define it
108
+ $timezone = $v->getProperty( 'X-WR-TIMEZONE' );
109
+ $timezone = $timezone[1];
110
+
111
+ // go over each event
112
+ while( $e = $v->getComponent( 'vevent' ) )
113
+ {
114
+ $start = $e->getProperty( 'dtstart' );
115
+ $end = $e->getProperty( 'dtend' );
116
+
117
+ // Event is all-day if no time components are defined
118
+ $allday = ! isset( $start['hour'] );
119
+
120
+ // convert times to GMT UNIX timestamps
121
+ $start = $this->time_array_to_timestamp( $start, $timezone );
122
+ $end = $this->time_array_to_timestamp( $end, $timezone );
123
+
124
+ // If all-day, and start and end times are equal, then this event has
125
+ // invalid end time (happens sometimes with poorly implemented iCalendar
126
+ // exports, such as in The Event Calendar), so set end time to 1 day
127
+ // after start time.
128
+ if( $allday && $start === $end )
129
+ $end += 24 * 60 * 60;
130
+
131
+ // Due to potential time zone differences (WP time zone vs. feed time
132
+ // zone), must convert all-day event start/end dates to date only (the
133
+ // *intended* local date, non-GMT-ified)
134
+ if( $allday ) {
135
+ $start = $ai1ec_events_helper->gmt_to_local( $start );
136
+ $start = $ai1ec_events_helper->gmgetdate( $start );
137
+ $start = gmmktime( 0, 0, 0, $start['mon'], $start['mday'], $start['year'] );
138
+ $start = $ai1ec_events_helper->local_to_gmt( $start );
139
+ $end = $ai1ec_events_helper->gmt_to_local( $end );
140
+ $end = $ai1ec_events_helper->gmgetdate( $end );
141
+ $end = gmmktime( 0, 0, 0, $end['mon'], $end['mday'], $end['year'] );
142
+ $end = $ai1ec_events_helper->local_to_gmt( $end );
143
+ }
144
+
145
+ if( $rrule = $e->createRrule() )
146
+ $rrule = trim( end( split( ':', $rrule ) ) );
147
+ if( $exrule = $e->createExrule() )
148
+ $exrule = trim( end( split( ':', $exrule ) ) );
149
+ if( $rdate = $e->createRdate() )
150
+ $rdate = trim( end( split( ':', $rdate ) ) );
151
+ if( $exdate = $e->createExdate() )
152
+ $exdate = trim( end( split( ':', $exdate ) ) );
153
+
154
+ $data = array(
155
+ 'start' => $start,
156
+ 'end' => $end,
157
+ 'allday' => $allday,
158
+ 'recurrence_rules' => $rrule,
159
+ 'exception_rules' => $exrule,
160
+ 'recurrence_dates' => $rdate,
161
+ 'exception_dates' => $exdate,
162
+ 'venue' => $e->getProperty( 'location' ),
163
+ 'ical_feed_url' => $feed->feed_url,
164
+ 'ical_source_url' => $e->getProperty( 'url' ),
165
+ 'ical_organizer' => $e->getProperty( 'organizer' ),
166
+ 'ical_contact' => $e->getProperty( 'contact' ),
167
+ 'ical_uid' => $e->getProperty( 'uid' ),
168
+ 'categories' => $feed->feed_category,
169
+ 'tags' => $feed->feed_tags,
170
+ 'post' => array(
171
+ 'post_status' => 'publish',
172
+ 'post_type' => AI1EC_POST_TYPE,
173
+ 'post_author' => 1,
174
+ 'post_title' => $e->getProperty( 'summary' ),
175
+ 'post_content' => stripslashes( str_replace( '\n', "\n", $e->getProperty( 'description' ) ) ),
176
+ ),
177
+ );
178
+
179
+ $event = new Ai1ec_Event( $data );
180
+
181
+ // TODO: when singular events change their times in an ICS feed from one
182
+ // import to another, the matching_event_id is null, which is wrong. We
183
+ // want to match that event that previously had a different time.
184
+ // However, we also want the function to NOT return a matching event in
185
+ // the case of recurring events, and different events with different
186
+ // RECURRENCE-IDs... ponder how to solve this.. may require saving the
187
+ // RECURRENCE-ID as another field in the database.
188
+ $matching_event_id = $ai1ec_events_helper->get_matching_event_id(
189
+ $event->ical_uid,
190
+ $event->ical_feed_url,
191
+ $event->start,
192
+ ! empty( $event->recurrence_rules )
193
+ );
194
+
195
+ if( is_null( $matching_event_id ) )
196
+ {
197
+ // =================================================
198
+ // = Event was not found, so store it and the post =
199
+ // =================================================
200
+ $event->save();
201
+ }
202
+ else
203
+ {
204
+ // ======================================================
205
+ // = Event was found, let's store the new event details =
206
+ // ======================================================
207
+
208
+ // Update the post
209
+ $post = get_post( $matching_event_id );
210
+ $post->post_title = $event->post->post_title;
211
+ $post->post_content = $event->post->post_content;
212
+ wp_update_post( $post );
213
+
214
+ // Update the event
215
+ $event->post_id = $matching_event_id;
216
+ $event->post = $post;
217
+ $event->save( true );
218
+
219
+ // Delete event's cache
220
+ $ai1ec_events_helper->delete_event_cache( $matching_event_id );
221
+ }
222
+
223
+ // Regenerate event's cache
224
+ $ai1ec_events_helper->cache_event( $event );
225
+
226
+ $count++;
227
+ }
228
+ }
229
+
230
+ return $count;
231
+ }
232
+ }
233
+ // END class
app/helper/class-ai1ec-settings-helper.php ADDED
@@ -0,0 +1,329 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-settings-helper.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_Settings_Helper class
11
+ *
12
+ * @package Helpers
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_Settings_Helper {
16
+ /**
17
+ * _instance class variable
18
+ *
19
+ * Class instance
20
+ *
21
+ * @var null | object
22
+ **/
23
+ private static $_instance = NULL;
24
+
25
+ /**
26
+ * get_instance function
27
+ *
28
+ * Return singleton instance
29
+ *
30
+ * @return object
31
+ **/
32
+ static function get_instance() {
33
+ if( self::$_instance === NULL ) {
34
+ self::$_instance = new self();
35
+ }
36
+
37
+ return self::$_instance;
38
+ }
39
+
40
+ /**
41
+ * Constructor
42
+ *
43
+ * Default constructor
44
+ **/
45
+ private function __construct() { }
46
+
47
+ /**
48
+ * wp_pages_dropdown function
49
+ *
50
+ * Display drop-down list selector of pages, including an "Auto-Create New Page"
51
+ * option which causes the plugin to generate a new page on user's behalf.
52
+ *
53
+ * @param string $field_name
54
+ * @param int $selected_page_id
55
+ * @param string $auto_page
56
+ * @param bool $include_disabled
57
+ *
58
+ * @return string
59
+ **/
60
+ function wp_pages_dropdown( $field_name, $selected_page_id = 0, $auto_page = '', $include_disabled = false ) {
61
+ global $wpdb;
62
+ ob_start();
63
+ $query = "SELECT
64
+ *
65
+ FROM
66
+ {$wpdb->posts}
67
+ WHERE
68
+ post_status = %s
69
+ AND
70
+ post_type = %s";
71
+
72
+ $query = $wpdb->prepare( $query, 'publish', 'page' );
73
+ $results = $wpdb->get_results( $query );
74
+ $pages = array();
75
+ if( $results ) {
76
+ $pages = $results;
77
+ }
78
+
79
+ ?>
80
+ <select class="inputwidth" name="<?php echo $field_name; ?>"
81
+ id="<?php echo $field_name; ?>"
82
+ class="wafp-dropdown wafp-pages-dropdown">
83
+ <?php if( ! empty( $auto_page ) ) { ?>
84
+ <option value="__auto_page:<?php echo $auto_page; ?>">
85
+ <?php _e( '- Auto-Create New Page -', THE_PLUGIN_NAME ); ?>
86
+ </option>
87
+ <?php }
88
+ foreach( $pages as $page ) {
89
+ if( $selected_page_id == $page->ID ) {
90
+ $selected = ' selected="selected"';
91
+ $selected_title = $page->post_title;
92
+ } else {
93
+ $selected = '';
94
+ }
95
+ ?>
96
+ <option value="<?php echo $page->ID ?>" <?php echo $selected; ?>>
97
+ <?php echo $page->post_title ?>
98
+ </option>
99
+ <?php } ?>
100
+ </select>
101
+ <?php
102
+ if( is_numeric( $selected_page_id ) && $selected_page_id > 0 ) {
103
+ $permalink = get_permalink( $selected_page_id );
104
+ ?>
105
+ <br /><a href="<?php echo $permalink ?>" target="_blank">
106
+ <?php printf( __( 'View "%s" »', AI1EC_PLUGIN_NAME ), $selected_title ) ?>
107
+ </a>
108
+ <?php
109
+ }
110
+ return ob_get_clean();
111
+ }
112
+
113
+ /**
114
+ * get_week_dropdown function
115
+ *
116
+ * Creates the dropdown element for selecting start of the week
117
+ *
118
+ * @param int $week_start_day Selected start day
119
+ *
120
+ * @return String dropdown element
121
+ **/
122
+ function get_week_dropdown( $week_start_day ) {
123
+ global $wp_locale;
124
+ ob_start();
125
+ ?>
126
+ <select class="inputwidth" name="week_start_day" id="week_start_day">
127
+ <?php
128
+ for( $day_index = 0; $day_index <= 6; $day_index++ ) :
129
+ $selected = ( $week_start_day == $day_index ) ? 'selected="selected"' : '';
130
+ echo "\n\t<option value='" . esc_attr($day_index) . "' $selected>" . $wp_locale->get_weekday($day_index) . '</option>';
131
+ endfor;
132
+ ?>
133
+ </select>
134
+ <?php
135
+ return ob_get_clean();
136
+ }
137
+
138
+ /**
139
+ * get_view_dropdown function
140
+ *
141
+ * @return void
142
+ **/
143
+ function get_view_dropdown( $view = null ) {
144
+ ob_start();
145
+ ?>
146
+ <select name="default_calendar_view">
147
+ <option value="month" <?php echo $view == 'month' ? 'selected' : '' ?>>
148
+ <?php _e( 'Month', AI1EC_PLUGIN_NAME ) ?>
149
+ </option>
150
+ <option value="agenda" <?php echo $view == 'agenda' ? 'selected' : '' ?>>
151
+ <?php _e( 'Agenda', AI1EC_PLUGIN_NAME ) ?>
152
+ </option>
153
+ </select>
154
+ <?php
155
+ return ob_get_clean();
156
+ }
157
+
158
+ /**
159
+ * get_cron_freq_dropdown function
160
+ *
161
+ * @return void
162
+ **/
163
+ function get_cron_freq_dropdown( $cron_freq = null ) {
164
+ ob_start();
165
+ ?>
166
+ <select name="cron_freq">
167
+ <option value="hourly" <?php echo $cron_freq == 'hourly' ? 'selected' : ''; ?>>
168
+ <?php _e( 'Hourly', AI1EC_PLUGIN_NAME ) ?>
169
+ </option>
170
+ <option value="twicedaily" <?php echo $cron_freq == 'twicedaily' ? 'selected' : '' ?>>
171
+ <?php _e( 'Twice Daily', AI1EC_PLUGIN_NAME ) ?>
172
+ </option>
173
+ <option value="daily" <?php echo $cron_freq == 'daily' ? 'selected' : '' ?>>
174
+ <?php _e( 'Daily', AI1EC_PLUGIN_NAME ) ?>
175
+ </option>
176
+ </select>
177
+ <?php
178
+ return ob_get_clean();
179
+ }
180
+
181
+ /**
182
+ * get_feed_rows function
183
+ *
184
+ * Creates feed rows to display on settings page
185
+ *
186
+ * @return String feed rows
187
+ **/
188
+ function get_feed_rows() {
189
+ global $wpdb,
190
+ $ai1ec_view_helper;
191
+
192
+ // Select all added feeds
193
+ $table_name = $wpdb->prefix . 'ai1ec_event_feeds';
194
+ $sql = "SELECT * FROM {$table_name}";
195
+ $rows = $wpdb->get_results( $sql );
196
+
197
+ ob_start();
198
+ foreach( $rows as $row ) :
199
+ $feed_category = get_term( $row->feed_category, 'events_categories' );
200
+ $table_name = $wpdb->prefix . 'ai1ec_events';
201
+ $sql = "SELECT COUNT(*) FROM {$table_name} WHERE ical_feed_url = '%s'";
202
+ $events = $wpdb->get_var( $wpdb->prepare( $sql, $row->feed_url ) );
203
+ $args = array(
204
+ 'feed_url' => $row->feed_url,
205
+ 'event_category' => $feed_category->name,
206
+ 'tags' => stripslashes( $row->feed_tags ),
207
+ 'feed_id' => $row->feed_id,
208
+ 'events' => $events
209
+ );
210
+ $ai1ec_view_helper->display( 'feed_row.php', $args );
211
+ endforeach;
212
+
213
+ return ob_get_clean();
214
+ }
215
+
216
+ /**
217
+ * get_event_categories_select function
218
+ *
219
+ * Creates the dropdown element for selecting feed category
220
+ *
221
+ * @param int|null $selected The selected category or null
222
+ *
223
+ * @return String dropdown element
224
+ **/
225
+ function get_event_categories_select( $selected = null) {
226
+ ob_start();
227
+ ?>
228
+ <select name="ai1ec_feed_category" id="ai1ec_feed_category">
229
+ <?php
230
+ foreach( get_terms( 'events_categories', array( 'hide_empty' => false ) ) as $term ) :
231
+ ?>
232
+ <option value="<?php echo $term->term_id; ?>" <?php echo ( $selected === $term->id ) ? 'selected' : '' ?>>
233
+ <?php echo $term->name; ?>
234
+ </option>
235
+ <?php
236
+ endforeach;
237
+ ?>
238
+ </select>
239
+ <?php
240
+ return ob_get_clean();
241
+ }
242
+
243
+ /**
244
+ * general_settings_meta_box function
245
+ *
246
+ *
247
+ *
248
+ * @return void
249
+ **/
250
+ function general_settings_meta_box( $object, $box ) {
251
+ global $ai1ec_view_helper,
252
+ $ai1ec_settings_helper,
253
+ $ai1ec_settings;
254
+
255
+ $calendar_page = $ai1ec_settings_helper->wp_pages_dropdown(
256
+ 'calendar_page_id',
257
+ $ai1ec_settings->calendar_page_id,
258
+ __( 'Calendar', AI1EC_PLUGIN_NAME )
259
+ );
260
+ $calendar_css_selector = $ai1ec_settings->calendar_css_selector;
261
+ $week_start_day = $ai1ec_settings_helper->get_week_dropdown( get_option( 'start_of_week' ) );
262
+ $agenda_events_per_page = $ai1ec_settings->agenda_events_per_page;
263
+ $include_events_in_rss =
264
+ '<input type="checkbox" name="include_events_in_rss"
265
+ id="include_events_in_rss" value="1"'
266
+ . ( $ai1ec_settings->include_events_in_rss ? ' checked="checked"' : '' )
267
+ . '/>';
268
+ $show_publish_button = $ai1ec_settings->show_publish_button ? 'checked=checked' : '';
269
+ $show_create_event_button = $ai1ec_settings->show_create_event_button ? 'checked=checked' : '';
270
+ $inject_categories = $ai1ec_settings->inject_categories ? 'checked=checked' : '';
271
+ $default_calendar_view = $ai1ec_settings_helper->get_view_dropdown( $ai1ec_settings->default_calendar_view );
272
+
273
+ $args = array(
274
+ 'calendar_page' => $calendar_page,
275
+ 'default_calendar_view' => $default_calendar_view,
276
+ 'calendar_css_selector' => $calendar_css_selector,
277
+ 'week_start_day' => $week_start_day,
278
+ 'agenda_events_per_page' => $agenda_events_per_page,
279
+ 'show_publish_button' => $show_publish_button,
280
+ 'show_create_event_button' => $show_create_event_button,
281
+ 'inject_categories' => $inject_categories,
282
+ );
283
+ $ai1ec_view_helper->display( 'box_general_settings.php', $args );
284
+ }
285
+
286
+ /**
287
+ * ics_import_settings_meta_box function
288
+ *
289
+ *
290
+ *
291
+ * @return void
292
+ **/
293
+ function ics_import_settings_meta_box( $object, $box ) {
294
+ global $ai1ec_view_helper,
295
+ $ai1ec_settings_helper;
296
+
297
+ $args = array(
298
+ 'cron_freq' => $ai1ec_settings_helper->get_cron_freq_dropdown( $ai1ec_settings->cron_freq ),
299
+ 'event_categories' => $ai1ec_settings_helper->get_event_categories_select(),
300
+ 'feed_rows' => $ai1ec_settings_helper->get_feed_rows()
301
+ );
302
+ $ai1ec_view_helper->display( 'box_ics_import_settings.php', $args );
303
+ }
304
+
305
+ /**
306
+ * the_seed_studio_meta_box function
307
+ *
308
+ *
309
+ *
310
+ * @return void
311
+ **/
312
+ function the_seed_studio_meta_box( $object, $box ) {
313
+ global $ai1ec_view_helper;
314
+ $ai1ec_view_helper->display( 'box_the_seed_studio.php' );
315
+ }
316
+
317
+ /**
318
+ * add_meta_boxes function
319
+ *
320
+ *
321
+ *
322
+ * @return void
323
+ **/
324
+ function add_meta_boxes(){
325
+ global $ai1ec_settings;
326
+ do_action( 'add_meta_boxes', $ai1ec_settings->settings_page );
327
+ }
328
+ }
329
+ // END class
app/helper/class-ai1ec-view-helper.php ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-view-helper.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_View_Helper class
11
+ *
12
+ * @package Helpers
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_View_Helper {
16
+ /**
17
+ * _instance class variable
18
+ *
19
+ * Class instance
20
+ *
21
+ * @var null | object
22
+ **/
23
+ private static $_instance = NULL;
24
+
25
+ /**
26
+ * Constructor
27
+ *
28
+ * Default constructor
29
+ **/
30
+ private function __construct() { }
31
+
32
+ /**
33
+ * get_instance function
34
+ *
35
+ * Return singleton instance
36
+ *
37
+ * @return object
38
+ **/
39
+ static function get_instance() {
40
+ if( self::$_instance === NULL ) {
41
+ self::$_instance = new self();
42
+ }
43
+
44
+ return self::$_instance;
45
+ }
46
+
47
+ /**
48
+ * display function
49
+ *
50
+ * Display the view specified by file $file and passed arguments $args.
51
+ *
52
+ * @param string $file
53
+ * @param array $args
54
+ *
55
+ * @return void
56
+ **/
57
+ function display( $file = false, $args = array() ) {
58
+ if( ! $file || empty( $file ) ) {
59
+ throw new Ai1ec_File_Not_Provided( "You need to specify a view file." );
60
+ }
61
+
62
+ $file = AI1EC_VIEW_PATH . "/" . $file;
63
+
64
+ if( ! file_exists( $file ) ) {
65
+ throw new Ai1ec_File_Not_Found( "The specified view file doesn't exist." );
66
+ } else {
67
+ extract( $args );
68
+ require( $file );
69
+ }
70
+ }
71
+
72
+ /**
73
+ * display_css function
74
+ *
75
+ * Renders the given stylesheet inline. If stylesheet has already been
76
+ * displayed once before with the same set of $args, does not display
77
+ * it again.
78
+ *
79
+ * @param string $file
80
+ * @param array $args
81
+ *
82
+ * @return void
83
+ **/
84
+ function display_css( $file = false, $args = array() ) {
85
+ static $displayed = array();
86
+ static $num = 0;
87
+
88
+ if( ! $file || empty( $file ) ) {
89
+ throw new Ai1ec_File_Not_Provided( 'You need to specify a css file.' );
90
+ }
91
+
92
+ $file = AI1EC_CSS_PATH . "/" . $file;
93
+
94
+ if( $displayed[$file] === $args ) // Skip if already displayed
95
+ return;
96
+
97
+ if( ! file_exists( $file ) ) {
98
+ throw new Ai1ec_File_Not_Found( "The specified css file doesn't exist." );
99
+ } else {
100
+ $displayed[$file] = $args; // Flag that we've displayed this file with these args
101
+
102
+ extract( $args );
103
+ echo '<style type="text/css">';
104
+ require( $file );
105
+ echo '</style>';
106
+ }
107
+ }
108
+
109
+ /**
110
+ * display_js function
111
+ *
112
+ * Renders the given script inline. If script has already been displayed
113
+ * once before with the same set of $args, does not display it again.
114
+ *
115
+ * @param string $file
116
+ * @param array $args
117
+ *
118
+ * @return void
119
+ **/
120
+ function display_js( $file = false, $args = array() ) {
121
+ static $displayed = array();
122
+
123
+ if( ! $file || empty( $file ) ) {
124
+ throw new Ai1ec_File_Not_Provided( "You need to specify a js file." );
125
+ }
126
+
127
+ $file = AI1EC_JS_PATH . "/" . $file;
128
+
129
+ if( $displayed[$file] === $args) // Skip if already displayed
130
+ return;
131
+
132
+ if( ! file_exists( $file ) ) {
133
+ throw new Ai1ec_File_Not_Found( "The specified js file doesn't exist." );
134
+ } else {
135
+ $displayed[$file] = $args; // Flag that we've displayed this file with these args
136
+
137
+ extract( $args );
138
+ echo '<script type="text/javascript" charset="utf-8">';
139
+ echo '/* <![CDATA[ */';
140
+ require( $file );
141
+ echo '/* ]]> */';
142
+ echo '</script>';
143
+ }
144
+ }
145
+
146
+ /**
147
+ * get_view function
148
+ *
149
+ * Return the output of a view as a string rather than output to response.
150
+ *
151
+ * @param string $file
152
+ * @param array $args
153
+ *
154
+ * @return void
155
+ **/
156
+ function get_view( $file = false, $args = array() ) {
157
+ ob_start();
158
+ $this->display( $file, $args );
159
+ return ob_get_clean();
160
+ }
161
+
162
+ /**
163
+ * json_response function
164
+ *
165
+ * Utility for properly outputting JSON data as an AJAX response.
166
+ *
167
+ * @param array $data
168
+ *
169
+ * @return void
170
+ **/
171
+ function json_response( $data ) {
172
+ header( 'Cache-Control: no-cache, must-revalidate' );
173
+ header( 'Pragma: no-cache' );
174
+ header( 'Content-type: application/json' );
175
+
176
+ // Output JSON-encoded result and quit
177
+ echo json_encode( $data );
178
+ exit;
179
+ }
180
+
181
+ }
182
+ // END class
app/model/class-ai1ec-event.php ADDED
@@ -0,0 +1,794 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-event.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_Event class
11
+ *
12
+ * @package Models
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_Event {
16
+ /**
17
+ * post class variable
18
+ *
19
+ * @var object
20
+ **/
21
+ var $post;
22
+
23
+ /**
24
+ * post_id class variable
25
+ *
26
+ * @var int
27
+ **/
28
+ var $post_id;
29
+
30
+ /**
31
+ * instance_id class variable
32
+ *
33
+ * Uniquely identifies the recurrence instance of this event object. This
34
+ * may be null.
35
+ *
36
+ * @var int|null
37
+ **/
38
+ var $instance_id;
39
+
40
+ /**
41
+ * start class variable
42
+ *
43
+ * @var int
44
+ **/
45
+ var $start;
46
+
47
+ /**
48
+ * end class variable
49
+ *
50
+ * @var int
51
+ **/
52
+ var $end;
53
+
54
+ /**
55
+ * allday class variable
56
+ *
57
+ * @var int
58
+ **/
59
+ var $allday;
60
+
61
+ /**
62
+ * recurrence_rules class variable
63
+ *
64
+ * @var string
65
+ **/
66
+ var $recurrence_rules;
67
+
68
+ /**
69
+ * exception_rules class variable
70
+ *
71
+ * @var string
72
+ **/
73
+ var $exception_rules;
74
+
75
+ /**
76
+ * recurrence_dates class variable
77
+ *
78
+ * @var string
79
+ **/
80
+ var $recurrence_dates;
81
+
82
+ /**
83
+ * exception_dates class variable
84
+ *
85
+ * @var string
86
+ **/
87
+ var $exception_dates;
88
+
89
+ /**
90
+ * venue class variable
91
+ *
92
+ * @var string
93
+ **/
94
+ var $venue;
95
+
96
+ /**
97
+ * country class variable
98
+ *
99
+ * @var string
100
+ **/
101
+ var $country;
102
+
103
+ /**
104
+ * address class variable
105
+ *
106
+ * @var string
107
+ **/
108
+ var $address;
109
+
110
+ /**
111
+ * city class variable
112
+ *
113
+ * @var string
114
+ **/
115
+ var $city;
116
+
117
+ /**
118
+ * province class variable
119
+ *
120
+ * @var string
121
+ **/
122
+ var $province;
123
+
124
+ /**
125
+ * postal_code class variable
126
+ *
127
+ * @var int
128
+ **/
129
+ var $postal_code;
130
+
131
+ /**
132
+ * show_map class variable
133
+ *
134
+ * @var int
135
+ **/
136
+ var $show_map;
137
+
138
+ /**
139
+ * contact_name class variable
140
+ *
141
+ * @var string
142
+ **/
143
+ var $contact_name;
144
+
145
+ /**
146
+ * contact_phone class variable
147
+ *
148
+ * @var string
149
+ **/
150
+ var $contact_phone;
151
+
152
+ /**
153
+ * contact_email class variable
154
+ *
155
+ * @var string
156
+ **/
157
+ var $contact_email;
158
+
159
+ /**
160
+ * cost class variable
161
+ *
162
+ * @var string
163
+ **/
164
+ var $cost;
165
+
166
+ // ====================================
167
+ // = iCalendar feed (.ics) properties =
168
+ // ====================================
169
+ /**
170
+ * ical_feed_url class variable
171
+ *
172
+ * @var string
173
+ **/
174
+ var $ical_feed_url;
175
+
176
+ /**
177
+ * ical_source_url class variable
178
+ *
179
+ * @var string
180
+ **/
181
+ var $ical_source_url;
182
+
183
+ /**
184
+ * ical_organizer class variable
185
+ *
186
+ * @var string
187
+ **/
188
+ var $ical_organizer;
189
+
190
+ /**
191
+ * ical_contact class variable
192
+ *
193
+ * @var string
194
+ **/
195
+ var $ical_contact;
196
+
197
+ /**
198
+ * ical_uid class variable
199
+ *
200
+ * @var string | int
201
+ **/
202
+ var $ical_uid;
203
+
204
+ // ============
205
+ // = Taxonomy =
206
+ // ============
207
+ /**
208
+ * tags class variable
209
+ *
210
+ * Associated event tag IDs, joined by commas.
211
+ *
212
+ * @var string
213
+ **/
214
+ var $tags;
215
+
216
+ /**
217
+ * categories class variable
218
+ *
219
+ * Associated event category IDs, joined by commas.
220
+ *
221
+ * @var string
222
+ **/
223
+ var $categories;
224
+
225
+ /**
226
+ * category_colors class variable
227
+ *
228
+ * @var string
229
+ **/
230
+ private $category_colors;
231
+
232
+ /**
233
+ * color_style class variable
234
+ *
235
+ * @var string
236
+ **/
237
+ private $color_style;
238
+
239
+ /**
240
+ * tags_html class variable
241
+ *
242
+ * A cache variable, used by __get().
243
+ *
244
+ * @var string
245
+ **/
246
+ private $tags_html;
247
+
248
+ /**
249
+ * categories_html class variable
250
+ *
251
+ * A cache variable, used by __get().
252
+ *
253
+ * @var string
254
+ **/
255
+ private $categories_html;
256
+
257
+ /**
258
+ * __construct function
259
+ *
260
+ * Create new event object, using provided data for initialization.
261
+ *
262
+ * @param int|array $data Look up post with id $data, or initialize fields
263
+ * with flat associative array $data containing both
264
+ * post and event fields returned by join query
265
+ *
266
+ * @return void
267
+ **/
268
+ function __construct( $data = null ) {
269
+ global $wpdb;
270
+
271
+ if( $data == null )
272
+ return;
273
+
274
+ // ===========
275
+ // = Post ID =
276
+ // ===========
277
+ if( is_numeric( $data ) )
278
+ {
279
+ // ============================
280
+ // = Fetch post from database =
281
+ // ============================
282
+ $post = get_post( $data );
283
+
284
+ if( ! $post || $post->post_status == 'auto-draft' )
285
+ throw new Ai1ec_Event_Not_Found( "Post with ID '$data' could not be retrieved from the database." );
286
+
287
+ // =============================
288
+ // = Fetch event from database =
289
+ // =============================
290
+ $query = $wpdb->prepare(
291
+ "SELECT e.post_id, UNIX_TIMESTAMP( e.start ) as start, UNIX_TIMESTAMP( e.end ) as end, e.allday, e.recurrence_rules, e.exception_rules,
292
+ e.recurrence_dates, e.exception_dates, e.venue, e.country, e.address, e.city, e.province, e.postal_code,
293
+ e.show_map, e.contact_name, e.contact_phone, e.contact_email, e.cost, e.ical_feed_url, e.ical_source_url,
294
+ e.ical_organizer, e.ical_contact, e.ical_uid, " .
295
+ "GROUP_CONCAT( ttc.term_id ) AS categories, " .
296
+ "GROUP_CONCAT( ttt.term_id ) AS tags " .
297
+ "FROM {$wpdb->prefix}ai1ec_events e " .
298
+ "LEFT JOIN $wpdb->term_relationships tr ON post_id = tr.object_id " .
299
+ "LEFT JOIN $wpdb->term_taxonomy ttc ON tr.term_taxonomy_id = ttc.term_taxonomy_id AND ttc.taxonomy = 'events_categories' " .
300
+ "LEFT JOIN $wpdb->term_taxonomy ttt ON tr.term_taxonomy_id = ttt.term_taxonomy_id AND ttt.taxonomy = 'events_tags' " .
301
+ "WHERE post_id = %d",
302
+ $data );
303
+ $event = $wpdb->get_row( $query );
304
+
305
+ if( $event === null || $event->post_id === null )
306
+ throw new Ai1ec_Event_Not_Found( "Event with ID '$data' could not be retrieved from the database." );
307
+
308
+ // ===========================
309
+ // = Assign post to property =
310
+ // ===========================
311
+ $this->post = $post;
312
+
313
+ // ==========================
314
+ // = Assign values to $this =
315
+ // ==========================
316
+ foreach( $this as $property => $value ) {
317
+ if( $property != 'post' )
318
+ $this->{$property} = $event->{$property};
319
+ }
320
+ }
321
+ // ===================
322
+ // = Post/event data =
323
+ // ===================
324
+ elseif( is_array( $data ) )
325
+ {
326
+ // =======================================================
327
+ // = Assign each event field the value from the database =
328
+ // =======================================================
329
+ foreach( $this as $property => $value ) {
330
+ if( $property != 'post' && array_key_exists( $property, $data ) ) {
331
+ $this->{$property} = $data[$property];
332
+ unset( $data[$property] );
333
+ }
334
+ }
335
+ if( isset( $data['post'] ) ) {
336
+ $this->post = (object) $data['post'];
337
+ } else {
338
+ // ========================================
339
+ // = Remaining fields are the post fields =
340
+ // ========================================
341
+ $this->post = (object) $data;
342
+ }
343
+ }
344
+ else {
345
+ throw new Ai1ec_Invalid_Argument( "Argument to constructor must be integer, array or null, not '$data'." );
346
+ }
347
+ }
348
+
349
+ /**
350
+ * __set function
351
+ *
352
+ * Magic set function
353
+ *
354
+ * @param string $name Property name
355
+ * @param mixed $value Property value
356
+ *
357
+ * @return void
358
+ **/
359
+ public function __set( $name, $value ) {
360
+ // Not currently used...
361
+ switch( $name ) {
362
+ default:
363
+ $this->{$name} = $value;
364
+ break;
365
+ }
366
+ }
367
+
368
+ /**
369
+ * __get function
370
+ *
371
+ * Magic get function
372
+ * Shortcuts for common formatted versions of event data.
373
+ *
374
+ * @param string $name Property name
375
+ *
376
+ * @return mixed Property value
377
+ **/
378
+ public function __get( $name ) {
379
+ global $post, $more, $ai1ec_events_helper;
380
+
381
+ switch( $name ) {
382
+
383
+ case 'uid':
384
+ return $this->post_id . '@' . bloginfo( 'url' );
385
+ // ========================
386
+ // = Get short-form dates =
387
+ // ========================
388
+ case 'short_start_time':
389
+ return $ai1ec_events_helper->get_short_time( $this->start );
390
+
391
+ case 'short_end_time':
392
+ return $ai1ec_events_helper->get_short_time( $this->end );
393
+
394
+ case 'short_start_date':
395
+ return $ai1ec_events_helper->get_short_date( $this->start );
396
+
397
+ case 'short_end_date':
398
+ // Subtract 1 second so that all-day events' end date still
399
+ // falls within the logical duration of days (since the end date
400
+ // is always midnight of the following day)
401
+ return $ai1ec_events_helper->get_short_date( $this->end - 1 );
402
+
403
+ // =========================
404
+ // = Get medium-form dates =
405
+ // =========================
406
+ case 'start_time':
407
+ return $ai1ec_events_helper->get_medium_time( $this->start );
408
+
409
+ case 'end_time':
410
+ return $ai1ec_events_helper->get_medium_time( $this->end );
411
+
412
+ // =======================
413
+ // = Get long-form times =
414
+ // =======================
415
+ case 'long_start_time':
416
+ return $ai1ec_events_helper->get_long_time( $this->start );
417
+
418
+ case 'long_end_time':
419
+ return $ai1ec_events_helper->get_long_time( $this->end );
420
+
421
+ // =======================
422
+ // = Get long-form dates =
423
+ // =======================
424
+ case 'long_start_date':
425
+ return $ai1ec_events_helper->get_long_date( $this->start );
426
+
427
+ case 'long_end_date':
428
+ // Subtract 1 second so that all-day events' end date still
429
+ // falls within the logical duration of days (since the end date
430
+ // is always midnight of the following day)
431
+ return $ai1ec_events_helper->get_long_date( $this->end - 1 );
432
+
433
+ case 'timespan_html':
434
+ $timespan = '';
435
+ $long_start_date = $this->long_start_date;
436
+ $long_end_date = $this->long_end_date;
437
+
438
+ if( $this->allday ) {
439
+ $timespan .= $long_start_date;
440
+ if( $long_end_date != $long_start_date )
441
+ $timespan .= " – $long_end_date";
442
+ $timespan = esc_html( $timespan );
443
+ $timespan .= '<span class="ai1ec-allday-label">';
444
+ $timespan .= __( ' (all-day)', AI1EC_PLUGIN_NAME );
445
+ $timespan .= '</span>';
446
+ } else {
447
+ if( $long_end_date != $long_start_date )
448
+ $timespan .= esc_html( $this->long_start_time . ' – ' . $this->long_end_time );
449
+ elseif( $this->start != $this->end )
450
+ $timespan .= esc_html( $this->long_start_time . ' - ' . $this->end_time );
451
+ else
452
+ $timespan .= esc_html( $this->long_start_time );
453
+ }
454
+ return $timespan;
455
+
456
+ // =====================================================
457
+ // = Get the post's excerpt for display in popup view. =
458
+ // =====================================================
459
+ case 'post_excerpt':
460
+ if( ! $this->post->post_excerpt ) {
461
+ $content = strip_tags( strip_shortcodes( $this->post->post_content ) );
462
+ $content = preg_replace( '/\s+/', ' ', $content );
463
+ $words = explode( ' ', $content );
464
+ if( count( $words ) > 25 )
465
+ $this->post->post_excerpt = implode( ' ', array_slice( $words, 0, 25 ) ) . ' [...]';
466
+ else
467
+ $this->post->post_excerpt = $content;
468
+ }
469
+ return $this->post->post_excerpt;
470
+
471
+ // ===============================================================
472
+ // = Return any available location details separated by newlines =
473
+ // ===============================================================
474
+ case 'location':
475
+ $location = '';
476
+ if( $this->venue ) $location .= "$this->venue\n";
477
+ if( $this->address ) {
478
+ $bits = explode( ',', $this->address );
479
+ $bits = array_map( 'trim', $bits );
480
+
481
+ // If more than three comma-separated values, treat first value as
482
+ // the street address, last value as the country, and everything
483
+ // in the middle as the city, state, etc.
484
+ if( count( $bits ) >= 3 ) {
485
+ // Append the street address
486
+ $street_address = array_shift( $bits ) . "\n";
487
+ if( $street_address ) $location .= $street_address;
488
+ // Save the country for the last line
489
+ $country = array_pop( $bits );
490
+ // Append the middle bit(s) (filtering out any zero-length strings)
491
+ $bits = array_filter( $bits, 'strval' );
492
+ if( $bits ) $location .= join( ',', $bits ) . "\n";
493
+ if( $country ) $location .= $country . "\n";
494
+ } else {
495
+ // There are two or less comma-separated values, so just append
496
+ // them each on their own line (filtering out any zero-length strings)
497
+ $bits = array_filter( $bits, 'strval' );
498
+ $location .= join( "\n", $bits );
499
+ }
500
+ }
501
+ return $location;
502
+
503
+ // ======================
504
+ // = Categories as HTML =
505
+ // ======================
506
+ case 'categories_html':
507
+ if( $this->categories_html === null ) {
508
+ $categories = wp_get_post_terms( $this->post_id, 'events_categories' );
509
+ foreach( $categories as &$category ) {
510
+ $category =
511
+ '<a class="ai1ec-category ai1ec-term-id-' . $category->term_id . '" ' .
512
+ ( $category->description ? 'title="' . esc_attr( $category->description ) . '" ' : '' ) .
513
+ 'href="' . get_term_link( $category ) . '">' .
514
+ $ai1ec_events_helper->get_category_color_square( $category->term_id ) . ' ' . esc_html( $category->name ) . '</a>';
515
+ }
516
+ $this->categories_html = join( ' ', $categories );
517
+ }
518
+ return $this->categories_html;
519
+
520
+ // ================
521
+ // = Tags as HTML =
522
+ // ================
523
+ case 'tags_html':
524
+ if( $this->tags_html === null ) {
525
+ $tags = wp_get_post_terms( $this->post_id, 'events_tags' );
526
+ foreach( $tags as &$tag ) {
527
+ $tag =
528
+ '<a class="ai1ec-tag ai1ec-term-id-' . $tag->term_id . '" ' .
529
+ ( $tag->description ? 'title="' . esc_attr( $tag->description ) . '" ' : '' ) .
530
+ 'href="' . get_term_link( $tag ) . '">' .
531
+ esc_html( $tag->name ) . '</a>';
532
+ }
533
+ $this->tags_html = join( ' ', $tags );
534
+ }
535
+ return $this->tags_html;
536
+
537
+ // ======================================
538
+ // = Style attribute for event category =
539
+ // ======================================
540
+ case 'color_style':
541
+ if( $this->color_style === null ) {
542
+ $categories = wp_get_post_terms( $this->post_id, 'events_categories' );
543
+ $this->color_style = $ai1ec_events_helper->get_event_category_color_style( $categories[0]->term_id, $this->allday );
544
+ }
545
+ return $this->color_style;
546
+
547
+ // ===============================================
548
+ // = HTML of category color boxes for this event =
549
+ // ===============================================
550
+ case 'category_colors':
551
+ if( $this->category_colors === null ) {
552
+ $categories = wp_get_post_terms( $this->post_id, 'events_categories' );
553
+ $this->category_colors = $ai1ec_events_helper->get_event_category_colors( $categories );
554
+ }
555
+ return $this->category_colors;
556
+
557
+ // ========================
558
+ // = Contact info as HTML =
559
+ // ========================
560
+ case 'contact_html':
561
+ $contact = '';
562
+ if( $this->contact_name )
563
+ $contact .= '<strong>' . esc_html( $this->contact_name ) . '</strong><br />';
564
+ if( $this->contact_phone )
565
+ $contact .= esc_html( $this->contact_phone ) . '<br />';
566
+ if( $this->contact_email )
567
+ $contact .= '<a href="mailto:' . esc_attr( $this->contact_email ) . '">' . esc_html( $this->contact_email ) . '</a>';
568
+ return $contact;
569
+
570
+ // ===========================
571
+ // = Recurrence info as HTML =
572
+ // ===========================
573
+ case 'recurrence_html':
574
+ if( ! $this->recurrence_rules )
575
+ return null;
576
+ $rules = $ai1ec_events_helper->parse_recurrence_rules( $this );
577
+ $patterns = $ai1ec_events_helper->get_repeat_patterns();
578
+
579
+ $recurrence_html = '<strong>' . esc_html( $patterns[$rules['repeat']] ) . '</strong>';
580
+ unset( $rules['repeat'] );
581
+
582
+ if( $rules['count'] ) {
583
+ $rules['count'] = sprintf( _n(
584
+ 'ending after <strong>%d</strong> time',
585
+ 'ending after <strong>%d</strong> times',
586
+ $rules['count'], AI1EC_PLUGIN_NAME ),
587
+ $rules['count'] );
588
+ } else {
589
+ unset( $rules['count'] );
590
+ }
591
+
592
+ if( $rules['until'] ) {
593
+ $rules['until'] = sprintf(
594
+ __( 'until <strong>%s</strong>', AI1EC_PLUGIN_NAME ),
595
+ esc_html( $ai1ec_events_helper->get_long_date( $rules['until'] ) ) );
596
+ } else {
597
+ unset( $rules['until'] );
598
+ }
599
+
600
+ $end = join( __( ' or ', AI1EC_PLUGIN_NAME ), $rules );
601
+
602
+ if( $end )
603
+ $recurrence_html .= ', ' . $end;
604
+
605
+ return $recurrence_html;
606
+ }
607
+ }
608
+
609
+ /**
610
+ * save function
611
+ *
612
+ * Saves the current event data to the database. If $this->post_id exists,
613
+ * but $update is false, creates a new record in the ai1ec_events table of
614
+ * this event data, but does not try to create a new post. Else if $update
615
+ * is true, updates existing event record. If $this->post_id is empty,
616
+ * creates a new post AND record in the ai1ec_events table for this event.
617
+ *
618
+ * @param bool $update Whether to update an existing event or create a
619
+ * new one
620
+ * @return int The post_id of the new or existing event.
621
+ **/
622
+ function save( $update = false )
623
+ {
624
+ global $wpdb,
625
+ $ai1ec_events_helper;
626
+
627
+ // ===========================
628
+ // = Insert events meta data =
629
+ // ===========================
630
+ $columns = array(
631
+ 'post_id' => $this->post_id,
632
+ 'start' => $this->start,
633
+ 'end' => $this->end,
634
+ 'allday' => $this->allday,
635
+ 'recurrence_rules' => $this->recurrence_rules,
636
+ 'exception_rules' => $this->exception_rules,
637
+ 'recurrence_dates' => $this->recurrence_dates,
638
+ 'exception_dates' => $this->exception_dates,
639
+ 'venue' => $this->venue,
640
+ 'country' => $this->country,
641
+ 'address' => $this->address,
642
+ 'city' => $this->city,
643
+ 'province' => $this->province,
644
+ 'postal_code' => $this->postal_code,
645
+ 'show_map' => $this->show_map,
646
+ 'contact_name' => $this->contact_name,
647
+ 'contact_phone' => $this->contact_phone,
648
+ 'contact_email' => $this->contact_email,
649
+ 'cost' => $this->cost,
650
+ 'ical_feed_url' => $this->ical_feed_url,
651
+ 'ical_source_url' => $this->ical_source_url,
652
+ 'ical_uid' => $this->ical_uid,
653
+ );
654
+
655
+ $format = array(
656
+ '%d',
657
+ 'FROM_UNIXTIME( %d )',
658
+ 'FROM_UNIXTIME( %d )',
659
+ '%d',
660
+ '%s',
661
+ '%s',
662
+ '%s',
663
+ '%s',
664
+ '%s',
665
+ '%s',
666
+ '%s',
667
+ '%s',
668
+ '%s',
669
+ '%s',
670
+ '%d',
671
+ '%s',
672
+ '%s',
673
+ '%s',
674
+ '%s',
675
+ '%s',
676
+ '%s',
677
+ '%s'
678
+ );
679
+
680
+ $table_name = $wpdb->prefix . 'ai1ec_events';
681
+ if( $this->post_id )
682
+ {
683
+ if( ! $update ) {
684
+ // =========================
685
+ // = Insert new event data =
686
+ // =========================
687
+ $wpdb->query( $wpdb->prepare(
688
+ "INSERT INTO $table_name ( " .
689
+ join( ', ', array_keys( $columns ) ) .
690
+ " ) VALUES ( " .
691
+ join( ', ', $format ) .
692
+ " )",
693
+ $columns ) );
694
+ } else {
695
+ // ==============================
696
+ // = Update existing event data =
697
+ // ==============================
698
+ $where = array( 'post_id' => $this->post_id );
699
+ $where_escape = array( '%d' );
700
+ $wpdb->update( $table_name, $columns, $where, $format, $where_escape );
701
+ }
702
+ } else {
703
+ // ===================
704
+ // = Insert new post =
705
+ // ===================
706
+ $this->post_id = wp_insert_post( $this->post );
707
+ $columns['post_id'] = $this->post_id;
708
+ wp_set_post_terms( $this->post_id, $this->categories, 'events_categories' );
709
+ wp_set_post_terms( $this->post_id, $this->tags, 'events_tags' );
710
+
711
+ // =========================
712
+ // = Insert new event data =
713
+ // =========================
714
+ $wpdb->query( $wpdb->prepare(
715
+ "INSERT INTO $table_name ( " .
716
+ join( ', ', array_keys( $columns ) ) .
717
+ " ) VALUES ( " .
718
+ join( ', ', $format ) .
719
+ " )",
720
+ $columns ) );
721
+ }
722
+
723
+ return $this->post_id;
724
+ }
725
+
726
+ /**
727
+ * getProperty function
728
+ *
729
+ * Returns $property value
730
+ *
731
+ * @param string $property Property name
732
+ *
733
+ * @return mixed
734
+ **/
735
+ function getProperty( $property ) {
736
+ return $this->property;
737
+ }
738
+
739
+ /**
740
+ * isWholeDay function
741
+ *
742
+ * Determines if an event is a whole day event
743
+ *
744
+ * @return bool
745
+ **/
746
+ function isWholeDay() {
747
+ return ( bool ) $this->allday;
748
+ }
749
+
750
+ /**
751
+ * getStart function
752
+ *
753
+ * Returns the start time of the event
754
+ *
755
+ * @return int
756
+ **/
757
+ function getStart() {
758
+ return $this->start;
759
+ }
760
+
761
+ /**
762
+ * getEnd function
763
+ *
764
+ * Returns the end time of the event
765
+ *
766
+ * @return int
767
+ **/
768
+ function getEnd() {
769
+ return $this->end;
770
+ }
771
+
772
+ /**
773
+ * getFrequency function
774
+ *
775
+ * Returns the frequency of the event
776
+ *
777
+ * @return object
778
+ **/
779
+ function getFrequency() {
780
+ return new SG_iCal_Freq( $this->recurrence_rules, $this->start );
781
+ }
782
+
783
+ /**
784
+ * getDuration function
785
+ *
786
+ * Returns the duration of the event
787
+ *
788
+ * @return int
789
+ **/
790
+ function getDuration() {
791
+ return $this->end - $this->start;
792
+ }
793
+ }
794
+ // END class
app/model/class-ai1ec-exporter.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-exporter.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_Event class
11
+ *
12
+ * @package Models
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_Exporter {
16
+
17
+ function __construct() {
18
+
19
+ }
20
+
21
+ function export( $events, $format = "ics" ) {
22
+
23
+ }
24
+
25
+ function export_to_ics() {
26
+
27
+ }
28
+ }
29
+ // END class
app/model/class-ai1ec-importer.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-importer.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_Importer class
11
+ *
12
+ * @package Models
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_Importer {
16
+ var $events;
17
+
18
+ function __construct() {
19
+
20
+ }
21
+
22
+ function parse_rss( $rss_feed ) {
23
+
24
+ }
25
+
26
+ function parse_file( $file ) {
27
+
28
+ }
29
+ }
30
+ // END class
app/model/class-ai1ec-settings.php ADDED
@@ -0,0 +1,288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //
3
+ // class-ai1ec-settings.php
4
+ // all-in-one-events-calendar
5
+ //
6
+ // Created by The Seed Studio on 2011-07-13.
7
+ //
8
+
9
+ /**
10
+ * Ai1ec_Settings class
11
+ *
12
+ * @package Models
13
+ * @author The Seed Studio
14
+ **/
15
+ class Ai1ec_Settings {
16
+ /**
17
+ * _instance class variable
18
+ *
19
+ * Class instance
20
+ *
21
+ * @var null | object
22
+ **/
23
+ private static $_instance = NULL;
24
+
25
+ /**
26
+ * calendar_page_id class variable
27
+ *
28
+ * @var int
29
+ **/
30
+ var $calendar_page_id;
31
+
32
+ /**
33
+ * default_calendar_view class variable
34
+ *
35
+ * @var string
36
+ **/
37
+ var $default_calendar_view;
38
+
39
+ /**
40
+ * week_start_day class variable
41
+ *
42
+ * @var int
43
+ **/
44
+ var $week_start_day;
45
+
46
+ /**
47
+ * agenda_events_per_page class variable
48
+ *
49
+ * @var int
50
+ **/
51
+ var $agenda_events_per_page;
52
+
53
+ /**
54
+ * calendar_css_selector class variable
55
+ *
56
+ * @var string
57
+ **/
58
+ var $calendar_css_selector;
59
+
60
+ /**
61
+ * include_events_in_rss class variable
62
+ *
63
+ * @var bool
64
+ **/
65
+ var $include_events_in_rss;
66
+
67
+ /**
68
+ * allow_publish_to_facebook class variable
69
+ *
70
+ * @var bool
71
+ **/
72
+ var $allow_publish_to_facebook;
73
+
74
+ /**
75
+ * facebook_credentials class variable
76
+ *
77
+ * @var array
78
+ **/
79
+ var $facebook_credentials;
80
+
81
+ /**
82
+ * user_role_can_create_event class variable
83
+ *
84
+ * @var bool
85
+ **/
86
+ var $user_role_can_create_event;
87
+
88
+ /**
89
+ * cron_freq class variable
90
+ *
91
+ * Cron frequency
92
+ *
93
+ * @var string
94
+ **/
95
+ var $cron_freq;
96
+
97
+ /**
98
+ * show_publish_button class variable
99
+ *
100
+ * Display publish button at the bottom of the
101
+ * submission form
102
+ *
103
+ * @var bool
104
+ **/
105
+ var $show_publish_button;
106
+
107
+ /**
108
+ * show_create_event_button class variable
109
+ *
110
+ * Display "Post Your Event" button on the calendar page for those users with
111
+ * the privilege.
112
+ *
113
+ * @var bool
114
+ **/
115
+ var $show_create_event_button;
116
+
117
+ /**
118
+ * inject_categories class variable
119
+ *
120
+ * Include Event Categories as part of the output of the wp_list_categories()
121
+ * template tag.
122
+ *
123
+ * @var bool
124
+ **/
125
+ var $inject_categories;
126
+
127
+ /**
128
+ * settings_page class variable
129
+ *
130
+ * Stores a reference to the settings page added using
131
+ * add_submenu_page function
132
+ *
133
+ * @var object
134
+ **/
135
+ var $settings_page;
136
+
137
+ /**
138
+ * __construct function
139
+ *
140
+ * Default constructor
141
+ **/
142
+ private function __construct() {
143
+ $this->set_defaults(); // set default settings
144
+ }
145
+
146
+ /**
147
+ * get_instance function
148
+ *
149
+ * Return singleton instance
150
+ *
151
+ * @return object
152
+ **/
153
+ static function get_instance()
154
+ {
155
+ if( self::$_instance === NULL ) {
156
+ // get the settings from the database
157
+ self::$_instance = get_option( 'ai1ec_settings' );
158
+
159
+ // if there are no settings in the database
160
+ // save default values for the settings
161
+ if( ! self::$_instance ) {
162
+ self::$_instance = new self();
163
+ delete_option( 'ai1ec_settings' );
164
+ add_option( 'ai1ec_settings', self::$_instance );
165
+ } else {
166
+ self::$_instance->set_defaults(); // set default settings
167
+ }
168
+ }
169
+
170
+ return self::$_instance;
171
+ }
172
+
173
+ /**
174
+ * save function
175
+ *
176
+ * Save settings to the database.
177
+ *
178
+ * @return void
179
+ **/
180
+ function save() {
181
+ update_option( 'ai1ec_settings', $this );
182
+ update_option( 'start_of_week', $this->week_start_day );
183
+ update_option( 'ai1ec_cron_version', get_option( 'ai1ec_cron_version' ) + 1 );
184
+ }
185
+
186
+ /**
187
+ * set_defaults function
188
+ *
189
+ * Set default values for settings.
190
+ *
191
+ * @return void
192
+ **/
193
+ function set_defaults() {
194
+ $defaults = array(
195
+ 'calendar_page_id' => 0,
196
+ 'default_calendar_view' => 'month',
197
+ 'calendar_css_selector' => '',
198
+ 'week_start_day' => get_option( 'start_of_week' ),
199
+ 'agenda_events_per_page' => get_option( 'posts_per_page' ),
200
+ 'include_events_in_rss' => false,
201
+ 'allow_publish_to_facebook' => false,
202
+ 'facebook_credentials' => null,
203
+ 'user_role_can_create_event' => null,
204
+ 'show_publish_button' => false,
205
+ 'show_create_event_button' => false,
206
+ 'inject_categories' => false,
207
+ 'cron_freq' => 'daily'
208
+ );
209
+
210
+ foreach( $defaults as $key => $default ) {
211
+ if( ! isset( $this->$key ) )
212
+ $this->$key = $default;
213
+ }
214
+ }
215
+
216
+ /**
217
+ * update function
218
+ *
219
+ * Updates field values with corresponding values found in $params
220
+ * associative array.
221
+ *
222
+ * @param array $params
223
+ *
224
+ * @return void
225
+ **/
226
+ function update( $params ) {
227
+ $this->update_page( 'calendar_page_id', $params );
228
+ $this->default_calendar_view = $params['default_calendar_view'];
229
+ $this->calendar_css_selector = $params['calendar_css_selector'];
230
+ $this->week_start_day = $params['week_start_day'];
231
+ $this->agenda_events_per_page = $params['agenda_events_per_page'];
232
+ $this->agenda_events_per_page = intval( $this->agenda_events_per_page );
233
+ if( $this->agenda_events_per_page <= 0 )
234
+ $this->agenda_events_per_page = 1;
235
+ $this->cron_freq = $params['cron_freq'];
236
+ $this->show_publish_button = $params['show_publish_button'];
237
+ $this->show_create_event_button = $params['show_create_event_button'];
238
+ $this->inject_categories = $params['inject_categories'];
239
+ $this->include_events_in_rss = $params['include_events_in_rss'];
240
+ $this->allow_events_posting_facebook = $params['allow_events_posting_facebook'];
241
+ $this->facebook_credentials = $params['facebook_credentials'];
242
+ $this->user_role_can_create_event = $params['user_role_can_create_event'];
243
+ }
244
+
245
+ /**
246
+ * update_page function
247
+ *
248
+ * Update page for the calendar with the one specified by the drop-down box.
249
+ * If the value is not numeric, user chose to auto-create a new page,
250
+ * therefore do so.
251
+ *
252
+ * @param string $field_name
253
+ * @param array $params
254
+ *
255
+ * @return void
256
+ **/
257
+ function update_page( $field_name, &$params ) {
258
+ if( ! is_numeric( $params[$field_name] ) &&
259
+ preg_match( '#^__auto_page:(.*?)$#', $params[$field_name], $matches ) )
260
+ {
261
+ $this->$field_name = $params[$field_name] = $this->auto_add_page( $matches[1] );
262
+ } else {
263
+ $this->$field_name = (int) $params[$field_name];
264
+ }
265
+ }
266
+
267
+ /**
268
+ * auto_add_page function
269
+ *
270
+ * Auto-create a WordPress page with given name for use by this plugin.
271
+ *
272
+ * @param string page_name
273
+ *
274
+ * @return int the new page's ID.
275
+ **/
276
+ function auto_add_page( $page_name ) {
277
+ return wp_insert_post(
278
+ array(
279
+ 'post_title' => $page_name,
280
+ 'post_type' => 'page',
281
+ 'post_status' => 'publish',
282
+ 'comment_status' => 'closed'
283
+ )
284
+ );
285
+ }
286
+
287
+ }
288
+ // END class
app/view/admin_notices.php ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ <div id="message" class="updated fade">
2
+ <p><strong><?php _e( 'All-in-One Events Calendar Notice:', AI1EC_PLUGIN_NAME ) ?></strong> <?php echo $msg ?></p>
3
+ </div>
app/view/agenda.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h2 class="ai1ec-calendar-title"><?php echo esc_html( $title ) ?></h2>
2
+ <span class="ai1ec-title-buttons">
3
+ <?php if( $dates ): ?>
4
+ <a id="ai1ec-expand-all" class="ai1ec-button">
5
+ <?php _e( '+ Expand All', AI1EC_JS_URL ) ?>
6
+ </a><a
7
+ id="ai1ec-collapse-all" class="ai1ec-button">
8
+ <?php _e( '− Collapse All', AI1EC_JS_URL ) ?>
9
+ </a
10
+ ><?php endif ?><a
11
+ id="ai1ec-today" class="ai1ec-load-view ai1ec-button" href="#action=ai1ec_agenda">
12
+ <?php _e( 'Today', AI1EC_JS_URL ) ?>
13
+ </a>
14
+ </span>
15
+ <ul class="ai1ec-pagination">
16
+ <?php foreach( $pagination_links as $link ): ?>
17
+ <li>
18
+ <a id="<?php echo $link['id'] ?>" class="ai1ec-load-view ai1ec-button ai1ec-pagination"
19
+ href="<?php echo esc_attr( $link['href'] ) ?>">
20
+ <?php echo esc_html( $link['text'] ) ?>
21
+ </a>
22
+ </li>
23
+ <?php endforeach ?>
24
+ </ul>
25
+ <ol class="ai1ec-agenda-view">
26
+ <?php if( ! $dates ): ?>
27
+ <p class="ai1ec-no-results">
28
+ <?php _e( 'There are no upcoming events to display at this time.' ) ?>
29
+ </p>
30
+ <?php else: ?>
31
+ <?php foreach( $dates as $timestamp => $date_info ): ?>
32
+ <li class="ai1ec-date <?php if( $date_info['today'] ) echo 'ai1ec-today' ?>">
33
+ <h3 class="ai1ec-date-title">
34
+ <div class="ai1ec-month"><?php echo strftime( '%b', $timestamp ) ?></div>
35
+ <div class="ai1ec-day"><?php echo strftime( '%e', $timestamp ) ?></div>
36
+ <div class="ai1ec-weekday"><?php echo strftime( '%a', $timestamp ) ?></div>
37
+ </h3>
38
+ <ol class="ai1ec-date-events">
39
+ <?php foreach( $date_info['events'] as $category ): ?>
40
+ <?php foreach( $category as $event ): ?>
41
+ <li class="ai1ec-event
42
+ ai1ec-event-id-<?php echo $event->post_id ?>
43
+ ai1ec-event-instance-id-<?php echo $event->instance_id ?>
44
+ <?php if( $event->allday ) echo 'ai1ec-allday' ?>
45
+ <?php if( $event->post_id == $active_event ) echo 'ai1ec-active-event' ?>">
46
+
47
+ <?php // Insert post ID for use by JavaScript filtering later ?>
48
+ <input type="hidden" class="ai1ec-post-id" value="<?php echo $event->post_id ?>" />
49
+
50
+ <?php // Hidden summary, until clicked ?>
51
+ <div class="ai1ec-event-summary">
52
+ <div class="ai1ec-event-click">
53
+ <div class="ai1ec-event-expand">−</div>
54
+ <div class="ai1ec-event-title">
55
+ <?php echo esc_html( $event->post->post_title ) ?>
56
+ <?php if( $event->allday ): ?>
57
+ <span class="ai1ec-allday-label"><?php _e( '(all-day)' ) ?></span>
58
+ <?php endif ?>
59
+ </div>
60
+ <div class="ai1ec-event-time">
61
+ <?php if( $event->allday ): ?>
62
+ <?php echo esc_html( $event->short_start_date ) ?>
63
+ <?php if( $event->short_end_date != $event->short_start_date ): ?>
64
+ – <?php echo esc_html( $event->short_end_date ) ?>
65
+ <?php endif ?>
66
+ <?php else: ?>
67
+ <?php echo esc_html( $event->start_time . ' – ' . $event->end_time ) ?></span>
68
+ <?php endif ?>
69
+ </div>
70
+ </div>
71
+ <div class="ai1ec-event-description">
72
+ <div class="ai1ec-event-overlay">
73
+ <a class="ai1ec-read-more ai1ec-button"
74
+ href="<?php echo esc_attr( get_permalink( $event->post_id ) ) ?>">
75
+ <?php _e( 'Read more »', AI1EP_PLUGIN_NAME ) ?>
76
+ </a>
77
+ <?php if( $event->categories_html ): ?>
78
+ <div class="ai1ec-categories">
79
+ <label class="ai1ec-label"><?php _e( 'Categories:', AI1EC_PLUGIN_NAME ) ?></label>
80
+ <?php echo $event->categories_html ?>
81
+ </div>
82
+ <?php endif ?>
83
+ <?php if( $event->tags_html ): ?>
84
+ <div class="ai1ec-tags">
85
+ <label class="ai1ec-label"><?php _e( 'Tags:', AI1EC_PLUGIN_NAME ) ?></label>
86
+ <?php echo $event->tags_html ?>
87
+ </div>
88
+ <?php endif ?>
89
+ </div>
90
+ <?php echo apply_filters( 'the_content', $event->post->post_content ) ?>
91
+ </div>
92
+ </div>
93
+
94
+ <div class="ai1ec-event-click">
95
+ <?php if( $event->category_colors ): ?>
96
+ <div class="ai1ec-category-colors"><?php echo $event->category_colors ?></div>
97
+ <?php endif ?>
98
+ <div class="ai1ec-event-expand">+</div>
99
+ <?php if( ! $event->allday ): ?>
100
+ <div class="ai1ec-event-time">
101
+ <?php echo esc_html( $event->start_time ) ?></span>
102
+ </div>
103
+ <?php endif ?>
104
+ <div class="ai1ec-event-title">
105
+ <?php echo esc_html( $event->post->post_title ) ?>
106
+ <?php if( $event->allday ): ?>
107
+ <span class="ai1ec-allday-label"><?php _e( '(all-day)' ) ?></span>
108
+ <?php endif ?>
109
+ </div>
110
+ </div>
111
+
112
+ </li>
113
+ <?php endforeach ?>
114
+ <?php endforeach ?>
115
+ </ol>
116
+ </li>
117
+ <?php endforeach ?>
118
+ <?php endif ?>
119
+ </ol>
app/view/box_event_contact.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h4 class="ai1ec-section-title"><?php _e( 'Organizer contact info', 'ai1ec' ); ?></h4>
2
+ <table class="ai1ec-form">
3
+ <tbody>
4
+ <tr>
5
+ <td class="ai1ec-first">
6
+ <label for="ai1ec_contact_name">
7
+ <?php _e( 'Contact name:', 'ai1ec' ); ?>
8
+ </label>
9
+ </td>
10
+ <td>
11
+ <input type="text" name="ai1ec_contact_name" id="ai1ec_contact_name" value="<?php echo $contact_name; ?>" />
12
+ </td>
13
+ </tr>
14
+ <tr>
15
+ <td>
16
+ <label for="ai1ec_contact_phone">
17
+ <?php _e( 'Phone:', 'ai1ec' ); ?>
18
+ </label>
19
+ </td>
20
+ <td>
21
+ <input type="text" name="ai1ec_contact_phone" id="ai1ec_contact_phone" value="<?php echo $contact_phone; ?>" />
22
+ </td>
23
+ </tr>
24
+ <tr>
25
+ <td>
26
+ <label for="ai1ec_contact_email">
27
+ <?php _e( 'E-mail:', 'ai1ec' ); ?>
28
+ </label>
29
+ </td>
30
+ <td>
31
+ <input type="text" name="ai1ec_contact_email" id="ai1ec_contact_email" value="<?php echo $contact_email; ?>" />
32
+ </td>
33
+ </tr>
34
+ </tbody>
35
+ </table>
app/view/box_event_cost.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h4 class="ai1ec-section-title"><?php _e( 'Event cost', 'ai1ec' ); ?></h4>
2
+ <table class="ai1ec-form">
3
+ <tbody>
4
+ <tr>
5
+ <td class="ai1ec-first">
6
+ <label for="ai1ec_cost">
7
+ <?php _e( 'Cost', 'ai1ec' ); ?>:
8
+ </label>
9
+ </td>
10
+ <td>
11
+ <input type="text" name="ai1ec_cost" id="ai1ec_cost" value="<?php echo $cost; ?>" />
12
+ </td>
13
+ </tr>
14
+ </tbody>
15
+ </table>
app/view/box_event_location.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h4 class="ai1ec-section-title"><?php _e( 'Event location details', 'ai1ec' ); ?></h4>
2
+ <table class="ai1ec-form ai1ec-location-form">
3
+ <tbody>
4
+ <tr>
5
+ <td class="ai1ec-first">
6
+ <label for="ai1ec_venue">
7
+ <?php _e( 'Venue name:', 'ai1ec' ); ?>
8
+ </label>
9
+ </td>
10
+ <td>
11
+ <input type="text" name="ai1ec_venue" id="ai1ec_venue" value="<?php echo $venue; ?>" />
12
+ </td>
13
+ </tr>
14
+ <tr>
15
+ <td>
16
+ <label for="ai1ec_address">
17
+ <?php _e( 'Address:', 'ai1ec' ); ?>
18
+ </label>
19
+ </td>
20
+ <td>
21
+ <input type="text" name="ai1ec_address" id="ai1ec_address" value="<?php echo $address; ?>" />
22
+ </td>
23
+ </tr>
24
+ <tr>
25
+ <td>
26
+ <label for="ai1ec_google_map">
27
+ <?php _e( 'Show Google Map:', 'ai1ec' ); ?>
28
+ </label>
29
+ </td>
30
+ <td>
31
+ <input type="checkbox" value="1" name="ai1ec_google_map" id="ai1ec_google_map" <?php echo $google_map; ?> />
32
+ </td>
33
+ </tr>
34
+ </tbody>
35
+ </table>
36
+ <div class="ai1ec_box_map <?php if( $show_map ) echo 'ai1ec_box_map_visible' ?>">
37
+ <div id="ai1ec_map_canvas"></div>
38
+ </div>
39
+ <input type="hidden" name="ai1ec_city" id="ai1ec_city" value="<?php echo $city; ?>" />
40
+ <input type="hidden" name="ai1ec_province" id="ai1ec_province" value="<?php echo $province; ?>" />
41
+ <input type="hidden" name="ai1ec_postal_code" id="ai1ec_postal_code" value="<?php echo $postal_code; ?>" />
42
+ <input type="hidden" name="ai1ec_country" id="ai1ec_country" value="<?php echo $country; ?>" />
app/view/box_eventbrite.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h4 class="box_h4"><?php _e( 'Eventbrite Ticketing', AI1EC_PLUGIN_NAME ); ?>:</h4>
2
+ <table>
3
+ <tbody>
4
+ <tr>
5
+ <td>
6
+ <label>
7
+ <?php _e( 'Register this event with Eventbrite.com?', AI1EC_PLUGIN_NAME ); ?>
8
+ </label>
9
+ </td>
10
+ <td>
11
+ <input type="checkbox" name="<?php echo AI1EC_PLUGIN_NAME; ?>_eventbrite_yes" id="<?php echo AI1EC_PLUGIN_NAME; ?>_eventbrite_yes" />
12
+ <label for="<?php echo AI1EC_PLUGIN_NAME; ?>_eventbrite_yes"><?php _e( 'Yes', AI1EC_PLUGIN_NAME ); ?></label>
13
+ <input type="checkbox" name="<?php echo AI1EC_PLUGIN_NAME; ?>_eventbrite_no" id="<?php echo AI1EC_PLUGIN_NAME; ?>_eventbrite_no" />
14
+ <label for="<?php echo AI1EC_PLUGIN_NAME; ?>_eventbrite_no"><?php _e( 'No', AI1EC_PLUGIN_NAME ); ?></label>
15
+ </td>
16
+ </tr>
17
+ </tbody>
18
+ </table>
19
+
20
+ <div id="<?php echo AI1EC_PLUGIN_NAME; ?>_eventbrite_body">
21
+ <h4>
22
+ <?php _e( 'Set up your first ticket', AI1EC_PLUGIN_NAME ); ?>
23
+ <small>
24
+ <?php _e( 'To create multiple tickets per event, submit this form, then follow the link to Eventbrite.', AI1EC_PLUGIN_NAME ); ?>
25
+ </small>
26
+ </h4>
27
+ <table>
28
+ <tbody>
29
+ <tr>
30
+ <td>
31
+ <label for="<?php echo AI1EC_PLUGIN_NAME; ?>_name">
32
+ <?php _e( 'Name', AI1EC_PLUGIN_NAME ); ?>:
33
+ </label>
34
+ </td>
35
+ <td>
36
+ <input type="text" name="<?php echo AI1EC_PLUGIN_NAME; ?>_name" id="<?php echo AI1EC_PLUGIN_NAME; ?>_name" />
37
+ </td>
38
+ </tr>
39
+ <tr>
40
+ <td>
41
+ <label for="<?php echo AI1EC_PLUGIN_NAME; ?>_description">
42
+ <?php _e( 'Description', AI1EC_PLUGIN_NAME ); ?>:
43
+ </label>
44
+ </td>
45
+ <td>
46
+ <textarea name="<?php echo AI1EC_PLUGIN_NAME; ?>_description" id="<?php echo AI1EC_PLUGIN_NAME; ?>_description">
47
+ </textarea>
48
+ </td>
49
+ </tr>
50
+ <tr>
51
+ <td>
52
+ <label>
53
+ <?php _e( 'Type', AI1EC_PLUGIN_NAME ); ?>:
54
+ </label>
55
+ </td>
56
+ <td>
57
+ <input type="radio" name="<?php echo AI1EC_PLUGIN_NAME; ?>_type" id="<?php echo AI1EC_PLUGIN_NAME; ?>_type_price" />
58
+ <label for="<?php echo AI1EC_PLUGIN_NAME; ?>_type_price"><?php _e( 'Set Price', AI1EC_PLUGIN_NAME ); ?></label>
59
+ <input type="radio" name="<?php echo AI1EC_PLUGIN_NAME; ?>_type" id="<?php echo AI1EC_PLUGIN_NAME; ?>_type_donation" />
60
+ <label for="<?php echo AI1EC_PLUGIN_NAME; ?>_type_donation"><?php _e( 'Donation Based', AI1EC_PLUGIN_NAME ); ?></label>
61
+ </td>
62
+ </tr>
63
+ <tr>
64
+ <td>
65
+ </td>
66
+ <td>
67
+ <small>
68
+ <?php _e( "The price for this event's first ticket will be taken from the Cost field above.", AI1EC_PLUGIN_NAME ); ?>
69
+ </small>
70
+ </td>
71
+ </tr>
72
+ <tr>
73
+ <td>
74
+ <label for="<?php echo AI1EC_PLUGIN_NAME; ?>_quantity">
75
+ <?php _e( 'Quantity', AI1EC_PLUGIN_NAME ); ?>:
76
+ </label>
77
+ </td>
78
+ <td>
79
+ <input type="text" name="<?php echo AI1EC_PLUGIN_NAME; ?>_quantity" id="<?php echo AI1EC_PLUGIN_NAME; ?>_quantity" />
80
+ </td>
81
+ </tr>
82
+ <tr>
83
+ <td>
84
+ <label>
85
+ <?php _e( 'Include Fee in Price', AI1EC_PLUGIN_NAME ); ?>:
86
+ </label>
87
+ </td>
88
+ <td>
89
+ <input type="radio" name="<?php echo AI1EC_PLUGIN_NAME; ?>_fee_in_price" id="<?php echo AI1EC_PLUGIN_NAME; ?>_add_fee" />
90
+ <label for="<?php echo AI1EC_PLUGIN_NAME; ?>_add_fee"><?php _e( 'Add Service Fee on top of price', AI1EC_PLUGIN_NAME ); ?></label>
91
+ <input type="radio" name="<?php echo AI1EC_PLUGIN_NAME; ?>_fee_in_price" id="<?php echo AI1EC_PLUGIN_NAME; ?>_include_fee" />
92
+ <label for="<?php echo AI1EC_PLUGIN_NAME; ?>_include_fee"><?php _e( 'Include Service fee in price', AI1EC_PLUGIN_NAME ); ?></label>
93
+ </td>
94
+ </tr>
95
+ <tr>
96
+ <td>
97
+ <label for="<?php echo AI1EC_PLUGIN_NAME; ?>">
98
+ <?php _e( 'Payment Options', AI1EC_PLUGIN_NAME ); ?>:
99
+ </label>
100
+ </td>
101
+ <td>
102
+ <input type="checkbox" name="<?php echo AI1EC_PLUGIN_NAME; ?>_payment_paypal" id="<?php echo AI1EC_PLUGIN_NAME; ?>_payment_paypal" />
103
+ <label for="<?php echo AI1EC_PLUGIN_NAME; ?>_payment_paypal"><?php _e( 'Paypal', AI1EC_PLUGIN_NAME ); ?></label>
104
+ <input type="checkbox" name="<?php echo AI1EC_PLUGIN_NAME; ?>_payment_google" id="<?php echo AI1EC_PLUGIN_NAME; ?>_payment_google" />
105
+ <label for="<?php echo AI1EC_PLUGIN_NAME; ?>_payment_google"><?php _e( 'Google Checkout', AI1EC_PLUGIN_NAME ); ?></label>
106
+ <input type="checkbox" name="<?php echo AI1EC_PLUGIN_NAME; ?>_payment_check" id="<?php echo AI1EC_PLUGIN_NAME; ?>_payment_check" />
107
+ <label for="<?php echo AI1EC_PLUGIN_NAME; ?>_payment_check"><?php _e( 'Check', AI1EC_PLUGIN_NAME ); ?></label>
108
+ <input type="checkbox" name="<?php echo AI1EC_PLUGIN_NAME; ?>_payment_cash" id="<?php echo AI1EC_PLUGIN_NAME; ?>_payment_cash" />
109
+ <label for="<?php echo AI1EC_PLUGIN_NAME; ?>_payment_cash"><?php _e( 'Cash', AI1EC_PLUGIN_NAME ); ?></label>
110
+ <input type="checkbox" name="<?php echo AI1EC_PLUGIN_NAME; ?>_payment_invoice" id="<?php echo AI1EC_PLUGIN_NAME; ?>_payment_invoice" />
111
+ <label for="<?php echo AI1EC_PLUGIN_NAME; ?>_payment_invoice"><?php _e( 'Send an Invoice', AI1EC_PLUGIN_NAME ); ?></label>
112
+ </td>
113
+ </tr>
114
+ </tbody>
115
+ </table>
116
+ </div>
app/view/box_general_settings.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <label class="textinput" for="calendar_page_id"><?php _e( 'Calendar page:', AI1EC_PLUGIN_NAME ) ?></label>
2
+ <div class="alignleft"><?php echo $calendar_page ?></div>
3
+ <br class="clear" />
4
+
5
+ <label class="textinput" for="default_calendar_view"><?php _e( 'Default calendar view:', AI1EC_PLUGIN_NAME ) ?></label>
6
+ <?php echo $default_calendar_view ?>
7
+ <br class="clear" />
8
+
9
+ <label class="textinput" for="calendar_css_selector"><?php _e( 'Contain calendar in this DOM element:', AI1EC_PLUGIN_NAME ) ?></label>
10
+ <input name="calendar_css_selector" id="calendar_css_selector" type="text" size="20" value="<?php echo esc_attr( $calendar_css_selector ) ?>" />
11
+ <div class="description"><?php _e( 'Optional. Provide a <a href="http://api.jquery.com/category/selectors/" target="_blank">jQuery selector</a> that evaluates to a single DOM element. Replaces any existing markup found within target. If left empty, calendar is shown in normal page content container.', AI1EC_PLUGIN_NAME ) ?></div>
12
+
13
+ <label class="textinput" for="week_start_day"><?php _e( 'Week starts on', AI1EC_PLUGIN_NAME ) ?></label>
14
+ <?php echo $week_start_day ?>
15
+ <br class="clear" />
16
+
17
+ <label class="textinput" for="agenda_events_per_page"><?php _e( 'Agenda pages show at most', AI1EC_PLUGIN_NAME ) ?></label>
18
+ <input name="agenda_events_per_page" id="agenda_events_per_page" type="text" size="1" value="<?php echo esc_attr( $agenda_events_per_page ) ?>" />&nbsp;<?php _e( 'events', AI1EC_PLUGIN_NAME ) ?>
19
+ <br class="clear" />
20
+
21
+ <label for="show_publish_button">
22
+ <input class="checkbox" name="show_publish_button" id="show_publish_button" type="checkbox" value="1" <?php echo $show_publish_button ?> />
23
+ <?php _e( 'Display <strong>Publish</strong> at bottom of Edit Event form', AI1EC_PLUGIN_NAME ) ?>
24
+ </label>
25
+ <br class="clear" />
26
+
27
+ <label for="show_create_event_button">
28
+ <input class="checkbox" name="show_create_event_button" id="show_create_event_button" type="checkbox" value="1" <?php echo $show_create_event_button ?> />
29
+ <?php _e( 'Show <strong>Post Your Event</strong> button to privileged users', AI1EC_PLUGIN_NAME ) ?>
30
+ </label>
31
+ <br class="clear" />
32
+
33
+ <label for="inject_categories">
34
+ <input class="checkbox" name="inject_categories" id="inject_categories" type="checkbox" value="1" <?php echo $inject_categories ?> />
35
+ <?php _e( 'Include <strong>event categories</strong> in post category lists', AI1EC_PLUGIN_NAME ) ?>
36
+ </label>
37
+ <br class="clear" />
app/view/box_ics_import_settings.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <label class="textinput" for="cron_freq">
2
+ <?php _e( 'Auto-refresh', AI1EC_PLUGIN_NAME ) ?>:
3
+ </label>
4
+ <?php echo $cron_freq ?>
5
+ <br class="clear" />
6
+
7
+ <div class="ai1ec-feed-container">
8
+ <h4 class="ai1ec_feed_h4"><?php _e( 'iCalendar/.ics Feed URL:', AI1EC_PLUGIN_NAME ) ?></h4>
9
+ <div class="ai1ec-feed-url"><input type="text" name="ai1ec_feed_url" id="ai1ec_feed_url" /></div>
10
+ <div class="ai1ec-feed-category">
11
+ <label for="ai1ec_feed_category">
12
+ <?php _e( 'Event category', AI1EC_PLUGIN_NAME ); ?>:
13
+ </label>
14
+ <?php echo $event_categories; ?>
15
+ </div>
16
+ <div class="ai1ec-feed-tags">
17
+ <label for="ai1ec_feed_tags">
18
+ <?php _e( 'Tag with', AI1EC_PLUGIN_NAME ); ?>:
19
+ </label>
20
+ <input type="text" name="ai1ec_feed_tags" id="ai1ec_feed_tags" />
21
+ </div>
22
+ <input type="button" id="ai1ec_add_new_ics" class="button" value="<?php _e( '+ Add new subscription', AI1EC_PLUGIN_NAME ) ?>" />
23
+ </div>
24
+
25
+ <?php echo $feed_rows; ?>
26
+ <br class="clear" />
app/view/box_publish_button.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <input type="button"
2
+ name="ai1ec_bottom_publish"
3
+ id="ai1ec_bottom_publish"
4
+ class="button-primary ai1ec_bottom_publish"
5
+ value="<?php echo $button_value ?>" />
app/view/box_the_seed_studio.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="ai1ec-plugin-branding">
2
+ <div class="ai1ec-plugin-logo">
3
+ <img src="http://yellowseedimages.s3.amazonaws.com/logo-plugin.png" alt="<?php esc_attr_e( 'The Seed Studio', AI1EC_PLUGIN_NAME ) ?>" class="pluginlogo" />
4
+ </div>
5
+ <p><?php _e( 'The Seed Studio provides web development and support services for clients and web developers.', AI1EC_PLUGIN_NAME ) ?></p>
6
+ <div class="ai1ec-follow-fan">
7
+ <div class="facebook-like-top">
8
+ <script src="http://connect.facebook.net/en_US/all.js#xfbml=1"></script>
9
+ <fb:like href="http://www.facebook.com/pages/The-Seed-Agency/66657743707" layout="button_count" show_faces="true" width="110" font="lucida grande"></fb:like>
10
+ </div>
11
+ <a href="http://twitter.com/the_seed_studio" class="twitter-follow-button"><?php _e( 'Follow @the_seed_studio', AI1EC_PLUGIN_NAME ) ?></a>
12
+ <script src="http://platform.twitter.com/widgets.js" type="text/javascript"></script>
13
+ <div class="clear"></div>
14
+ </div>
15
+ <div class="ai1ec-support-button">
16
+ <a href="http://theseedstudio.com/get-supported/" target="_blank"><?php _e( 'Get Support<span> from one of our experienced pros</span>', AI1EC_PLUGIN_NAME ) ?></a>
17
+ </div>
18
+ <h2><?php _e( 'Support', AI1EC_PLUGIN_NAME ) ?></h2>
19
+ <p>
20
+ <a href="http://theseedstudio.com/software/all-in-one-events-calendar-wordpress/all-in-one-events-calendar-documentation/" target="_blank"><?php _e( 'View plugin documentation', AI1EC_PLUGIN_NAME ) ?></a>
21
+ </p>
22
+ <p>
23
+ <?php _e( 'You can also hire The Seed for support on a contract or per-hour basis for this plugin, for your website or for any of your Internet marketing needs (we can really help!).', AI1EC_PLUGIN_NAME ) ?>
24
+ </p>
25
+ <p>
26
+ <?php _e( 'Plugin users: The Seed gives support priority to clients. For free support from other WordPress users, visit the plugin\'s forum.', AI1EC_PLUGIN_NAME ) ?>
27
+ </p>
28
+
29
+ <h2><?php _e( 'Vote and Share', AI1EC_PLUGIN_NAME ) ?></h2>
30
+
31
+ <p>
32
+ <?php _e( 'This plugin is offered free to the Wordpress Community under the GPL3 license. All we ask is that you:', AI1EC_PLUGIN_NAME ) ?>
33
+ <ol>
34
+ <li><?php _e( '<a href="http://wordpress.org/extend/plugins/all-in-one-event-calendar/" target="_blank">Give it a five-star rating on wordpress.org</a> (if you think it deserves it!)', AI1EC_PLUGIN_NAME ) ?></li>
35
+ <li><a href="http://theseedstudio.com/software/all-in-one-events-calendar-wordpress/" target="_blank"><?php _e( 'Link to the plugin page on our website', AI1EC_PLUGIN_NAME ) ?></a></li>
36
+ <li><a href="http://www.facebook.com/theseednet" target="_blank"><?php _e( 'Become a Fan on Facebook', AI1EC_PLUGIN_NAME ) ?></a></li>
37
+ <li><a href="https://twitter.com/intent/user?screen_name=the_seed_studio" target="_blank"><?php _e( 'Follow us on Twitter', AI1EC_PLUGIN_NAME ) ?></a></li>
38
+ </ol>
39
+ </p>
40
+ </div>
41
+ <br class="clear" />
app/view/box_time_and_date.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php wp_nonce_field( 'ai1ec', AI1EC_POST_TYPE ); ?>
2
+ <h4 class="ai1ec-section-title"><?php _e( 'Event date and time'); ?></h4>
3
+ <table class="ai1ec-form">
4
+ <tbody>
5
+ <tr>
6
+ <td class="ai1ec-first">
7
+ <label for="ai1ec_all_day_event">
8
+ <?php _e( 'All-day event', 'ai1ec' ); ?>?
9
+ </label>
10
+ </td>
11
+ <td>
12
+ <input type="checkbox" name="ai1ec_all_day_event" id="ai1ec_all_day_event" <?php echo $all_day_event; ?> />
13
+ </td>
14
+ </tr>
15
+ <tr>
16
+ <td>
17
+ <label for="ai1ec_start-date-input">
18
+ <?php _e( 'Start date / time', 'ai1ec' ); ?>:
19
+ </label>
20
+ </td>
21
+ <td>
22
+ <input type="text" class="ai1ec-date-input" id="ai1ec_start-date-input" />
23
+ <input type="text" class="ai1ec-time-input" id="ai1ec_start-time-input" />
24
+ <small><?php echo $timezone ?></small>
25
+ <input type="hidden" name="ai1ec_start_time" id="ai1ec_start-time" value="<?php echo $start_timestamp ?>" />
26
+ </td>
27
+ </tr>
28
+ <tr>
29
+ <td>
30
+ <label for="ai1ec_end-date-input">
31
+ <?php _e( 'End date / time', 'ai1ec' ) ?>:
32
+ </label>
33
+ </td>
34
+ <td>
35
+ <input type="text" class="ai1ec-date-input" id="ai1ec_end-date-input" />
36
+ <input type="text" class="ai1ec-time-input" id="ai1ec_end-time-input" />
37
+ <small><?php echo $timezone ?></small>
38
+ <input type="hidden" name="ai1ec_end_time" id="ai1ec_end-time" value="<?php echo $end_timestamp ?>" />
39
+ </td>
40
+ </tr>
41
+ <tr>
42
+ <td>
43
+ <label for="ai1ec_repeat">
44
+ <?php _e( 'Repeat', 'ai1ec' ) ?>:
45
+ </label>
46
+ </td>
47
+ <td>
48
+ <?php echo $repeat; ?>
49
+ </td>
50
+ </tr>
51
+ <tr id="ai1ec_end_holder" <?php if( ! $repeating_event ) echo 'class="ai1ec_hidden"' ?>>
52
+ <td>
53
+ <label for="ai1ec_end">
54
+ <?php _e( 'End', 'ai1ec' ) ?>:
55
+ </label>
56
+ </td>
57
+ <td>
58
+ <?php echo $end ?>
59
+ </td>
60
+ </tr>
61
+ <tr id="ai1ec_count_holder" <?php if( $ending != 1 ) echo 'class="ai1ec_hidden"' ?>>
62
+ <td>
63
+ <label for="ai1ec_count">
64
+ <?php _e( 'Ending after', 'ai1ec' ) ?>:
65
+ </label>
66
+ </td>
67
+ <td>
68
+ <?php echo $count; ?>
69
+ </td>
70
+ </tr>
71
+ <tr id="ai1ec_until_holder" <?php if( $ending != 2 ) echo 'class="ai1ec_hidden"' ?>>
72
+ <td>
73
+ <label for="ai1ec_until-date-input">
74
+ <?php _e( 'On date', 'ai1ec' ) ?>:
75
+ </label>
76
+ </td>
77
+ <td>
78
+ <input type="text" class="ai1ec-date-input" id="ai1ec_until-date-input" />
79
+ <input type="hidden" name="ai1ec_until_time" id="ai1ec_until-time" value="<?php echo !is_null( $until ) && $until > 0 ? $until : '' ?>" />
80
+ </td>
81
+ </tr>
82
+ </tbody>
83
+ </table>
app/view/calendar.php ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <table class="ai1ec-calendar-toolbar">
2
+ <tbody>
3
+ <tr>
4
+
5
+ <td>
6
+ <ul class="ai1ec-view-tabs">
7
+ <li>
8
+ <a id="ai1ec-view-month" class="ai1ec-load-view ai1ec-button"
9
+ href="#action=ai1ec_month">
10
+ <img src="<?php echo AI1EC_IMAGE_URL ?>/month-view.png" alt="<?php _e( 'Month', AI1EC_PLUGIN_NAME ) ?>" />
11
+ <?php _e( 'Month', AI1EC_PLUGIN_NAME ) ?>
12
+ </a>
13
+ </li>
14
+ <li>
15
+ <a id="ai1ec-view-agenda" class="ai1ec-load-view ai1ec-button"
16
+ href="#action=ai1ec_agenda">
17
+ <img src="<?php echo AI1EC_IMAGE_URL ?>/agenda-view.png" alt="<?php _e( 'Month', AI1EC_PLUGIN_NAME ) ?>" />
18
+ <?php _e( 'Agenda', AI1EC_PLUGIN_NAME ) ?>
19
+ </a>
20
+ </li>
21
+ </ul>
22
+ </td>
23
+
24
+ <?php if( $create_event_url ): ?>
25
+ <td>
26
+ <a class="ai1ec-button" href="<?php echo $create_event_url ?>">
27
+ <?php _e( '+ Post Your Event', AI1EC_PLUGIN_NAME ) ?>
28
+ </a>
29
+ </td>
30
+ <?php endif ?>
31
+
32
+ <?php if( $categories || $tags ): ?>
33
+ <td>
34
+ <div class="ai1ec-filters-container">
35
+ <label class="ai1ec-label">
36
+ <a class="ai1ec-clear-filters" title="<?php _e( 'Clear Filters', AI1EC_PLUGIN_NAME ) ?>"><?php _e( '✘', AI1EC_PLUGIN_NAME ) ?></a>
37
+ <?php _e( 'Filter:', AI1EC_PLUGIN_NAME ) ?>
38
+ </label>
39
+
40
+ <?php if( $categories ): ?>
41
+ <span class="ai1ec-filter-selector-container">
42
+ <a class="ai1ec-button ai1ec-dropdown"><?php _e( 'Categories ▾', AI1EC_PLUGIN_NAME ) ?></a>
43
+ <input class="ai1ec-selected-terms"
44
+ id="ai1ec-selected-categories"
45
+ type="hidden"
46
+ value="<?php echo $selected_cat_ids ?>" />
47
+ <div class="ai1ec-filter-selector ai1ec-category-filter-selector">
48
+ <ul>
49
+ <?php foreach( $categories as $cat ): ?>
50
+ <li class="ai1ec-category"
51
+ <?php if( $cat->description ) echo 'title="' . esc_attr( $cat->description ) . '"' ?>>
52
+ <?php echo $cat->color ?>
53
+ <?php echo esc_html( $cat->name ) ?>
54
+ <input class="ai1ec-term-ids" name="ai1ec-categories" type="hidden" value="<?php echo $cat->term_id ?>" />
55
+ </li>
56
+ <?php endforeach ?>
57
+ </ul>
58
+ </div>
59
+ </span>
60
+ <?php endif // $categories ?>
61
+
62
+ <?php if( $tags ): ?>
63
+ <span class="ai1ec-filter-selector-container">
64
+ <a class="ai1ec-button ai1ec-dropdown"><?php _e( 'Tags ▾', AI1EC_PLUGIN_NAME ) ?></a>
65
+ <input class="ai1ec-selected-terms"
66
+ id="ai1ec-selected-tags"
67
+ type="hidden"
68
+ value="<?php echo $selected_tag_ids ?>" />
69
+ <div class="ai1ec-filter-selector ai1ec-tag-filter-selector">
70
+ <ul>
71
+ <?php foreach( $tags as $tag ): ?>
72
+ <li class="ai1ec-tag"
73
+ <?php if( $tag->description ) echo 'title="' . esc_attr( $tag->description ) . '"' ?>
74
+ style="<?php echo $tag->count > 1 ? 'font-weight: bold;' : 'font-size: 10px !important;' ?>">
75
+ <?php echo esc_html( $tag->name ) . " ($tag->count)" ?>
76
+ <input class="ai1ec-term-ids" name="ai1ec-tags" type="hidden" value="<?php echo $tag->term_id ?>" />
77
+ </li>
78
+ <?php endforeach ?>
79
+ </ul>
80
+ <input class="ai1ec-selected-terms" id="ai1ec-selected-tags" type="hidden" />
81
+ </div>
82
+ </span>
83
+ <?php endif // $tags ?>
84
+ </div>
85
+ </td>
86
+ <?php endif // $categories || $tags ?>
87
+ </tr>
88
+ </tbody>
89
+ </table>
90
+
91
+ <div id="ai1ec-calendar-view-container">
92
+ <div id="ai1ec-calendar-view-loading" class="ai1ec-loading"></div>
93
+ <div id="ai1ec-calendar-view">
94
+ <?php echo $view ?>
95
+ </div>
96
+ </div>
97
+
98
+ <a class="ai1ec-button ai1ec-subscribe"
99
+ href="<?php echo AI1EC_EXPORT_URL ?>"
100
+ title="<?php _e( 'Subscribe to this calendar using your favourite calendar program (iCal, Outlook, etc.)', AI1EC_PLUGIN_NAME ) ?>" />
101
+ <?php _e( '✔ Subscribe', AI1EC_PLUGIN_NAME ) ?>
102
+ <span class="ai1ec-subscribe-filtered"><?php _e( 'to this filtered calendar', AI1EC_PLUGIN_NAME ) ?></span>
103
+ </a>
104
+ <a class="ai1ec-button ai1ec-subscribe-google" target="_blank"
105
+ href="http://www.google.com/calendar/render?cid=<?php echo urlencode( str_replace( 'webcal://', 'http://', AI1EC_EXPORT_URL ) ) ?>"
106
+ title="<?php _e( 'Subscribe to this calendar in your Google Calendar', AI1EC_PLUGIN_NAME ) ?>" />
107
+ <img src="<?php echo AI1EC_IMAGE_URL ?>/google-calendar.png" />
108
+ <?php _e( 'Subscribe in Google Calendar', AI1EC_PLUGIN_NAME ) ?>
109
+ </a>
app/view/donate_button.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
2
+ <input type="hidden" name="cmd" value="_s-xclick" />
3
+ <input type="hidden" name="hosted_button_id" value="9JJMUW48W2ED8" />
4
+ <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!" />
5
+ <img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1" />
6
+ </form>
app/view/event-excerpt.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <div class="ai1ec-excerpt">
2
+ <a class="ai1ec-button ai1ec-event-link" href="<?php the_permalink() ?>">
3
+ <?php _e( 'View Event »', AI1EC_PLUGIN_NAME ) ?>
4
+ </a>
5
+ <div class="ai1ec-time"><label class="ai1ec-label"><?php _e( 'When:', AI1EC_PLUGIN_NAME ) ?></label> <?php echo $event->timespan_html ?></div>
6
+ <?php if( $location ): ?>
7
+ <div class="ai1ec-location"><label class="ai1ec-label"><?php _e( 'Where:', AI1EC_PLUGIN_NAME ) ?></label> <?php echo $location ?></div>
8
+ <?php endif ?>
9
+ </div>
app/view/event-map.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <div class="ai1ec-gmap-container">
2
+ <div id="ai1ec-gmap-canvas"></div>
3
+ <input type="hidden" id="ai1ec-gmap-address" value="<?php echo esc_attr( $address ) ?>" />
4
+ </div>
5
+ <a class="ai1ec-gmap-link ai1ec-button"
6
+ href="<?php echo $gmap_url_link ?>" target="_blank">
7
+ <?php _e( 'View Full-Size Map »', AI1EC_PLUGIN_NAME ) ?>
8
+ </a>
app/view/event-multi.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <table class="ai1ec-event ai1ec-multi-event ai1ec-event-id-<?php echo $event->post_id ?> <?php if( $event->allday ) echo 'ai1ec-allday' ?>">
2
+ <tbody>
3
+ <tr>
4
+ <th scope="row" class="ai1ec-time"><?php _e( 'When:', AI1EC_PLUGIN_NAME ) ?></th>
5
+ <td class="ai1ec-time">
6
+ <a class="ai1ec-button ai1ec-calendar-link" href="<?php echo $calendar_url ?>">
7
+ <?php _e( 'View in Calendar »', AI1EC_PLUGIN_NAME ) ?>
8
+ </a>
9
+ <?php echo $event->timespan_html ?>
10
+ </td>
11
+ </tr>
12
+ <?php if( $recurrence ): ?>
13
+ <tr>
14
+ <th scope="row" class="ai1ec-recurrence"><?php _e( 'Repeats:', AI1EC_PLUGIN_NAME ) ?></th>
15
+ <td class="ai1ec-recurrence" colspan="2"><?php echo $recurrence ?></td>
16
+ </tr>
17
+ <?php endif ?>
18
+ <tr>
19
+ <?php if( $location ): ?>
20
+ <th scope="row" class="ai1ec-location"><?php _e( 'Where:', AI1EC_PLUGIN_NAME ) ?></th>
21
+ <td class="ai1ec-location">
22
+ <?php if( $event->show_map ): ?>
23
+ <a class="ai1ec-button ai1ec-gmap-link" href="<?php the_permalink() ?>#ai1ec-event">
24
+ <?php _e( 'View Map »', AI1EC_PLUGIN_NAME ) ?>
25
+ </a>
26
+ <?php endif ?>
27
+ <?php echo $location ?>
28
+ </td>
29
+ <?php endif ?>
30
+ </tr>
31
+ <tr>
32
+ <?php if( $event->cost ): ?>
33
+ <th scope="row" class="ai1ec-cost"><?php _e( 'Cost:', AI1EC_PLUGIN_NAME ) ?></th>
34
+ <td class="ai1ec-cost"><?php echo esc_html( $event->cost ) ?></td>
35
+ <?php endif ?>
36
+ </tr>
37
+ <tr>
38
+ <?php if( $contact ): ?>
39
+ <th scope="row" class="ai1ec-contact"><?php _e( 'Contact:', AI1EC_PLUGIN_NAME ) ?></th>
40
+ <td class="ai1ec-contact"><?php echo $contact ?></td>
41
+ <?php endif ?>
42
+ </tr>
43
+ <tr>
44
+ <?php if( $categories ): ?>
45
+ <th scope="row" class="ai1ec-categories"><?php _e( 'Categories:', AI1EC_PLUGIN_NAME ) ?></th>
46
+ <td class="ai1ec-categories"><?php echo $categories ?></td>
47
+ <?php endif ?>
48
+ </tr>
49
+ <tr>
50
+ <?php if( $tags ): ?>
51
+ <th scope="row" class="ai1ec-tags"><?php _e( 'Tags:', AI1EC_PLUGIN_NAME ) ?></th>
52
+ <td class="ai1ec-tags"><?php echo $tags ?></td>
53
+ <?php endif ?>
54
+ </tr>
55
+ </tbody>
56
+ </table>
app/view/event-single-footer.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="ai1ec-event-footer">
2
+ <?php if( $event->ical_feed_url ): ?>
3
+ <p class="ai1ec-source-link">
4
+ <?php _e( sprintf( 'This post was replicated from another site\'s <a class="ai1ec-ics-icon" href="%s" title="iCalendar feed">calendar feed</a>.',
5
+ esc_attr( str_replace( 'http://', 'webcal://', $event->ical_feed_url ) ) ), AI1EC_PLUGIN_NAME ) ?>
6
+ <?php if( $event->ical_source_url ): ?>
7
+ <a href="<?php echo esc_attr( $event->ical_source_url ) ?>" target="_blank">
8
+ <?php _e( 'View original post »', AI1EC_PLUGIN_NAME ) ?>
9
+ </a>
10
+ <?php endif ?>
11
+ </p>
12
+ <?php endif ?>
13
+ </div>
app/view/event-single.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <a name="ai1ec-event"></a>
2
+ <table class="ai1ec-full-event ai1ec-single-event ai1ec-event-id-<?php echo $event->post_id ?> <?php if( $event->allday ) echo 'ai1ec-allday' ?>">
3
+ <tbody>
4
+ <tr>
5
+ <th scope="row" class="ai1ec-time"><?php _e( 'When:', AI1EC_PLUGIN_NAME ) ?></th>
6
+ <td colspan="2" class="ai1ec-time">
7
+ <a class="ai1ec-button ai1ec-calendar-link" href="<?php echo $calendar_url ?>">
8
+ <?php _e( 'Back to Calendar »', AI1EC_PLUGIN_NAME ) ?>
9
+ </a>
10
+ <?php echo $event->timespan_html ?>
11
+ </td>
12
+ </tr>
13
+ <?php if( $recurrence ): ?>
14
+ <tr>
15
+ <th scope="row" class="ai1ec-recurrence"><?php _e( 'Repeats:', AI1EC_PLUGIN_NAME ) ?></th>
16
+ <td class="ai1ec-recurrence" colspan="2"><?php echo $recurrence ?></td>
17
+ </tr>
18
+ <?php endif ?>
19
+ <tr>
20
+ <th scope="row" class="ai1ec-location <?php if( ! $location ) echo 'ai1ec-empty' ?>"><?php if( $location ) _e( 'Where:', AI1EC_PLUGIN_NAME ) ?></th>
21
+ <td class="ai1ec-location <?php if( ! $location ) echo 'ai1ec-empty' ?>"><?php echo $location ?></td>
22
+ <td rowspan="5" class="ai1ec-map <?php if( $map ) echo 'ai1ec-has-map' ?>">
23
+ <?php echo $map ?>
24
+ <a class="ai1ec-button ai1ec-subscribe"
25
+ href="<?php echo esc_attr( $subscribe_url ) ?>"
26
+ title="<?php _e( 'Add this event to your favourite calendar program (iCal, Outlook, etc.)', AI1EC_PLUGIN_NAME ) ?>" />
27
+ <?php _e( '✔ Add to Calendar', AI1EC_PLUGIN_NAME ) ?></a>
28
+ <a class="ai1ec-button ai1ec-subscribe-google" target="_blank"
29
+ href="<?php echo esc_attr( $google_url ) ?>"
30
+ title="<?php _e( 'Add this event to your Google Calendar', AI1EC_PLUGIN_NAME ) ?>" />
31
+ <img src="<?php echo AI1EC_IMAGE_URL ?>/google-calendar.png" />
32
+ <?php _e( 'Add to Google Calendar', AI1EC_PLUGIN_NAME ) ?>
33
+ </a>
34
+ </td>
35
+ </tr>
36
+ <tr>
37
+ <?php if( $event->cost ): ?>
38
+ <th scope="row" class="ai1ec-cost"><?php _e( 'Cost:', AI1EC_PLUGIN_NAME ) ?></th>
39
+ <td class="ai1ec-cost"><?php echo esc_html( $event->cost ) ?></td>
40
+ <?php endif ?>
41
+ </tr>
42
+ <tr>
43
+ <?php if( $contact ): ?>
44
+ <th scope="row" class="ai1ec-contact"><?php _e( 'Contact:', AI1EC_PLUGIN_NAME ) ?></th>
45
+ <td class="ai1ec-contact"><?php echo $contact ?></td>
46
+ <?php endif ?>
47
+ </tr>
48
+ <tr>
49
+ <?php if( $categories ): ?>
50
+ <th scope="row" class="ai1ec-categories"><?php _e( 'Categories:', AI1EC_PLUGIN_NAME ) ?></th>
51
+ <td class="ai1ec-categories"><?php echo $categories ?></td>
52
+ <?php endif ?>
53
+ </tr>
54
+ <tr>
55
+ <th scope="row" class="ai1ec-tags">
56
+ <?php if( $tags ): ?>
57
+ <?php _e( 'Tags:', AI1EC_PLUGIN_NAME ) ?>
58
+ <?php endif ?>
59
+ </th>
60
+ <td class="ai1ec-tags"><?php echo $tags ?></td>
61
+ </tr>
62
+ </tbody>
63
+ </table>
app/view/event_categories-color_picker.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php if( $edit ) : ?>
2
+ <tr class="form-field">
3
+ <th scope="row" valign="top">
4
+ <label for="tag-color">
5
+ <?php _e( 'Category Color', AI1EC_PLUGIN_NAME ) ?>
6
+ </label>
7
+ </th>
8
+ <td>
9
+ <div id="tag-color">
10
+ <div id="tag-color-background"<?php echo $style ?>></div>
11
+ </div>
12
+ <input type="hidden" name="tag-color-value" id="tag-color-value" value="<?php echo $color ?>" />
13
+ <p class="description"><?php _e( 'Events in this category will be identified by this color', AI1EC_PLUGIN_NAME ) ?>.</p>
14
+ </td>
15
+ </tr>
16
+ <?php else : ?>
17
+ <div class="form-field">
18
+ <label for="tag-color">
19
+ <?php _e( 'Category Color', AI1EC_PLUGIN_NAME ) ?>
20
+ </label>
21
+ <div id="tag-color">
22
+ <div id="tag-color-background"></div>
23
+ </div>
24
+ <input type="hidden" name="tag-color-value" id="tag-color-value" value="" />
25
+ <p><?php _e( 'Events in this category will be identified by this color', AI1EC_PLUGIN_NAME ) ?>.</p>
26
+ </div>
27
+ <?php endif; ?>
app/view/feed_row.php ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="ai1ec-feed-container">
2
+ <h4 class="ai1ec_feed_h4">
3
+ <?php _e( 'iCalendar/.ics Feed URL:', AI1EC_PLUGIN_NAME ); ?>
4
+ </h4>
5
+ <div class="ai1ec-feed-url"><input type="text" class="ai1ec-feed-url" readonly="readonly" value="<?php echo esc_attr( $feed_url ) ?>" /></div>
6
+ <input type="hidden" name="feed_id" class="ai1ec_feed_id" value="<?php echo $feed_id;?>" />
7
+ <div class="ai1ec-feed-category">
8
+ <?php _e( 'Event category:', AI1EC_PLUGIN_NAME ); ?>
9
+ <strong><?php echo $event_category; ?></strong>
10
+ </div>
11
+ <?php if( $tags ): ?>
12
+ <div class="ai1ec-feed-tags">
13
+ <?php _e( 'Tag with', AI1EC_PLUGIN_NAME ); ?>:
14
+ <strong><?php echo $tags; ?></strong>
15
+ </div>
16
+ <?php endif ?>
17
+ <input type="button" class="button ai1ec_delete_ics" value="<?php _e( '× Delete', AI1EC_PLUGIN_NAME ); ?>" />
18
+ <input type="button" class="button ai1ec_update_ics" value="<?php _e( 'Update', AI1EC_PLUGIN_NAME ); ?>" />
19
+ <?php if( $events ): ?>
20
+ <input type="button" class="button ai1ec_flush_ics" value="<?php printf( _n( 'Flush 1 event', 'Flush %s events', $events, AI1EC_PLUGIN_NAME ), $events ) ?>" />
21
+ <?php endif ?>
22
+ <img src="images/wpspin_light.gif" class="ajax-loading" alt="" />
23
+ </div>
app/view/import.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="wrap">
2
+ <table class="form-table">
3
+ <tbody>
4
+ <tr valign="top">
5
+ <th scope="row">
6
+ <?php _e( 'Successfully imported events:', AI1EC_PLUGIN_NAME ) ?>
7
+ </th>
8
+ <td>
9
+ <?php echo $imported_events ?>
10
+ </td>
11
+ </tr>
12
+ </tbody>
13
+ </table>
14
+ </div>
app/view/month.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h2 class="ai1ec-calendar-title"><?php echo esc_html( $title ) ?></h2>
2
+ <span class="ai1ec-title-buttons">
3
+ <a id="ai1ec-today" class="ai1ec-load-view ai1ec-button" href="#action=ai1ec_month">
4
+ <?php _e( 'Today', AI1EC_JS_URL ) ?>
5
+ </a>
6
+ </span>
7
+ <ul class="ai1ec-pagination">
8
+ <?php foreach( $pagination_links as $link ): ?>
9
+ <li>
10
+ <a id="<?php echo $link['id'] ?>"
11
+ class="ai1ec-load-view ai1ec-button"
12
+ href="<?php echo esc_attr( $link['href'] ) ?>">
13
+ <?php echo esc_html( $link['text'] ) ?>
14
+ </a>
15
+ </li>
16
+ <?php endforeach ?>
17
+ </ul>
18
+ <table class="ai1ec-month-view">
19
+ <thead>
20
+ <tr>
21
+ <?php foreach( $weekdays as $weekday ): ?>
22
+ <th class="ai1ec-weekday"><?php echo $weekday; ?></th>
23
+ <?php endforeach // weekday ?>
24
+ </tr>
25
+ </thead>
26
+ <tbody>
27
+ <?php foreach( $cell_array as $week ): ?>
28
+ <tr class="ai1ec-week">
29
+ <?php foreach( $week as $day ): ?>
30
+ <?php if( $day['date'] ): ?>
31
+ <td <?php if( $day['today'] ) echo 'class="ai1ec-today"' ?>>
32
+ <div class="ai1ec-day">
33
+ <div class="ai1ec-date"><?php echo $day['date'] ?></div>
34
+ <?php foreach( $day['events'] as $event ): ?>
35
+ <a href="<?php echo esc_attr( get_permalink( $event->post_id ) ) ?>"
36
+ class="ai1ec-event-container
37
+ ai1ec-event-id-<?php echo $event->post_id ?>
38
+ ai1ec-event-instance-id-<?php echo $event->instance_id ?>
39
+ <?php if( $event->allday ) echo 'ai1ec-allday' ?>">
40
+
41
+ <?php // Insert post ID for use by JavaScript filtering later ?>
42
+ <input type="hidden" class="ai1ec-post-id" value="<?php echo $event->post_id ?>" />
43
+
44
+ <div class="ai1ec-event-popup">
45
+ <div class="ai1ec-event-summary">
46
+ <?php if( $event->category_colors ): ?>
47
+ <div class="ai1ec-category-colors"><?php echo $event->category_colors ?></div>
48
+ <?php endif ?>
49
+ <?php if( $event->post_excerpt ): ?>
50
+ <strong><?php _e( 'Summary:', AI1EC_PLUGIN_NAME ) ?></strong>
51
+ <p><?php echo esc_html( $event->post_excerpt ) ?></p>
52
+ <?php endif ?>
53
+ <div class="ai1ec-read-more"><?php esc_html_e( 'click anywhere for details', AI1EC_PLUGIN_NAME ) ?></div>
54
+ </div>
55
+ <div class="ai1ec-event-popup-bg">
56
+ <?php if( ! $event->allday ): ?>
57
+ <span class="ai1ec-event-time"><?php echo esc_html( $event->short_start_time ) ?></span>
58
+ <?php endif ?>
59
+ <span class="ai1ec-event-title"><?php echo esc_html( mb_strimwidth( $event->post->post_title, 0, 35, '...' ) ) ?></span>
60
+ <?php if( $event->allday ): ?>
61
+ <small><?php esc_html_e( '(all-day)', AI1EC_PLUGIN_NAME ) ?></small>
62
+ <?php endif ?>
63
+ </div>
64
+ </div><!-- .event-popup -->
65
+
66
+ <div class="ai1ec-event <?php if( $event->post_id == $active_event ) echo 'ai1ec-active-event' ?>" <?php echo $event->color_style; ?>>
67
+ <?php if( ! $event->allday ): ?>
68
+ <span class="ai1ec-event-time"><?php echo esc_html( $event->short_start_time ) ?></span>
69
+ <?php endif ?>
70
+ <span class="ai1ec-event-title"><?php echo esc_html( $event->post->post_title ) ?></span>
71
+ </div>
72
+
73
+ </a>
74
+ <?php endforeach // events ?>
75
+ </div>
76
+ </td>
77
+ <?php else: ?>
78
+ <td class="ai1ec-empty"></td>
79
+ <?php endif // date ?>
80
+ <?php endforeach // day ?>
81
+ </tr>
82
+ <?php endforeach // week ?>
83
+ </tbody>
84
+ </table>
app/view/save_successful.php ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ <div class="updated">
2
+ <p>
3
+ <strong>
4
+ <?php echo $msg; ?>
5
+ </strong>
6
+ </p>
7
+ </div>
app/view/settings.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="wrap">
2
+
3
+ <?php screen_icon(); ?>
4
+
5
+ <h2><?php _e( 'All-in-one Event Calendar', AI1EC_PLUGIN_NAME ) ?></h2>
6
+
7
+ <div id="poststuff">
8
+
9
+ <form method="post" action="">
10
+ <?php wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); ?>
11
+ <?php wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); ?>
12
+
13
+ <div class="metabox-holder">
14
+ <div class="post-box-container column-1-ai1ec left-side"><?php do_meta_boxes( $settings_page, 'left-side', null ); ?></div>
15
+ <div class="post-box-container column-2-ai1ec right-side"><?php do_meta_boxes( $settings_page, 'right-side', null ); ?></div>
16
+ <div class="post-box-container column-3-ai1ec full"><?php do_meta_boxes( $settings_page, 'full', null ); ?></div>
17
+ </div>
18
+ <?php submit_button( esc_attr__( 'Update Settings', AI1EC_PLUGIN_NAME ), 'primary', 'ai1ec_save_settings' ); ?>
19
+
20
+ </form>
21
+
22
+ </div><!-- #poststuff -->
23
+
24
+ </div><!-- .wrap -->
app/view/subscription_button.php ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank">
2
+ <input type="hidden" name="cmd" value="_s-xclick" />
3
+ <input type="hidden" name="hosted_button_id" value="RGE42APRSL5UA" />
4
+ <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_subscribeCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!" />
5
+ <img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1" />
6
+ </form>
css/add_new_event.css ADDED
@@ -0,0 +1 @@
 
1
+ #ai1ec_event{overflow:hidden}.ai1ec-form{width:100%;margin-bottom:1em}.ai1ec-form td.ai1ec-first{width:8.5em}.ai1ec-form label{font-size:9pt}.ai1ec-form td{line-height:1.5em}.ai1ec-section-title{text-transform:uppercase;border-bottom:1px solid #eee;padding-bottom:.4em;color:#aaa;clear:both}.ai1ec_hidden{display:none}.ai1ec_week_days_list,.ai1ec_month_days_list,.ai1ec_yearly_months_list{margin:0;padding:0;list-style:none}.ai1ec_week_days_list li,.ai1ec_month_days_list li,.ai1ec_yearly_months_list li{float:left;padding:.3em 0;width:2.2em;border:1px solid #dfdfdf;border-radius:.4em;-moz-border-radius:.4em;-webkit-border-radius:.4em;margin-right:.2em;background-color:#fff;text-align:center}.ai1ec_yearly_months_list li{width:2.5em!important;padding:.5em 0!important}.ai1ec_week_days_list li:hover,.ai1ec_month_days_list li:hover,.ai1ec_yearly_months_list li:hover{cursor:pointer}.ai1ec_selected{background-color:#e2e2e2!important}.ai1ec-date-input{width:7em}.ai1ec-time-input{width:5.5em}#ai1ec_venue,#ai1ec_address,#ai1ec_cost,#ai1ec_contact_name,#ai1ec_contact_phone,#ai1ec_contact_email{width:100%;max-width:20em}.ai1ec-form.ai1ec-location-form{float:left;width:33em}#side-info-column .ai1ec-form.ai1ec-location-form{float:none;width:100%}.ac_results img{float:left;margin:.2em .7em .2em .2em}.ai1ec_box_map{float:right;margin-bottom:1em;position:absolute;visibility:hidden;border:1px solid #ddd}.ai1ec_box_map.ai1ec_box_map_visible{position:static;visibility:visible}#ai1ec_map_canvas{width:264px;height:15em}.ai1ec_bottom_publish{min-width:80px;text-align:center}.calendricalDatePopup,.calendricalTimePopup{z-index:2}.calendricalDatePopup{background:white;border:solid 1px #999;padding:2px;text-align:center;width:200px}.calendricalDatePopup table{border-collapse:collapse;width:200px}.calendricalDatePopup table .monthCell{padding:2px 0}.calendricalDatePopup table .monthCell a{display:block;float:left;line-height:20px}.calendricalDatePopup table .monthCell .prevMonth,.calendricalDatePopup table .monthCell .nextMonth{width:24px}.calendricalDatePopup table .monthCell .monthName{width:150px}.calendricalDatePopup table a{text-decoration:none!important;padding:0}.calendricalDatePopup table th{text-align:center}.calendricalDatePopup table td{text-align:center;font-size:12px;padding:0;font-family:inherit}.calendricalDatePopup table td a{display:block;color:black;padding:2px 3px}.calendricalDatePopup table td a:hover{background:#ccf;border:0;padding:2px 3px}.calendricalDatePopup table td.today a{background:#f8f8c0}.calendricalDatePopup table td.selected a{background:#ccf}.calendricalDatePopup table td.today_selected a{background:#f8f8c0;border:solid 1px #dd6;padding:1px 2px}.calendricalDatePopup table td.nonMonth a{color:#999}.calendricalTimePopup{background:white;border:solid 1px #999;width:110px;height:130px;overflow:auto}.calendricalTimePopup ul{margin:0!important;padding:0!important}.calendricalTimePopup ul li{list-style:none!important;padding:0!important;margin:0}.calendricalTimePopup ul li a,.calendricalTimePopup ul li a:visited{text-indent:10px;padding:4px;display:block;color:black;text-decoration:none!important}.calendricalTimePopup ul li a:hover,.calendricalTimePopup ul li.selected a{background:#ccf}.calendricalEndTimePopup{width:200px}.ai1ec-slider{background:#eee;height:.4em;position:relative;cursor:pointer;border:1px solid #ccc;width:8.8em;margin:.7em .25em 0 0;float:left;clear:right;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px}.ai1ec-progress{height:.5em;background-color:#c5ff00;display:none}.ai1ec-handle{background:#fff;height:1em;width:1em;top:-0.5em;margin-top:1px;position:absolute;z-index:0;display:block;border:1px solid #ccc;cursor:move;border-radius:.25em;-moz-border-radius:.25em;-webkit-border-radius:.25em;box-shadow:0 2px 5px rgba(0,0,0,0.15);-moz-box-shadow:0 2px 5px rgba(0,0,0,0.15);-webkit-box-shadow:0 2px 5px rgba(0,0,0,0.15)}.ai1ec-range{text-align:center;width:3em;border:1px solid #dfdfdf;background:#fff;border-radius:4px;-moz-border-radius:4px;-webkit-border-radius:4px;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box}
css/calendar.css ADDED
@@ -0,0 +1 @@
 
1
+ .ai1ec-container{font-size:13pt!important}.ai1ec-container small{margin:0!important}.ai1ec-action-month #ai1ec-view-month,.ai1ec-action-agenda #ai1ec-view-agenda{color:#000;background:#ddd;background:-o-linear-gradient(#ccc,#eee);background:-ms-linear-gradient(#ccc,#eee);background:-moz-linear-gradient(#ccc,#eee);background:-webkit-gradient(linear,0% 0,0% 100%,from(#ccc),to(#eee));background:-webkit-linear-gradient(#ccc,#eee)}#ai1ec-calendar-view-container{position:relative;clear:both;margin-bottom:.2em}table.ai1ec-calendar-toolbar{width:100%;border:0!important;margin:0!important}.ai1ec-calendar-toolbar td{text-align:center;width:1em;vertical-align:middle;border:0!important;padding:0!important;white-space:nowrap;background:none!important}.ai1ec-calendar-toolbar td:last-child{text-align:right}.ai1ec-calendar-toolbar td:first-child{text-align:left}.ai1ec-loading{position:absolute;z-index:9;top:0;left:0;width:100%;height:100%;background:#fff url(../img/ajax-loader.gif) no-repeat center center;display:none}ul.ai1ec-view-tabs{margin:0 0 .2em!important;padding:0!important;display:inline-block}.ai1ec-view-tabs li{list-style:none!important;float:left;margin:.1em 0!important;padding:0!important}.ai1ec-view-tabs a{line-height:25px!important;padding:.2em .5em}.ai1ec-view-tabs a img{border:0!important;padding:0!important;margin:0!important;float:none!important;position:static!important;vertical-align:middle;background:none!important}.ai1ec-filters-container{display:inline-block}.ai1ec-filters-container .ai1ec-button{margin:0}.ai1ec-filter-selector-container{position:relative;display:inline-block;text-align:left;white-space:normal}.ai1ec-filter-selector{display:none;position:absolute;z-index:10;right:0;top:100%;overflow:auto;max-width:20em;max-height:30em;background:#fff;border:1px solid #ddd;box-shadow:1px 5px 8px rgba(0,0,0,0.08);-o-box-shadow:1px 5px 8px rgba(0,0,0,0.08);-ms-box-shadow:1px 5px 8px rgba(0,0,0,0.08);-moz-box-shadow:1px 5px 8px rgba(0,0,0,0.08);-webkit-box-shadow:1px 5px 8px rgba(0,0,0,0.08);padding:.1em}.ai1ec-filter-selector ul{margin:0!important;padding:0!important}.ai1ec-filter-selector li{list-style:none}.ai1ec-filter-selector .ai1ec-category,.ai1ec-filter-selector .ai1ec-tag{font-size:8pt!important}.ai1ec-clear-filters{display:none;font-size:11pt!important;font-weight:normal;color:#7a86d9!important;cursor:pointer;text-decoration:none!important}.ai1ec-clear-filters:hover{color:#000!important}h2.ai1ec-calendar-title{float:left;font-size:1.15em!important;line-height:1.3em!important;margin:0 0 .2em!important}.ai1ec-title-buttons{margin:0 .5em}.ai1ec-title-buttons .ai1ec-button{font-size:8pt!important;margin:.3em .1em}.ai1ec-today #ai1ec-today{display:none}ul.ai1ec-pagination{margin:0 0 .3em!important;padding:0!important;float:right}a.ai1ec-pagination{margin:0 0 .3em!important}.ai1ec-pagination li{list-style:none!important;padding:0!important;float:left}.ai1ec-action-month .ai1ec-pagination a{width:7em}.ai1ec-action-agenda a.ai1ec-pagination{float:right;width:9em}#ai1ec-prev-year,#ai1ec-next-year{width:4em}table.ai1ec-month-view{border-collapse:collapse;border:1px solid #ddd!important;margin:0!important;background:#fff;table-layout:fixed;clear:both;width:100%}.ai1ec-month-view td{border:1px solid #ddd!important;vertical-align:top;background:none!important}.ai1ec-month-view td.ai1ec-empty{background:#f8f8f8!important}.ai1ec-month-view th{padding:.2em!important;border-bottom:1px solid #ddd;font:bold 9pt Tahoma,Geneva,sans-serif!important;text-align:center!important;background:#f2f2f2!important;border:none!important;color:#999!important;text-shadow:0 1px 0 #fff;-o-text-shadow:0 1px 0 #fff;-ms-text-shadow:0 1px 0 #fff;-moz-text-shadow:0 1px 0 #fff;-webkit-text-shadow:0 1px 0 #fff}.ai1ec-month-view td{padding:0!important;text-align:left}.ai1ec-month-view .ai1ec-day{position:relative;min-height:5em}.ai1ec-month-view .ai1ec-today{background:#ffd;background:rgba(255,255,128,0.3)}.ai1ec-month-view .ai1ec-date{text-align:right;font-size:10pt;line-height:12pt;padding:0 .4em;background:#eaf4ff;font:8pt Tahoma,Geneva,sans-serif;color:#666;text-shadow:0 1px 0 #fff;-o-text-shadow:0 1px 0 #fff;-ms-text-shadow:0 1px 0 #fff;-moz-text-shadow:0 1px 0 #fff;-webkit-text-shadow:0 1px 0 #fff}.ai1ec-month-view a.ai1ec-event-container{position:relative;font:9pt Tahoma,Geneva,sans-serif!important;text-decoration:none!important;display:block;border:none!important}.ai1ec-month-view .ai1ec-event{border-radius:.3em;-o-border-radius:.3em;-moz-border-radius:.3em;-webkit-border-radius:.3em;margin:1px 1px 0 1px;padding:0 0 1px 2px;white-space:nowrap;overflow:hidden;color:#568!important}.ai1ec-month-view .ai1ec-allday .ai1ec-event{background:#568;color:#fff!important}.ai1ec-month-view .ai1ec-category-colors{float:right;font-size:1.2em;margin-top:1px}.ai1ec-month-view .ai1ec-event-popup,.ai1ec-month-view .ai1ec-event-summary{border:2px solid #d4c4b0;box-shadow:1px 5px 8px rgba(0,0,0,0.08);-o-box-shadow:1px 5px 8px rgba(0,0,0,0.08);-ms-box-shadow:1px 5px 8px rgba(0,0,0,0.08);-moz-box-shadow:1px 5px 8px rgba(0,0,0,0.08);-webkit-box-shadow:1px 5px 8px rgba(0,0,0,0.08);background:#fff}.ai1ec-month-view .ai1ec-event-popup{position:absolute;z-index:5;display:none;white-space:nowrap;color:#000!important;top:-4px;left:-2px;min-width:100%;border-radius:0 .3em .3em 0;-o-border-radius:0 .3em .3em 0;-moz-border-radius:0 .3em .3em 0;-webkit-border-radius:0 .3em .3em 0}.ai1ec-month-view .ai1ec-event-popup.ai1ec-shifted-right{border-radius:.3em 0 0 .3em;-o-border-radius:.3em 0 0 .3em;-ms-border-radius:.3em 0 0 .3em;-moz-border-radius:.3em 0 0 .3em;-webkit-border-radius:.3em 0 0 .3em}.ai1ec-month-view .ai1ec-event-popup-bg{position:relative;left:-2px;margin-right:-1px;padding:2px 2px 2px 5px;background:#fff}.ai1ec-month-view .ai1ec-shifted-right .ai1ec-event-popup-bg{left:1px;padding:2px 5px 2px 2px}.ai1ec-month-view .ai1ec-event-summary,.ai1ec-month-view .ai1ec-event-summary p{font-size:.95em!important}.ai1ec-month-view .ai1ec-event-summary p{line-height:1.4em!important}.ai1ec-month-view .ai1ec-event-summary{position:absolute;overflow:hidden;top:-2px;white-space:normal;right:100%;width:13em;padding:2px 4px 3px;border-radius:.3em 0 .3em .3em;-o-border-radius:.3em 0 .3em .3em;-ms-border-radius:.3em 0 .3em .3em;-moz-border-radius:.3em 0 .3em .3em;-webkit-border-radius:.3em 0 .3em .3em;background:linear-gradient(#fff 1.5em,#fdfadc);background:-o-linear-gradient(#fff 1.5em,#fdfadc);background:-ms-linear-gradient(#fff 1.5em,#fdfadc);background:-moz-linear-gradient(#fff 1.5em,#fdfadc);background:-webkit-gradient(linear,0% 1.5em,0% 100%,from(#fff),to(#fdfadc));background:-webkit-linear-gradient(#fff 1.5em,#fdfadc);color:#6d5e4a}.ai1ec-month-view .ai1ec-shifted-right .ai1ec-event-summary{left:100%;border-radius:0 .3em .3em .3em;-o-border-radius:0 .3em .3em .3em;-moz-border-radius:0 .3em .3em .3em;-webkit-border-radius:0 .3em .3em .3em}.ai1ec-month-view .ai1ec-event-summary p{margin:0 0 .3em!important;padding:0!important}.ai1ec-month-view .ai1ec-event-time{font-size:8pt;font-weight:bold}.ai1ec-month-view .ai1ec-read-more{text-align:center;font-size:8pt;color:#aaa;color:rgba(0,0,0,0.4)}ol.ai1ec-agenda-view{clear:both;overflow:hidden;margin:0!important;padding:0!important}.ai1ec-agenda-view .ai1ec-no-results{font-size:10pt!important;font-style:italic}ol.ai1ec-date-events{overflow:hidden;margin:0 .75em 0 0!important;padding:0!important}ol.ai1ec-agenda-view>li,ol.ai1ec-date-events>li{list-style:none!important}.ai1ec-agenda-view .ai1ec-date{overflow:hidden;margin:0!important;padding:0!important;border-top:1px solid #f2f2f2;border-bottom:1px solid #fff;background:#f8f8f8;background:-o-linear-gradient(#f2f2f2 1em,#fff);background:-ms-linear-gradient(#f2f2f2 1em,#fff);background:-moz-linear-gradient(#f2f2f2 1em,#fff);background:-webkit-gradient(linear,0 1em,0 100%,from(#f2f2f2),to(#fff));background:-webkit-linear-gradient(#f2f2f2 1em,#fff)}.ai1ec-agenda-view .ai1ec-date.ai1ec-today{border-top:1px solid #eec;border-bottom:1px solid #fff;background:#ffd;background:-o-linear-gradient(#f8f8ce 1em,#fff);background:-ms-linear-gradient(#f8f8ce 1em,#fff);background:-moz-linear-gradient(#f8f8ce 1em,#fff);background:-webkit-gradient(linear,0 1em,0 100%,from(#f8f8ce),to(#fff));background:-webkit-linear-gradient(#f8f8ce 1em,#fff);background:-o-linear-gradient(#f8f8ce 1em,#fff)}.ai1ec-agenda-view h3.ai1ec-date-title{width:4em;float:left;margin:0 .75em .5em!important;font:10pt/1.1em Tahoma,Geneva,sans-serif!important;color:#444;text-shadow:0 1px 0 #fff;-o-text-shadow:0 1px 0 #fff;-ms-text-shadow:0 1px 0 #fff;-moz-text-shadow:0 1px 0 #fff;-webkit-text-shadow:0 1px 0 #fff;line-height:1.1em;text-align:center;border:2px solid #7e9dbd;border-radius:0 0 5px 5px;-o-border-radius:0 0 .5em .5em;-moz-border-radius:0 0 .5em .5em;-webkit-border-radius:0 0 .5em .5em;box-shadow:0 2px 2px rgba(0,0,0,0.08);-o-box-shadow:0 2px 2px rgba(0,0,0,0.08);-ms-box-shadow:0 2px 2px rgba(0,0,0,0.08);-moz-box-shadow:0 2px 2px rgba(0,0,0,0.08);-webkit-box-shadow:0 2px 2px rgba(0,0,0,0.08);background:#fff;background:-o-linear-gradient(#fff 1em,#eee);background:-ms-linear-gradient(#fff 1em,#eee);background:-moz-linear-gradient(#fff 1em,#eee);background:-webkit-gradient(linear,0% 1em,0% 100%,from(#fff),to(#eee));background:-webkit-linear-gradient(#fff 1em,#eee)}.ai1ec-agenda-view .ai1ec-month{text-shadow:0 -1px 0 #000;-o-text-shadow:0 -1px 0 #000;-ms-text-shadow:0 -1px 0 #000;-moz-text-shadow:0 -1px 0 #000;-webkit-text-shadow:0 -1px 0 #000;background:#7e9dbd;font-size:.8em!important;padding:.1em;text-transform:uppercase;letter-spacing:.2em;color:#fff}.ai1ec-agenda-view .ai1ec-day{font-size:2.2em!important;padding:.3em 0 .2em!important}.ai1ec-agenda-view .ai1ec-weekday{font-size:.8em!important;line-height:1.5em}.ai1ec-agenda-view .ai1ec-event{position:relative;clear:right;overflow:hidden;font-size:10pt;text-decoration:none;padding:.1em .3em!important;margin:.5em 0!important;background:#fff;border:1px solid #ddd;border-radius:.5em;-o-border-radius:.5em;-moz-border-radius:.5em;-webkit-border-radius:.5em}.ai1ec-agenda-view .ai1ec-event.ai1ec-expanded{border:2px solid #d4c4b0;box-shadow:1px 5px 8px rgba(0,0,0,0.08);-o-box-shadow:1px 5px 8px rgba(0,0,0,0.08);-ms-box-shadow:1px 5px 8px rgba(0,0,0,0.08);-moz-box-shadow:1px 5px 8px rgba(0,0,0,0.08);-webkit-box-shadow:1px 5px 8px rgba(0,0,0,0.08)}.ai1ec-agenda-view .ai1ec-event-click{font-family:Tahoma,Geneva,sans-serif!important;cursor:pointer;line-height:1.3em;color:#568;text-shadow:0 1px 0 #fff;-o-text-shadow:0 1px 0 #fff;-ms-text-shadow:0 1px 0 #fff;-moz-text-shadow:0 1px 0 #fff;-webkit-text-shadow:0 1px 0 #fff}.ai1ec-agenda-view .ai1ec-event-time{float:left;margin-right:.5em;min-height:1em;font-weight:bold;line-height:1.3em}.ai1ec-agenda-view .ai1ec-event-title{margin:0 0 0 8em;font-size:10pt!important;font-weight:normal}.ai1ec-agenda-view .ai1ec-event.ai1ec-allday{padding:0!important}.ai1ec-agenda-view .ai1ec-allday-label{font-size:8pt!important;color:#b9c0ce;padding-left:.2em}.ai1ec-agenda-view .ai1ec-allday .ai1ec-event-click{color:#fff;background:#568;padding:.1em .4em;border-radius:.3em;-o-border-radius:.3em;-moz-border-radius:.3em;-webkit-border-radius:.3em;text-shadow:none;-o-text-shadow:none;-ms-text-shadow:none;-moz-text-shadow:none;-webkit-text-shadow:none}.ai1ec-agenda-view .ai1ec-allday .ai1ec-event-time{color:#fff}.ai1ec-agenda-view .ai1ec-allday .ai1ec-event-title{margin:0}.ai1ec-agenda-view .ai1ec-event-expand{float:right;margin-left:.5em;visibility:hidden;font-size:9pt!important}.ai1ec-agenda-view .ai1ec-event:hover,.ai1ec-agenda-view .ai1ec-event.ai1ec-expanded:hover{border-color:#aaa}.ai1ec-agenda-view .ai1ec-event:hover .ai1ec-event-click{color:#000}.ai1ec-agenda-view .ai1ec-allday:hover .ai1ec-event-click{color:#fff;background:#3b475f}.ai1ec-agenda-view .ai1ec-event-click:hover>.ai1ec-event-expand{visibility:visible}.ai1ec-agenda-view .ai1ec-category-colors{float:left;font-size:.9em;margin:.2em .2em 0 -0.2em}.ai1ec-agenda-view .ai1ec-category-colors .ai1ec-category-color{border:1px solid rgba(255,255,255,0.7)}.ai1ec-agenda-view .ai1ec-event-summary{display:none}.ai1ec-agenda-view .ai1ec-event-summary .ai1ec-event-title{margin:.1em 0;font-size:11pt!important;line-height:1.1em}.ai1ec-agenda-view .ai1ec-event-summary .ai1ec-event-time{width:auto;float:none;font-size:9pt!important;line-height:1.5em}.ai1ec-agenda-view .ai1ec-event-summary .ai1ec-event-click{border-bottom:1px solid #ddd}.ai1ec-agenda-view .ai1ec-allday .ai1ec-event-summary .ai1ec-event-click{border-bottom:1px solid #ddd;border-radius:.3em .3em 0 0;-o-border-radius:.3em .3em 0 0;-moz-border-radius:.3em .3em 0 0;-webkit-border-radius:.3em .3em 0 0}.ai1ec-agenda-view .ai1ec-categories,.ai1ec-agenda-view .ai1ec-tags{font-size:8pt!important}.ai1ec-agenda-view .ai1ec-event-description{clear:both;overflow:hidden;padding:0 0 3.6em;max-height:20em}.ai1ec-agenda-view .ai1ec-event-description,.ai1ec-agenda-view .ai1ec-event-description p{font-size:9pt!important;line-height:1.5em!important}.ai1ec-agenda-view .ai1ec-event-description p{margin:.5em 0 .75em!important;padding:0!important}.ai1ec-agenda-view .ai1ec-allday .ai1ec-event-description{position:relative;overflow:hidden;padding:0 .4em 3.5em}.ai1ec-agenda-view .ai1ec-event-description .ai1ec-label{font-size:8pt!important;font-weight:normal}.ai1ec-agenda-view .ai1ec-event-overlay{position:absolute;z-index:5;bottom:0;left:0;right:0;padding:.4em .5em .4em .2em;border-radius:0 0 .5em .5em;-o-border-radius:0 0 .5em .5em;-moz-border-radius:0 0 .5em .5em;-webkit-border-radius:0 0 .5em .5em;background:rgba(255,255,255,0.75);background:-o-linear-gradient(rgba(255,255,255,0.75),#fdfadc);background:-ms-linear-gradient(rgba(255,255,255,0.75),#fdfadc);background:-moz-linear-gradient(rgba(255,255,255,0.75),#fdfadc);background:-webkit-gradient(linear,0 0,0 100%,from(rgba(255,255,255,0.75)),to(#fdfadc));background:-webkit-linear-gradient(rgba(255,255,255,0.75),#fdfadc)}.ai1ec-agenda-view .ai1ec-read-more{float:right;margin:.6em 0 .2em}.ai1ec-subscribe{clear:both}.ai1ec-subscribe-filtered{display:none}
css/colorpicker.css ADDED
@@ -0,0 +1 @@
 
1
+ #tag-color{width:39px;height:24px;background:url(../img/color-picker.png);cursor:pointer}#tag-color:hover{background-position:0 -24px}#tag-color-background{position:relative;top:3px;left:3px;width:20px;height:18px;background:transparent}ul.colorpicker-list{padding:.1em}.colorpicker-list li{float:left;width:13px;height:13px;margin:1px;cursor:pointer;border-radius:2px;-o-border-radius:2px;-moz-border-radius:2px;-webkit-border-radius:2px}.select-more-colors{width:73px!important;line-height:22px;height:22px!important;border-color:#fff!important;margin:2px 1px 2px 1px!important;text-align:center;font-family:Tahoma,Verdana,Arial,Helvetica;font-size:11px}li.select-more-colors:hover{border-color:#0a246a!important;background:#b6bdd2!important}.color-1{background:#60a!important}.color-2{background:#807!important}.color-3{background:#920!important}.color-4{background:#a60!important}.color-5{background:#990!important}.color-6{background:#080!important}.color-7{background:#077!important}.color-8{background:#00a!important}.color-9{background:#000!important}.color-10{background:#444!important}.color-11{background:#85e!important}.color-12{background:#d5d!important}.color-13{background:#d43!important}.color-14{background:#d90!important}.color-15{background:#bb0!important}.color-16{background:#2b0!important}.color-17{background:#0ba!important}.color-18{background:#26d!important}.color-19{background:#777!important}.color-20{background:#aaa!important}.colorpicker{width:356px;height:176px;overflow:hidden;position:absolute;background:url(../img/colorpicker_background.png);font-family:Arial,Helvetica,sans-serif;display:none;z-index:2}.colorpicker_color{width:150px;height:150px;left:14px;top:13px;position:absolute;background:#f00;overflow:hidden;cursor:crosshair}.colorpicker_color div{position:absolute;top:0;left:0;width:150px;height:150px;background:url(../img/colorpicker_overlay.png)}.colorpicker_color div div{position:absolute;top:0;left:0;width:11px;height:11px;overflow:hidden;background:url(../img/colorpicker_select.gif);margin:-5px 0 0 -5px}.colorpicker_hue{position:absolute;top:13px;left:171px;width:35px;height:150px;cursor:n-resize}.colorpicker_hue div{position:absolute;width:35px;height:9px;overflow:hidden;background:url(../img/colorpicker_indic.gif) left top;margin:-4px 0 0 0;left:0}.colorpicker_new_color{position:absolute;width:60px;height:30px;left:213px;top:13px;background:#f00}.colorpicker_current_color{position:absolute;width:60px;height:30px;left:283px;top:13px;background:#f00}.colorpicker input{background-color:transparent!important;border:1px solid transparent;border-color:transparent!important;border-radius:0!important;box-sizing:content-box!important;position:absolute;font-size:10px;font-family:Arial,Helvetica,sans-serif;color:#898989;top:4px;right:11px;text-align:right;margin:0;padding:0;height:11px}.colorpicker_hex{position:absolute;width:72px;height:22px;background:url(../img/colorpicker_hex.png) top;left:212px;top:142px}.colorpicker_hex input{right:6px}.colorpicker_field{height:22px;width:62px;background-position:top;position:absolute}.colorpicker_field span{position:absolute;width:12px;height:22px;overflow:hidden;top:0;right:0;cursor:n-resize}.colorpicker_rgb_r{background-image:url(../img/colorpicker_rgb_r.png);top:52px;left:212px}.colorpicker_rgb_g{background-image:url(../img/colorpicker_rgb_g.png);top:82px;left:212px}.colorpicker_rgb_b{background-image:url(../img/colorpicker_rgb_b.png);top:112px;left:212px}.colorpicker_hsb_h{background-image:url(../img/colorpicker_hsb_h.png);top:52px;left:282px}.colorpicker_hsb_s{background-image:url(../img/colorpicker_hsb_s.png);top:82px;left:282px}.colorpicker_hsb_b{background-image:url(../img/colorpicker_hsb_b.png);top:112px;left:282px}.colorpicker_submit{position:absolute;width:50px;height:22px;background:url(../img/colorpicker_submit.png) top;left:290px;top:142px;overflow:hidden}.colorpicker_focus{background-position:center}.colorpicker_hex.colorpicker_focus{background-position:bottom}.colorpicker_submit.colorpicker_focus{background-position:bottom}.colorpicker_slider{background-position:bottom}
css/event.css ADDED
@@ -0,0 +1 @@
 
1
+ table.ai1ec-full-event{border:none!important;border-collapse:collapse!important;width:100%;margin-bottom:.5em!important}.ai1ec-full-event td{font-size:10pt!important}.ai1ec-full-event th,.ai1ec-full-event td{line-height:14pt!important;padding:.3em .5em!important;border:none!important;vertical-align:top}.ai1ec-multi-event th,.ai1ec-multi-event td{padding:.3em .5em!important}.ai1ec-full-event th.ai1ec-empty,.ai1ec-full-event td.ai1ec-empty{padding:0!important}.ai1ec-full-event th{width:6em;font-size:9pt!important;font-family:sans-serif!important;font-weight:bold;color:#888!important;text-align:right;text-shadow:0 1px 0 #fff;-moz-text-shadow:0 1px 0 #fff;-webkit-text-shadow:0 1px 0 #fff;background:-moz-linear-gradient(right,#fff,#ececec 1em,#f8f8f8);background:-webkit-gradient(linear,0 0,100% 0,from(#f8f8f8),to(#ececec));background:-webkit-linear-gradient(right,#fff,#ececec 1em,#f8f8f8);background:-o-linear-gradient(right,#fff,#ececec 1em,#f8f8f8)}.ai1ec-full-event .ai1ec-time{line-height:16pt!important;vertical-align:middle}.ai1ec-multi-event .ai1ec-time{line-height:15pt!important}.ai1ec-full-event td.ai1ec-time{font-weight:bold;font-size:11pt!important}.ai1ec-multi-event td.ai1ec-time{font-size:10pt!important}.ai1ec-full-event .ai1ec-allday-label{font-size:10pt!important;color:#bbb;padding-left:.2em}.ai1ec-multi-event .ai1ec-allday-label{font-size:9pt!important}.ai1ec-full-event .ai1ec-calendar-link{float:right;margin:-0.1em 0}.ai1ec-multi-event .ai1ec-calendar-link{font-size:8pt!important}.ai1ec-full-event td.ai1ec-location{min-width:30%}.ai1ec-full-event td.ai1ec-map{text-align:right;vertical-align:bottom}.ai1ec-full-event td.ai1ec-map.ai1ec-has-map{width:55%;text-align:left}.ai1ec-full-event .ai1ec-gmap-container{border:1px solid #ddd;margin-bottom:.3em}.ai1ec-full-event #ai1ec-gmap-canvas{min-width:18em;width:100%;height:12em}.ai1ec-full-event .ai1ec-gmap-link{float:right}.ai1ec-multi-event .ai1ec-gmap-link{font-size:8pt!important;margin:-0.2em 0}.ai1ec-full-event #ai1ec-gmap-canvas input{margin:0!important}.ai1ec-full-event .ai1ec-contact a{white-space:nowrap}.ai1ec-full-event td.ai1ec-tags,.ai1ec-full-event td.ai1ec-categories{padding:.1em .2em!important}.ai1ec-full-event .ai1ec-category{float:left}.ai1ec-multi-event .ai1ec-category{font:8pt Tahoma,Geneva,sans-serif!important}.ai1ec-full-event .ai1ec-tag{font-size:.9em!important}.ai1ec-event-footer{clear:left;font-size:.9em!important;font-style:italic}.ai1ec-ics-icon{background:url(../img/ics-icon.png) no-repeat right;padding-right:15px}.ai1ec-excerpt{margin-bottom:.5em;font-size:10pt!important;line-height:1.6em;overflow:hidden}.ai1ec-excerpt>div{padding-left:7.2em}.ai1ec-excerpt label{padding:0 .6em!important;background:-moz-linear-gradient(right,#fff,#ececec 1em,#f8f8f8);background:-webkit-gradient(linear,0 0,100% 0,from(#f8f8f8),to(#ececec));background:-webkit-linear-gradient(right,#fff,#ececec 1em,#f8f8f8);background:-o-linear-gradient(right,#fff,#ececec 1em,#f8f8f8);text-align:right;text-shadow:0 1px 0 #fff;-moz-text-shadow:0 1px 0 #fff;-webkit-text-shadow:0 1px 0 #fff;clear:left;float:left;width:6em;margin-left:-7.2em}.ai1ec-excerpt .ai1ec-time{font-weight:bold}.ai1ec-excerpt .ai1ec-event-link{float:right;font-size:8pt!important}.ai1ec-excerpt .ai1ec-allday-label{font-size:9pt!important;color:#bbb;padding-left:.2em}
css/general.css ADDED
@@ -0,0 +1 @@
 
1
+ a.ai1ec-button{cursor:pointer;font:10pt/1.1em Tahoma,Geneva,sans-serif!important;text-decoration:none!important;color:#666!important;text-shadow:0 1px 0 #fff;-moz-text-shadow:0 1px 0 #fff;-webkit-text-shadow:0 1px 0 #fff;padding:.4em .5em;margin:.1em;background:#eee;background:-moz-linear-gradient(#f8f8f8,#ddd);background:-webkit-gradient(linear,0% 0,0% 100%,from(#f8f8f8),to(#ddd));background:-webkit-linear-gradient(#f8f8f8,#ddd);background:-o-linear-gradient(#f8f8f8,#ddd);box-shadow:0 2px 2px rgba(0,0,0,0.08);-moz-box-shadow:0 2px 2px rgba(0,0,0,0.08);-webkit-box-shadow:0 2px 2px rgba(0,0,0,0.08);border-radius:.4em;-moz-border-radius:.4em;-webkit-border-radius:.4em;text-align:center;border:1px solid #ccc;border-width:1px;display:inline-block;white-space:nowrap}a.ai1ec-button:hover{color:#444!important;background:#ddd;background:-moz-linear-gradient(#eee,#ccc);background:-webkit-gradient(linear,0% 0,0% 100%,from(#eee),to(#ccc));background:-webkit-linear-gradient(#eee,#ccc);background:-o-linear-gradient(#eee,#ccc)}a.ai1ec-button.ai1ec-selected{color:#000!important;border-color:#909ce0;background:#dde6f8;background:-moz-linear-gradient(#edf6ff,#c5d4f3);background:-webkit-gradient(linear,0% 0,0% 100%,from(#edf6ff),to(#c5d4f3));background:-webkit-linear-gradient(#edf6ff,#c5d4f3);background:-o-linear-gradient(#edf6ff,#c5d4f3)}a.ai1ec-button.ai1ec-active{color:#000!important;background:#ddd;background:-moz-linear-gradient(#ccc,#eee);background:-webkit-gradient(linear,0% 0,0% 100%,from(#ccc),to(#eee));background:-webkit-linear-gradient(#ccc,#eee);background:-o-linear-gradient(#ccc,#eee)}a.ai1ec-button.ai1ec-active.ai1ec-selected{background:#dde6f8;background:-moz-linear-gradient(#c5d4f3,#edf6ff);background:-webkit-gradient(linear,0% 0,0% 100%,from(#c5d4f3),to(#edf6ff));background:-webkit-linear-gradient(#c5d4f3,#edf6ff);background:-o-linear-gradient(#c5d4f3,#edf6ff)}li>a.ai1ec-button{display:block;margin:0;border-radius:0;-moz-border-radius:0;-webkit-border-radius:0;border-left-width:0}li:first-child>a.ai1ec-button{border-radius:.4em 0 0 .4em;-moz-border-radius:.4em 0 0 .4em;-webkit-border-radius:.4em 0 0 .4em;border-left-width:1px}li:last-child>a.ai1ec-button{border-radius:0 .4em .4em 0;-moz-border-radius:0 .4em .4em 0;-webkit-border-radius:0 .4em .4em 0}li:first-child:last-child>a.ai1ec-button{border-radius:.4em;-moz-border-radius:.4em;-webkit-border-radius:.4em}label.ai1ec-label{padding:0 .2em;font-family:Tahoma,Geneva,sans-serif!important;font-size:9pt!important;font-weight:bold;color:#888!important}.ai1ec-category{cursor:pointer;font:.9em Tahoma,Geneva,sans-serif!important;border:1px solid #dcdcdc;text-shadow:0 1px 0 #fff;-moz-text-shadow:0 1px 0 #fff;-webkit-text-shadow:0 1px 0 #fff;padding:2pt 3pt!important;margin:.2em!important;text-decoration:none!important;text-transform:uppercase;letter-spacing:1pt;white-space:nowrap;color:#666!important;background:#f0f0f0;border-radius:.3em;-moz-border-radius:.3em;-webkit-border-radius:.3em}.ai1ec-category:hover{color:#222!important;background:#dcdcdc}.ai1ec-category.ai1ec-selected{background:#c5d4f3;border-color:#7a86d9}.ai1ec-tag{color:#568!important;font-family:Tahoma,Geneva,sans-serif!important;line-height:1.3em!important;cursor:pointer;white-space:nowrap;margin:.1em .2em;padding:.1em .2em;border-radius:.3em;-moz-border-radius:.3em;-webkit-border-radius:.3em;display:inline-block}.ai1ec-tag:hover{color:#000!important;text-decoration:underline}.ai1ec-tag.ai1ec-selected{background:#cddbf5}.ai1ec-category-color{display:inline-block;width:.9em;height:.9em;margin:0 1px;border-radius:.2em;-o-border-radius:.2em;-moz-border-radius:.2em;-webkit-border-radius:.2em}.ai1ec-subscribe-google img{float:left;margin:-10px .5em -8px 0;padding:0!important;border:none!important;background:none!important}
css/jquery.autocomplete.css ADDED
@@ -0,0 +1 @@
 
1
+ .ac_results{padding:0;border:1px solid black;background-color:white;overflow:hidden;z-index:99999}.ac_results ul{width:100%;list-style-position:outside;list-style:none;padding:0;margin:0}.ac_results li{margin:0;padding:2px 5px;cursor:default;display:block;font:menu;font-size:12px;line-height:16px;overflow:hidden}.ac_loading{background:white url('../img/indicator.gif') right center no-repeat}.ac_odd{background-color:#eee}.ac_over{background-color:#0a246a;color:white}
css/selector.css ADDED
@@ -0,0 +1 @@
 
1
+ <?php echo $selector ?>{visibility:hidden}
css/settings.css ADDED
@@ -0,0 +1 @@
 
1
+ #the-seed-studio-settings div.inside{background:#fff;margin:0!important;padding:6px 0 8px}#the-seed-studio-settings a{font-weight:bold}.ai1ec_feed_h4{margin-bottom:5px}.column-1-ai1ec{float:left;width:54%}.column-2-ai1ec{float:right;width:45%}.column-3-ai1ec{float:left;width:100%}.ai1ec-plugin-branding{padding:0 20px 0 20px}.ai1ec-plugin-logo{position:relative;margin-left:-15px}.ai1ec-support-button{float:right;width:280px;text-align:center;margin:1em 0 1em 1em}.ai1ec-support-button a{font-size:24px;color:#fff;text-decoration:none;text-transform:uppercase;text-align:center;display:block;padding:10px 30px 10px 30px;margin:0 auto;border-radius:10px;-o-border-radius:10px;-moz-border-radius:10px;-webkit-border-radius:10px;text-shadow:-1px -1px 1px rgba(0,0,0,.2);-o-text-shadow:-1px -1px 1px rgba(0,0,0,.2);-moz-text-shadow:-1px -1px 1px rgba(0,0,0,.2);-webkit-text-shadow:-1px -1px 1px rgba(0,0,0,.2);box-shadow:0 .3em .3em rgba(0,0,0,0.2);-o-box-shadow:0 .3em .3em rgba(0,0,0,0.2);-moz-box-shadow:0 .3em .3em rgba(0,0,0,0.2);-webkit-box-shadow:0 .3em .3em rgba(0,0,0,0.2);border:1px solid #fff}.ai1ec-support-button a:link,.ai1ec-support-button a:visited{background:#88b633;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#91bb35',endColorstr='#548a26');background:linear-gradient(#91bb35,#548a26);background:-o-linear-gradient(#91bb35,#548a26);background:-moz-linear-gradient(#91bb35,#548a26);background:-webkit-gradient(linear,left top,left bottom,from(#91bb35),to(#548a26));background:-webkit-linear-gradient(#91bb35,#548a26)}.ai1ec-support-button a:hover{color:#fff;background:#88b633;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#d2e7a2',endColorstr='#91bb35');background:linear-gradient(#d2e7a2,#91bb35);background:-o-linear-gradient(#d2e7a2,#91bb35);background:-moz-linear-gradient(#d2e7a2,#91bb35);background:-webkit-gradient(linear,left top,left bottom,from(#d2e7a2),to(#91bb35));background:-webkit-linear-gradient(#d2e7a2,#91bb35)}.ai1ec-support-button a span{display:block;padding-top:2px;font-size:.5em;color:#e5f3ca;text-align:center;text-transform:lowercase}#general-settings{line-height:1.7em}#general-settings div.inside label{float:left;clear:left;margin:.25em 0}#general-settings .checkbox{float:left;margin:.5em .5em 0 0}#general-settings div.description{clear:left;font-size:.95em;line-height:1.5em;margin-bottom:1em}label.textinput{width:230px}.ai1ec-follow-fan{padding:10px 0 10px 0;margin:10px 0 0 0;border-top:1px solid #ccc;border-bottom:1px solid #ccc}.facebook-like-top{float:left}#ai1ec_feed_url,.ai1ec-feed-url{width:100%}.ai1ec-feed-category,.ai1ec-feed-tags{float:left;overflow:hidden;line-height:2em}.ai1ec-feed-category strong,.ai1ec-feed-tags strong{margin:0 1em 0 .2em}#ai1ec_feed_category,#ai1ec_feed_tags{width:15em}#ai1ec_feed_category{margin-right:.5em}.ai1ec-feed-container{background:#eee;border:1px solid #ddd;padding:.3em .5em;border-radius:.5em;-moz-border-radius:.5em;-webkit-border-radius:.5em;overflow:hidden;position:relative;margin:1em 0 0}.ai1ec-feed-container .ajax-loading{clear:left;display:block}.ai1ec_feed_h4{margin:.5em 0}#ai1ec_add_new_ics,.ai1ec_delete_ics{clear:left}#ai1ec_add_new_ics,.ai1ec_delete_ics,.ai1ec_update_ics,.ai1ec_flush_ics{float:right}#ai1ec_save_settings{clear:left;display:block}
img/agenda-view.png ADDED
Binary file
img/ajax-loader.gif ADDED
Binary file
img/blank.gif ADDED
Binary file
img/color-picker.png ADDED
Binary file
img/colorpicker_background.png ADDED
Binary file
img/colorpicker_hex.png ADDED
Binary file
img/colorpicker_hsb_b.png ADDED
Binary file
img/colorpicker_hsb_h.png ADDED
Binary file
img/colorpicker_hsb_s.png ADDED
Binary file
img/colorpicker_indic.gif ADDED
Binary file
img/colorpicker_overlay.png ADDED
Binary file
img/colorpicker_rgb_b.png ADDED
Binary file
img/colorpicker_rgb_g.png ADDED
Binary file
img/colorpicker_rgb_r.png ADDED
Binary file
img/colorpicker_select.gif ADDED
Binary file
img/colorpicker_submit.png ADDED
Binary file
img/custom_background.png ADDED
Binary file
img/custom_hex.png ADDED
Binary file
img/custom_hsb_b.png ADDED
Binary file
img/custom_hsb_h.png ADDED
Binary file
img/custom_hsb_s.png ADDED
Binary file
img/custom_indic.gif ADDED
Binary file
img/custom_rgb_b.png ADDED
Binary file
img/custom_rgb_g.png ADDED
Binary file
img/custom_rgb_r.png ADDED
Binary file
img/custom_submit.png ADDED
Binary file
img/google-calendar.png ADDED
Binary file
img/ics-icon.png ADDED
Binary file
img/indicator.gif ADDED
Binary file
img/month-view.png ADDED
Binary file
img/slider.png ADDED
Binary file
js/add_new_event.js ADDED
@@ -0,0 +1 @@
 
1
+ jQuery(function(f){if(f("#ai1ec_event").length){element_selector(".ai1ec_week_days_list > li","ai1ec_selected","ai1ec_weekday_value","#ai1ec_weekly_days");element_selector(".ai1ec_month_days_list > li","ai1ec_selected","ai1ec_monthday_value","#ai1ec_monthly_days");element_selector(".ai1ec_yearly_months_list > li","ai1ec_selected","ai1ec_yearmonth_value","#ai1ec_yearly_months");var a=new Date(ai1ec_add_new_event.now*1000);var g={allday:"#ai1ec_all_day_event",start_date_input:"#ai1ec_start-date-input",start_time_input:"#ai1ec_start-time-input",start_time:"#ai1ec_start-time",end_date_input:"#ai1ec_end-date-input",end_time_input:"#ai1ec_end-time-input",end_time:"#ai1ec_end-time",now:a};f.timespan(g);g={start_date_input:"#ai1ec_until-date-input",start_time:"#ai1ec_until-time",now:a};f.inputdate(g);f("#ai1ec_count").rangeinput({css:{input:"ai1ec-range",slider:"ai1ec-slider",progress:"ai1ec-progress",handle:"ai1ec-handle"}});var k=new google.maps.Geocoder();var j=new google.maps.LatLng(9.965,-83.327);var h={zoom:0,mapTypeId:google.maps.MapTypeId.ROADMAP,center:j};var e=new google.maps.Map(f("#ai1ec_map_canvas").get(0),h);var d=new google.maps.Marker({map:e});function l(w){e.setCenter(w.geometry.location);e.setZoom(15);d.setPosition(w.geometry.location);f("#ai1ec_address").val(w.formatted_address);var v="";var u="";var q="";var o="";var t=0;var p=0;for(var r=0;r<w.address_components.length;r++){switch(w.address_components[r].types[0]){case"street_number":v=w.address_components[r].long_name;break;case"route":u=w.address_components[r].long_name;break;case"locality":q=w.address_components[r].long_name;break;case"administrative_area_level_1":province=w.address_components[r].long_name;break;case"postal_code":t=w.address_components[r].long_name;break;case"country":p=w.address_components[r].long_name;break}}var s=v.length>0?v+" ":"";s+=u.length>0?u:"";t=t!=0?t:"";f("#ai1ec_city").val(q);f("#ai1ec_province").val(province);f("#ai1ec_postal_code").val(t);f("#ai1ec_country").val(p)}f("#ai1ec_address").geo_autocomplete(new google.maps.Geocoder,{selectFirst:false,minChars:3,cacheLength:50,width:300,scroll:true,scrollHeight:330}).result(function(o,p){if(p){l(p)}}).change(function(){if(f(this).val().length>0){var o=f(this).val();k.geocode({address:o},function(q,p){if(p==google.maps.GeocoderStatus.OK){l(q[0])}})}}).change();f("#ai1ec_google_map").click(function(){if(f(this).is(":checked")){f(".ai1ec_box_map").addClass("ai1ec_box_map_visible").hide().slideDown("fast")}else{f(".ai1ec_box_map").slideUp("fast")}});f("#ai1ec_repeat").change(function(){var o=f("#ai1ec_repeat option:selected").val();switch(o){case" ":i();break;default:c();break}});f("#ai1ec_end").change(m);function c(){f("#ai1ec_end_holder").fadeIn();m()}function i(){b();f("#ai1ec_end_holder").fadeOut()}function m(){var o=f("#ai1ec_end option:selected").val();switch(o){case"0":b();break;case"1":if(f("#ai1ec_count_holder").css("display")=="none"){b();f("#ai1ec_count_holder").fadeIn()}break;case"2":if(f("#ai1ec_until_holder").css("display")=="none"){b();f("#ai1ec_until_holder").fadeIn()}break}}function b(){f("#ai1ec_count_holder, #ai1ec_until_holder").hide()}if(f("#ai1ec_bottom_publish").length>0){f("#ai1ec_bottom_publish").click(function(){f("#publish").trigger("click")})}}if(f("#ai1ec_add_new_ics").length){f("#ai1ec_add_new_ics").click(function(){var q=f(this);var o=f("#ai1ec_feed_url");if(!n(o.val())){o.css("border-color","#FF0000").focus()}else{q.attr("disabled",true);o.css("border-color","#DFDFDF");var p={action:"ai1ec_add_ics",feed_url:o.val(),feed_category:f("#ai1ec_feed_category option:selected").val(),feed_tags:f("#ai1ec_feed_tags").val()};f.getJSON(ajaxurl,p,function(r){q.removeAttr("disabled");if(r.error){alert(r.message)}else{o.val("");f("#ai1ec-feeds-after").after(r.message)}})}});f(".ai1ec_delete_ics").live("click",function(){var r=f(this);r.attr("disabled",true);var q=r.closest(".ai1ec-feed-container");var p=r.siblings(".ai1ec_feed_id").val();var o={action:"ai1ec_delete_ics",ics_id:p};f.getJSON(ajaxurl,o,function(s){r.removeAttr("disabled");if(s.error){alert(s.message)}else{q.remove()}})});f(".ai1ec_flush_ics").live("click",function(){var q=f(this);q.attr("disabled",true);var p=q.siblings(".ai1ec_feed_id").val();q.siblings(".ajax-loading").css("visibility","visible");var o={action:"ai1ec_flush_ics",ics_id:p};f.getJSON(ajaxurl,o,function(r){if(r.error){alert(r.message)}else{q.fadeOut()}q.siblings(".ajax-loading").css("visibility","hidden")})});f(".ai1ec_update_ics").live("click",function(){var q=f(this);q.attr("disabled",true);var p=q.siblings(".ai1ec_feed_id").val();q.siblings(".ajax-loading").css("visibility","visible");var o={action:"ai1ec_update_ics",ics_id:p};f.getJSON(ajaxurl,o,function(r){if(r.error){alert(r.message)}else{q.siblings(".ai1ec_flush_ics").remove();if(r.count){q.after('<input type="button" class="button ai1ec_flush_ics" value="'+r.flush_label+'" />')}}q.attr("disabled",false).siblings(".ajax-loading").css("visibility","hidden")})});function n(o){var p=/(http|https|webcal):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;return p.test(o)}}});
js/calendar.js ADDED
@@ -0,0 +1 @@
 
1
+ jQuery(document).ready(function(i){if(ai1ec_calendar.selector!=undefined&&ai1ec_calendar.selector!=""&&i(ai1ec_calendar.selector).length==1){var n=i(":header:contains("+ai1ec_calendar.title+"):first");if(!n.length){n=i('<h1 class="page-title"></h1>');n.text(ai1ec_calendar.title)}var c=i("#ai1ec-container").detach().before(n);i(ai1ec_calendar.selector).empty().append(c).hide().css("visibility","visible").fadeIn("fast")}var h="";var l;var f=i("body").attr("class");if(f==undefined){f=""}if(f.match(/\s?\bai1ec-[\w-]+\b/)==null){f+=" "+ai1ec_calendar.body_class;i("body").attr("class",f)}function d(){var q=document.location.hash;var p=convertEntities(ai1ec_calendar.default_hash);if(h!=q&&(q!=""||h!=p)){var r=q;if(!r){r=p}j(r)}}setInterval(d,300);function j(p){i("#ai1ec-calendar-view-loading").fadeTo("fast",0.7,function(){var q=p.substring(1);i.getJSON(ai1ec_calendar.ajaxurl,q,function(t){var s=i("body").attr("class");s=s.replace(/\s?\bai1ec-[\w-]+\b/g,"");s+=" "+t.body_class;i("body").attr("class",s);var u=i("#ai1ec-calendar-view-container");u.height(u.height());var r=i("#ai1ec-calendar-view").html(t.html).height();u.animate({height:r},{complete:function(){u.height("auto")}});i("#ai1ec-calendar-view-loading").fadeOut("fast");o()})});h=p}i("a.ai1ec-load-view").live("click",function(){j(i(this).attr("href"))});function b(){var r=i(this).prev();if(!r.data("ai1ec_offset")){r.css("visibility","hidden").show();var q=i("#ai1ec-calendar-view-container");var u=r.width();var t=r.offset();var s=q.offset();var p=s.left+q.width();if(t.left+u>p){r.offset({left:p-u,top:t.top})}if(i(".ai1ec-event-summary",r).offset().left<s.left){r.addClass("ai1ec-shifted-right")}r.hide().css("visibility","visible");r.data("ai1ec_offset",true)}r.fadeIn(100,function(){if(!i(this).data("ai1ec_mouseinside")){i(this).each(e)}})}function e(){i(this).fadeOut(100,function(){i(this).parent().css({zIndex:"auto"})}).data("ai1ec_mouseinside",false)}i(".ai1ec-month-view .ai1ec-event").live("mouseenter",b);i(".ai1ec-month-view .ai1ec-event-popup").live("mouseleave",e).live("mousemove",function(){i(this).data("ai1ec_mouseinside",true)});if(i(".ai1ec-month-view").length){i(window).blur(function(){i(".ai1ec-event-popup:visible").each(e)})}function m(){i(this).hide().parent().addClass("ai1ec-expanded").end().prev().show().find(".ai1ec-event-description").hide().slideDown("fast")}function g(){i(this).next().slideUp("fast",function(){i(this).parent().parent().removeClass("ai1ec-expanded").end().hide().next().show()})}i(".ai1ec-agenda-view .ai1ec-event > .ai1ec-event-click").live("click",m);i(".ai1ec-agenda-view .ai1ec-event-summary > .ai1ec-event-click").live("click",g);i(".ai1ec-action-agenda #ai1ec-expand-all").live("click",function(){i(".ai1ec-event > .ai1ec-event-click:visible").click()});i(".ai1ec-action-agenda #ai1ec-collapse-all").live("click",function(){i(".ai1ec-event-summary > .ai1ec-event-click:visible").click()});element_selector(".ai1ec-category-filter-selector li","ai1ec-selected","ai1ec-categories","#ai1ec-selected-categories");element_selector(".ai1ec-tag-filter-selector li","ai1ec-selected","ai1ec-tags","#ai1ec-selected-tags");i(".ai1ec-dropdown").click(function(){i(".ai1ec-filter-selector").css("zIndex",10);var p=i(this).siblings(".ai1ec-filter-selector").css("zIndex",11);if(i(this).hasClass("ai1ec-active")){i(this).removeClass("ai1ec-active");p.slideUp(150)}else{i(this).addClass("ai1ec-active");p.slideDown(150)}});function k(){var s=new Array();selected_cats=i(".ai1ec-filters-container .ai1ec-dropdown.ai1ec-selected + #ai1ec-selected-categories").val();if(selected_cats){s.push(selected_cats);selected_cats="&ai1ec_cat_ids="+selected_cats}else{selected_cats=""}selected_tags=i(".ai1ec-filters-container .ai1ec-dropdown.ai1ec-selected + #ai1ec-selected-tags").val();if(selected_tags){s.push(selected_tags);selected_tags="&ai1ec_tag_ids="+selected_tags}else{selected_tags=""}s=s.join();var p;if(s.length){p=convertEntities(ai1ec_calendar.export_url)+selected_cats+selected_tags;i(".ai1ec-subscribe-filtered").fadeIn("fast")}else{p=convertEntities(ai1ec_calendar.export_url);i(".ai1ec-subscribe-filtered").fadeOut("fast")}i(".ai1ec-subscribe").attr("href",p);i(".ai1ec-subscribe-google").attr("href","http://www.google.com/calendar/render?cid="+escape(p.replace("webcal://","http://")));var r={action:"ai1ec_term_filter",ai1ec_post_ids:l,ai1ec_term_ids:s};var q=i("#ai1ec-calendar-view-loading").delay(500).fadeTo("fast",0.7);i.getJSON(ai1ec_calendar.ajaxurl,r,function(u){q.clearQueue().fadeOut("fast");var t=new Array();i.each(u.matching_ids,function(v,w){t.push(".ai1ec-event-id-"+w)});i(t.join()).fadeIn("fast");t=new Array();i.each(u.unmatching_ids,function(v,w){t.push(".ai1ec-event-id-"+w)});i(t.join()).fadeOut("fast")})}function a(){i(".ai1ec-filter-selector-container").each(function(){if(i("li.ai1ec-selected",this).length){i(".ai1ec-dropdown",this).addClass("ai1ec-selected")}else{i(".ai1ec-dropdown",this).removeClass("ai1ec-selected")}});if(i(".ai1ec-filters-container .ai1ec-selected").length){i(".ai1ec-clear-filters").fadeIn("fast")}else{i(".ai1ec-clear-filters").fadeOut("fast")}k()}i(".ai1ec-filter-selector li").click(a);i(".ai1ec-clear-filters").click(function(){i(".ai1ec-filter-selector-container li").removeClass("ai1ec-selected");a()});function o(){l=new Array();i(".ai1ec-post-id").each(function(){l.push(this.value)});l=l.join();if(i(".ai1ec-active-event:first").length){i(".ai1ec-month-view .ai1ec-active-event:first").each(function(){i(this).each(b).prev().data("ai1ec_mouseinside",true)});i(".ai1ec-agenda-view .ai1ec-active-event:first > .ai1ec-event-click").each(m);i.scrollTo(".ai1ec-active-event:first",1000,{offset:{left:0,top:-window.innerHeight/2+100}})}if(i(".ai1ec-dropdown.ai1ec-selected").length){i(".ai1ec-month-view .ai1ec-event-container, .ai1ec-agenda-view .ai1ec-event").hide();k()}}if(i("#ai1ec-selected-categories").val()!=""||i("#ai1ec-selected-tags").val()!=""){a()}o()});
js/colorpicker.js ADDED
@@ -0,0 +1 @@
 
1
+ (function(b){var a=function(){var S={},c,N=65,t,P='<div class="colorpicker"><div class="colorpicker_color"><div><div></div></div></div><div class="colorpicker_hue"><div></div></div><div class="colorpicker_new_color"></div><div class="colorpicker_current_color"></div><div class="colorpicker_hex"><input type="text" maxlength="6" size="6" /></div><div class="colorpicker_rgb_r colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_rgb_g colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_rgb_b colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_hsb_h colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_hsb_s colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_hsb_b colorpicker_field"><input type="text" maxlength="3" size="3" /><span></span></div><div class="colorpicker_submit"></div></div>',B={eventName:"click",onShow:function(){},onBeforeShow:function(){},onHide:function(){},onChange:function(){},onSubmit:function(){},color:"ff0000",livePreview:true,flat:false},J=function(T,V){var U=j(T);b(V).data("colorpicker").fields.eq(1).val(U.r).end().eq(2).val(U.g).end().eq(3).val(U.b).end()},u=function(T,U){b(U).data("colorpicker").fields.eq(4).val(T.h).end().eq(5).val(T.s).end().eq(6).val(T.b).end()},g=function(T,U){b(U).data("colorpicker").fields.eq(0).val(R(T)).end()},l=function(T,U){b(U).data("colorpicker").selector.css("backgroundColor","#"+R({h:T.h,s:100,b:100}));b(U).data("colorpicker").selectorIndic.css({left:parseInt(150*T.s/100,10),top:parseInt(150*(100-T.b)/100,10)})},G=function(T,U){b(U).data("colorpicker").hue.css("top",parseInt(150-150*T.h/360,10))},h=function(T,U){b(U).data("colorpicker").currentColor.css("backgroundColor","#"+R(T))},E=function(T,U){b(U).data("colorpicker").newColor.css("backgroundColor","#"+R(T))},n=function(T){var V=T.charCode||T.keyCode||-1;if((V>N&&V<=90)||V==32){return false}var U=b(this).parent().parent();if(U.data("colorpicker").livePreview===true){e.apply(this)}},e=function(U){var V=b(this).parent().parent(),T;if(this.parentNode.className.indexOf("_hex")>0){V.data("colorpicker").color=T=m(y(this.value))}else{if(this.parentNode.className.indexOf("_hsb")>0){V.data("colorpicker").color=T=f({h:parseInt(V.data("colorpicker").fields.eq(4).val(),10),s:parseInt(V.data("colorpicker").fields.eq(5).val(),10),b:parseInt(V.data("colorpicker").fields.eq(6).val(),10)})}else{V.data("colorpicker").color=T=i(M({r:parseInt(V.data("colorpicker").fields.eq(1).val(),10),g:parseInt(V.data("colorpicker").fields.eq(2).val(),10),b:parseInt(V.data("colorpicker").fields.eq(3).val(),10)}))}}if(U){J(T,V.get(0));g(T,V.get(0));u(T,V.get(0))}l(T,V.get(0));G(T,V.get(0));E(T,V.get(0));V.data("colorpicker").onChange.apply(V,[T,R(T),j(T)])},o=function(T){var U=b(this).parent().parent();U.data("colorpicker").fields.parent().removeClass("colorpicker_focus")},K=function(){N=this.parentNode.className.indexOf("_hex")>0?70:65;b(this).parent().parent().data("colorpicker").fields.parent().removeClass("colorpicker_focus");b(this).parent().addClass("colorpicker_focus")},I=function(T){var V=b(this).parent().find("input").focus();var U={el:b(this).parent().addClass("colorpicker_slider"),max:this.parentNode.className.indexOf("_hsb_h")>0?360:(this.parentNode.className.indexOf("_hsb")>0?100:255),y:T.pageY,field:V,val:parseInt(V.val(),10),preview:b(this).parent().parent().data("colorpicker").livePreview};b(document).bind("mouseup",U,s);b(document).bind("mousemove",U,L)},L=function(T){T.data.field.val(Math.max(0,Math.min(T.data.max,parseInt(T.data.val+T.pageY-T.data.y,10))));if(T.data.preview){e.apply(T.data.field.get(0),[true])}return false},s=function(T){e.apply(T.data.field.get(0),[true]);T.data.el.removeClass("colorpicker_slider").find("input").focus();b(document).unbind("mouseup",s);b(document).unbind("mousemove",L);return false},w=function(T){var U={cal:b(this).parent(),y:b(this).offset().top};U.preview=U.cal.data("colorpicker").livePreview;b(document).bind("mouseup",U,r);b(document).bind("mousemove",U,k)},k=function(T){e.apply(T.data.cal.data("colorpicker").fields.eq(4).val(parseInt(360*(150-Math.max(0,Math.min(150,(T.pageY-T.data.y))))/150,10)).get(0),[T.data.preview]);return false},r=function(T){J(T.data.cal.data("colorpicker").color,T.data.cal.get(0));g(T.data.cal.data("colorpicker").color,T.data.cal.get(0));b(document).unbind("mouseup",r);b(document).unbind("mousemove",k);return false},x=function(T){var U={cal:b(this).parent(),pos:b(this).offset()};U.preview=U.cal.data("colorpicker").livePreview;b(document).bind("mouseup",U,A);b(document).bind("mousemove",U,q)},q=function(T){e.apply(T.data.cal.data("colorpicker").fields.eq(6).val(parseInt(100*(150-Math.max(0,Math.min(150,(T.pageY-T.data.pos.top))))/150,10)).end().eq(5).val(parseInt(100*(Math.max(0,Math.min(150,(T.pageX-T.data.pos.left))))/150,10)).get(0),[T.data.preview]);return false},A=function(T){J(T.data.cal.data("colorpicker").color,T.data.cal.get(0));g(T.data.cal.data("colorpicker").color,T.data.cal.get(0));b(document).unbind("mouseup",A);b(document).unbind("mousemove",q);return false},v=function(T){b(this).addClass("colorpicker_focus")},Q=function(T){b(this).removeClass("colorpicker_focus")},p=function(U){var V=b(this).parent();var T=V.data("colorpicker").color;V.data("colorpicker").origColor=T;h(T,V.get(0));V.data("colorpicker").onSubmit(T,R(T),j(T),V.data("colorpicker").el)},D=function(U){var Y=b("#"+b(this).data("colorpickerId"));Y.data("colorpicker").onBeforeShow.apply(this,[Y.get(0)]);var Z=b(this).offset();var X=z();var T=b("#tag-color").offset();var W=(T.top)+b("#tag-color").height();var V=(T.left+1);Y.css({left:V+"px",top:W+"px"});if(Y.data("colorpicker").onShow.apply(this,[Y.get(0)])!=false){Y.show()}b(document).bind("mousedown",{cal:Y},O);return false},O=function(T){if(!H(T.data.cal.get(0),T.target,T.data.cal.get(0))){if(T.data.cal.data("colorpicker").onHide.apply(this,[T.data.cal.get(0)])!=false){T.data.cal.hide()}b(document).unbind("mousedown",O)}},H=function(V,U,T){if(V==U){return true}if(V.contains){return V.contains(U)}if(V.compareDocumentPosition){return !!(V.compareDocumentPosition(U)&16)}var W=U.parentNode;while(W&&W!=T){if(W==V){return true}W=W.parentNode}return false},z=function(){var T=document.compatMode=="CSS1Compat";return{l:window.pageXOffset||(T?document.documentElement.scrollLeft:document.body.scrollLeft),t:window.pageYOffset||(T?document.documentElement.scrollTop:document.body.scrollTop),w:window.innerWidth||(T?document.documentElement.clientWidth:document.body.clientWidth),h:window.innerHeight||(T?document.documentElement.clientHeight:document.body.clientHeight)}},f=function(T){return{h:Math.min(360,Math.max(0,T.h)),s:Math.min(100,Math.max(0,T.s)),b:Math.min(100,Math.max(0,T.b))}},M=function(T){return{r:Math.min(255,Math.max(0,T.r)),g:Math.min(255,Math.max(0,T.g)),b:Math.min(255,Math.max(0,T.b))}},y=function(V){var T=6-V.length;if(T>0){var W=[];for(var U=0;U<T;U++){W.push("0")}W.push(V);V=W.join("")}return V},d=function(T){var T=parseInt(((T.indexOf("#")>-1)?T.substring(1):T),16);return{r:T>>16,g:(T&65280)>>8,b:(T&255)}},m=function(T){return i(d(T))},i=function(V){var U={h:0,s:0,b:0};var W=Math.min(V.r,V.g,V.b);var T=Math.max(V.r,V.g,V.b);var X=T-W;U.b=T;if(T!=0){}U.s=T!=0?255*X/T:0;if(U.s!=0){if(V.r==T){U.h=(V.g-V.b)/X}else{if(V.g==T){U.h=2+(V.b-V.r)/X}else{U.h=4+(V.r-V.g)/X}}}else{U.h=-1}U.h*=60;if(U.h<0){U.h+=360}U.s*=100/255;U.b*=100/255;return U},j=function(T){var V={};var Z=Math.round(T.h);var Y=Math.round(T.s*255/100);var U=Math.round(T.b*255/100);if(Y==0){V.r=V.g=V.b=U}else{var aa=U;var X=(255-Y)*U/255;var W=(aa-X)*(Z%60)/60;if(Z==360){Z=0}if(Z<60){V.r=aa;V.b=X;V.g=X+W}else{if(Z<120){V.g=aa;V.b=X;V.r=aa-W}else{if(Z<180){V.g=aa;V.r=X;V.b=X+W}else{if(Z<240){V.b=aa;V.r=X;V.g=aa-W}else{if(Z<300){V.b=aa;V.g=X;V.r=X+W}else{if(Z<360){V.r=aa;V.g=X;V.b=aa-W}else{V.r=0;V.g=0;V.b=0}}}}}}}return{r:Math.round(V.r),g:Math.round(V.g),b:Math.round(V.b)}},C=function(T){var U=[T.r.toString(16),T.g.toString(16),T.b.toString(16)];b.each(U,function(V,W){if(W.length==1){U[V]="0"+W}});return U.join("")},R=function(T){return C(j(T))},F=function(){var U=b(this).parent();var T=U.data("colorpicker").origColor;U.data("colorpicker").color=T;J(T,U.get(0));g(T,U.get(0));u(T,U.get(0));l(T,U.get(0));G(T,U.get(0));E(T,U.get(0))};return{init:function(T){T=b.extend({},B,T||{});if(typeof T.color=="string"){T.color=m(T.color)}else{if(T.color.r!=undefined&&T.color.g!=undefined&&T.color.b!=undefined){T.color=i(T.color)}else{if(T.color.h!=undefined&&T.color.s!=undefined&&T.color.b!=undefined){T.color=f(T.color)}else{return this}}}return this.each(function(){if(!b(this).data("colorpickerId")){var U=b.extend({},T);U.origColor=T.color;var W="collorpicker_"+parseInt(Math.random()*1000);b(this).data("colorpickerId",W);var V=b(P).attr("id",W);if(U.flat){V.appendTo(this).show()}else{V.appendTo(document.body)}U.fields=V.find("input").bind("keyup",n).bind("change",e).bind("blur",o).bind("focus",K);V.find("span").bind("mousedown",I).end().find(">div.colorpicker_current_color").bind("click",F);U.selector=V.find("div.colorpicker_color").bind("mousedown",x);U.selectorIndic=U.selector.find("div div");U.el=this;U.hue=V.find("div.colorpicker_hue div");V.find("div.colorpicker_hue").bind("mousedown",w);U.newColor=V.find("div.colorpicker_new_color");U.currentColor=V.find("div.colorpicker_current_color");V.data("colorpicker",U);V.find("div.colorpicker_submit").bind("mouseenter",v).bind("mouseleave",Q).bind("click",p);J(U.color,V.get(0));u(U.color,V.get(0));g(U.color,V.get(0));G(U.color,V.get(0));l(U.color,V.get(0));h(U.color,V.get(0));E(U.color,V.get(0));if(U.flat){V.css({position:"relative",display:"block"})}else{b(this).bind(U.eventName,D)}}})},showPicker:function(){return this.each(function(){if(b(this).data("colorpickerId")){D.apply(this)}})},hidePicker:function(){return this.each(function(){if(b(this).data("colorpickerId")){b("#"+b(this).data("colorpickerId")).hide()}})},setColor:function(T){if(typeof T=="string"){T=m(T)}else{if(T.r!=undefined&&T.g!=undefined&&T.b!=undefined){T=i(T)}else{if(T.h!=undefined&&T.s!=undefined&&T.b!=undefined){T=f(T)}else{return this}}}return this.each(function(){if(b(this).data("colorpickerId")){var U=b("#"+b(this).data("colorpickerId"));U.data("colorpicker").color=T;U.data("colorpicker").origColor=T;J(T,U.get(0));u(T,U.get(0));g(T,U.get(0));G(T,U.get(0));l(T,U.get(0));h(T,U.get(0));E(T,U.get(0))}})}}}();b.fn.extend({ColorPicker:a.init,ColorPickerHide:a.hidePicker,ColorPickerShow:a.showPicker,ColorPickerSetColor:a.setColor})})(jQuery);jQuery(function(e){e("#tag-color").click(function(){var g=e("#tag-color").offset();var k=g.top+e("#tag-color").height();var j=g.left+1;var i=e("<ul></ul>");var h=e('<li style="color: #60a;" class="color-1"></li><li style="color: #807;" class="color-2"></li><li style="color: #920;" class="color-3"></li><li style="color: #a60;" class="color-4"></li><li style="color: #990;" class="color-5"></li><li style="color: #080;" class="color-6"></li><li style="color: #077;" class="color-7"></li><li style="color: #00a;" class="color-8"></li><li style="color: #000;" class="color-9"></li><li style="color: #444;" class="color-10"></li><li style="color: #85e;" class="color-11"></li><li style="color: #d5d;" class="color-12"></li><li style="color: #d43;" class="color-13"></li><li style="color: #d90;" class="color-14"></li><li style="color: #bb0;" class="color-15"></li><li style="color: #2b0;" class="color-16"></li><li style="color: #0ba;" class="color-17"></li><li style="color: #26d;" class="color-18"></li><li style="color: #777;" class="color-19"></li><li style="color: #aaa;" class="color-20"></li>');var f=e('<li class="select-more-colors">More colors</li>');e(f).ColorPicker({onSubmit:function(l,o,m,n){e("#tag-color-background").css("background-color","#"+o);e("#tag-color-value").val("#"+o);e(n).ColorPickerHide();i.remove()},onBeforeShow:function(){i.hide();e(document).unbind("mousedown",a);var l=e("#tag-color-value").val();l=l.length>0?l:"#ffffff";e(this).ColorPickerSetColor(l)}});h.click(function(){e("#tag-color-background").css("background-color",e(this).css("color"));e("#tag-color-value").val(c(e(this).css("color")));i.remove()});i.append(h).append(f);i.appendTo("body").css({position:"absolute",top:k+"px",left:j+"px",width:"75px",height:"85px","z-index":1,background:"#fff",border:"1px solid #ccc"}).addClass("colorpicker-list");e(document).bind("mousedown",{ls:i},a)});var c=function(f){f=f.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);return"#"+d(f[1])+d(f[2])+d(f[3])};var d=function(f){return("0"+parseInt(f).toString(16)).slice(-2)};var a=function(f){if(!b(f.data.ls.get(0),f.target,f.data.ls.get(0))){e(f.data.ls.get(0)).remove();e(document).unbind("mousedown",a)}};var b=function(h,g,f){if(h==g){return true}if(h.contains){return h.contains(g)}if(h.compareDocumentPosition){return !!(h.compareDocumentPosition(g)&16)}var i=g.parentNode;while(i&&i!=f){if(i==h){return true}i=i.parentNode}return false}});
js/element-selector.js ADDED
@@ -0,0 +1 @@
 
1
+ function element_selector(a,c,g,b){var f=jQuery;f(a).click(function(){var h=new Array();if(f(this).hasClass(c)){f(this).removeClass(c)}else{f(this).addClass(c)}f(a+"."+c).each(function(){var i=f(this).find('input[name="'+g+'"]:first').val();h.push(i)});f(b).val(h.join())});var d=f(b).val();if(d!=undefined&&d!=""){var e=d.split(",");f(e).each(function(h,j){e[h]='input[name="'+g+'"][value="'+j+'"]'});e=e.join();f(a).has(e).addClass(c)}};
js/event.js ADDED
@@ -0,0 +1 @@
 
1
+ function ai1ec_load_map(){var b={zoom:14,mapTypeId:google.maps.MapTypeId.ROADMAP};var d=new google.maps.Map(document.getElementById("ai1ec-gmap-canvas"),b);var a=new google.maps.Marker({map:d});var c=new google.maps.Geocoder();c.geocode({address:document.getElementById("ai1ec-gmap-address").value,},function(f,e){if(e==google.maps.GeocoderStatus.OK){d.setCenter(f[0].geometry.location);a.setPosition(f[0].geometry.location)}})}var orig_onload=window.onload;window.onload=function(){if(typeof(orig_onload)=="function"){orig_onload()}if(document.getElementById("ai1ec-gmap-canvas")){var a=document.createElement("script");a.type="text/javascript";a.src="http://maps.google.com/maps/api/js?sensor=false&callback=ai1ec_load_map";document.body.appendChild(a)}};
js/geo_autocomplete.js ADDED
@@ -0,0 +1 @@
 
1
+ (function(a){a.fn.extend({geo_autocomplete:function(c,b){options=a.extend({},a.Autocompleter.defaults,{geocoder:c,mapwidth:100,mapheight:100,maptype:"terrain",mapkey:"ABQIAAAAbnvDoAoYOSW2iqoXiGTpYBT2yXp_ZAY8_ufC3CFXhHIE1NvwkxQNumU68AwGqjbSNF9YO8NokKst8w",mapsensor:false,parse:function(d,f,e){var g=[];if(d&&f&&f=="OK"){a.each(d,function(j,i){if(i.geometry&&i.geometry.viewport){var h=i.formatted_address.split(",");var k=h[0];a.each(h,function(m,l){if(l.toLowerCase().indexOf(e.toLowerCase())!=-1){k=a.trim(l);return false}});g.push({data:i,value:k,result:k})}})}return g},formatItem:function(f,h,e,d){var i="http://maps.google.com/maps/api/staticmap?visible="+f.geometry.viewport.getSouthWest().toUrlValue()+"|"+f.geometry.viewport.getNorthEast().toUrlValue()+"&size="+options.mapwidth+"x"+options.mapheight+"&maptype="+options.maptype+"&key="+options.mapkey+"&sensor="+(options.mapsensor?"true":"false");var g=f.formatted_address.replace(/,/gi,",<br/>");return'<img src="'+i+'" width="'+options.mapwidth+'" height="'+options.mapheight+'" /> '+g+'<br clear="both"/>'}},b);options.highlight=options.highlight||function(d){return d};options.formatMatch=options.formatMatch||options.formatItem;return this.each(function(){new a.Autocompleter(this,options)})}})})(jQuery);
js/jquery.autocomplete_geomod.js ADDED
@@ -0,0 +1 @@
 
1
+ (function(a){a.fn.extend({autocomplete:function(b,c){var d=typeof b=="string";c=a.extend({},a.Autocompleter.defaults,{url:d?b:null,data:d?null:b,delay:d?a.Autocompleter.defaults.delay:10,max:c&&!c.scroll?10:150},c);c.highlight=c.highlight||function(e){return e};c.formatMatch=c.formatMatch||c.formatItem;return this.each(function(){new a.Autocompleter(this,c)})},result:function(b){return this.bind("result",b)},search:function(b){return this.trigger("search",[b])},flushCache:function(){return this.trigger("flushCache")},setOptions:function(b){return this.trigger("setOptions",[b])},unautocomplete:function(){return this.trigger("unautocomplete")}});a.Autocompleter=function(l,g){var c={UP:38,DOWN:40,DEL:46,TAB:9,RETURN:13,ESC:27,COMMA:188,PAGEUP:33,PAGEDOWN:34,BACKSPACE:8};var b=a(l).attr("autocomplete","off").addClass(g.inputClass);var j;var p="";var m=a.Autocompleter.Cache(g);var e=0;var u;var x={mouseDownOnSelect:false};var r=a.Autocompleter.Select(g,l,d,x);var w;a.browser.opera&&a(l.form).bind("submit.autocomplete",function(){if(w){w=false;return false}});b.bind((a.browser.opera?"keypress":"keydown")+".autocomplete",function(y){e=1;u=y.keyCode;switch(y.keyCode){case c.UP:y.preventDefault();if(r.visible()){r.prev()}else{t(0,true)}break;case c.DOWN:y.preventDefault();if(r.visible()){r.next()}else{t(0,true)}break;case c.PAGEUP:y.preventDefault();if(r.visible()){r.pageUp()}else{t(0,true)}break;case c.PAGEDOWN:y.preventDefault();if(r.visible()){r.pageDown()}else{t(0,true)}break;case g.multiple&&a.trim(g.multipleSeparator)==","&&c.COMMA:case c.TAB:case c.RETURN:if(d()){y.preventDefault();w=true;return false}break;case c.ESC:r.hide();break;default:clearTimeout(j);j=setTimeout(t,g.delay);break}}).focus(function(){e++}).blur(function(){e=0;if(!x.mouseDownOnSelect){s()}}).click(function(){if(e++>1&&!r.visible()){t(0,true)}}).bind("search",function(){var y=(arguments.length>1)?arguments[1]:null;function z(D,C){var A;if(C&&C.length){for(var B=0;B<C.length;B++){if(C[B].result.toLowerCase()==D.toLowerCase()){A=C[B];break}}}if(typeof y=="function"){y(A)}else{b.trigger("result",A&&[A.data,A.value])}}a.each(h(b.val()),function(A,B){f(B,z,z)})}).bind("flushCache",function(){m.flush()}).bind("setOptions",function(){a.extend(g,arguments[1]);if("data" in arguments[1]){m.populate()}}).bind("unautocomplete",function(){r.unbind();b.unbind();a(l.form).unbind(".autocomplete")});function d(){var B=r.selected();if(!B){return false}var y=B.result;p=y;if(g.multiple){var E=h(b.val());if(E.length>1){var A=g.multipleSeparator.length;var D=a(l).selection().start;var C,z=0;a.each(E,function(F,G){z+=G.length;if(D<=z){C=F;return false}z+=A});E[C]=y;y=E.join(g.multipleSeparator)}y+=g.multipleSeparator}b.val(y);v();b.trigger("result",[B.data,B.value]);return true}function t(A,z){if(u==c.DEL){r.hide();return}var y=b.val();if(!z&&y==p){return}p=y;y=i(y);if(y.length>=g.minChars){b.addClass(g.loadingClass);if(!g.matchCase){y=y.toLowerCase()}f(y,k,v)}else{n();r.hide()}}function h(y){if(!y){return[""]}if(!g.multiple){return[a.trim(y)]}return a.map(y.split(g.multipleSeparator),function(z){return a.trim(y).length?a.trim(z):null})}function i(y){if(!g.multiple){return y}var A=h(y);if(A.length==1){return A[0]}var z=a(l).selection().start;if(z==y.length){A=h(y)}else{A=h(y.replace(y.substring(z),""))}return A[A.length-1]}function q(y,z){if(g.autoFill&&(i(b.val()).toLowerCase()==y.toLowerCase())&&u!=c.BACKSPACE){b.val(b.val()+z.substring(i(p).length));a(l).selection(p.length,p.length+z.length)}}function s(){clearTimeout(j);j=setTimeout(v,200)}function v(){var y=r.visible();r.hide();clearTimeout(j);n();if(g.mustMatch){b.search(function(z){if(!z){if(g.multiple){var A=h(b.val()).slice(0,-1);b.val(A.join(g.multipleSeparator)+(A.length?g.multipleSeparator:""))}else{b.val("");b.trigger("result",null)}}})}}function k(z,y){if(y&&y.length&&e){n();r.display(y,z);q(z,y[0].value);r.show()}else{v()}}function f(A,C,z){if(!g.matchCase){A=A.toLowerCase()}var B=m.load(A);if(B&&B.length){C(A,B)}else{if(g.geocoder){var y=i(A);g.geocoder.geocode({address:y},function(E,G){var F=g.parse(E,G,y);m.add(A,F);C(A,F)})}else{if((typeof g.url=="string")&&(g.url.length>0)){var D={timestamp:+new Date()};a.each(g.extraParams,function(E,F){D[E]=typeof F=="function"?F():F});a.ajax({mode:"abort",port:"autocomplete"+l.name,dataType:g.dataType,url:g.url,data:a.extend({q:i(A),limit:g.max},D),success:function(F){var E=g.parse&&g.parse(F)||o(F);m.add(A,E);C(A,E)}})}else{r.emptyList();z(A)}}}}function o(B){var y=[];var A=B.split("\n");for(var z=0;z<A.length;z++){var C=a.trim(A[z]);if(C){C=C.split("|");y[y.length]={data:C,value:C[0],result:g.formatResult&&g.formatResult(C,C[0])||C[0]}}}return y}function n(){b.removeClass(g.loadingClass)}};a.Autocompleter.defaults={inputClass:"ac_input",resultsClass:"ac_results",loadingClass:"ac_loading",minChars:1,delay:400,matchCase:false,matchSubset:true,matchContains:false,cacheLength:10,max:100,mustMatch:false,extraParams:{},selectFirst:true,formatItem:function(b){return b[0]},formatMatch:null,autoFill:false,width:0,multiple:false,multipleSeparator:", ",highlight:function(c,b){return c.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)("+b.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi,"\\$1")+")(?![^<>]*>)(?![^&;]+;)","gi"),"<strong>$1</strong>")},scroll:true,scrollHeight:180};a.Autocompleter.Cache=function(c){var f={};var d=0;function h(l,k){if(!c.matchCase){l=l.toLowerCase()}var j=l.indexOf(k);if(c.matchContains=="word"){j=l.toLowerCase().search("\\b"+k.toLowerCase())}if(j==-1){return false}return j==0||c.matchContains}function g(j,i){if(d>c.cacheLength){b()}if(!f[j]){d++}f[j]=i}function e(){if(!c.data){return false}var k={},j=0;if(!c.url){c.cacheLength=1}k[""]=[];for(var m=0,l=c.data.length;m<l;m++){var p=c.data[m];p=(typeof p=="string")?[p]:p;var o=c.formatMatch(p,m+1,c.data.length);if(o===false){continue}var n=o.charAt(0).toLowerCase();if(!k[n]){k[n]=[]}var q={value:o,data:p,result:c.formatResult&&c.formatResult(p)||o};k[n].push(q);if(j++<c.max){k[""].push(q)}}a.each(k,function(r,s){c.cacheLength++;g(r,s)})}setTimeout(e,25);function b(){f={};d=0}return{flush:b,add:g,populate:e,load:function(n){if(!c.cacheLength||!d){return null}if(!c.url&&c.matchContains){var m=[];for(var j in f){if(j.length>0){var o=f[j];a.each(o,function(p,k){if(h(k.value,n)){m.push(k)}})}}return m}else{if(f[n]){return f[n]}else{if(c.matchSubset){for(var l=n.length-1;l>=c.minChars;l--){var o=f[n.substr(0,l)];if(o){var m=[];a.each(o,function(p,k){if(h(k.value,n)){m[m.length]=k}});return m}}}}}return null}}};a.Autocompleter.Select=function(e,j,l,p){var i={ACTIVE:"ac_over"};var k,f=-1,r,m="",s=true,c,o;function n(){if(!s){return}c=a("<div/>").hide().addClass(e.resultsClass).css("position","absolute").appendTo(document.body);o=a("<ul/>").appendTo(c).mouseover(function(t){if(q(t).nodeName&&q(t).nodeName.toUpperCase()=="LI"){f=a("li",o).removeClass(i.ACTIVE).index(q(t));a(q(t)).addClass(i.ACTIVE)}}).click(function(t){a(q(t)).addClass(i.ACTIVE);l();j.focus();return false}).mousedown(function(){p.mouseDownOnSelect=true}).mouseup(function(){p.mouseDownOnSelect=false});if(e.width>0){c.css("width",e.width)}s=false}function q(u){var t=u.target;while(t&&t.tagName!="LI"){t=t.parentNode}if(!t){return[]}return t}function h(t){k.slice(f,f+1).removeClass(i.ACTIVE);g(t);var v=k.slice(f,f+1).addClass(i.ACTIVE);if(e.scroll){var u=0;k.slice(0,f).each(function(){u+=this.offsetHeight});if((u+v[0].offsetHeight-o.scrollTop())>o[0].clientHeight){o.scrollTop(u+v[0].offsetHeight-o.innerHeight())}else{if(u<o.scrollTop()){o.scrollTop(u)}}}}function g(t){f+=t;if(f<0){f=k.size()-1}else{if(f>=k.size()){f=0}}}function b(t){return e.max&&e.max<t?e.max:t}function d(){o.empty();var u=b(r.length);for(var v=0;v<u;v++){if(!r[v]){continue}var w=e.formatItem(r[v].data,v+1,u,r[v].value,m);if(w===false){continue}var t=a("<li/>").html(e.highlight(w,m)).addClass(v%2==0?"ac_even":"ac_odd").appendTo(o)[0];a.data(t,"ac_data",r[v])}k=o.find("li");if(e.selectFirst){k.slice(0,1).addClass(i.ACTIVE);f=0}if(a.fn.bgiframe){o.bgiframe()}}return{display:function(u,t){n();r=u;m=t;d()},next:function(){h(1)},prev:function(){h(-1)},pageUp:function(){if(f!=0&&f-8<0){h(-f)}else{h(-8)}},pageDown:function(){if(f!=k.size()-1&&f+8>k.size()){h(k.size()-1-f)}else{h(8)}},hide:function(){c&&c.hide();k&&k.removeClass(i.ACTIVE);f=-1},visible:function(){return c&&c.is(":visible")},current:function(){return this.visible()&&(k.filter("."+i.ACTIVE)[0]||e.selectFirst&&k[0])},show:function(){var v=a(j).offset();c.css({width:typeof e.width=="string"||e.width>0?e.width:a(j).width(),top:v.top+j.offsetHeight,left:v.left}).show();if(e.scroll){o.scrollTop(0);o.css({maxHeight:e.scrollHeight,overflow:"auto"});if(a.browser.msie&&typeof document.body.style.maxHeight==="undefined"){var t=0;k.each(function(){t+=this.offsetHeight});var u=t>e.scrollHeight;o.css("height",u?e.scrollHeight:t);if(!u){k.width(o.width()-parseInt(k.css("padding-left"))-parseInt(k.css("padding-right")))}}}},selected:function(){var t=k&&k.filter("."+i.ACTIVE).removeClass(i.ACTIVE);return t&&t.length&&a.data(t[0],"ac_data")},emptyList:function(){o&&o.empty()},unbind:function(){c&&c.remove()}}};a.fn.selection=function(i,b){if(i!==undefined){return this.each(function(){if(this.createTextRange){var j=this.createTextRange();if(b===undefined||i==b){j.move("character",i);j.select()}else{j.collapse(true);j.moveStart("character",i);j.moveEnd("character",b);j.select()}}else{if(this.setSelectionRange){this.setSelectionRange(i,b)}else{if(this.selectionStart){this.selectionStart=i;this.selectionEnd=b}}}})}var g=this[0];if(g.createTextRange){var c=document.selection.createRange(),h=g.value,f="<->",d=c.text.length;c.text=f;var e=g.value.indexOf(f);g.value=h;this.selection(e,e+d);return{start:e,end:e+d}}else{if(g.selectionStart!==undefined){return{start:g.selectionStart,end:g.selectionEnd}}}}})(jQuery);
js/jquery.calendrical.js ADDED
@@ -0,0 +1 @@
 
1
+ function formatDate(b,c){return(c?((b.getUTCMonth()+1)+"/"+b.getUTCDate()):(b.getUTCDate()+"/"+(b.getUTCMonth()+1)))+"/"+b.getUTCFullYear()}function formatTime(b,g,e){var d=g;if(g<10){d="0"+g}if(e){var c=b;if(c<10){c="0"+b}return c+":"+d}else{var c=b%12;if(c==0){c=12}var f=(b<12)?"am":"pm";return c+":"+d+f}}function parseDate(c,d){c+=" GMT";if(d){return new Date(c)}a=c.split(/[\.\-\/]/);var b=a.shift();var e=a.shift();a.unshift(b);a.unshift(e);return new Date(a.join("/"))}function parseTime(e){var c=c=/(\d+)\s*[:\-\.,]\s*(\d+)\s*(am|pm)?/i.exec(e);if(c&&c.length>=3){var b=Number(c[1]);var d=Number(c[2]);if(b==12&&c[3]){b-=12}if(c[3]&&c[3].toLowerCase()=="pm"){b+=12}return{hour:b,minute:d}}else{return null}}(function(h){var i=["January","February","March","April","May","June","July","August","September","October","November","December"];function e(){var m=new Date();return new Date(m.getFullYear(),m.getMonth(),m.getDate())}function b(n,m){return String(n)==String(m)}function c(m,n){if(m instanceof Date){return c(m.getUTCFullYear(),m.getUTCMonth())}if(n==1){var o=(m%4==0)&&(!(m%100==0)||(m%400==0));return o?29:28}else{if(n==3||n==5||n==8||n==10){return 30}else{return 31}}}function k(m){return new Date(m.getTime()+(1*24*60*60*1000))}function f(m){return new Date(m.getTime()-(1*24*60*60*1000))}function l(m,n){return(n==11)?new Date(m+1,0,1):new Date(m,n+1,1)}function j(n,o,r,m){var q=h("<thead />");var p=h("<tr />").appendTo(q);h("<th />").addClass("monthCell").append(h('<a href="javascript:;">&laquo;</a>').addClass("prevMonth").mousedown(function(t){g(n,r==0?(o-1):o,r==0?11:(r-1),m);t.preventDefault()})).appendTo(p);h("<th />").addClass("monthCell").attr("colSpan",5).append(h('<a href="javascript:;">'+i[r]+" "+o+"</a>").addClass("monthName")).appendTo(p);h("<th />").addClass("monthCell").append(h('<a href="javascript:;">&raquo;</a>').addClass("nextMonth").mousedown(function(){g(n,r==11?(o+1):o,r==11?0:(r+1),m)})).appendTo(p);var s=h("<tr />").appendTo(q);h.each(String("SMTWTFS").split(""),function(u,t){h("<td />").addClass("dayName").append(t).appendTo(s)});return q}function g(t,x,w,B){B=B||{};var y=B.today?B.today:e();var o=new Date(x,w,1);var u=l(x,w);var m=6-u.getUTCDay();if(m<6){m+=7}for(var s=0;s<m;s++){u=k(u)}var z=h("<table />");j(t,x,w,B).appendTo(z);var r=h("<tbody />").appendTo(z);var A=h("<tr />");var q=o.getUTCDay()+7;for(var s=0;s<q;s++){o=f(o)}while(o<=u){var p=h("<td />").addClass("day").append(h('<a href="javascript:;">'+o.getUTCDate()+"</a>").click((function(){var C=o;return function(){if(B&&B.selectDate){B.selectDate(C)}}}()))).appendTo(A);var v=b(o,y);var n=B.selected&&b(B.selected,o);if(v){p.addClass("today")}if(n){p.addClass("selected")}if(v&&n){p.addClass("today_selected")}if(o.getUTCMonth()!=w){p.addClass("nonMonth")}dow=o.getUTCDay();if(dow==6){r.append(A);A=h("<tr />")}o=k(o)}if(A.children().length){r.append(A)}else{A.remove()}t.empty().append(z)}function d(q,o){var s=o.selection&&parseTime(o.selection);if(s){s.minute=Math.floor(s.minute/15)*15}var r=o.startTime&&(o.startTime.hour*60+o.startTime.minute);var n;var p=h("<ul />");for(var m=0;m<24;m++){for(var t=0;t<60;t+=15){if(r&&r>(m*60+t)){continue}(function(){var v=formatTime(m,t,o.isoTime);var w=v;if(r!=null){var x=(m*60+t)-r;if(x<60){w+=" ("+x+" min)"}else{if(x==60){w+=" (1 hr)"}else{w+=" ("+Math.round(x/60)+" hr "+(x%60)+" min)"}}}var u=h("<li />").append(h('<a href="javascript:;">'+w+"</a>").click(function(){if(o&&o.selectTime){o.selectTime(v)}}).mousemove(function(){h("li.selected",p).removeClass("selected")})).appendTo(p);if(!n&&m==o.defaultHour){n=u}if(s&&s.hour==m&&s.minute==t){u.addClass("selected");n=u}})()}}if(n){setTimeout(function(){q[0].scrollTop=n[0].offsetTop-n.height()*2},0)}q.empty().append(p)}h.fn.calendricalDate=function(m){m=m||{};m.padding=m.padding||4;return this.each(function(){var o=h(this);var p;var n=false;o.bind("focus",function(){if(p){return}var s=o.position();var r=o.css("padding-left");p=h("<div />").addClass("calendricalDatePopup").mouseenter(function(){n=true}).mouseleave(function(){n=false}).mousedown(function(t){t.preventDefault()}).css({position:"absolute",left:s.left,top:s.top+o.height()+m.padding*2});o.after(p);var q=parseDate(o.val(),m.usa);if(!q.getUTCFullYear()){q=m.today?m.today:e()}g(p,q.getUTCFullYear(),q.getUTCMonth(),{today:m.today,selected:q,selectDate:function(t){n=false;o.val(formatDate(t,m.usa));p.remove();p=null;if(m.endDate){var u=parseDate(m.endDate.val(),m.usa);if(u>=q){m.endDate.val(formatDate(new Date(t.getTime()+u.getTime()-q.getTime()),m.usa))}}}})}).blur(function(){if(n){if(p){o.focus()}return}if(!p){return}p.remove();p=null})})};h.fn.calendricalDateRange=function(m){if(this.length>=2){h(this[0]).calendricalDate(h.extend({endDate:h(this[1])},m));h(this[1]).calendricalDate(m)}return this};h.fn.calendricalDateRangeSingle=function(m){if(this.length==1){h(this).calendricalDate(m)}return this};h.fn.calendricalTime=function(m){m=m||{};m.padding=m.padding||4;return this.each(function(){var o=h(this);var p;var n=false;o.bind("focus click",function(){if(p){return}var q=m.startTime;if(q){if(m.startDate&&m.endDate&&!b(parseDate(m.startDate.val()),parseDate(m.endDate.val()))){q=false}}var s=o.position();p=h("<div />").addClass("calendricalTimePopup").mouseenter(function(){n=true}).mouseleave(function(){n=false}).mousedown(function(t){t.preventDefault()}).css({position:"absolute",left:s.left,top:s.top+o.height()+m.padding*2});if(q){p.addClass("calendricalEndTimePopup")}o.after(p);var r={selection:o.val(),selectTime:function(t){n=false;o.val(t);p.remove();p=null},isoTime:m.isoTime||false,defaultHour:(m.defaultHour!=null)?m.defaultHour:8};if(q){r.startTime=parseTime(m.startTime.val())}d(p,r)}).blur(function(){if(n){if(p){o.focus()}return}if(!p){return}p.remove();p=null})})},h.fn.calendricalTimeRange=function(m){if(this.length>=2){h(this[0]).calendricalTime(m);h(this[1]).calendricalTime(h.extend({startTime:h(this[0])},m))}return this};h.fn.calendricalDateTimeRange=function(m){if(this.length>=4){h(this[0]).calendricalDate(h.extend({endDate:h(this[2])},m));h(this[1]).calendricalTime(m);h(this[2]).calendricalDate(m);h(this[3]).calendricalTime(h.extend({startTime:h(this[1]),startDate:h(this[0]),endDate:h(this[2])},m))}return this}})(jQuery);
js/jquery.inputdate.js ADDED
@@ -0,0 +1 @@
 
1
+ (function(d){function a(g){g.addClass("error").fadeOut("normal",function(){g.val(g.data("timespan.stored")).removeClass("error").fadeIn("fast")})}function e(){d(this).data("timespan.stored",this.value)}function c(h,i,j,g){i.val(i.data("timespan.initial_value"));var k=parseInt(i.val());if(!isNaN(parseInt(k))){k=new Date(parseInt(k)*1000)}else{k=new Date(g)}h.val(formatDate(k,false));h.each(e)}var f={start_date_input:"date-input",start_time:"time",twentyfour_hour:false,now:new Date()};var b={init:function(i){var l=d.extend({},f,i);var j=d(l.start_date_input);var k=d(l.start_time);var h=j;var g=j;g.bind("focus.timespan",e);h.calendricalDate({today:new Date(l.now.getFullYear(),l.now.getMonth(),l.now.getDate())});h.bind("blur.timespan",function(){var m=parseDate(this.value,false);if(isNaN(m)){a(d(this))}else{d(this).data("timespan.stored",this.value);d(this).val(formatDate(m,false))}});j.bind("focus.timespan",function(){var m=parseDate(j.val(),false).getTime()/1000}).bind("blur.timespan",function(){var m=parseDate(j.data("timespan.stored"),false)});j.closest("form").bind("submit.timespan",function(){var m=parseDate(j.val(),false).getTime()/1000;if(isNaN(m)){m=""}k.val(m)});k.data("timespan.initial_value",k.val());c(j,k,l.twentyfour_hour,l.now);return this},reset:function(g){var h=d.extend({},f,g);c(d(h.start_date_input),d(h.start_time),h.twentyfour_hour,h.now);return this},destroy:function(g){g=d.extend({},f,g);d.each(g,function(i,h){d(h).unbind(".timespan")});d(g.start_date_input).closest("form").unbind(".timespan");return this}};d.inputdate=function(g){if(b[g]){return b[g].apply(this,Array.prototype.slice.call(arguments,1))}else{if(typeof g==="object"||!g){return b.init.apply(this,arguments)}else{d.error("Method "+g+" does not exist on jQuery.timespan")}}}})(jQuery);
js/jquery.scrollTo-min.js ADDED
@@ -0,0 +1 @@
 
1
+ (function(c){var a=c.scrollTo=function(d,f,g){c(window).scrollTo(d,f,g)};a.defaults={axis:"xy",duration:parseFloat(c.fn.jquery)>=1.3?0:1};a.window=function(d){return c(window)._scrollable()};c.fn._scrollable=function(){return this.map(function(){var d=this,f=!d.nodeName||c.inArray(d.nodeName.toLowerCase(),["iframe","#document","html","body"])!=-1;if(!f){return d}var g=(d.contentWindow||d).document||d.ownerDocument||d;return c.browser.safari||g.compatMode=="BackCompat"?g.body:g.documentElement})};c.fn.scrollTo=function(f,e,d){if(typeof e=="object"){d=e;e=0}if(typeof d=="function"){d={onAfter:d}}if(f=="max"){f=9000000000}d=c.extend({},a.defaults,d);e=e||d.speed||d.duration;d.queue=d.queue&&d.axis.length>1;if(d.queue){e/=2}d.offset=b(d.offset);d.over=b(d.over);return this._scrollable().each(function(){var n=this,l=c(n),m=f,j,k={},h=l.is("html,body");switch(typeof m){case"number":case"string":if(/^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(m)){m=b(m);break}m=c(m,this);case"object":if(m.is||m.style){j=(m=c(m)).offset()}}c.each(d.axis.split(""),function(q,r){var t=r=="x"?"Left":"Top",s=t.toLowerCase(),v="scroll"+t,p=n[v],g=a.max(n,r);if(j){k[v]=j[s]+(h?0:p-l.offset()[s]);if(d.margin){k[v]-=parseInt(m.css("margin"+t))||0;k[v]-=parseInt(m.css("border"+t+"Width"))||0}k[v]+=d.offset[s]||0;if(d.over[s]){k[v]+=m[r=="x"?"width":"height"]()*d.over[s]}}else{var u=m[s];k[v]=u.slice&&u.slice(-1)=="%"?parseFloat(u)/100*g:u}if(/^\d+$/.test(k[v])){k[v]=k[v]<=0?0:Math.min(k[v],g)}if(!q&&d.queue){if(p!=k[v]){i(d.onAfterFirst)}delete k[v]}});i(d.onAfter);function i(g){l.animate(k,e,d.easing,g&&function(){g.call(this,f,d)})}}).end()};a.max=function(g,j){var n=j=="x"?"Width":"Height",k="scroll"+n;if(!c(g).is("html,body")){return g[k]-c(g)[n.toLowerCase()]()}var o="client"+n,f=g.ownerDocument.documentElement,d=g.ownerDocument.body;return Math.max(f[k],d[k])-Math.min(f[o],d[o])};function b(d){return typeof d=="object"?d:{top:d,left:d}}})(jQuery);
js/jquery.timespan.js ADDED
@@ -0,0 +1 @@
 
1
+ (function(d){function a(g){g.addClass("error").fadeOut("normal",function(){g.val(g.data("timespan.stored")).removeClass("error").fadeIn("fast")})}function e(){d(this).data("timespan.stored",this.value)}function c(l,p,o,j,n,i,q,m,h){o.val(o.data("timespan.initial_value"));i.val(i.data("timespan.initial_value"));q.get(0).checked=q.data("timespan.initial_value");var g=parseInt(o.val());if(!isNaN(parseInt(g))){g=new Date(parseInt(g)*1000);p.val(formatTime(g.getUTCHours(),g.getUTCMinutes(),m))}else{g=new Date(h);p.val(formatTime(g.getUTCHours(),g.getUTCMinutes()-g.getUTCMinutes()%15,m))}l.val(formatDate(g,false));var k=parseInt(i.val());if(!isNaN(parseInt(k))){k=new Date(parseInt(k)*1000);n.val(formatTime(k.getUTCHours(),k.getUTCMinutes(),m))}else{k=new Date(g.getTime()+3600000);n.val(formatTime(k.getUTCHours(),k.getUTCMinutes()-k.getUTCMinutes()%15,m))}if(q.get(0).checked){k.setUTCDate(k.getUTCDate()-1)}j.val(formatDate(k,false));l.each(e);p.each(e);j.each(e);n.each(e);q.trigger("change.timespan")}var f={allday:"#allday",start_date_input:"#start-date-input",start_time_input:"#start-time-input",start_time:"#start-time",end_date_input:"#end-date-input",end_time_input:"#end-time-input",end_time:"#end-time",twentyfour_hour:false,now:new Date(),};var b={init:function(t){var h=d.extend({},f,t);var s=d(h.allday);var k=d(h.start_date_input);var p=d(h.start_time_input);var m=d(h.start_time);var j=d(h.end_date_input);var l=d(h.end_time_input);var g=d(h.end_time);var q=k.add(h.end_date_input);var r=p.add(h.end_time_input);var i=k.add(h.start_time_input).add(h.end_date_input).add(h.end_time_input);i.bind("focus.timespan",e);var n=new Date(h.now.getFullYear(),h.now.getMonth(),h.now.getDate());s.bind("change.timespan",function(){if(this.checked){r.fadeOut();q.calendricalDateRange({today:n})}else{r.fadeIn();i.calendricalDateTimeRange({today:n})}}).get().checked=false;q.bind("blur.timespan",function(){var o=parseDate(this.value,false);if(isNaN(o)){a(d(this))}else{d(this).data("timespan.stored",this.value);d(this).val(formatDate(o,false))}});r.bind("blur.timespan",function(){var o=parseTime(this.value);if(!o){a(d(this))}else{d(this).data("timespan.stored",this.value);d(this).val(formatTime(o.hour,o.minute,false))}});k.add(h.start_time_input).bind("focus.timespan",function(){var o=parseDate(k.val(),false).getTime()/1000;var v=parseTime(p.val());o+=v.hour*3600+v.minute*60;var u=parseDate(j.val(),false).getTime()/1000;var w=parseTime(l.val());u+=w.hour*3600+w.minute*60;k.data("time_diff",u-o)}).bind("blur.timespan",function(){var o=parseDate(k.data("timespan.stored"),false);var u=parseTime(p.data("timespan.stored"));var v=o.getTime()/1000+u.hour*3600+u.minute*60+k.data("time_diff");v=new Date(v*1000);j.val(formatDate(v,false));l.val(formatTime(v.getUTCHours(),v.getUTCMinutes(),false))});k.closest("form").bind("submit.timespan",function(){var o=parseDate(k.val(),false).getTime()/1000;if(!isNaN(o)){if(!s.get(0).checked){var u=parseTime(p.val());if(u){o+=u.hour*3600+u.minute*60}else{o=""}}}else{o=""}m.val(o);var v=parseDate(j.val(),false).getTime()/1000;if(!isNaN(v)){if(s.get(0).checked){v+=24*60*60}else{var u=parseTime(l.val());if(u){v+=u.hour*3600+u.minute*60}else{v=""}}}else{v=""}g.val(v)});m.data("timespan.initial_value",m.val());g.data("timespan.initial_value",g.val());s.data("timespan.initial_value",s.get(0).checked);c(k,p,m,j,l,g,s,h.twentyfour_hour,h.now);return this},reset:function(g){var h=d.extend({},f,g);c(d(h.start_date_input),d(h.start_time_input),d(h.start_time),d(h.end_date_input),d(h.end_time_input),d(h.end_time),d(h.allday),h.twentyfour_hour,h.now);return this},destroy:function(g){g=d.extend({},f,g);d.each(g,function(i,h){d(h).unbind(".timespan")});d(g.start_date_input).closest("form").unbind(".timespan");return this}};d.timespan=function(g){if(b[g]){return b[g].apply(this,Array.prototype.slice.call(arguments,1))}else{if(typeof g==="object"||!g){return b.init.apply(this,arguments)}else{d.error("Method "+g+" does not exist on jQuery.timespan")}}}})(jQuery);
js/settings.js ADDED
@@ -0,0 +1 @@
 
1
+ jQuery(function(a){a(".if-js-closed").removeClass("if-js-closed").addClass("closed");postboxes.add_postbox_toggles(ai1ec_settings_page)});
lib/SG_iCal.php ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ define('SG_ICALREADER_VERSION', '0.7.0');
4
+
5
+ /**
6
+ * A simple iCal parser. Should take care of most stuff for ya
7
+ * http://github.com/fangel/SG-iCalendar
8
+ *
9
+ * Roadmap:
10
+ * * Finish FREQUENCY-parsing.
11
+ * * Add API for recurring events
12
+ *
13
+ * A simple example:
14
+ * <?php
15
+ * $ical = new SG_iCalReader("http://example.com/calendar.ics");
16
+ * foreach( $ical->getEvents() As $event ) {
17
+ * // Do stuff with the event $event
18
+ * }
19
+ * ?>
20
+ *
21
+ * @package SG_iCalReader
22
+ * @author Morten Fangel (C) 2008
23
+ * @author xonev (C) 2010
24
+ * @author Tanguy Pruvot (C) 2010
25
+ * @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
26
+ */
27
+ class SG_iCal {
28
+
29
+ //objects
30
+ public $information; //SG_iCal_VCalendar
31
+ public $timezones; //SG_iCal_VTimeZone
32
+
33
+ protected $events; //SG_iCal_VEvent[]
34
+
35
+ /**
36
+ * Constructs a new iCalReader. You can supply the url now, or later using setUrl
37
+ * @param $url string
38
+ */
39
+ public function __construct($url = false) {
40
+ $this->setUrl($url);
41
+ }
42
+
43
+ /**
44
+ * Sets (or resets) the url this reader reads from.
45
+ * @param $url string
46
+ */
47
+ public function setUrl( $url = false ) {
48
+ if( $url !== false ) {
49
+ SG_iCal_Parser::Parse($url, $this);
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Returns the main calendar info. You can then query the returned
55
+ * object with ie getTitle().
56
+ * @return SG_iCal_VCalendar
57
+ */
58
+ public function getCalendarInfo() {
59
+ return $this->information;
60
+ }
61
+
62
+ /**
63
+ * Sets the calendar info for this calendar
64
+ * @param SG_iCal_VCalendar $info
65
+ */
66
+ public function setCalendarInfo( SG_iCal_VCalendar $info ) {
67
+ $this->information = $info;
68
+ }
69
+
70
+
71
+ /**
72
+ * Returns a given timezone for the calendar. This is mainly used
73
+ * by VEvents to adjust their date-times if they have specified a
74
+ * timezone.
75
+ *
76
+ * If no timezone is given, all timezones in the calendar is
77
+ * returned.
78
+ *
79
+ * @param $tzid string
80
+ * @return SG_iCal_VTimeZone
81
+ */
82
+ public function getTimeZoneInfo( $tzid = null ) {
83
+ if( $tzid == null ) {
84
+ return $this->timezones;
85
+ } else {
86
+ if ( !isset($this->timezones)) {
87
+ return null;
88
+ }
89
+ foreach( $this->timezones AS $tz ) {
90
+ if( $tz->getTimeZoneId() == $tzid ) {
91
+ return $tz;
92
+ }
93
+ }
94
+ return null;
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Adds a new timezone to this calendar
100
+ * @param SG_iCal_VTimeZone $tz
101
+ */
102
+ public function addTimeZone( SG_iCal_VTimeZone $tz ) {
103
+ $this->timezones[] = $tz;
104
+ }
105
+
106
+ /**
107
+ * Returns the events found
108
+ * @return array
109
+ */
110
+ public function getEvents() {
111
+ return $this->events;
112
+ }
113
+
114
+ /**
115
+ * Adds a event to this calendar
116
+ * @param SG_iCal_VEvent $event
117
+ */
118
+ public function addEvent( SG_iCal_VEvent $event ) {
119
+ $this->events[] = $event;
120
+ }
121
+ }
122
+
123
+ /**
124
+ * For legacy reasons, we keep the name SG_iCalReader..
125
+ */
126
+ class SG_iCalReader extends SG_iCal {}
lib/blocks/SG_iCal_VCalendar.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php // BUILD: Remove line
2
+
3
+ /**
4
+ * The wrapper for the main vcalendar data. Used instead of ArrayObject
5
+ * so you can easily query for title and description.
6
+ * Exposes a iterator that will loop though all the data
7
+ *
8
+ * @package SG_iCalReader
9
+ * @author Morten Fangel (C) 2008
10
+ * @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
11
+ */
12
+ class SG_iCal_VCalendar implements IteratorAggregate {
13
+ protected $data;
14
+
15
+ /**
16
+ * Creates a new SG_iCal_VCalendar.
17
+ */
18
+ public function __construct($data) {
19
+ $this->data = $data;
20
+ }
21
+
22
+ /**
23
+ * Returns the title of the calendar. If no title is known, NULL
24
+ * will be returned
25
+ * @return string
26
+ */
27
+ public function getTitle() {
28
+ if( isset($this->data['x-wr-calname']) ) {
29
+ return $this->data['x-wr-calname'];
30
+ } else {
31
+ return null;
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Returns the description of the calendar. If no description is
37
+ * known, NULL will be returned.
38
+ * @return string
39
+ */
40
+ public function getDescription() {
41
+ if( isset($this->data['x-wr-caldesc']) ) {
42
+ return $this->data['x-wr-caldesc'];
43
+ } else {
44
+ return null;
45
+ }
46
+ }
47
+
48
+ public function getTimezone() {
49
+ if( isset($this->data['x-wr-timezone']) ) {
50
+ return $this->data['x-wr-timezone'];
51
+ } else {
52
+ return null;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * @see IteratorAggregate.getIterator()
58
+ */
59
+ public function getIterator() {
60
+ return new ArrayIterator($this->data);
61
+ }
62
+ }
63
+
64
+ ?>
lib/blocks/SG_iCal_VEvent.php ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php // BUILD: Remove line
2
+
3
+ /**
4
+ * The wrapper for vevents. Will reveal a unified and simple api for
5
+ * the events, which include always finding a start and end (except
6
+ * when no end or duration is given) and checking if the event is
7
+ * blocking or similar.
8
+ *
9
+ * Will apply the specified timezone to timestamps if a tzid is
10
+ * specified
11
+ *
12
+ * @package SG_iCalReader
13
+ * @author Morten Fangel (C) 2008
14
+ * @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
15
+ */
16
+ class SG_iCal_VEvent {
17
+ const DEFAULT_CONFIRMED = true;
18
+
19
+ public $uid;
20
+
21
+ public $start;
22
+ public $end;
23
+
24
+ public $summary;
25
+ public $description;
26
+ public $location;
27
+
28
+ public $laststart;
29
+ public $lastend;
30
+
31
+ public $recurrence; //RRULE
32
+ public $recurex; //EXRULE
33
+ public $excluded; //EXDATE(s)
34
+ public $added; //RDATE(s)
35
+
36
+ public $freq; //getFrequency() SG_iCal_Freq
37
+
38
+ public $data;
39
+
40
+ /**
41
+ * Constructs a new SG_iCal_VEvent. Needs the SG_iCalReader
42
+ * supplied so it can query for timezones.
43
+ * @param SG_iCal_Line[] $data
44
+ * @param SG_iCalReader $ical
45
+ */
46
+ public function __construct($data, SG_iCal $ical) {
47
+
48
+ $this->uid = $data['uid']->getData();
49
+ unset($data['uid']);
50
+
51
+ if ( isset($data['rrule']) ) {
52
+ $this->recurrence = new SG_iCal_Recurrence($data['rrule']);
53
+ unset($data['rrule']);
54
+ }
55
+
56
+ if ( isset($data['exrule']) ) {
57
+ $this->recurex = new SG_iCal_Recurrence($data['exrule']);
58
+ unset($data['exrule']);
59
+ }
60
+
61
+ if( isset($data['dtstart']) ) {
62
+ $this->start = $this->getTimestamp($data['dtstart'], $ical);
63
+ unset($data['dtstart']);
64
+ }
65
+
66
+ if( isset($data['dtend']) ) {
67
+ $this->end = $this->getTimestamp($data['dtend'], $ical);
68
+ unset($data['dtend']);
69
+ } elseif( isset($data['duration']) ) {
70
+ $dur = new SG_iCal_Duration( $data['duration']->getData() );
71
+ $this->end = $this->start + $dur->getDuration();
72
+ unset($data['duration']);
73
+ }
74
+
75
+ //google cal set dtend as end of initial event (duration)
76
+ if ( isset($this->recurrence) ) {
77
+ //if there is a recurrence rule
78
+
79
+ //exclusions
80
+ if ( isset($data['exdate']) ) {
81
+ foreach ($data['exdate'] as $exdate) {
82
+ foreach ($exdate->getDataAsArray() as $ts) {
83
+ $this->excluded[] = strtotime($ts);
84
+ }
85
+ }
86
+ unset($data['exdate']);
87
+ }
88
+ //additions
89
+ if ( isset($data['rdate']) ) {
90
+ foreach ($data['rdate'] as $rdate) {
91
+ foreach ($rdate->getDataAsArray() as $ts) {
92
+ $this->added[] = strtotime($ts);
93
+ }
94
+ }
95
+ unset($data['rdate']);
96
+ }
97
+
98
+ $until = $this->recurrence->getUntil();
99
+ $count = $this->recurrence->getCount();
100
+ //check if there is either 'until' or 'count' set
101
+ if ( $until ) {
102
+ //ok..
103
+ } elseif ($count) {
104
+ //if count is set, then figure out the last occurrence and set that as the end date
105
+ $this->getFrequency();
106
+ $until = $this->freq->lastOccurrence($this->start);
107
+ } else {
108
+ //forever... limit to 3 years
109
+ $this->recurrence->setUntil('+3 years');
110
+ $until = $this->recurrence->getUntil();
111
+ }
112
+ //date_default_timezone_set( xx ) needed ?;
113
+ $this->laststart = strtotime($until);
114
+ $this->lastend = $this->laststart + $this->getDuration();
115
+ }
116
+
117
+ $imports = array('summary','description','location');
118
+ foreach( $imports AS $import ) {
119
+ if( isset($data[$import]) ) {
120
+ $this->$import = $data[$import]->getData();
121
+ unset($data[$import]);
122
+ }
123
+ }
124
+
125
+ if( isset($this->previous_tz) ) {
126
+ date_default_timezone_set($this->previous_tz);
127
+ }
128
+
129
+ $this->data = SG_iCal_Line::Remove_Line($data);
130
+ }
131
+
132
+
133
+ /**
134
+ * Returns the Event Occurrences Iterator (if recurrence set)
135
+ * @return SG_iCal_Freq
136
+ */
137
+ public function getFrequency() {
138
+ if (! isset($this->freq)) {
139
+ if ( isset($this->recurrence) ) {
140
+ $this->freq = new SG_iCal_Freq($this->recurrence->rrule, $this->start, $this->excluded, $this->added);
141
+ }
142
+ }
143
+ return $this->freq;
144
+ }
145
+
146
+ /**
147
+ * Returns the UID of the event
148
+ * @return string
149
+ */
150
+ public function getUID() {
151
+ return $this->uid;
152
+ }
153
+
154
+ /**
155
+ * Returns the summary (or null if none is given) of the event
156
+ * @return string
157
+ */
158
+ public function getSummary() {
159
+ return $this->summary;
160
+ }
161
+
162
+ /**
163
+ * Returns the description (or null if none is given) of the event
164
+ * @return string
165
+ */
166
+ public function getDescription() {
167
+ return $this->description;
168
+ }
169
+
170
+ /**
171
+ * Returns the location (or null if none is given) of the event
172
+ * @return string
173
+ */
174
+ public function getLocation() {
175
+ return $this->location;
176
+ }
177
+
178
+ /**
179
+ * Returns true if the event is blocking (ie not transparent)
180
+ * @return bool
181
+ */
182
+ public function isBlocking() {
183
+ return !(isset($this->data['transp']) && $this->data['transp'] == 'TRANSPARENT');
184
+ }
185
+
186
+ /**
187
+ * Returns true if the event is confirmed
188
+ * @return bool
189
+ */
190
+ public function isConfirmed() {
191
+ if( !isset($this->data['status']) ) {
192
+ return self::DEFAULT_CONFIRMED;
193
+ } else {
194
+ return $this->data['status'] == 'CONFIRMED';
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Returns true if duration is multiple of 86400
200
+ * @return bool
201
+ */
202
+ public function isWholeDay() {
203
+ $dur = $this->getDuration();
204
+ if ($dur > 0 && ($dur % 86400) == 0) {
205
+ return true;
206
+ }
207
+ return false;
208
+ }
209
+
210
+ /**
211
+ * Returns the timestamp for the beginning of the event
212
+ * @return int
213
+ */
214
+ public function getStart() {
215
+ return $this->start;
216
+ }
217
+
218
+ /**
219
+ * Returns the timestamp for the end of the event
220
+ * @return int
221
+ */
222
+ public function getEnd() {
223
+ return $this->end;
224
+ }
225
+
226
+ /**
227
+ * Returns the timestamp for the end of the last event
228
+ * @return int
229
+ */
230
+ public function getRangeEnd() {
231
+ return max($this->end,$this->lastend);
232
+ }
233
+
234
+ /**
235
+ * Returns the duration of this event in seconds
236
+ * @return int
237
+ */
238
+ public function getDuration() {
239
+ return $this->end - $this->start;
240
+ }
241
+
242
+ /**
243
+ * Returns the given property of the event.
244
+ * @param string $prop
245
+ * @return string
246
+ */
247
+ public function getProperty( $prop ) {
248
+ if( isset($this->$prop) ) {
249
+ return $this->$prop;
250
+ } elseif( isset($this->data[$prop]) ) {
251
+ return $this->data[$prop];
252
+ } else {
253
+ return null;
254
+ }
255
+ }
256
+
257
+
258
+
259
+ /**
260
+ * Set default timezone (temporary) to get timestamps
261
+ * @return string
262
+ */
263
+ protected function setLineTimeZone(SG_iCal_Line $line) {
264
+ if( isset($line['tzid']) ) {
265
+ if (!isset($this->previous_tz)) {
266
+ $this->previous_tz = @ date_default_timezone_get();
267
+ }
268
+ $this->tzid = $line['tzid'];
269
+ date_default_timezone_set($this->tzid);
270
+ return true;
271
+ }
272
+ return false;
273
+ }
274
+
275
+ /**
276
+ * Calculates the timestamp from a DT line.
277
+ * @param $line SG_iCal_Line
278
+ * @return int
279
+ */
280
+ protected function getTimestamp( SG_iCal_Line $line, SG_iCal $ical ) {
281
+
282
+ if( isset($line['tzid']) ) {
283
+ $this->setLineTimeZone($line);
284
+ //$tz = $ical->getTimeZoneInfo($line['tzid']);
285
+ //$offset = $tz->getOffset($ts);
286
+ //$ts = strtotime(date('D, d M Y H:i:s', $ts) . ' ' . $offset);
287
+ }
288
+ $ts = strtotime($line->getData());
289
+
290
+ return $ts;
291
+ }
292
+ }
lib/blocks/SG_iCal_VTimeZone.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php // BUILD: Remove line
2
+
3
+ /**
4
+ * The wrapper for vtimezones. Stores the timezone-id and the setup for
5
+ * daylight savings and standard time.
6
+ *
7
+ * @package SG_iCalReader
8
+ * @author Morten Fangel (C) 2008
9
+ * @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
10
+ */
11
+ class SG_iCal_VTimeZone {
12
+ protected $tzid;
13
+ protected $daylight;
14
+ protected $standard;
15
+ protected $cache = array();
16
+
17
+ /**
18
+ * Constructs a new SG_iCal_VTimeZone
19
+ */
20
+ public function __construct( $data ) {
21
+ require_once dirname(__FILE__).'/../helpers/SG_iCal_Freq.php'; // BUILD: Remove line
22
+
23
+ $this->tzid = $data['tzid'];
24
+ $this->daylight = $data['daylight'];
25
+ $this->standard = $data['standard'];
26
+ }
27
+
28
+ /**
29
+ * Returns the timezone-id for this timezone. (Used to
30
+ * differentiate between different tzs in a calendar)
31
+ * @return string
32
+ */
33
+ public function getTimeZoneId() {
34
+ return $this->tzid;
35
+ }
36
+
37
+ /**
38
+ * Returns the given offset in this timezone for the given
39
+ * timestamp. (eg +0200)
40
+ * @param int $ts
41
+ * @return string
42
+ */
43
+ public function getOffset( $ts ) {
44
+ $act = $this->getActive($ts);
45
+ return $this->{$act}['tzoffsetto'];
46
+ }
47
+
48
+ /**
49
+ * Returns the timezone name for the given timestamp (eg CEST)
50
+ * @param int $ts
51
+ * @return string
52
+ */
53
+ public function getTimeZoneName($ts) {
54
+ $act = $this->getActive($ts);
55
+ return $this->{$act}['tzname'];
56
+ }
57
+
58
+ /**
59
+ * Determines which of the daylight or standard is the active
60
+ * setting.
61
+ * The call is cached for a given timestamp, so a call to
62
+ * getOffset and getTimeZoneName with the same ts won't calculate
63
+ * the answer twice.
64
+ * @param int $ts
65
+ * @return string standard|daylight
66
+ */
67
+ private function getActive( $ts ) {
68
+
69
+ if (class_exists('DateTimeZone')) {
70
+
71
+ //PHP >= 5.2
72
+ $tz = new DateTimeZone( $this->tzid );
73
+ $date = new DateTime("@$ts", $tz);
74
+ return ($date->format('I') == 1) ? 'daylight' : 'standard';
75
+
76
+ } else {
77
+
78
+ if( isset($this->cache[$ts]) ) {
79
+ return $this->cache[$ts];
80
+ }
81
+
82
+ $daylight_freq = new SG_iCal_Freq($this->daylight['rrule'], strtotime($this->daylight['dtstart']));
83
+ $standard_freq = new SG_iCal_Freq($this->standard['rrule'], strtotime($this->standard['dtstart']));
84
+ $last_standard = $standard_freq->previousOccurrence($ts);
85
+ $last_dst = $daylight_freq->previousOccurrence($ts);
86
+ if( $last_dst > $last_standard ) {
87
+ $this->cache[$ts] = 'daylight';
88
+ } else {
89
+ $this->cache[$ts] = 'standard';
90
+ }
91
+
92
+ return $this->cache[$ts];
93
+ }
94
+ }
95
+ }
lib/helpers/SG_iCal_Duration.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php // BUILD: Remove line
2
+
3
+ /**
4
+ * A class for calculating how many seconds a duration-string is
5
+ *
6
+ * @package SG_iCalReader
7
+ * @author Morten Fangel (C) 2008
8
+ * @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
9
+ */
10
+
11
+ class SG_iCal_Duration {
12
+ protected $dur;
13
+
14
+ /**
15
+ * Constructs a new SG_iCal_Duration from a duration-rule.
16
+ * The basic build-up of DURATIONs are:
17
+ * (["+"] / "-") "P" (dur-date / dur-date + "T" + dur-time / dur-time / dur-week)
18
+ * Is solved via a really fugly reg-exp with way to many ()'s..
19
+ *
20
+ * @param $duration string
21
+ */
22
+ public function __construct( $duration ) {
23
+
24
+ $ts = 0;
25
+
26
+ if (preg_match('/[\\+\\-]{0,1}P((\d+)W)?((\d+)D)?(T)?((\d+)H)?((\d+)M)?((\d+)S)?/', $duration, $matches) === 1) {
27
+ $results = array(
28
+ 'weeks'=> (int)@ $matches[2],
29
+ 'days'=> (int)@ $matches[4],
30
+ 'hours'=> (int)@ $matches[7],
31
+ 'minutes'=>(int)@ $matches[9],
32
+ 'seconds'=>(int)@ $matches[11]
33
+ );
34
+
35
+ $ts += $results['seconds'];
36
+ $ts += 60 * $results['minutes'];
37
+ $ts += 60 * 60 * $results['hours'];
38
+ $ts += 24 * 60 * 60 * $results['days'];
39
+ $ts += 7 * 24 * 60 * 60 * $results['weeks'];
40
+ } else {
41
+ // Invalid duration!
42
+ }
43
+
44
+ $dir = ($duration{0} == '-') ? -1 : 1;
45
+
46
+ $this->dur = $dir * $ts;
47
+ }
48
+
49
+ /**
50
+ * Returns the duration in seconds
51
+ * @return int
52
+ */
53
+ public function getDuration() {
54
+ return $this->dur;
55
+ }
56
+ }
lib/helpers/SG_iCal_Factory.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php // BUILD: Remove line
2
+
3
+ /**
4
+ * A simple Factory for converting a section/data pair into the
5
+ * corrosponding block-object. If the section isn't known a simple
6
+ * ArrayObject is used instead.
7
+ *
8
+ * @package SG_iCalReader
9
+ * @author Morten Fangel (C) 2008
10
+ * @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
11
+ */
12
+ class SG_iCal_Factory {
13
+ /**
14
+ * Returns a new block-object for the section/data-pair. The list
15
+ * of returned objects is:
16
+ *
17
+ * vcalendar => SG_iCal_VCalendar
18
+ * vtimezone => SG_iCal_VTimeZone
19
+ * vevent => SG_iCal_VEvent
20
+ * * => ArrayObject
21
+ *
22
+ * @param $ical SG_iCalReader The reader this section/data-pair belongs to
23
+ * @param $section string
24
+ * @param SG_iCal_Line[]
25
+ */
26
+ public static function factory( SG_iCal $ical, $section, $data ) {
27
+ switch( $section ) {
28
+ case "vcalendar":
29
+ require_once dirname(__FILE__).'/../blocks/SG_iCal_VCalendar.php'; // BUILD: Remove line
30
+ return new SG_iCal_VCalendar(SG_iCal_Line::Remove_Line($data), $ical );
31
+ case "vtimezone":
32
+ require_once dirname(__FILE__).'/../blocks/SG_iCal_VTimeZone.php'; // BUILD: Remove line
33
+ return new SG_iCal_VTimeZone(SG_iCal_Line::Remove_Line($data), $ical );
34
+ case "vevent":
35
+ require_once dirname(__FILE__).'/../blocks/SG_iCal_VEvent.php'; // BUILD: Remove line
36
+ return new SG_iCal_VEvent($data, $ical );
37
+
38
+ default:
39
+ return new ArrayObject(SG_iCal_Line::Remove_Line((array) $data) );
40
+ }
41
+ }
42
+ }
lib/helpers/SG_iCal_Freq.php ADDED
@@ -0,0 +1,554 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php // BUILD: Remove line
2
+
3
+ /**
4
+ * A class to store Frequency-rules in. Will allow a easy way to find the
5
+ * last and next occurrence of the rule.
6
+ *
7
+ * No - this is so not pretty. But.. ehh.. You do it better, and I will
8
+ * gladly accept patches.
9
+ *
10
+ * Created by trail-and-error on the examples given in the RFC.
11
+ *
12
+ * TODO: Update to a better way of doing calculating the different options.
13
+ * Instead of only keeping track of the best of the current dates found
14
+ * it should instead keep a array of all the calculated dates within the
15
+ * period.
16
+ * This should fix the issues with multi-rule + multi-rule interference,
17
+ * and make it possible to implement the SETPOS rule.
18
+ * By pushing the next period onto the stack as the last option will
19
+ * (hopefully) remove the need for the awful simpleMode
20
+ *
21
+ * @package SG_iCalReader
22
+ * @author Morten Fangel (C) 2008
23
+ * @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
24
+ */
25
+ class SG_iCal_Freq {
26
+ protected $weekdays = array('MO'=>'monday', 'TU'=>'tuesday', 'WE'=>'wednesday', 'TH'=>'thursday', 'FR'=>'friday', 'SA'=>'saturday', 'SU'=>'sunday');
27
+ protected $knownRules = array('month', 'weekno', 'day', 'monthday', 'yearday', 'hour', 'minute'); //others : 'setpos', 'second'
28
+ protected $ruleModifiers = array('wkst');
29
+ protected $simpleMode = true;
30
+
31
+ protected $rules = array('freq'=>'yearly', 'interval'=>1);
32
+ protected $start = 0;
33
+ protected $freq = '';
34
+
35
+ protected $excluded; //EXDATE
36
+ protected $added; //RDATE
37
+
38
+ protected $cache; // getAllOccurrences()
39
+
40
+ /**
41
+ * Constructs a new Freqency-rule
42
+ * @param $rule string
43
+ * @param $start int Unix-timestamp (important : Need to be the start of Event)
44
+ * @param $excluded array of int (timestamps), see EXDATE documentation
45
+ * @param $added array of int (timestamps), see RDATE documentation
46
+ */
47
+ public function __construct( $rule, $start, $excluded=array(), $added=array()) {
48
+ $this->start = $start;
49
+ $this->excluded = array();
50
+
51
+ $rules = array();
52
+ foreach( explode(';', $rule) AS $v) {
53
+ list($k, $v) = explode('=', $v);
54
+ $this->rules[ strtolower($k) ] = $v;
55
+ }
56
+
57
+ if( isset($this->rules['until']) && is_string($this->rules['until']) ) {
58
+ $this->rules['until'] = strtotime($this->rules['until']);
59
+ }
60
+ $this->freq = strtolower($this->rules['freq']);
61
+
62
+ foreach( $this->knownRules AS $rule ) {
63
+ if( isset($this->rules['by' . $rule]) ) {
64
+ if( $this->isPrerule($rule, $this->freq) ) {
65
+ $this->simpleMode = false;
66
+ }
67
+ }
68
+ }
69
+
70
+ if(!$this->simpleMode) {
71
+ if(! (isset($this->rules['byday']) || isset($this->rules['bymonthday']) || isset($this->rules['byyearday']))) {
72
+ $this->rules['bymonthday'] = date('d', $this->start);
73
+ }
74
+ }
75
+
76
+ //set until, and cache
77
+ if( isset($this->rules['count']) ) {
78
+
79
+ $cache[$ts] = $ts = $this->start;
80
+ for($n=1; $n < $this->rules['count']; $n++) {
81
+ $ts = $this->findNext($ts);
82
+ $cache[$ts] = $ts;
83
+ }
84
+ $this->rules['until'] = $ts;
85
+
86
+ //EXDATE
87
+ if (!empty($excluded)) {
88
+ foreach($excluded as $ts) {
89
+ unset($cache[$ts]);
90
+ }
91
+ }
92
+ //RDATE
93
+ if (!empty($added)) {
94
+ $cache = $cache + $added;
95
+ asort($cache);
96
+ }
97
+
98
+ $this->cache = array_values($cache);
99
+ }
100
+
101
+ $this->excluded = $excluded;
102
+ $this->added = $added;
103
+ }
104
+
105
+
106
+ /**
107
+ * Returns all timestamps array(), build the cache if not made before
108
+ * @return array
109
+ */
110
+ public function getAllOccurrences() {
111
+ if (empty($this->cache)) {
112
+ //build cache
113
+ $next = $this->firstOccurrence();
114
+ while ($next) {
115
+ $cache[] = $next;
116
+ $next = $this->findNext($next);
117
+ }
118
+ if (!empty($this->added)) {
119
+ $cache = $cache + $this->added;
120
+ asort($cache);
121
+ }
122
+ $this->cache = $cache;
123
+ }
124
+ return $this->cache;
125
+ }
126
+
127
+ /**
128
+ * Returns the previous (most recent) occurrence of the rule from the
129
+ * given offset
130
+ * @param int $offset
131
+ * @return int
132
+ */
133
+ public function previousOccurrence( $offset ) {
134
+ if (!empty($this->cache)) {
135
+ $t2=$this->start;
136
+ foreach($this->cache as $ts) {
137
+ if ($ts >= $offset)
138
+ return $t2;
139
+ $t2 = $ts;
140
+ }
141
+ } else {
142
+ $ts = $this->start;
143
+ while( ($t2 = $this->findNext($ts)) < $offset) {
144
+ if( $t2 == false ){
145
+ break;
146
+ }
147
+ $ts = $t2;
148
+ }
149
+ }
150
+ return $ts;
151
+ }
152
+
153
+ /**
154
+ * Returns the next occurrence of this rule after the given offset
155
+ * @param int $offset
156
+ * @return int
157
+ */
158
+ public function nextOccurrence( $offset ) {
159
+ if ($offset < $this->start)
160
+ return $this->firstOccurrence();
161
+ return $this->findNext($offset);
162
+ }
163
+
164
+ /**
165
+ * Finds the first occurrence of the rule.
166
+ * @return int timestamp
167
+ */
168
+ public function firstOccurrence() {
169
+ $t = $this->start;
170
+ if ( is_array( $this->excluded ) && in_array($t, $this->excluded))
171
+ $t = $this->findNext($t);
172
+ return $t;
173
+ }
174
+
175
+ /**
176
+ * Finds the absolute last occurrence of the rule from the given offset.
177
+ * Builds also the cache, if not set before...
178
+ * @return int timestamp
179
+ */
180
+ public function lastOccurrence() {
181
+ //build cache if not done
182
+ $this->getAllOccurrences();
183
+ //return last timestamp in cache
184
+ return end($this->cache);
185
+ }
186
+
187
+ /**
188
+ * Calculates the next time after the given offset that the rule
189
+ * will apply.
190
+ *
191
+ * The approach to finding the next is as follows:
192
+ * First we establish a timeframe to find timestamps in. This is
193
+ * between $offset and the end of the period that $offset is in.
194
+ *
195
+ * We then loop though all the rules (that is a Prerule in the
196
+ * current freq.), and finds the smallest timestamp inside the
197
+ * timeframe.
198
+ *
199
+ * If we find something, we check if the date is a valid recurrence
200
+ * (with validDate). If it is, we return it. Otherwise we try to
201
+ * find a new date inside the same timeframe (but using the new-
202
+ * found date as offset)
203
+ *
204
+ * If no new timestamps were found in the period, we try in the
205
+ * next period
206
+ *
207
+ * @param int $offset
208
+ * @return int
209
+ */
210
+ public function findNext($offset) {
211
+ if (!empty($this->cache)) {
212
+ foreach($this->cache as $ts) {
213
+ if ($ts > $offset)
214
+ return $ts;
215
+ }
216
+ }
217
+
218
+ $debug = false;
219
+
220
+ //make sure the offset is valid
221
+ if( $offset === false || (isset($this->rules['until']) && $offset > $this->rules['until']) ) {
222
+ if($debug) echo 'STOP: ' . date('r', $offset) . "\n";
223
+ return false;
224
+ }
225
+
226
+ $found = true;
227
+
228
+ //set the timestamp of the offset (ignoring hours and minutes unless we want them to be
229
+ //part of the calculations.
230
+ if($debug) echo 'O: ' . date('r', $offset) . "\n";
231
+ $hour = (in_array($this->freq, array('hourly','minutely')) && $offset > $this->start) ? date('H', $offset) : date('H', $this->start);
232
+ $minute = (($this->freq == 'minutely' || isset($this->rules['byminute'])) && $offset > $this->start) ? date('i', $offset) : date('i', $this->start);
233
+ $t = mktime($hour, $minute, date('s', $this->start), date('m', $offset), date('d', $offset), date('Y',$offset));
234
+ if($debug) echo 'START: ' . date('r', $t) . "\n";
235
+
236
+ if( $this->simpleMode ) {
237
+ if( $offset < $t ) {
238
+ $ts = $t;
239
+ if ($ts && in_array($ts, $this->excluded))
240
+ $ts = $this->findNext($ts);
241
+ } else {
242
+ $ts = $this->findStartingPoint( $t, $this->rules['interval'], false );
243
+ if( !$this->validDate( $ts ) ) {
244
+ $ts = $this->findNext($ts);
245
+ }
246
+ }
247
+ return $ts;
248
+ }
249
+
250
+ $eop = $this->findEndOfPeriod($offset);
251
+ if($debug) echo 'EOP: ' . date('r', $eop) . "\n";
252
+
253
+ foreach( $this->knownRules AS $rule ) {
254
+ if( $found && isset($this->rules['by' . $rule]) ) {
255
+ if( $this->isPrerule($rule, $this->freq) ) {
256
+ $subrules = explode(',', $this->rules['by' . $rule]);
257
+ $_t = null;
258
+ foreach( $subrules AS $subrule ) {
259
+ $imm = call_user_func_array(array($this, 'ruleBy' . $rule), array($subrule, $t));
260
+ if( $imm === false ) {
261
+ break;
262
+ }
263
+ if($debug) echo strtoupper($rule) . ': ' . date('r', $imm) . ' A: ' . ((int) ($imm > $offset && $imm < $eop)) . "\n";
264
+ if( $imm > $offset && $imm < $eop && ($_t == null || $imm < $_t) ) {
265
+ $_t = $imm;
266
+ }
267
+ }
268
+ if( $_t !== null ) {
269
+ $t = $_t;
270
+ } else {
271
+ $found = $this->validDate($t);
272
+ }
273
+ }
274
+ }
275
+ }
276
+
277
+ if( $offset < $this->start && $this->start < $t ) {
278
+ $ts = $this->start;
279
+ } else if( $found && ($t != $offset)) {
280
+ if( $this->validDate( $t ) ) {
281
+ if($debug) echo 'OK' . "\n";
282
+ $ts = $t;
283
+ } else {
284
+ if($debug) echo 'Invalid' . "\n";
285
+ $ts = $this->findNext($t);
286
+ }
287
+ } else {
288
+ if($debug) echo 'Not found' . "\n";
289
+ $ts = $this->findNext( $this->findStartingPoint( $offset, $this->rules['interval'] ) );
290
+ }
291
+ if ( is_array( $this->excluded ) && $ts && in_array($ts, $this->excluded))
292
+ return $this->findNext($ts);
293
+
294
+ return $ts;
295
+ }
296
+
297
+ /**
298
+ * Finds the starting point for the next rule. It goes $interval
299
+ * 'freq' forward in time since the given offset
300
+ * @param int $offset
301
+ * @param int $interval
302
+ * @param boolean $truncate
303
+ * @return int
304
+ */
305
+ private function findStartingPoint( $offset, $interval, $truncate = true ) {
306
+ $_freq = ($this->freq == 'daily') ? 'day__' : $this->freq;
307
+ $t = '+' . $interval . ' ' . substr($_freq,0,-2) . 's';
308
+ if( $_freq == 'monthly' && $truncate ) {
309
+ if( $interval > 1) {
310
+ $offset = strtotime('+' . ($interval - 1) . ' months ', $offset);
311
+ }
312
+ $t = '+' . (date('t', $offset) - date('d', $offset) + 1) . ' days';
313
+ }
314
+
315
+ $sp = strtotime($t, $offset);
316
+
317
+ if( $truncate ) {
318
+ $sp = $this->truncateToPeriod($sp, $this->freq);
319
+ }
320
+
321
+ return $sp;
322
+ }
323
+
324
+ /**
325
+ * Finds the earliest timestamp posible outside this perioid
326
+ * @param int $offset
327
+ * @return int
328
+ */
329
+ public function findEndOfPeriod($offset) {
330
+ return $this->findStartingPoint($offset, 1);
331
+ }
332
+
333
+ /**
334
+ * Resets the timestamp to the beginning of the
335
+ * period specified by freq
336
+ *
337
+ * Yes - the fall-through is on purpose!
338
+ *
339
+ * @param int $time
340
+ * @param int $freq
341
+ * @return int
342
+ */
343
+ private function truncateToPeriod( $time, $freq ) {
344
+ $date = getdate($time);
345
+ switch( $freq ) {
346
+ case "yearly":
347
+ $date['mon'] = 1;
348
+ case "monthly":
349
+ $date['mday'] = 1;
350
+ case "daily":
351
+ $date['hours'] = 0;
352
+ case 'hourly':
353
+ $date['minutes'] = 0;
354
+ case "minutely":
355
+ $date['seconds'] = 0;
356
+ break;
357
+ case "weekly":
358
+ if( date('N', $time) == 1) {
359
+ $date['hours'] = 0;
360
+ $date['minutes'] = 0;
361
+ $date['seconds'] = 0;
362
+ } else {
363
+ $date = getdate(strtotime("last monday 0:00", $time));
364
+ }
365
+ break;
366
+ }
367
+ $d = mktime($date['hours'], $date['minutes'], $date['seconds'], $date['mon'], $date['mday'], $date['year']);
368
+ return $d;
369
+ }
370
+
371
+ /**
372
+ * Applies the BYDAY rule to the given timestamp
373
+ * @param string $rule
374
+ * @param int $t
375
+ * @return int
376
+ */
377
+ private function ruleByday($rule, $t) {
378
+ $dir = ($rule{0} == '-') ? -1 : 1;
379
+ $dir_t = ($dir == 1) ? 'next' : 'last';
380
+
381
+
382
+ $d = $this->weekdays[substr($rule,-2)];
383
+ $s = $dir_t . ' ' . $d . ' ' . date('H:i:s',$t);
384
+
385
+ if( $rule == substr($rule, -2) ) {
386
+ if( date('l', $t) == ucfirst($d) ) {
387
+ $s = 'today ' . date('H:i:s',$t);
388
+ }
389
+
390
+ $_t = strtotime($s, $t);
391
+
392
+ if( $_t == $t && in_array($this->freq, array('monthly', 'yearly')) ) {
393
+ // Yes. This is not a great idea.. but hey, it works.. for now
394
+ $s = 'next ' . $d . ' ' . date('H:i:s',$t);
395
+ $_t = strtotime($s, $_t);
396
+ }
397
+
398
+ return $_t;
399
+ } else {
400
+ $_f = $this->freq;
401
+ if( isset($this->rules['bymonth']) && $this->freq == 'yearly' ) {
402
+ $this->freq = 'monthly';
403
+ }
404
+ if( $dir == -1 ) {
405
+ $_t = $this->findEndOfPeriod($t);
406
+ } else {
407
+ $_t = $this->truncateToPeriod($t, $this->freq);
408
+ }
409
+ $this->freq = $_f;
410
+
411
+ $c = preg_replace('/[^0-9]/','',$rule);
412
+ $c = ($c == '') ? 1 : $c;
413
+
414
+ $n = $_t;
415
+ while($c > 0 ) {
416
+ if( $dir == 1 && $c == 1 && date('l', $t) == ucfirst($d) ) {
417
+ $s = 'today ' . date('H:i:s',$t);
418
+ }
419
+ $n = strtotime($s, $n);
420
+ $c--;
421
+ }
422
+
423
+ return $n;
424
+ }
425
+ }
426
+
427
+ private function ruleBymonth($rule, $t) {
428
+ $_t = mktime(date('H',$t), date('i',$t), date('s',$t), $rule, date('d', $t), date('Y', $t));
429
+ if( $t == $_t && isset($this->rules['byday']) ) {
430
+ // TODO: this should check if one of the by*day's exists, and have a multi-day value
431
+ return false;
432
+ } else {
433
+ return $_t;
434
+ }
435
+ }
436
+
437
+ private function ruleBymonthday($rule, $t) {
438
+ if( $rule < 0 ) {
439
+ $rule = date('t', $t) + $rule + 1;
440
+ }
441
+ return mktime(date('H',$t), date('i',$t), date('s',$t), date('m', $t), $rule, date('Y', $t));
442
+ }
443
+
444
+ private function ruleByyearday($rule, $t) {
445
+ if( $rule < 0 ) {
446
+ $_t = $this->findEndOfPeriod();
447
+ $d = '-';
448
+ } else {
449
+ $_t = $this->truncateToPeriod($t, $this->freq);
450
+ $d = '+';
451
+ }
452
+ $s = $d . abs($rule -1) . ' days ' . date('H:i:s',$t);
453
+ return strtotime($s, $_t);
454
+ }
455
+
456
+ private function ruleByweekno($rule, $t) {
457
+ if( $rule < 0 ) {
458
+ $_t = $this->findEndOfPeriod();
459
+ $d = '-';
460
+ } else {
461
+ $_t = $this->truncateToPeriod($t, $this->freq);
462
+ $d = '+';
463
+ }
464
+
465
+ $sub = (date('W', $_t) == 1) ? 2 : 1;
466
+ $s = $d . abs($rule - $sub) . ' weeks ' . date('H:i:s',$t);
467
+ $_t = strtotime($s, $_t);
468
+
469
+ return $_t;
470
+ }
471
+
472
+ private function ruleByhour($rule, $t) {
473
+ $_t = mktime($rule, date('i',$t), date('s',$t), date('m',$t), date('d', $t), date('Y', $t));
474
+ return $_t;
475
+ }
476
+
477
+ private function ruleByminute($rule, $t) {
478
+ $_t = mktime(date('h',$t), $rule, date('s',$t), date('m',$t), date('d', $t), date('Y', $t));
479
+ return $_t;
480
+ }
481
+
482
+ private function validDate( $t ) {
483
+ if( isset($this->rules['until']) && $t > $this->rules['until'] ) {
484
+ return false;
485
+ }
486
+
487
+ if ( is_array( $this->excluded ) && in_array($t, $this->excluded)) {
488
+ return false;
489
+ }
490
+
491
+ if( isset($this->rules['bymonth']) ) {
492
+ $months = explode(',', $this->rules['bymonth']);
493
+ if( !in_array(date('m', $t), $months)) {
494
+ return false;
495
+ }
496
+ }
497
+ if( isset($this->rules['byday']) ) {
498
+ $days = explode(',', $this->rules['byday']);
499
+ foreach( $days As $i => $k ) {
500
+ $days[$i] = $this->weekdays[ preg_replace('/[^A-Z]/', '', $k)];
501
+ }
502
+ if( !in_array(strtolower(date('l', $t)), $days)) {
503
+ return false;
504
+ }
505
+ }
506
+ if( isset($this->rules['byweekno']) ) {
507
+ $weeks = explode(',', $this->rules['byweekno']);
508
+ if( !in_array(date('W', $t), $weeks)) {
509
+ return false;
510
+ }
511
+ }
512
+ if( isset($this->rules['bymonthday'])) {
513
+ $weekdays = explode(',', $this->rules['bymonthday']);
514
+ foreach( $weekdays As $i => $k ) {
515
+ if( $k < 0 ) {
516
+ $weekdays[$i] = date('t', $t) + $k + 1;
517
+ }
518
+ }
519
+ if( !in_array(date('d', $t), $weekdays)) {
520
+ return false;
521
+ }
522
+ }
523
+ if( isset($this->rules['byhour']) ) {
524
+ $hours = explode(',', $this->rules['byhour']);
525
+ if( !in_array(date('H', $t), $hours)) {
526
+ return false;
527
+ }
528
+ }
529
+
530
+ return true;
531
+ }
532
+
533
+ private function isPrerule($rule, $freq) {
534
+ if( $rule == 'year')
535
+ return false;
536
+ if( $rule == 'month' && $freq == 'yearly')
537
+ return true;
538
+ if( $rule == 'monthday' && in_array($freq, array('yearly', 'monthly')) && !isset($this->rules['byday']))
539
+ return true;
540
+ // TODO: is it faster to do monthday first, and ignore day if monthday exists? - prolly by a factor of 4..
541
+ if( $rule == 'yearday' && $freq == 'yearly' )
542
+ return true;
543
+ if( $rule == 'weekno' && $freq == 'yearly' )
544
+ return true;
545
+ if( $rule == 'day' && in_array($freq, array('yearly', 'monthly', 'weekly')))
546
+ return true;
547
+ if( $rule == 'hour' && in_array($freq, array('yearly', 'monthly', 'weekly', 'daily')))
548
+ return true;
549
+ if( $rule == 'minute' )
550
+ return true;
551
+
552
+ return false;
553
+ }
554
+ }
lib/helpers/SG_iCal_Line.php ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php // BUILD: Remove line
2
+
3
+ /**
4
+ * A class for storing a single (complete) line of the iCal file.
5
+ * Will find the line-type, the arguments and the data of the file and
6
+ * store them.
7
+ *
8
+ * The line-type can be found by querying getIdent(), data via either
9
+ * getData() or typecasting to a string.
10
+ * Params can be access via the ArrayAccess. A iterator is also avilable
11
+ * to iterator over the params.
12
+ *
13
+ * @package SG_iCalReader
14
+ * @author Morten Fangel (C) 2008
15
+ * @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
16
+ */
17
+ class SG_iCal_Line implements ArrayAccess, Countable, IteratorAggregate {
18
+ protected $ident;
19
+ protected $data;
20
+ protected $params = array();
21
+
22
+ protected $replacements = array('from'=>array('\\,', '\\n', '\\;', '\\:', '\\"'), 'to'=>array(',', "\n", ';', ':', '"'));
23
+
24
+ /**
25
+ * Constructs a new line.
26
+ */
27
+ public function __construct( $line ) {
28
+ $split = strpos($line, ':');
29
+ $idents = explode(';', substr($line, 0, $split));
30
+ $ident = strtolower(array_shift($idents));
31
+
32
+ $data = trim(substr($line, $split+1));
33
+ $data = str_replace($this->replacements['from'], $this->replacements['to'], $data);
34
+
35
+ $params = array();
36
+ foreach( $idents AS $v) {
37
+ list($k, $v) = explode('=', $v);
38
+ $params[ strtolower($k) ] = $v;
39
+ }
40
+
41
+ $this->ident = $ident;
42
+ $this->params = $params;
43
+ $this->data = $data;
44
+ }
45
+
46
+ /**
47
+ * Is this line the begining of a new block?
48
+ * @return bool
49
+ */
50
+ public function isBegin() {
51
+ return $this->ident == 'begin';
52
+ }
53
+
54
+ /**
55
+ * Is this line the end of a block?
56
+ * @return bool
57
+ */
58
+ public function isEnd() {
59
+ return $this->ident == 'end';
60
+ }
61
+
62
+ /**
63
+ * Returns the line-type (ident) of the line
64
+ * @return string
65
+ */
66
+ public function getIdent() {
67
+ return $this->ident;
68
+ }
69
+
70
+ /**
71
+ * Returns the content of the line
72
+ * @return string
73
+ */
74
+ public function getData() {
75
+ return $this->data;
76
+ }
77
+
78
+ /**
79
+ * Returns the content of the line
80
+ * @return string
81
+ */
82
+ public function getDataAsArray() {
83
+ if (strpos($this->data,",") !== false) {
84
+ return explode(",",$this->data);
85
+ }
86
+ else
87
+ return array($this->data);
88
+ }
89
+
90
+ /**
91
+ * A static helper to get a array of SG_iCal_Line's, and calls
92
+ * getData() on each of them to lay the data "bare"..
93
+ *
94
+ * @param SG_iCal_Line[]
95
+ * @return array
96
+ */
97
+ public static function Remove_Line($arr) {
98
+ $rtn = array();
99
+ foreach( $arr AS $k => $v ) {
100
+ if(is_array($v)) {
101
+ $rtn[$k] = self::Remove_Line($v);
102
+ } elseif( $v instanceof SG_iCal_Line ) {
103
+ $rtn[$k] = $v->getData();
104
+ } else {
105
+ $rtn[$k] = $v;
106
+ }
107
+ }
108
+ return $rtn;
109
+ }
110
+
111
+ /**
112
+ * @see ArrayAccess.offsetExists
113
+ */
114
+ public function offsetExists( $param ) {
115
+ return isset($this->params[ strtolower($param) ]);
116
+ }
117
+
118
+ /**
119
+ * @see ArrayAccess.offsetGet
120
+ */
121
+ public function offsetGet( $param ) {
122
+ $index = strtolower($param);
123
+ if (isset($this->params[ $index ])) {
124
+ return $this->params[ $index ];
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Disabled ArrayAccess requirement
130
+ * @see ArrayAccess.offsetSet
131
+ */
132
+ public function offsetSet( $param, $val ) {
133
+ return false;
134
+ }
135
+
136
+ /**
137
+ * Disabled ArrayAccess requirement
138
+ * @see ArrayAccess.offsetUnset
139
+ */
140
+ public function offsetUnset( $param ) {
141
+ return false;
142
+ }
143
+
144
+ /**
145
+ * toString method.
146
+ * @see getData()
147
+ */
148
+ public function __toString() {
149
+ return $this->getData();
150
+ }
151
+
152
+ /**
153
+ * @see Countable.count
154
+ */
155
+ public function count() {
156
+ return count($this->params);
157
+ }
158
+
159
+ /**
160
+ * @see IteratorAggregate.getIterator
161
+ */
162
+ public function getIterator() {
163
+ return new ArrayIterator($this->params);
164
+ }
165
+ }
lib/helpers/SG_iCal_Parser.php ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php // BUILD: Remove line
2
+
3
+ class SG_iCal_Parser {
4
+ /**
5
+ * Fetches $url and passes it on to be parsed
6
+ * @param string $url
7
+ * @param SG_iCal $ical
8
+ */
9
+ public static function Parse( $url, SG_iCal $ical ) {
10
+ $content = self::Fetch( $url );
11
+ $content = self::UnfoldLines($content);
12
+ self::_Parse( $content, $ical );
13
+ }
14
+
15
+ /**
16
+ * Passes a text string on to be parsed
17
+ * @param string $content
18
+ * @param SG_iCal $ical
19
+ */
20
+ public static function ParseString($content, SG_iCal $ical ) {
21
+ $content = self::UnfoldLines($content);
22
+ self::_Parse( $content, $ical );
23
+ }
24
+
25
+ /**
26
+ * Fetches a resource and tries to make sure it's UTF8
27
+ * encoded
28
+ * @return string
29
+ */
30
+ protected static function Fetch( $resource ) {
31
+ $is_utf8 = true;
32
+
33
+ if( is_file( $resource ) ) {
34
+ // The resource is a local file
35
+ $content = file_get_contents($resource);
36
+
37
+ if( ! self::_ValidUtf8( $content ) ) {
38
+ // The file doesn't appear to be UTF8
39
+ $is_utf8 = false;
40
+ }
41
+ } else {
42
+ // The resource isn't local, so it's assumed to
43
+ // be a URL
44
+ $c = curl_init();
45
+ curl_setopt($c, CURLOPT_URL, $resource);
46
+ curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
47
+ curl_setopt($c, CURLOPT_USERAGENT, 'PHP/'.PHP_VERSION);
48
+ curl_setopt($c, CURLOPT_ENCODING, '');
49
+ if( strstr( $resource, 'https' ) !== FALSE ) {
50
+ curl_setopt($c, CURLOPT_SSLVERSION, 3);
51
+ curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
52
+ curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
53
+ }
54
+ curl_setopt($c, CURLOPT_COOKIESESSION, true);
55
+ curl_setopt($c, CURLOPT_HEADER, true);
56
+ if( !ini_get('safe_mode') ){
57
+ curl_setopt($c, CURLOPT_FOLLOWLOCATION, true);
58
+ }
59
+ $content = curl_exec($c);
60
+ $ct = curl_getinfo($c, CURLINFO_CONTENT_TYPE);
61
+ $enc = preg_replace('/^.*charset=([-a-zA-Z0-9]+).*$/', '$1', $ct);
62
+ if( ! self::_ValidUtf8( $content ) ) {
63
+ // The data isn't utf-8
64
+ $is_utf8 = false;
65
+ }
66
+ }
67
+
68
+ if( !$is_utf8 ) {
69
+ $content = utf8_encode($content);
70
+ }
71
+
72
+ return $content;
73
+ }
74
+
75
+ /**
76
+ * Takes the string $content, and creates a array of iCal lines.
77
+ * This includes unfolding multi-line entries into a single line.
78
+ * @param $content string
79
+ */
80
+ protected static function UnfoldLines($content) {
81
+ $data = array();
82
+ $content = explode("\n", $content);
83
+ for( $i=0; $i < count($content); $i++) {
84
+ $line = rtrim($content[$i]);
85
+ while( isset($content[$i+1]) && strlen($content[$i+1]) > 0 && ($content[$i+1]{0} == ' ' || $content[$i+1]{0} == "\t" )) {
86
+ $line .= rtrim(substr($content[++$i],1));
87
+ }
88
+ $data[] = $line;
89
+ }
90
+ return $data;
91
+ }
92
+
93
+ /**
94
+ * Parses the feed found in content and calls storeSection to store
95
+ * parsed data
96
+ * @param string $content
97
+ * @param SG_iCal $ical
98
+ */
99
+ private static function _Parse( $content, SG_iCal $ical ) {
100
+ $main_sections = array('vevent', 'vjournal', 'vtodo', 'vtimezone', 'vcalendar');
101
+ $array_idents = array('exdate','rdate');
102
+ $sections = array();
103
+ $section = '';
104
+ $current_data = array();
105
+ foreach( $content AS $line ) {
106
+ $line = new SG_iCal_Line($line);
107
+ if( $line->isBegin() ) {
108
+ // New block of data, $section = new block
109
+ $section = strtolower($line->getData());
110
+ $sections[] = strtolower($line->getData());
111
+ } elseif( $line->isEnd() ) {
112
+ // End of block of data ($removed = just ended block, $section = new top-block)
113
+ $removed = array_pop($sections);
114
+ $section = end($sections);
115
+
116
+ if( array_search($removed, $main_sections) !== false ) {
117
+ self::StoreSection( $removed, $current_data[$removed], $ical);
118
+ $current_data[$removed] = array();
119
+ }
120
+ } else {
121
+ // Data line
122
+ foreach( $main_sections AS $s ) {
123
+ // Loops though the main sections
124
+ if( array_search($s, $sections) !== false ) {
125
+ // This section is in the main section
126
+ if( $section == $s ) {
127
+ // It _is_ the main section else
128
+ if (in_array($line->getIdent(), $array_idents))
129
+ //exdate could appears more that once
130
+ $current_data[$s][$line->getIdent()][] = $line;
131
+ else {
132
+ $current_data[$s][$line->getIdent()] = $line;
133
+ }
134
+ } else {
135
+ // Sub section
136
+ $current_data[$s][$section][$line->getIdent()] = $line;
137
+ }
138
+ break;
139
+ }
140
+ }
141
+ }
142
+ }
143
+ $current_data = array();
144
+ }
145
+
146
+ /**
147
+ * Stores the data in provided SG_iCal object
148
+ * @param string $section eg 'vcalender', 'vevent' etc
149
+ * @param string $data
150
+ * @param SG_iCal $ical
151
+ */
152
+ protected static function storeSection( $section, $data, SG_iCal $ical ) {
153
+ $data = SG_iCal_Factory::Factory($ical, $section, $data);
154
+ switch( $section ) {
155
+ case 'vcalendar':
156
+ return $ical->setCalendarInfo( $data );
157
+ case 'vevent':
158
+ return $ical->addEvent( $data );
159
+ case 'vjournal':
160
+ case 'vtodo':
161
+ return true; // TODO: Implement
162
+ case 'vtimezone':
163
+ return $ical->addTimeZone( $data );
164
+ }
165
+ }
166
+
167
+ /**
168
+ * This functions does some regexp checking to see if the value is
169
+ * valid UTF-8.
170
+ *
171
+ * The function is from the book "Building Scalable Web Sites" by
172
+ * Cal Henderson.
173
+ *
174
+ * @param string $data
175
+ * @return bool
176
+ */
177
+ private static function _ValidUtf8( $data ) {
178
+ $rx = '[\xC0-\xDF]([^\x80-\xBF]|$)';
179
+ $rx .= '|[\xE0-\xEF].{0,1}([^\x80-\xBF]|$)';
180
+ $rx .= '|[\xF0-\xF7].{0,2}([^\x80-\xBF]|$)';
181
+ $rx .= '|[\xF8-\xFB].{0,3}([^\x80-\xBF]|$)';
182
+ $rx .= '|[\xFC-\xFD].{0,4}([^\x80-\xBF]|$)';
183
+ $rx .= '|[\xFE-\xFE].{0,5}([^\x80-\xBF]|$)';
184
+ $rx .= '|[\x00-\x7F][\x80-\xBF]';
185
+ $rx .= '|[\xC0-\xDF].[\x80-\xBF]';
186
+ $rx .= '|[\xE0-\xEF]..[\x80-\xBF]';
187
+ $rx .= '|[\xF0-\xF7]...[\x80-\xBF]';
188
+ $rx .= '|[\xF8-\xFB]....[\x80-\xBF]';
189
+ $rx .= '|[\xFC-\xFD].....[\x80-\xBF]';
190
+ $rx .= '|[\xFE-\xFE]......[\x80-\xBF]';
191
+ $rx .= '|^[\x80-\xBF]';
192
+
193
+ return ( ! (bool) preg_match('!'.$rx.'!', $data) );
194
+ }
195
+ }
196
+
197
+
lib/helpers/SG_iCal_Query.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php // BUILD: Remove line
2
+
3
+ /**
4
+ * A collection of functions to query the events in a calendar.
5
+ *
6
+ * @package SG_iCalReader
7
+ * @author Morten Fangel (C) 2008
8
+ * @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
9
+ */
10
+ class SG_iCal_Query {
11
+ /**
12
+ * Returns all events from the calendar between two timestamps
13
+ *
14
+ * Note that the events returned needs only slightly overlap.
15
+ *
16
+ * @param SG_iCalReader|array $ical The calendar to query
17
+ * @param int $start
18
+ * @param int $end
19
+ * @return SG_iCal_VEvent[]
20
+ */
21
+ public static function Between( $ical, $start, $end ) {
22
+ if( $ical instanceof SG_iCalReader ) {
23
+ $ical = $ical->getEvents();
24
+ }
25
+ if( !is_array($ical) ) {
26
+ throw new Exception('SG_iCal_Query::Between called with invalid input!');
27
+ }
28
+
29
+ $rtn = array();
30
+ foreach( $ical AS $e ) {
31
+ if( ($start <= $e->getStart() && $e->getStart() < $end)
32
+ || ($start < $e->getRangeEnd() && $e->getRangeEnd() <= $end) ) {
33
+ $rtn[] = $e;
34
+ }
35
+ }
36
+ return $rtn;
37
+ }
38
+
39
+ /**
40
+ * Returns all events from the calendar after a given timestamp
41
+ *
42
+ * @param SG_iCalReader|array $ical The calendar to query
43
+ * @param int $start
44
+ * @return SG_iCal_VEvent[]
45
+ */
46
+ public static function After( $ical, $start ) {
47
+ if( $ical instanceof SG_iCalReader ) {
48
+ $ical = $ical->getEvents();
49
+ }
50
+ if( !is_array($ical) ) {
51
+ throw new Exception('SG_iCal_Query::After called with invalid input!');
52
+ }
53
+
54
+ $rtn = array();
55
+ foreach( $ical AS $e ) {
56
+ if($e->getStart() >= $start || $e->getRangeEnd() >= $start) {
57
+ $rtn[] = $e;
58
+ }
59
+ }
60
+ return $rtn;
61
+ }
62
+
63
+ /**
64
+ * Sorts the events from the calendar after the specified column.
65
+ * Column can be all valid entires that getProperty can return.
66
+ * So stuff like uid, start, end, summary etc.
67
+ * @param SG_iCalReader|array $ical The calendar to query
68
+ * @param string $column
69
+ * @return SG_iCal_VEvent[]
70
+ */
71
+ public static function Sort( $ical, $column ) {
72
+ if( $ical instanceof SG_iCalReader ) {
73
+ $ical = $ical->getEvents();
74
+ }
75
+ if( !is_array($ical) ) {
76
+ throw new Exception('SG_iCal_Query::Sort called with invalid input!');
77
+ }
78
+
79
+ $cmp = create_function('$a, $b', 'return strcmp($a->getProperty("' . $column . '"), $b->getProperty("' . $column . '"));');
80
+ usort($ical, $cmp);
81
+ return $ical;
82
+ }
83
+ }
84
+
lib/helpers/SG_iCal_Recurrence.php ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php // BUILD: Remove line
2
+
3
+ /**
4
+ * A wrapper for recurrence rules in iCalendar. Parses the given line and puts the
5
+ * recurrence rules in the correct field of this object.
6
+ *
7
+ * See http://tools.ietf.org/html/rfc2445 for more information. Page 39 and onward contains more
8
+ * information on the recurrence rules themselves. Page 116 and onward contains
9
+ * some great examples which were often used for testing.
10
+ *
11
+ * @package SG_iCalReader
12
+ * @author Steven Oxley
13
+ * @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
14
+ */
15
+ class SG_iCal_Recurrence {
16
+
17
+ public $rrule;
18
+
19
+ protected $freq;
20
+
21
+ protected $until;
22
+ protected $count;
23
+
24
+ protected $interval;
25
+ protected $bysecond;
26
+ protected $byminute;
27
+ protected $byhour;
28
+ protected $byday;
29
+ protected $bymonthday;
30
+ protected $byyearday;
31
+ protected $byyearno;
32
+ protected $bymonth;
33
+ protected $bysetpos;
34
+
35
+ protected $wkst;
36
+
37
+ /**
38
+ * A list of the properties that can have comma-separated lists for values.
39
+ * @var array
40
+ */
41
+ protected $listProperties = array(
42
+ 'bysecond', 'byminute', 'byhour', 'byday', 'bymonthday',
43
+ 'byyearday', 'byyearno', 'bymonth', 'bysetpos'
44
+ );
45
+
46
+ /**
47
+ * Creates an recurrence object with a passed in line. Parses the line.
48
+ * @param object $line an SG_iCal_Line object which will be parsed to get the
49
+ * desired information.
50
+ */
51
+ public function __construct(SG_iCal_Line $line) {
52
+ $this->parseLine($line->getData());
53
+ }
54
+
55
+ /**
56
+ * Parses an 'RRULE' line and sets the member variables of this object.
57
+ * Expects a string that looks like this: 'FREQ=WEEKLY;INTERVAL=2;BYDAY=SU,TU,WE'
58
+ * @param string $line the line to be parsed
59
+ */
60
+ protected function parseLine($line) {
61
+ $this->rrule = $line;
62
+
63
+ //split up the properties
64
+ $recurProperties = explode(';', $line);
65
+ $recur = array();
66
+
67
+ //loop through the properties in the line and set their associated
68
+ //member variables
69
+ foreach ($recurProperties as $property) {
70
+ $nameAndValue = explode('=', $property);
71
+
72
+ //need the lower-case name for setting the member variable
73
+ $propertyName = strtolower($nameAndValue[0]);
74
+ $propertyValue = $nameAndValue[1];
75
+
76
+ //split up the list of values into an array (if it's a list)
77
+ if (in_array($propertyName, $this->listProperties)) {
78
+ $propertyValue = explode(',', $propertyValue);
79
+ }
80
+ $this->$propertyName = $propertyValue;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Set the $until member
86
+ * @param mixed timestamp (int) / Valid DateTime format (string)
87
+ */
88
+ public function setUntil($ts) {
89
+ if ( is_int($ts) )
90
+ $dt = new DateTime('@'.$ts);
91
+ else
92
+ $dt = new DateTime($ts);
93
+ $this->until = $dt->format('Ymd\THisO');
94
+ }
95
+
96
+ /**
97
+ * Retrieves the desired member variable and returns it (if it's set)
98
+ * @param string $member name of the member variable
99
+ * @return mixed the variable value (if set), false otherwise
100
+ */
101
+ protected function getMember($member)
102
+ {
103
+ if (isset($this->$member)) {
104
+ return $this->$member;
105
+ }
106
+ return false;
107
+ }
108
+
109
+ /**
110
+ * Returns the frequency - corresponds to FREQ in RFC 2445.
111
+ * @return mixed string if the member has been set, false otherwise
112
+ */
113
+ public function getFreq() {
114
+ return $this->getMember('freq');
115
+ }
116
+
117
+ /**
118
+ * Returns when the event will go until - corresponds to UNTIL in RFC 2445.
119
+ * @return mixed string if the member has been set, false otherwise
120
+ */
121
+ public function getUntil() {
122
+ return $this->getMember('until');
123
+ }
124
+
125
+ /**
126
+ * Returns the count of the times the event will occur (should only appear if 'until'
127
+ * does not appear) - corresponds to COUNT in RFC 2445.
128
+ * @return mixed string if the member has been set, false otherwise
129
+ */
130
+ public function getCount() {
131
+ return $this->getMember('count');
132
+ }
133
+
134
+ /**
135
+ * Returns the interval - corresponds to INTERVAL in RFC 2445.
136
+ * @return mixed string if the member has been set, false otherwise
137
+ */
138
+ public function getInterval() {
139
+ return $this->getMember('interval');
140
+ }
141
+
142
+ /**
143
+ * Returns the bysecond part of the event - corresponds to BYSECOND in RFC 2445.
144
+ * @return mixed string if the member has been set, false otherwise
145
+ */
146
+ public function getBySecond() {
147
+ return $this->getMember('bysecond');
148
+ }
149
+
150
+ /**
151
+ * Returns the byminute information for the event - corresponds to BYMINUTE in RFC 2445.
152
+ * @return mixed string if the member has been set, false otherwise
153
+ */
154
+ public function getByMinute() {
155
+ return $this->getMember('byminute');
156
+ }
157
+
158
+ /**
159
+ * Corresponds to BYHOUR in RFC 2445.
160
+ * @return mixed string if the member has been set, false otherwise
161
+ */
162
+ public function getByHour() {
163
+ return $this->getMember('byhour');
164
+ }
165
+
166
+ /**
167
+ *Corresponds to BYDAY in RFC 2445.
168
+ * @return mixed string if the member has been set, false otherwise
169
+ */
170
+ public function getByDay() {
171
+ return $this->getMember('byday');
172
+ }
173
+
174
+ /**
175
+ * Corresponds to BYMONTHDAY in RFC 2445.
176
+ * @return mixed string if the member has been set, false otherwise
177
+ */
178
+ public function getByMonthDay() {
179
+ return $this->getMember('bymonthday');
180
+ }
181
+
182
+ /**
183
+ * Corresponds to BYYEARDAY in RFC 2445.
184
+ * @return mixed string if the member has been set, false otherwise
185
+ */
186
+ public function getByYearDay() {
187
+ return $this->getMember('byyearday');
188
+ }
189
+
190
+ /**
191
+ * Corresponds to BYYEARNO in RFC 2445.
192
+ * @return mixed string if the member has been set, false otherwise
193
+ */
194
+ public function getByYearNo() {
195
+ return $this->getMember('byyearno');
196
+ }
197
+
198
+ /**
199
+ * Corresponds to BYMONTH in RFC 2445.
200
+ * @return mixed string if the member has been set, false otherwise
201
+ */
202
+ public function getByMonth() {
203
+ return $this->getMember('bymonth');
204
+ }
205
+
206
+ /**
207
+ * Corresponds to BYSETPOS in RFC 2445.
208
+ * @return mixed string if the member has been set, false otherwise
209
+ */
210
+ public function getBySetPos() {
211
+ return $this->getMember('bysetpos');
212
+ }
213
+
214
+ /**
215
+ * Corresponds to WKST in RFC 2445.
216
+ * @return mixed string if the member has been set, false otherwise
217
+ */
218
+ public function getWkst() {
219
+ return $this->getMember('wkst');
220
+ }
221
+ }
lib/iCalUtilityFunctions.class.php ADDED
@@ -0,0 +1,1505 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * iCalcreator class v2.10
4
+ * copyright (c) 2007-2011 Kjell-Inge Gustafsson kigkonsult
5
+ * www.kigkonsult.se/iCalcreator/index.php
6
+ * ical@kigkonsult.se
7
+ *
8
+ * This library is free software; you can redistribute it and/or
9
+ * modify it under the terms of the GNU Lesser General Public
10
+ * License as published by the Free Software Foundation; either
11
+ * version 2.1 of the License, or (at your option) any later version.
12
+ *
13
+ * This library is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
+ * Lesser General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU Lesser General Public
19
+ * License along with this library; if not, write to the Free Software
20
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
+ */
22
+ /**
23
+ * moving all utility (static) functions to a utility class
24
+ *
25
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
26
+ * @since 2.6.22 - 2010-09-25
27
+ *
28
+ */
29
+ class iCalUtilityFunctions {
30
+ // Store the single instance of iCalUtilityFunctions
31
+ private static $m_pInstance;
32
+
33
+ // Private constructor to limit object instantiation to within the class
34
+ private function __construct() {
35
+ $m_pInstance = FALSE;
36
+ }
37
+
38
+ // Getter method for creating/returning the single instance of this class
39
+ public static function getInstance() {
40
+ if (!self::$m_pInstance)
41
+ self::$m_pInstance = new iCalUtilityFunctions();
42
+
43
+ return self::$m_pInstance;
44
+ }
45
+ /**
46
+ * check a date(-time) for an opt. timezone and if it is a DATE-TIME or DATE
47
+ *
48
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
49
+ * @since 2.4.16 - 2008-10-25
50
+ * @param array $date, date to check
51
+ * @param int $parno, no of date parts (i.e. year, month.. .)
52
+ * @return array $params, property parameters
53
+ */
54
+ public static function _chkdatecfg( $theDate, & $parno, & $params ) {
55
+ if( isset( $params['TZID'] ))
56
+ $parno = 6;
57
+ elseif( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] ))
58
+ $parno = 3;
59
+ else {
60
+ if( isset( $params['VALUE'] ) && ( 'PERIOD' == $params['VALUE'] ))
61
+ $parno = 7;
62
+ if( is_array( $theDate )) {
63
+ if( isset( $theDate['timestamp'] ))
64
+ $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : null;
65
+ else
66
+ $tzid = ( isset( $theDate['tz'] )) ? $theDate['tz'] : ( 7 == count( $theDate )) ? end( $theDate ) : null;
67
+ if( !empty( $tzid )) {
68
+ $parno = 7;
69
+ if( !iCalUtilityFunctions::_isOffset( $tzid ))
70
+ $params['TZID'] = $tzid; // save only timezone
71
+ }
72
+ elseif( !$parno && ( 3 == count( $theDate )) &&
73
+ ( isset( $params['VALUE'] ) && ( 'DATE' == $params['VALUE'] )))
74
+ $parno = 3;
75
+ else
76
+ $parno = 6;
77
+ }
78
+ else { // string
79
+ $date = trim( $theDate );
80
+ if( 'Z' == substr( $date, -1 ))
81
+ $parno = 7; // UTC DATE-TIME
82
+ elseif((( 8 == strlen( $date ) && ctype_digit( $date )) || ( 11 >= strlen( $date ))) &&
83
+ ( !isset( $params['VALUE'] ) || !in_array( $params['VALUE'], array( 'DATE-TIME', 'PERIOD' ))))
84
+ $parno = 3; // DATE
85
+ $date = iCalUtilityFunctions::_date_time_string( $date, $parno );
86
+ if( !empty( $date['tz'] )) {
87
+ $parno = 7;
88
+ if( !iCalUtilityFunctions::_isOffset( $date['tz'] ))
89
+ $params['TZID'] = $date['tz']; // save only timezone
90
+ }
91
+ elseif( empty( $parno ))
92
+ $parno = 6;
93
+ }
94
+ if( isset( $params['TZID'] ))
95
+ $parno = 6;
96
+ }
97
+ }
98
+ /**
99
+ * create (very simple) timezone and standard/daylight components
100
+ *
101
+ * Result when 'Europe/Stockholm' is used as timezone:
102
+ *
103
+ * BEGIN:VTIMEZONE
104
+ * TZID:Europe/Stockholm
105
+ * BEGIN:STANDARD
106
+ * DTSTART:20101031T020000
107
+ * TZOFFSETFROM:+0200
108
+ * TZOFFSETTO:+0100
109
+ * RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
110
+ * TZNAME:CET
111
+ * END:STANDARD
112
+ * BEGIN:DAYLIGHT
113
+ * DTSTART:20100328T030000
114
+ * TZOFFSETFROM:+0100
115
+ * TZOFFSETTO:+0200
116
+ * RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
117
+ * TZNAME:CEST
118
+ * END:DAYLIGHT
119
+ * END:VTIMEZONE
120
+ *
121
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
122
+ * @since 2.9.2 - 2011-05-30
123
+ * @param object $calendar, reference to an iCalcreator calendar instance
124
+ * @param string $timezone, a PHP5 (DateTimeZone) valid timezone
125
+ * @param array $xProp, *[x-propName => x-propValue], optional
126
+ * @return bool
127
+ */
128
+ public static function createTimezone( & $calendar, $timezone, $xProp=array() ) {
129
+ if( !class_exists( 'DateTimeZone' ))
130
+ return FALSE;
131
+ if( empty( $timezone ))
132
+ return FALSE;
133
+ try {
134
+ $dtz = new DateTimeZone( $timezone );
135
+ $transitions = $dtz->getTransitions();
136
+ $stdDTSTART = $stdTZOFFSETTO = $stdTZOFFSETFROM = $stdTZNAME = $dlghtDTSTART = $dlghtTZOFFSETTO = $dlghtTZOFFSETFROM = $dlghtTZNAME = FALSE;
137
+ foreach( $transitions as $trans ) {
138
+ if( substr( $trans['time'], 0, 4 ) > ( date( 'Y' ) - 1 ))
139
+ break;
140
+ if( $trans['isdst'] !== TRUE ) {
141
+ $stdDTSTART = $trans['time'];
142
+ $stdTZOFFSETTO = $dlghtTZOFFSETFROM = iCalUtilityFunctions::offsetSec2His( $trans['offset'] );
143
+ $stdTZNAME = $trans['abbr'];
144
+ }
145
+ else {
146
+ $dlghtDTSTART = $trans['time'];
147
+ $dlghtTZOFFSETTO = $stdTZOFFSETFROM = iCalUtilityFunctions::offsetSec2His( $trans['offset'] );
148
+ $dlghtTZNAME = $trans['abbr'];
149
+ }
150
+ }
151
+ if( $stdDTSTART && $stdTZOFFSETTO && $stdTZOFFSETFROM && $stdTZNAME && $dlghtDTSTART && $dlghtTZOFFSETTO && $dlghtTZOFFSETFROM && $dlghtTZNAME ) {
152
+ $tz = & $calendar->newComponent( 'vtimezone' );
153
+ $tz->setproperty( 'tzid', $timezone );
154
+ if( !empty( $xProp )) {
155
+ foreach( $xProp as $xPropName => $xPropValue )
156
+ if( 'x-' == strtolower( substr( $xPropName, 0, 2 )))
157
+ $tz->setproperty( $xPropName, $xPropValue );
158
+ }
159
+ $std = & $tz->newComponent( 'standard' );
160
+ $std->setProperty( 'dtstart', $stdDTSTART );
161
+ $std->setProperty( 'tzname', $stdTZNAME );
162
+ $std->setProperty( 'tzoffsetto', $stdTZOFFSETTO );
163
+ $std->setProperty( 'tzoffsetfrom', $stdTZOFFSETFROM );
164
+ $std->setProperty( 'RRULE', array( 'FREQ' => 'YEARLY', 'BYDAY' => array( '-1', 'DAY' => 'SU' ), 'BYMONTH' => 10 ));
165
+ $dlght = & $tz->newComponent( 'daylight' );
166
+ $dlght->setProperty( 'dtstart', $dlghtDTSTART );
167
+ $dlght->setProperty( 'tzname', $dlghtTZNAME );
168
+ $dlght->setProperty( 'tzoffsetto', $dlghtTZOFFSETTO );
169
+ $dlght->setProperty( 'tzoffsetfrom', $dlghtTZOFFSETFROM );
170
+ $dlght->setProperty( 'RRULE', array( 'FREQ' => 'YEARLY', 'BYDAY' => array( '-1', 'DAY' => 'SU' ), 'BYMONTH' => 3 ));
171
+ }
172
+ }
173
+ catch( Exception $e ) {
174
+ return FALSE;
175
+ }
176
+ return TRUE;
177
+ }
178
+ /**
179
+ * convert date/datetime to timestamp
180
+ *
181
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
182
+ * @since 2.4.8 - 2008-10-30
183
+ * @param array $datetime datetime/(date)
184
+ * @param string $tz timezone
185
+ * @return timestamp
186
+ */
187
+ public static function _date2timestamp( $datetime, $tz=null ) {
188
+ $output = null;
189
+ if( !isset( $datetime['hour'] )) $datetime['hour'] = '0';
190
+ if( !isset( $datetime['min'] )) $datetime['min'] = '0';
191
+ if( !isset( $datetime['sec'] )) $datetime['sec'] = '0';
192
+ foreach( $datetime as $dkey => $dvalue ) {
193
+ if( 'tz' != $dkey )
194
+ $datetime[$dkey] = (integer) $dvalue;
195
+ }
196
+ if( $tz )
197
+ $datetime['tz'] = $tz;
198
+ $offset = ( isset( $datetime['tz'] ) && ( '' < trim ( $datetime['tz'] ))) ? iCalUtilityFunctions::_tz2offset( $datetime['tz'] ) : 0;
199
+ $output = mktime( $datetime['hour'], $datetime['min'], ($datetime['sec'] + $offset), $datetime['month'], $datetime['day'], $datetime['year'] );
200
+ return $output;
201
+ }
202
+ /**
203
+ * ensures internal date-time/date format for input date-time/date in array format
204
+ *
205
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
206
+ * @since 0.3.0 - 2006-08-15
207
+ * @param array $datetime
208
+ * @param int $parno optional, default FALSE
209
+ * @return array
210
+ */
211
+ public static function _date_time_array( $datetime, $parno=FALSE ) {
212
+ $output = array();
213
+ foreach( $datetime as $dateKey => $datePart ) {
214
+ switch ( $dateKey ) {
215
+ case '0': case 'year': $output['year'] = $datePart; break;
216
+ case '1': case 'month': $output['month'] = $datePart; break;
217
+ case '2': case 'day': $output['day'] = $datePart; break;
218
+ }
219
+ if( 3 != $parno ) {
220
+ switch ( $dateKey ) {
221
+ case '0':
222
+ case '1':
223
+ case '2': break;
224
+ case '3': case 'hour': $output['hour'] = $datePart; break;
225
+ case '4': case 'min' : $output['min'] = $datePart; break;
226
+ case '5': case 'sec' : $output['sec'] = $datePart; break;
227
+ case '6': case 'tz' : $output['tz'] = $datePart; break;
228
+ }
229
+ }
230
+ }
231
+ if( 3 != $parno ) {
232
+ if( !isset( $output['hour'] ))
233
+ $output['hour'] = 0;
234
+ if( !isset( $output['min'] ))
235
+ $output['min'] = 0;
236
+ if( !isset( $output['sec'] ))
237
+ $output['sec'] = 0;
238
+ }
239
+ return $output;
240
+ }
241
+ /**
242
+ * ensures internal date-time/date format for input date-time/date in string fromat
243
+ *
244
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
245
+ * @since 2.6.35 - 2010-12-03
246
+ * @param array $datetime
247
+ * @param int $parno optional, default FALSE
248
+ * @return array
249
+ */
250
+ public static function _date_time_string( $datetime, $parno=FALSE ) {
251
+ $datetime = (string) trim( $datetime );
252
+ $tz = null;
253
+ $len = strlen( $datetime ) - 1;
254
+ if( 'Z' == substr( $datetime, -1 )) {
255
+ $tz = 'Z';
256
+ $datetime = trim( substr( $datetime, 0, $len ));
257
+ }
258
+ elseif( ( ctype_digit( substr( $datetime, -2, 2 ))) && // time or date
259
+ ( '-' == substr( $datetime, -3, 1 )) ||
260
+ ( ':' == substr( $datetime, -3, 1 )) ||
261
+ ( '.' == substr( $datetime, -3, 1 ))) {
262
+ $continue = TRUE;
263
+ }
264
+ elseif( ( ctype_digit( substr( $datetime, -4, 4 ))) && // 4 pos offset
265
+ ( ' +' == substr( $datetime, -6, 2 )) ||
266
+ ( ' -' == substr( $datetime, -6, 2 ))) {
267
+ $tz = substr( $datetime, -5, 5 );
268
+ $datetime = substr( $datetime, 0, ($len - 5));
269
+ }
270
+ elseif( ( ctype_digit( substr( $datetime, -6, 6 ))) && // 6 pos offset
271
+ ( ' +' == substr( $datetime, -8, 2 )) ||
272
+ ( ' -' == substr( $datetime, -8, 2 ))) {
273
+ $tz = substr( $datetime, -7, 7 );
274
+ $datetime = substr( $datetime, 0, ($len - 7));
275
+ }
276
+ elseif( ( 6 < $len ) && ( ctype_digit( substr( $datetime, -6, 6 )))) {
277
+ $continue = TRUE;
278
+ }
279
+ elseif( 'T' == substr( $datetime, -7, 1 )) {
280
+ $continue = TRUE;
281
+ }
282
+ else {
283
+ $cx = $tx = 0; // 19970415T133000 US-Eastern
284
+ for( $cx = -1; $cx > ( 9 - $len ); $cx-- ) {
285
+ $char = substr( $datetime, $cx, 1 );
286
+ if(( ' ' == $char) || ctype_digit( $char))
287
+ break; // if exists, tz ends here.. . ?
288
+ else
289
+ $tx--; // tz length counter
290
+ }
291
+ if( 0 > $tx ) {
292
+ $tz = substr( $datetime, $tx );
293
+ $datetime = trim( substr( $datetime, 0, $len + $tx + 1 ));
294
+ }
295
+ }
296
+ if( 0 < substr_count( $datetime, '-' )) {
297
+ $datetime = str_replace( '-', '/', $datetime );
298
+ }
299
+ elseif( ctype_digit( substr( $datetime, 0, 8 )) &&
300
+ ( 'T' == substr( $datetime, 8, 1 )) &&
301
+ ctype_digit( substr( $datetime, 9, 6 ))) {
302
+ $datetime = substr( $datetime, 4, 2 )
303
+ .'/'.substr( $datetime, 6, 2 )
304
+ .'/'.substr( $datetime, 0, 4 )
305
+ .' '.substr( $datetime, 9, 2 )
306
+ .':'.substr( $datetime, 11, 2 )
307
+ .':'.substr( $datetime, 13);
308
+ }
309
+ $datestring = date( 'Y-m-d H:i:s', strtotime( $datetime ));
310
+ $tz = trim( $tz );
311
+ $output = array();
312
+ $output['year'] = substr( $datestring, 0, 4 );
313
+ $output['month'] = substr( $datestring, 5, 2 );
314
+ $output['day'] = substr( $datestring, 8, 2 );
315
+ if(( 6 == $parno ) || ( 7 == $parno ) || ( !$parno && ( 'Z' == $tz ))) {
316
+ $output['hour'] = substr( $datestring, 11, 2 );
317
+ $output['min'] = substr( $datestring, 14, 2 );
318
+ $output['sec'] = substr( $datestring, 17, 2 );
319
+ if( !empty( $tz ))
320
+ $output['tz'] = $tz;
321
+ }
322
+ elseif( 3 != $parno ) {
323
+ if(( '00' < substr( $datestring, 11, 2 )) ||
324
+ ( '00' < substr( $datestring, 14, 2 )) ||
325
+ ( '00' < substr( $datestring, 17, 2 ))) {
326
+ $output['hour'] = substr( $datestring, 11, 2 );
327
+ $output['min'] = substr( $datestring, 14, 2 );
328
+ $output['sec'] = substr( $datestring, 17, 2 );
329
+ }
330
+ if( !empty( $tz ))
331
+ $output['tz'] = $tz;
332
+ }
333
+ return $output;
334
+ }
335
+ /**
336
+ * convert local startdate/enddate (Ymd[His]) to duration array
337
+ *
338
+ * uses this component dates if missing input dates
339
+ *
340
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
341
+ * @since 2.6.11 - 2010-10-21
342
+ * @param array $startdate
343
+ * @param array $duration
344
+ * @return array duration
345
+ */
346
+ function _date2duration( $startdate, $enddate ) {
347
+ $startWdate = mktime( 0, 0, 0, $startdate['month'], $startdate['day'], $startdate['year'] );
348
+ $endWdate = mktime( 0, 0, 0, $enddate['month'], $enddate['day'], $enddate['year'] );
349
+ $wduration = $endWdate - $startWdate;
350
+ $dur = array();
351
+ $dur['week'] = (int) floor( $wduration / ( 7 * 24 * 60 * 60 ));
352
+ $wduration = $wduration % ( 7 * 24 * 60 * 60 );
353
+ $dur['day'] = (int) floor( $wduration / ( 24 * 60 * 60 ));
354
+ $wduration = $wduration % ( 24 * 60 * 60 );
355
+ $dur['hour'] = (int) floor( $wduration / ( 60 * 60 ));
356
+ $wduration = $wduration % ( 60 * 60 );
357
+ $dur['min'] = (int) floor( $wduration / ( 60 ));
358
+ $dur['sec'] = (int) $wduration % ( 60 );
359
+ return $dur;
360
+ }
361
+ /**
362
+ * ensures internal duration format for input in array format
363
+ *
364
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
365
+ * @since 2.1.1 - 2007-06-24
366
+ * @param array $duration
367
+ * @return array
368
+ */
369
+ public static function _duration_array( $duration ) {
370
+ $output = array();
371
+ if( is_array( $duration ) &&
372
+ ( 1 == count( $duration )) &&
373
+ isset( $duration['sec'] ) &&
374
+ ( 60 < $duration['sec'] )) {
375
+ $durseconds = $duration['sec'];
376
+ $output['week'] = floor( $durseconds / ( 60 * 60 * 24 * 7 ));
377
+ $durseconds = $durseconds % ( 60 * 60 * 24 * 7 );
378
+ $output['day'] = floor( $durseconds / ( 60 * 60 * 24 ));
379
+ $durseconds = $durseconds % ( 60 * 60 * 24 );
380
+ $output['hour'] = floor( $durseconds / ( 60 * 60 ));
381
+ $durseconds = $durseconds % ( 60 * 60 );
382
+ $output['min'] = floor( $durseconds / ( 60 ));
383
+ $output['sec'] = ( $durseconds % ( 60 ));
384
+ }
385
+ else {
386
+ foreach( $duration as $durKey => $durValue ) {
387
+ if( empty( $durValue )) continue;
388
+ switch ( $durKey ) {
389
+ case '0': case 'week': $output['week'] = $durValue; break;
390
+ case '1': case 'day': $output['day'] = $durValue; break;
391
+ case '2': case 'hour': $output['hour'] = $durValue; break;
392
+ case '3': case 'min': $output['min'] = $durValue; break;
393
+ case '4': case 'sec': $output['sec'] = $durValue; break;
394
+ }
395
+ }
396
+ }
397
+ if( isset( $output['week'] ) && ( 0 < $output['week'] )) {
398
+ unset( $output['day'], $output['hour'], $output['min'], $output['sec'] );
399
+ return $output;
400
+ }
401
+ unset( $output['week'] );
402
+ if( empty( $output['day'] ))
403
+ unset( $output['day'] );
404
+ if ( isset( $output['hour'] ) || isset( $output['min'] ) || isset( $output['sec'] )) {
405
+ if( !isset( $output['hour'] )) $output['hour'] = 0;
406
+ if( !isset( $output['min'] )) $output['min'] = 0;
407
+ if( !isset( $output['sec'] )) $output['sec'] = 0;
408
+ if(( 0 == $output['hour'] ) && ( 0 == $output['min'] ) && ( 0 == $output['sec'] ))
409
+ unset( $output['hour'], $output['min'], $output['sec'] );
410
+ }
411
+ return $output;
412
+ }
413
+ /**
414
+ * ensures internal duration format for input in string format
415
+ *
416
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
417
+ * @since 2.0.5 - 2007-03-14
418
+ * @param string $duration
419
+ * @return array
420
+ */
421
+ public static function _duration_string( $duration ) {
422
+ $duration = (string) trim( $duration );
423
+ while( 'P' != strtoupper( substr( $duration, 0, 1 ))) {
424
+ if( 0 < strlen( $duration ))
425
+ $duration = substr( $duration, 1 );
426
+ else
427
+ return false; // no leading P !?!?
428
+ }
429
+ $duration = substr( $duration, 1 ); // skip P
430
+ $duration = str_replace ( 't', 'T', $duration );
431
+ $duration = str_replace ( 'T', '', $duration );
432
+ $output = array();
433
+ $val = null;
434
+ for( $ix=0; $ix < strlen( $duration ); $ix++ ) {
435
+ switch( strtoupper( substr( $duration, $ix, 1 ))) {
436
+ case 'W':
437
+ $output['week'] = $val;
438
+ $val = null;
439
+ break;
440
+ case 'D':
441
+ $output['day'] = $val;
442
+ $val = null;
443
+ break;
444
+ case 'H':
445
+ $output['hour'] = $val;
446
+ $val = null;
447
+ break;
448
+ case 'M':
449
+ $output['min'] = $val;
450
+ $val = null;
451
+ break;
452
+ case 'S':
453
+ $output['sec'] = $val;
454
+ $val = null;
455
+ break;
456
+ default:
457
+ if( !ctype_digit( substr( $duration, $ix, 1 )))
458
+ return false; // unknown duration control character !?!?
459
+ else
460
+ $val .= substr( $duration, $ix, 1 );
461
+ }
462
+ }
463
+ return iCalUtilityFunctions::_duration_array( $output );
464
+ }
465
+ /**
466
+ * convert duration to date in array format
467
+ *
468
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
469
+ * @since 2.8.7 - 2011-03-03
470
+ * @param array $startdate
471
+ * @param array $duration
472
+ * @return array, date format
473
+ */
474
+ function _duration2date( $startdate=null, $duration=null ) {
475
+ if( empty( $startdate )) return FALSE;
476
+ if( empty( $duration )) return FALSE;
477
+ $dateOnly = ( isset( $startdate['hour'] ) || isset( $startdate['min'] ) || isset( $startdate['sec'] )) ? FALSE : TRUE;
478
+ $startdate['hour'] = ( isset( $startdate['hour'] )) ? $startdate['hour'] : 0;
479
+ $startdate['min'] = ( isset( $startdate['min'] )) ? $startdate['min'] : 0;
480
+ $startdate['sec'] = ( isset( $startdate['sec'] )) ? $startdate['sec'] : 0;
481
+ $dtend = 0;
482
+ if( isset( $duration['week'] ))
483
+ $dtend += ( $duration['week'] * 7 * 24 * 60 * 60 );
484
+ if( isset( $duration['day'] ))
485
+ $dtend += ( $duration['day'] * 24 * 60 * 60 );
486
+ if( isset( $duration['hour'] ))
487
+ $dtend += ( $duration['hour'] * 60 *60 );
488
+ if( isset( $duration['min'] ))
489
+ $dtend += ( $duration['min'] * 60 );
490
+ if( isset( $duration['sec'] ))
491
+ $dtend += $duration['sec'];
492
+ $dtend = mktime( $startdate['hour'], $startdate['min'], ( $startdate['sec'] + $dtend ), $startdate['month'], $startdate['day'], $startdate['year'] );
493
+ $dtend2 = array();
494
+ $dtend2['year'] = date('Y', $dtend );
495
+ $dtend2['month'] = date('m', $dtend );
496
+ $dtend2['day'] = date('d', $dtend );
497
+ $dtend2['hour'] = date('H', $dtend );
498
+ $dtend2['min'] = date('i', $dtend );
499
+ $dtend2['sec'] = date('s', $dtend );
500
+ if( isset( $startdate['tz'] ))
501
+ $dtend2['tz'] = $startdate['tz'];
502
+ if( $dateOnly && (( 0 == $dtend2['hour'] ) && ( 0 == $dtend2['min'] ) && ( 0 == $dtend2['sec'] )))
503
+ unset( $dtend2['hour'], $dtend2['min'], $dtend2['sec'] );
504
+ return $dtend2;
505
+ }
506
+ /**
507
+ * if not preSet, if exist, remove key with expected value from array and return hit value else return elseValue
508
+ *
509
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
510
+ * @since 2.4.16 - 2008-11-08
511
+ * @param array $array
512
+ * @param string $expkey, expected key
513
+ * @param string $expval, expected value
514
+ * @param int $hitVal optional, return value if found
515
+ * @param int $elseVal optional, return value if not found
516
+ * @param int $preSet optional, return value if already preset
517
+ * @return int
518
+ */
519
+ public static function _existRem( &$array, $expkey, $expval=FALSE, $hitVal=null, $elseVal=null, $preSet=null ) {
520
+ if( $preSet )
521
+ return $preSet;
522
+ if( !is_array( $array ) || ( 0 == count( $array )))
523
+ return $elseVal;
524
+ foreach( $array as $key => $value ) {
525
+ if( strtoupper( $expkey ) == strtoupper( $key )) {
526
+ if( !$expval || ( strtoupper( $expval ) == strtoupper( $array[$key] ))) {
527
+ unset( $array[$key] );
528
+ return $hitVal;
529
+ }
530
+ }
531
+ }
532
+ return $elseVal;
533
+ }
534
+ /**
535
+ * creates formatted output for calendar component property data value type date/date-time
536
+ *
537
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
538
+ * @since 2.4.8 - 2008-10-30
539
+ * @param array $datetime
540
+ * @param int $parno, optional, default 6
541
+ * @return string
542
+ */
543
+ public static function _format_date_time( $datetime, $parno=6 ) {
544
+ if( !isset( $datetime['year'] ) &&
545
+ !isset( $datetime['month'] ) &&
546
+ !isset( $datetime['day'] ) &&
547
+ !isset( $datetime['hour'] ) &&
548
+ !isset( $datetime['min'] ) &&
549
+ !isset( $datetime['sec'] ))
550
+ return ;
551
+ $output = null;
552
+ // if( !isset( $datetime['day'] )) { $o=''; foreach($datetime as $k=>$v) {if(is_array($v)) $v=implode('-',$v);$o.=" $k=>$v";} echo " day SAKNAS : $o <br />\n"; }
553
+ foreach( $datetime as $dkey => & $dvalue )
554
+ if( 'tz' != $dkey ) $dvalue = (integer) $dvalue;
555
+ $output = date('Ymd', mktime( 0, 0, 0, $datetime['month'], $datetime['day'], $datetime['year']));
556
+ if( isset( $datetime['hour'] ) ||
557
+ isset( $datetime['min'] ) ||
558
+ isset( $datetime['sec'] ) ||
559
+ isset( $datetime['tz'] )) {
560
+ if( isset( $datetime['tz'] ) &&
561
+ !isset( $datetime['hour'] ))
562
+ $datetime['hour'] = 0;
563
+ if( isset( $datetime['hour'] ) &&
564
+ !isset( $datetime['min'] ))
565
+ $datetime['min'] = 0;
566
+ if( isset( $datetime['hour'] ) &&
567
+ isset( $datetime['min'] ) &&
568
+ !isset( $datetime['sec'] ))
569
+ $datetime['sec'] = 0;
570
+ $date = mktime( $datetime['hour'], $datetime['min'], $datetime['sec'], $datetime['month'], $datetime['day'], $datetime['year']);
571
+ $output .= date('\THis', $date );
572
+ if( isset( $datetime['tz'] ) && ( '' < trim ( $datetime['tz'] ))) {
573
+ $datetime['tz'] = trim( $datetime['tz'] );
574
+ if( 'Z' == $datetime['tz'] )
575
+ $output .= 'Z';
576
+ $offset = iCalUtilityFunctions::_tz2offset( $datetime['tz'] );
577
+ if( 0 != $offset ) {
578
+ $date = mktime( $datetime['hour'], $datetime['min'], ($datetime['sec'] + $offset), $datetime['month'], $datetime['day'], $datetime['year']);
579
+ $output = date( 'Ymd\THis\Z', $date );
580
+ }
581
+ }
582
+ elseif( 7 == $parno )
583
+ $output .= 'Z';
584
+ }
585
+ return $output;
586
+ }
587
+ /**
588
+ * creates formatted output for calendar component property data value type duration
589
+ *
590
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
591
+ * @since 2.9.9 - 2011-06-17
592
+ * @param array $duration ( week, day, hour, min, sec )
593
+ * @return string
594
+ */
595
+ public static function _format_duration( $duration ) {
596
+ if( isset( $duration['week'] ) ||
597
+ isset( $duration['day'] ) ||
598
+ isset( $duration['hour'] ) ||
599
+ isset( $duration['min'] ) ||
600
+ isset( $duration['sec'] ))
601
+ $ok = TRUE;
602
+ else
603
+ return;
604
+ if( isset( $duration['week'] ) && ( 0 < $duration['week'] ))
605
+ return 'P'.$duration['week'].'W';
606
+ $output = 'P';
607
+ if( isset($duration['day'] ) && ( 0 < $duration['day'] ))
608
+ $output .= $duration['day'].'D';
609
+ if(( isset( $duration['hour']) && ( 0 < $duration['hour'] )) ||
610
+ ( isset( $duration['min']) && ( 0 < $duration['min'] )) ||
611
+ ( isset( $duration['sec']) && ( 0 < $duration['sec'] )))
612
+ $output .= 'T';
613
+ $output .= ( isset( $duration['hour']) && ( 0 < $duration['hour'] )) ? $duration['hour'].'H' : '';
614
+ $output .= ( isset( $duration['min']) && ( 0 < $duration['min'] )) ? $duration['min']. 'M' : '';
615
+ $output .= ( isset( $duration['sec']) && ( 0 < $duration['sec'] )) ? $duration['sec']. 'S' : '';
616
+ if( 'P' == $output )
617
+ $output = 'PT0S';
618
+ return $output;
619
+ }
620
+ /**
621
+ * checks if input array contains a date
622
+ *
623
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
624
+ * @since 2.4.16 - 2008-10-25
625
+ * @param array $input
626
+ * @return bool
627
+ */
628
+ public static function _isArrayDate( $input ) {
629
+ if( isset( $input['week'] ) || ( !in_array( count( $input ), array( 3, 6, 7 ))))
630
+ return FALSE;
631
+ if( 7 == count( $input ))
632
+ return TRUE;
633
+ if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] ))
634
+ return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] );
635
+ if( isset( $input['day'] ) || isset( $input['hour'] ) || isset( $input['min'] ) || isset( $input['sec'] ))
636
+ return FALSE;
637
+ if( in_array( 0, $input ))
638
+ return FALSE;
639
+ if(( 1970 > $input[0] ) || ( 12 < $input[1] ) || ( 31 < $input[2] ))
640
+ return FALSE;
641
+ if(( isset( $input[0] ) && isset( $input[1] ) && isset( $input[2] )) &&
642
+ checkdate( (int) $input[1], (int) $input[2], (int) $input[0] ))
643
+ return TRUE;
644
+ $input = iCalUtilityFunctions::_date_time_string( $input[1].'/'.$input[2].'/'.$input[0], 3 ); // m - d - Y
645
+ if( isset( $input['year'] ) && isset( $input['month'] ) && isset( $input['day'] ))
646
+ return checkdate( (int) $input['month'], (int) $input['day'], (int) $input['year'] );
647
+ return FALSE;
648
+ }
649
+ /**
650
+ * checks if input array contains a timestamp date
651
+ *
652
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
653
+ * @since 2.4.16 - 2008-10-18
654
+ * @param array $input
655
+ * @return bool
656
+ */
657
+ public static function _isArrayTimestampDate( $input ) {
658
+ return ( is_array( $input ) && isset( $input['timestamp'] )) ? TRUE : FALSE ;
659
+ }
660
+ /**
661
+ * controll if input string contains trailing UTC offset
662
+ *
663
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
664
+ * @since 2.4.16 - 2008-10-19
665
+ * @param string $input
666
+ * @return bool
667
+ */
668
+ public static function _isOffset( $input ) {
669
+ $input = trim( (string) $input );
670
+ if( 'Z' == substr( $input, -1 ))
671
+ return TRUE;
672
+ elseif(( 5 <= strlen( $input )) &&
673
+ ( in_array( substr( $input, -5, 1 ), array( '+', '-' ))) &&
674
+ ( '0000' < substr( $input, -4 )) && ( '9999' >= substr( $input, -4 )))
675
+ return TRUE;
676
+ elseif(( 7 <= strlen( $input )) &&
677
+ ( in_array( substr( $input, -7, 1 ), array( '+', '-' ))) &&
678
+ ( '000000' < substr( $input, -6 )) && ( '999999' >= substr( $input, -6 )))
679
+ return TRUE;
680
+ return FALSE;
681
+
682
+ }
683
+ /**
684
+ * transform offset in seconds to [-/+]hhmm[ss]
685
+ *
686
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
687
+ * @since 2011-05-02
688
+ * @param string $seconds
689
+ * @return string
690
+ */
691
+ public static function offsetSec2His( $seconds ) {
692
+ if( '-' == substr( $seconds, 0, 1 )) {
693
+ $prefix = '-';
694
+ $seconds = substr( $seconds, 1 );
695
+ }
696
+ elseif( '+' == substr( $seconds, 0, 1 )) {
697
+ $prefix = '+';
698
+ $seconds = substr( $seconds, 1 );
699
+ }
700
+ else
701
+ $prefix = '+';
702
+ $output = '';
703
+ $hour = (int) floor( $seconds / 3600 );
704
+ if( 10 > $hour )
705
+ $hour = '0'.$hour;
706
+ $seconds = $seconds % 3600;
707
+ $min = (int) floor( $seconds / 60 );
708
+ if( 10 > $min )
709
+ $min = '0'.$min;
710
+ $output = $hour.$min;
711
+ $seconds = $seconds % 60;
712
+ if( 0 < $seconds) {
713
+ if( 9 < $seconds)
714
+ $output .= $seconds;
715
+ else
716
+ $output .= '0'.$seconds;
717
+ }
718
+ return $prefix.$output;
719
+ }
720
+ /**
721
+ * remakes a recur pattern to an array of dates
722
+ *
723
+ * if missing, UNTIL is set 1 year from startdate (emergency break)
724
+ *
725
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
726
+ * @since 2.9.10 - 2011-07-07
727
+ * @param array $result, array to update, array([timestamp] => timestamp)
728
+ * @param array $recur, pattern for recurrency (only value part, params ignored)
729
+ * @param array $wdate, component start date
730
+ * @param array $startdate, start date
731
+ * @param array $enddate, optional
732
+ * @return array of recurrence (start-)dates as index
733
+ * @todo BYHOUR, BYMINUTE, BYSECOND, ev. BYSETPOS due to ambiguity, WEEKLY at year end/start
734
+ */
735
+ public static function _recur2date( & $result, $recur, $wdate, $startdate, $enddate=FALSE ) {
736
+ foreach( $wdate as $k => $v ) if( ctype_digit( $v )) $wdate[$k] = (int) $v;
737
+ $wdateStart = $wdate;
738
+ $wdatets = iCalUtilityFunctions::_date2timestamp( $wdate );
739
+ $startdatets = iCalUtilityFunctions::_date2timestamp( $startdate );
740
+ if( !$enddate ) {
741
+ $enddate = $startdate;
742
+ $enddate['year'] += 1;
743
+ }
744
+ // echo "recur __in_ comp start ".implode('-',$wdate)." period start ".implode('-',$startdate)." period end ".implode('-',$enddate)."<br />\n";print_r($recur);echo "<br />\n";//test###
745
+ $endDatets = iCalUtilityFunctions::_date2timestamp( $enddate ); // fix break
746
+ if( !isset( $recur['COUNT'] ) && !isset( $recur['UNTIL'] ))
747
+ $recur['UNTIL'] = $enddate; // create break
748
+ if( isset( $recur['UNTIL'] )) {
749
+ $tdatets = iCalUtilityFunctions::_date2timestamp( $recur['UNTIL'] );
750
+ if( $endDatets > $tdatets ) {
751
+ $endDatets = $tdatets; // emergency break
752
+ $enddate = iCalUtilityFunctions::_timestamp2date( $endDatets, 6 );
753
+ }
754
+ else
755
+ $recur['UNTIL'] = iCalUtilityFunctions::_timestamp2date( $endDatets, 6 );
756
+ }
757
+ if( $wdatets > $endDatets ) {
758
+ // echo "recur out of date ".implode('-',iCalUtilityFunctions::_date_time_string(date('Y-m-d H:i:s',$wdatets),6))."<br />\n";//test
759
+ return array(); // nothing to do.. .
760
+ }
761
+ if( !isset( $recur['FREQ'] )) // "MUST be specified.. ."
762
+ $recur['FREQ'] = 'DAILY'; // ??
763
+ $wkst = ( isset( $recur['WKST'] ) && ( 'SU' == $recur['WKST'] )) ? 24*60*60 : 0; // ??
764
+ $weekStart = (int) date( 'W', ( $wdatets + $wkst ));
765
+ if( !isset( $recur['INTERVAL'] ))
766
+ $recur['INTERVAL'] = 1;
767
+ $countcnt = ( !isset( $recur['BYSETPOS'] )) ? 1 : 0; // DTSTART counts as the first occurrence
768
+ /* find out how to step up dates and set index for interval count */
769
+ $step = array();
770
+ if( 'YEARLY' == $recur['FREQ'] )
771
+ $step['year'] = 1;
772
+ elseif( 'MONTHLY' == $recur['FREQ'] )
773
+ $step['month'] = 1;
774
+ elseif( 'WEEKLY' == $recur['FREQ'] )
775
+ $step['day'] = 7;
776
+ else
777
+ $step['day'] = 1;
778
+ if( isset( $step['year'] ) && isset( $recur['BYMONTH'] ))
779
+ $step = array( 'month' => 1 );
780
+ if( empty( $step ) && isset( $recur['BYWEEKNO'] )) // ??
781
+ $step = array( 'day' => 7 );
782
+ if( isset( $recur['BYYEARDAY'] ) || isset( $recur['BYMONTHDAY'] ) || isset( $recur['BYDAY'] ))
783
+ $step = array( 'day' => 1 );
784
+ $intervalarr = array();
785
+ if( 1 < $recur['INTERVAL'] ) {
786
+ $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst );
787
+ $intervalarr = array( $intervalix => 0 );
788
+ }
789
+ if( isset( $recur['BYSETPOS'] )) { // save start date + weekno
790
+ $bysetposymd1 = $bysetposymd2 = $bysetposw1 = $bysetposw2 = array();
791
+ // echo "bysetposXold_start=$bysetposYold $bysetposMold $bysetposDold<br />\n"; // test ###
792
+ if( is_array( $recur['BYSETPOS'] )) {
793
+ foreach( $recur['BYSETPOS'] as $bix => $bval )
794
+ $recur['BYSETPOS'][$bix] = (int) $bval;
795
+ }
796
+ else
797
+ $recur['BYSETPOS'] = array( (int) $recur['BYSETPOS'] );
798
+ if( 'YEARLY' == $recur['FREQ'] ) {
799
+ $wdate['month'] = $wdate['day'] = 1; // start from beginning of year
800
+ $wdatets = iCalUtilityFunctions::_date2timestamp( $wdate );
801
+ iCalUtilityFunctions::_stepdate( $enddate, $endDatets, array( 'year' => 1 )); // make sure to count whole last year
802
+ }
803
+ elseif( 'MONTHLY' == $recur['FREQ'] ) {
804
+ $wdate['day'] = 1; // start from beginning of month
805
+ $wdatets = iCalUtilityFunctions::_date2timestamp( $wdate );
806
+ iCalUtilityFunctions::_stepdate( $enddate, $endDatets, array( 'month' => 1 )); // make sure to count whole last month
807
+ }
808
+ else
809
+ iCalUtilityFunctions::_stepdate( $enddate, $endDatets, $step); // make sure to count whole last period
810
+ // echo "BYSETPOS endDat++ =".implode('-',$enddate).' step='.var_export($step,TRUE)."<br />\n";//test###
811
+ $bysetposWold = (int) date( 'W', ( $wdatets + $wkst ));
812
+ $bysetposYold = $wdate['year'];
813
+ $bysetposMold = $wdate['month'];
814
+ $bysetposDold = $wdate['day'];
815
+ }
816
+ else
817
+ iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step);
818
+ $year_old = null;
819
+ $daynames = array( 'SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA' );
820
+ /* MAIN LOOP */
821
+ // echo "recur start ".implode('-',$wdate)." end ".implode('-',$enddate)."<br />\n";//test
822
+ while( TRUE ) {
823
+ if( isset( $endDatets ) && ( $wdatets > $endDatets ))
824
+ break;
825
+ if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] ))
826
+ break;
827
+ if( $year_old != $wdate['year'] ) {
828
+ $year_old = $wdate['year'];
829
+ $daycnts = array();
830
+ $yeardays = $weekno = 0;
831
+ $yeardaycnt = array();
832
+ for( $m = 1; $m <= 12; $m++ ) { // count up and update up-counters
833
+ $daycnts[$m] = array();
834
+ $weekdaycnt = array();
835
+ foreach( $daynames as $dn )
836
+ $yeardaycnt[$dn] = $weekdaycnt[$dn] = 0;
837
+ $mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] ));
838
+ for( $d = 1; $d <= $mcnt; $d++ ) {
839
+ $daycnts[$m][$d] = array();
840
+ if( isset( $recur['BYYEARDAY'] )) {
841
+ $yeardays++;
842
+ $daycnts[$m][$d]['yearcnt_up'] = $yeardays;
843
+ }
844
+ if( isset( $recur['BYDAY'] )) {
845
+ $day = date( 'w', mktime( 0, 0, 0, $m, $d, $wdate['year'] ));
846
+ $day = $daynames[$day];
847
+ $daycnts[$m][$d]['DAY'] = $day;
848
+ $weekdaycnt[$day]++;
849
+ $daycnts[$m][$d]['monthdayno_up'] = $weekdaycnt[$day];
850
+ $yeardaycnt[$day]++;
851
+ $daycnts[$m][$d]['yeardayno_up'] = $yeardaycnt[$day];
852
+ }
853
+ if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' ))
854
+ $daycnts[$m][$d]['weekno_up'] =(int)date('W',mktime(0,0,$wkst,$m,$d,$wdate['year']));
855
+ }
856
+ }
857
+ $daycnt = 0;
858
+ $yeardaycnt = array();
859
+ if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' )) {
860
+ $weekno = null;
861
+ for( $d=31; $d > 25; $d-- ) { // get last weekno for year
862
+ if( !$weekno )
863
+ $weekno = $daycnts[12][$d]['weekno_up'];
864
+ elseif( $weekno < $daycnts[12][$d]['weekno_up'] ) {
865
+ $weekno = $daycnts[12][$d]['weekno_up'];
866
+ break;
867
+ }
868
+ }
869
+ }
870
+ for( $m = 12; $m > 0; $m-- ) { // count down and update down-counters
871
+ $weekdaycnt = array();
872
+ foreach( $daynames as $dn )
873
+ $yeardaycnt[$dn] = $weekdaycnt[$dn] = 0;
874
+ $monthcnt = 0;
875
+ $mcnt = date( 't', mktime( 0, 0, 0, $m, 1, $wdate['year'] ));
876
+ for( $d = $mcnt; $d > 0; $d-- ) {
877
+ if( isset( $recur['BYYEARDAY'] )) {
878
+ $daycnt -= 1;
879
+ $daycnts[$m][$d]['yearcnt_down'] = $daycnt;
880
+ }
881
+ if( isset( $recur['BYMONTHDAY'] )) {
882
+ $monthcnt -= 1;
883
+ $daycnts[$m][$d]['monthcnt_down'] = $monthcnt;
884
+ }
885
+ if( isset( $recur['BYDAY'] )) {
886
+ $day = $daycnts[$m][$d]['DAY'];
887
+ $weekdaycnt[$day] -= 1;
888
+ $daycnts[$m][$d]['monthdayno_down'] = $weekdaycnt[$day];
889
+ $yeardaycnt[$day] -= 1;
890
+ $daycnts[$m][$d]['yeardayno_down'] = $yeardaycnt[$day];
891
+ }
892
+ if( isset( $recur['BYWEEKNO'] ) || ( $recur['FREQ'] == 'WEEKLY' ))
893
+ $daycnts[$m][$d]['weekno_down'] = ($daycnts[$m][$d]['weekno_up'] - $weekno - 1);
894
+ }
895
+ }
896
+ }
897
+ /* check interval */
898
+ if( 1 < $recur['INTERVAL'] ) {
899
+ /* create interval index */
900
+ $intervalix = iCalUtilityFunctions::_recurIntervalIx( $recur['FREQ'], $wdate, $wkst );
901
+ /* check interval */
902
+ $currentKey = array_keys( $intervalarr );
903
+ $currentKey = end( $currentKey ); // get last index
904
+ if( $currentKey != $intervalix )
905
+ $intervalarr = array( $intervalix => ( $intervalarr[$currentKey] + 1 ));
906
+ if(( $recur['INTERVAL'] != $intervalarr[$intervalix] ) &&
907
+ ( 0 != $intervalarr[$intervalix] )) {
908
+ /* step up date */
909
+ // echo "skip: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br />\n";//test
910
+ iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step);
911
+ continue;
912
+ }
913
+ else // continue within the selected interval
914
+ $intervalarr[$intervalix] = 0;
915
+ // echo "cont: ".implode('-',$wdate)." ix=$intervalix old=$currentKey interval=".$intervalarr[$intervalix]."<br />\n";//test
916
+ }
917
+ $updateOK = TRUE;
918
+ if( $updateOK && isset( $recur['BYMONTH'] ))
919
+ $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTH']
920
+ , $wdate['month']
921
+ ,($wdate['month'] - 13));
922
+ if( $updateOK && isset( $recur['BYWEEKNO'] ))
923
+ $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYWEEKNO']
924
+ , $daycnts[$wdate['month']][$wdate['day']]['weekno_up']
925
+ , $daycnts[$wdate['month']][$wdate['day']]['weekno_down'] );
926
+ if( $updateOK && isset( $recur['BYYEARDAY'] ))
927
+ $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYYEARDAY']
928
+ , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_up']
929
+ , $daycnts[$wdate['month']][$wdate['day']]['yearcnt_down'] );
930
+ if( $updateOK && isset( $recur['BYMONTHDAY'] ))
931
+ $updateOK = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYMONTHDAY']
932
+ , $wdate['day']
933
+ , $daycnts[$wdate['month']][$wdate['day']]['monthcnt_down'] );
934
+ // echo "efter BYMONTHDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br />\n";//test###
935
+ if( $updateOK && isset( $recur['BYDAY'] )) {
936
+ $updateOK = FALSE;
937
+ $m = $wdate['month'];
938
+ $d = $wdate['day'];
939
+ if( isset( $recur['BYDAY']['DAY'] )) { // single day, opt with year/month day order no
940
+ $daynoexists = $daynosw = $daynamesw = FALSE;
941
+ if( $recur['BYDAY']['DAY'] == $daycnts[$m][$d]['DAY'] )
942
+ $daynamesw = TRUE;
943
+ if( isset( $recur['BYDAY'][0] )) {
944
+ $daynoexists = TRUE;
945
+ if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) || isset( $recur['BYMONTH'] ))
946
+ $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0]
947
+ , $daycnts[$m][$d]['monthdayno_up']
948
+ , $daycnts[$m][$d]['monthdayno_down'] );
949
+ elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' ))
950
+ $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $recur['BYDAY'][0]
951
+ , $daycnts[$m][$d]['yeardayno_up']
952
+ , $daycnts[$m][$d]['yeardayno_down'] );
953
+ }
954
+ if(( $daynoexists && $daynosw && $daynamesw ) ||
955
+ ( !$daynoexists && !$daynosw && $daynamesw )) {
956
+ $updateOK = TRUE;
957
+ }
958
+ // echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw<br />\n"; // test ###
959
+ }
960
+ else {
961
+ foreach( $recur['BYDAY'] as $bydayvalue ) {
962
+ $daynoexists = $daynosw = $daynamesw = FALSE;
963
+ if( isset( $bydayvalue['DAY'] ) &&
964
+ ( $bydayvalue['DAY'] == $daycnts[$m][$d]['DAY'] ))
965
+ $daynamesw = TRUE;
966
+ if( isset( $bydayvalue[0] )) {
967
+ $daynoexists = TRUE;
968
+ if(( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'MONTHLY' )) ||
969
+ isset( $recur['BYMONTH'] ))
970
+ $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0']
971
+ , $daycnts[$m][$d]['monthdayno_up']
972
+ , $daycnts[$m][$d]['monthdayno_down'] );
973
+ elseif( isset( $recur['FREQ'] ) && ( $recur['FREQ'] == 'YEARLY' ))
974
+ $daynosw = iCalUtilityFunctions::_recurBYcntcheck( $bydayvalue['0']
975
+ , $daycnts[$m][$d]['yeardayno_up']
976
+ , $daycnts[$m][$d]['yeardayno_down'] );
977
+ }
978
+ // echo "daynoexists:$daynoexists daynosw:$daynosw daynamesw:$daynamesw<br />\n"; // test ###
979
+ if(( $daynoexists && $daynosw && $daynamesw ) ||
980
+ ( !$daynoexists && !$daynosw && $daynamesw )) {
981
+ $updateOK = TRUE;
982
+ break;
983
+ }
984
+ }
985
+ }
986
+ }
987
+ // echo "efter BYDAY: ".implode('-',$wdate).' status: '; echo ($updateOK) ? 'TRUE' : 'FALSE'; echo "<br />\n"; // test ###
988
+ /* check BYSETPOS */
989
+ if( $updateOK ) {
990
+ if( isset( $recur['BYSETPOS'] ) &&
991
+ ( in_array( $recur['FREQ'], array( 'YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY' )))) {
992
+ if( isset( $recur['WEEKLY'] )) {
993
+ if( $bysetposWold == $daycnts[$wdate['month']][$wdate['day']]['weekno_up'] )
994
+ $bysetposw1[] = $wdatets;
995
+ else
996
+ $bysetposw2[] = $wdatets;
997
+ }
998
+ else {
999
+ if(( isset( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) &&
1000
+ ( $bysetposYold == $wdate['year'] )) ||
1001
+ ( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] ) &&
1002
+ (( $bysetposYold == $wdate['year'] ) &&
1003
+ ( $bysetposMold == $wdate['month'] ))) ||
1004
+ ( isset( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) &&
1005
+ (( $bysetposYold == $wdate['year'] ) &&
1006
+ ( $bysetposMold == $wdate['month']) &&
1007
+ ( $bysetposDold == $wdate['day'] )))) {
1008
+ // echo "bysetposymd1[]=".implode('-',iCalUtilityFunctions::_date_time_string(date('Y-m-d H:i:s',$wdatets),6))."<br />\n";//test
1009
+ $bysetposymd1[] = $wdatets;
1010
+ }
1011
+ else {
1012
+ // echo "bysetposymd2[]=".implode('-',iCalUtilityFunctions::_date_time_string(date('Y-m-d H:i:s',$wdatets),6))."<br />\n";//test
1013
+ $bysetposymd2[] = $wdatets;
1014
+ }
1015
+ }
1016
+ }
1017
+ else {
1018
+ /* update result array if BYSETPOS is set */
1019
+ $countcnt++;
1020
+ if( $startdatets <= $wdatets ) { // only output within period
1021
+ $result[$wdatets] = TRUE;
1022
+ // echo "recur ".implode('-',iCalUtilityFunctions::_date_time_string(date('Y-m-d H:i:s',$wdatets),6))."<br />\n";//test
1023
+ }
1024
+ // echo "recur undate ".implode('-',iCalUtilityFunctions::_date_time_string(date('Y-m-d H:i:s',$wdatets),6))." okdatstart ".implode('-',iCalUtilityFunctions::_date_time_string(date('Y-m-d H:i:s',$startdatets),6))."<br />\n";//test
1025
+ $updateOK = FALSE;
1026
+ }
1027
+ }
1028
+ /* step up date */
1029
+ iCalUtilityFunctions::_stepdate( $wdate, $wdatets, $step);
1030
+ /* check if BYSETPOS is set for updating result array */
1031
+ if( $updateOK && isset( $recur['BYSETPOS'] )) {
1032
+ $bysetpos = FALSE;
1033
+ if( isset( $recur['FREQ'] ) && ( 'YEARLY' == $recur['FREQ'] ) &&
1034
+ ( $bysetposYold != $wdate['year'] )) {
1035
+ $bysetpos = TRUE;
1036
+ $bysetposYold = $wdate['year'];
1037
+ }
1038
+ elseif( isset( $recur['FREQ'] ) && ( 'MONTHLY' == $recur['FREQ'] &&
1039
+ (( $bysetposYold != $wdate['year'] ) || ( $bysetposMold != $wdate['month'] )))) {
1040
+ $bysetpos = TRUE;
1041
+ $bysetposYold = $wdate['year'];
1042
+ $bysetposMold = $wdate['month'];
1043
+ }
1044
+ elseif( isset( $recur['FREQ'] ) && ( 'WEEKLY' == $recur['FREQ'] )) {
1045
+ $weekno = (int) date( 'W', mktime( 0, 0, $wkst, $wdate['month'], $wdate['day'], $wdate['year']));
1046
+ if( $bysetposWold != $weekno ) {
1047
+ $bysetposWold = $weekno;
1048
+ $bysetpos = TRUE;
1049
+ }
1050
+ }
1051
+ elseif( isset( $recur['FREQ'] ) && ( 'DAILY' == $recur['FREQ'] ) &&
1052
+ (( $bysetposYold != $wdate['year'] ) ||
1053
+ ( $bysetposMold != $wdate['month'] ) ||
1054
+ ( $bysetposDold != $wdate['day'] ))) {
1055
+ $bysetpos = TRUE;
1056
+ $bysetposYold = $wdate['year'];
1057
+ $bysetposMold = $wdate['month'];
1058
+ $bysetposDold = $wdate['day'];
1059
+ }
1060
+ if( $bysetpos ) {
1061
+ if( isset( $recur['BYWEEKNO'] )) {
1062
+ $bysetposarr1 = & $bysetposw1;
1063
+ $bysetposarr2 = & $bysetposw2;
1064
+ }
1065
+ else {
1066
+ $bysetposarr1 = & $bysetposymd1;
1067
+ $bysetposarr2 = & $bysetposymd2;
1068
+ }
1069
+ // echo 'test före out startYMD (weekno)='.$wdateStart['year'].':'.$wdateStart['month'].':'.$wdateStart['day']." ($weekStart) "; // test ###
1070
+ foreach( $recur['BYSETPOS'] as $ix ) {
1071
+ if( 0 > $ix ) // both positive and negative BYSETPOS allowed
1072
+ $ix = ( count( $bysetposarr1 ) + $ix + 1);
1073
+ $ix--;
1074
+ if( isset( $bysetposarr1[$ix] )) {
1075
+ if( $startdatets <= $bysetposarr1[$ix] ) { // only output within period
1076
+ // $testdate = iCalUtilityFunctions::_timestamp2date( $bysetposarr1[$ix], 6 ); // test ###
1077
+ // $testweekno = (int) date( 'W', mktime( 0, 0, $wkst, $testdate['month'], $testdate['day'], $testdate['year'] )); // test ###
1078
+ // echo " testYMD (weekno)=".$testdate['year'].':'.$testdate['month'].':'.$testdate['day']." ($testweekno)"; // test ###
1079
+ $result[$bysetposarr1[$ix]] = TRUE;
1080
+ // echo " recur ".implode('-',iCalUtilityFunctions::_date_time_string(date('Y-m-d H:i:s',$bysetposarr1[$ix]),6)); // test ###
1081
+ }
1082
+ $countcnt++;
1083
+ }
1084
+ if( isset( $recur['COUNT'] ) && ( $countcnt >= $recur['COUNT'] ))
1085
+ break;
1086
+ }
1087
+ // echo "<br />\n"; // test ###
1088
+ $bysetposarr1 = $bysetposarr2;
1089
+ $bysetposarr2 = array();
1090
+ }
1091
+ }
1092
+ }
1093
+ }
1094
+ public static function _recurBYcntcheck( $BYvalue, $upValue, $downValue ) {
1095
+ if( is_array( $BYvalue ) &&
1096
+ ( in_array( $upValue, $BYvalue ) || in_array( $downValue, $BYvalue )))
1097
+ return TRUE;
1098
+ elseif(( $BYvalue == $upValue ) || ( $BYvalue == $downValue ))
1099
+ return TRUE;
1100
+ else
1101
+ return FALSE;
1102
+ }
1103
+ public static function _recurIntervalIx( $freq, $date, $wkst ) {
1104
+ /* create interval index */
1105
+ switch( $freq ) {
1106
+ case 'YEARLY':
1107
+ $intervalix = $date['year'];
1108
+ break;
1109
+ case 'MONTHLY':
1110
+ $intervalix = $date['year'].'-'.$date['month'];
1111
+ break;
1112
+ case 'WEEKLY':
1113
+ $wdatets = iCalUtilityFunctions::_date2timestamp( $date );
1114
+ $intervalix = (int) date( 'W', ( $wdatets + $wkst ));
1115
+ break;
1116
+ case 'DAILY':
1117
+ default:
1118
+ $intervalix = $date['year'].'-'.$date['month'].'-'.$date['day'];
1119
+ break;
1120
+ }
1121
+ return $intervalix;
1122
+ }
1123
+ /**
1124
+ * convert input format for exrule and rrule to internal format
1125
+ *
1126
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1127
+ * @since 2.9.10 - 2011-07-07
1128
+ * @param array $rexrule
1129
+ * @return array
1130
+ */
1131
+ public static function _setRexrule( $rexrule ) {
1132
+ $input = array();
1133
+ if( empty( $rexrule ))
1134
+ return $input;
1135
+ foreach( $rexrule as $rexrulelabel => $rexrulevalue ) {
1136
+ $rexrulelabel = strtoupper( $rexrulelabel );
1137
+ if( 'UNTIL' != $rexrulelabel )
1138
+ $input[$rexrulelabel] = $rexrulevalue;
1139
+ else {
1140
+ if( iCalUtilityFunctions::_isArrayTimestampDate( $rexrulevalue )) // timestamp date
1141
+ $input[$rexrulelabel] = iCalUtilityFunctions::_timestamp2date( $rexrulevalue, 6 );
1142
+ elseif( iCalUtilityFunctions::_isArrayDate( $rexrulevalue )) // date-time
1143
+ $input[$rexrulelabel] = iCalUtilityFunctions::_date_time_array( $rexrulevalue, 6 );
1144
+ elseif( 8 <= strlen( trim( $rexrulevalue ))) // ex. 2006-08-03 10:12:18
1145
+ $input[$rexrulelabel] = iCalUtilityFunctions::_date_time_string( $rexrulevalue );
1146
+ if(( 3 < count( $input[$rexrulelabel] )) && !isset( $input[$rexrulelabel]['tz'] ))
1147
+ $input[$rexrulelabel]['tz'] = 'Z';
1148
+ }
1149
+ }
1150
+ /* set recurrence rule specification in rfc2445 order */
1151
+ $input2 = array();
1152
+ if( isset( $input['FREQ'] ))
1153
+ $input2['FREQ'] = $input['FREQ'];
1154
+ if( isset( $input['UNTIL'] ))
1155
+ $input2['UNTIL'] = $input['UNTIL'];
1156
+ elseif( isset( $input['COUNT'] ))
1157
+ $input2['COUNT'] = $input['COUNT'];
1158
+ if( isset( $input['INTERVAL'] ))
1159
+ $input2['INTERVAL'] = $input['INTERVAL'];
1160
+ if( isset( $input['BYSECOND'] ))
1161
+ $input2['BYSECOND'] = $input['BYSECOND'];
1162
+ if( isset( $input['BYMINUTE'] ))
1163
+ $input2['BYMINUTE'] = $input['BYMINUTE'];
1164
+ if( isset( $input['BYHOUR'] ))
1165
+ $input2['BYHOUR'] = $input['BYHOUR'];
1166
+ if( isset( $input['BYDAY'] )) {
1167
+ if( !is_array( $input['BYDAY'] )) // ensure upper case.. .
1168
+ $input2['BYDAY'] = strtoupper( $input['BYDAY'] );
1169
+ else {
1170
+ foreach( $input['BYDAY'] as $BYDAYx => $BYDAYv ) {
1171
+ if( 'DAY' == strtoupper( $BYDAYx ))
1172
+ $input2['BYDAY']['DAY'] = strtoupper( $BYDAYv );
1173
+ elseif( !is_array( $BYDAYv )) {
1174
+ $input2['BYDAY'][$BYDAYx] = $BYDAYv;
1175
+ }
1176
+ else {
1177
+ foreach( $BYDAYv as $BYDAYx2 => $BYDAYv2 ) {
1178
+ if( 'DAY' == strtoupper( $BYDAYx2 ))
1179
+ $input2['BYDAY'][$BYDAYx]['DAY'] = strtoupper( $BYDAYv2 );
1180
+ else
1181
+ $input2['BYDAY'][$BYDAYx][$BYDAYx2] = $BYDAYv2;
1182
+ }
1183
+ }
1184
+ }
1185
+ }
1186
+ }
1187
+ if( isset( $input['BYMONTHDAY'] ))
1188
+ $input2['BYMONTHDAY'] = $input['BYMONTHDAY'];
1189
+ if( isset( $input['BYYEARDAY'] ))
1190
+ $input2['BYYEARDAY'] = $input['BYYEARDAY'];
1191
+ if( isset( $input['BYWEEKNO'] ))
1192
+ $input2['BYWEEKNO'] = $input['BYWEEKNO'];
1193
+ if( isset( $input['BYMONTH'] ))
1194
+ $input2['BYMONTH'] = $input['BYMONTH'];
1195
+ if( isset( $input['BYSETPOS'] ))
1196
+ $input2['BYSETPOS'] = $input['BYSETPOS'];
1197
+ if( isset( $input['WKST'] ))
1198
+ $input2['WKST'] = $input['WKST'];
1199
+ return $input2;
1200
+ }
1201
+ /**
1202
+ * convert format for input date to internal date with parameters
1203
+ *
1204
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1205
+ * @since 2.9.6 - 2011-05-14
1206
+ * @param mixed $year
1207
+ * @param mixed $month optional
1208
+ * @param int $day optional
1209
+ * @param int $hour optional
1210
+ * @param int $min optional
1211
+ * @param int $sec optional
1212
+ * @param string $tz optional
1213
+ * @param array $params optional
1214
+ * @param string $caller optional
1215
+ * @param string $objName optional
1216
+ * @param string $tzid optional
1217
+ * @return array
1218
+ */
1219
+ 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 ) {
1220
+ $input = $parno = null;
1221
+ $localtime = (( 'dtstart' == $caller ) && in_array( $objName, array( 'vtimezone', 'standard', 'daylight' ))) ? TRUE : FALSE;
1222
+ if( iCalUtilityFunctions::_isArrayDate( $year )) {
1223
+ if( $localtime ) unset ( $month['VALUE'], $month['TZID'] );
1224
+ $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
1225
+ if( isset( $input['params']['TZID'] )) {
1226
+ $input['params']['VALUE'] = 'DATE-TIME';
1227
+ unset( $year['tz'] );
1228
+ }
1229
+ $hitval = (( !empty( $year['tz'] ) || !empty( $year[6] ))) ? 7 : 6;
1230
+ $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval );
1231
+ $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, count( $year ), $parno );
1232
+ $input['value'] = iCalUtilityFunctions::_date_time_array( $year, $parno );
1233
+ }
1234
+ elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) {
1235
+ if( $localtime ) unset ( $month['VALUE'], $month['TZID'] );
1236
+ $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
1237
+ if( isset( $input['params']['TZID'] )) {
1238
+ $input['params']['VALUE'] = 'DATE-TIME';
1239
+ unset( $year['tz'] );
1240
+ }
1241
+ $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 );
1242
+ $hitval = ( isset( $year['tz'] )) ? 7 : 6;
1243
+ $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno );
1244
+ $input['value'] = iCalUtilityFunctions::_timestamp2date( $year, $parno );
1245
+ }
1246
+ elseif( 8 <= strlen( trim( $year ))) { // ex. 2006-08-03 10:12:18
1247
+ if( $localtime ) unset ( $month['VALUE'], $month['TZID'] );
1248
+ $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ));
1249
+ if( isset( $input['params']['TZID'] )) {
1250
+ $input['params']['VALUE'] = 'DATE-TIME';
1251
+ $parno = 6;
1252
+ }
1253
+ $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7, $parno );
1254
+ $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3, $parno, $parno );
1255
+ $input['value'] = iCalUtilityFunctions::_date_time_string( $year, $parno );
1256
+ }
1257
+ else {
1258
+ if( is_array( $params )) {
1259
+ if( $localtime ) unset ( $params['VALUE'], $params['TZID'] );
1260
+ $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ));
1261
+ }
1262
+ elseif( is_array( $tz )) {
1263
+ $input['params'] = iCalUtilityFunctions::_setParams( $tz, array( 'VALUE' => 'DATE-TIME' ));
1264
+ $tz = FALSE;
1265
+ }
1266
+ elseif( is_array( $hour )) {
1267
+ $input['params'] = iCalUtilityFunctions::_setParams( $hour, array( 'VALUE' => 'DATE-TIME' ));
1268
+ $hour = $min = $sec = $tz = FALSE;
1269
+ }
1270
+ if( isset( $input['params']['TZID'] )) {
1271
+ $tz = null;
1272
+ $input['params']['VALUE'] = 'DATE-TIME';
1273
+ }
1274
+ $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE', 3 );
1275
+ $hitval = ( !empty( $tz )) ? 7 : 6;
1276
+ $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', $hitval, $parno, $parno );
1277
+ $input['value'] = array( 'year' => $year, 'month' => $month, 'day' => $day );
1278
+ if( 3 != $parno ) {
1279
+ $input['value']['hour'] = ( $hour ) ? $hour : '0';
1280
+ $input['value']['min'] = ( $min ) ? $min : '0';
1281
+ $input['value']['sec'] = ( $sec ) ? $sec : '0';
1282
+ if( !empty( $tz ))
1283
+ $input['value']['tz'] = $tz;
1284
+ }
1285
+ }
1286
+ if( 3 == $parno ) {
1287
+ $input['params']['VALUE'] = 'DATE';
1288
+ unset( $input['value']['tz'] );
1289
+ unset( $input['params']['TZID'] );
1290
+ }
1291
+ elseif( isset( $input['params']['TZID'] ))
1292
+ unset( $input['value']['tz'] );
1293
+ if( $localtime )
1294
+ unset( $input['value']['tz'], $input['params']['TZID'] );
1295
+ elseif(( !isset( $input['params']['VALUE'] ) || ( $input['params']['VALUE'] != 'DATE' )) && !isset( $input['params']['TZID'] ) && $tzid )
1296
+ $input['params']['TZID'] = $tzid;
1297
+ if( isset( $input['value']['tz'] ))
1298
+ $input['value']['tz'] = (string) $input['value']['tz'];
1299
+ if( !empty( $input['value']['tz'] ) && ( 'Z' != $input['value']['tz'] ) &&
1300
+ ( !iCalUtilityFunctions::_isOffset( $input['value']['tz'] )))
1301
+ $input['params']['TZID'] = $input['value']['tz'];
1302
+ return $input;
1303
+ }
1304
+ /**
1305
+ * convert format for input date (UTC) to internal date with parameters
1306
+ *
1307
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1308
+ * @since 2.4.17 - 2008-10-31
1309
+ * @param mixed $year
1310
+ * @param mixed $month optional
1311
+ * @param int $day optional
1312
+ * @param int $hour optional
1313
+ * @param int $min optional
1314
+ * @param int $sec optional
1315
+ * @param array $params optional
1316
+ * @return array
1317
+ */
1318
+ public static function _setDate2( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
1319
+ $input = null;
1320
+ if( iCalUtilityFunctions::_isArrayDate( $year )) {
1321
+ $input['value'] = iCalUtilityFunctions::_date_time_array( $year, 7 );
1322
+ $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ) );
1323
+ }
1324
+ elseif( iCalUtilityFunctions::_isArrayTimestampDate( $year )) {
1325
+ $input['value'] = iCalUtilityFunctions::_timestamp2date( $year, 7 );
1326
+ $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ) );
1327
+ }
1328
+ elseif( 8 <= strlen( trim( $year ))) { // ex. 2006-08-03 10:12:18
1329
+ $input['value'] = iCalUtilityFunctions::_date_time_string( $year, 7 );
1330
+ $input['params'] = iCalUtilityFunctions::_setParams( $month, array( 'VALUE' => 'DATE-TIME' ) );
1331
+ }
1332
+ else {
1333
+ $input['value'] = array( 'year' => $year
1334
+ , 'month' => $month
1335
+ , 'day' => $day
1336
+ , 'hour' => $hour
1337
+ , 'min' => $min
1338
+ , 'sec' => $sec );
1339
+ $input['params'] = iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' ));
1340
+ }
1341
+ $parno = iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME', 7 ); // remove default
1342
+ if( !isset( $input['value']['hour'] ))
1343
+ $input['value']['hour'] = 0;
1344
+ if( !isset( $input['value']['min'] ))
1345
+ $input['value']['min'] = 0;
1346
+ if( !isset( $input['value']['sec'] ))
1347
+ $input['value']['sec'] = 0;
1348
+ if( !isset( $input['value']['tz'] ) || !iCalUtilityFunctions::_isOffset( $input['value']['tz'] ))
1349
+ $input['value']['tz'] = 'Z';
1350
+ return $input;
1351
+ }
1352
+ /**
1353
+ * check index and set (an indexed) content in multiple value array
1354
+ *
1355
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1356
+ * @since 2.6.12 - 2011-01-03
1357
+ * @param array $valArr
1358
+ * @param mixed $value
1359
+ * @param array $params
1360
+ * @param array $defaults
1361
+ * @param int $index
1362
+ * @return void
1363
+ */
1364
+ public static function _setMval( & $valArr, $value, $params=FALSE, $defaults=FALSE, $index=FALSE ) {
1365
+ if( !is_array( $valArr )) $valArr = array();
1366
+ if( $index )
1367
+ $index = $index - 1;
1368
+ elseif( 0 < count( $valArr )) {
1369
+ $keys = array_keys( $valArr );
1370
+ $index = end( $keys ) + 1;
1371
+ }
1372
+ else
1373
+ $index = 0;
1374
+ $valArr[$index] = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params, $defaults ));
1375
+ ksort( $valArr );
1376
+ }
1377
+ /**
1378
+ * set input (formatted) parameters- component property attributes
1379
+ *
1380
+ * default parameters can be set, if missing
1381
+ *
1382
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1383
+ * @since 1.x.x - 2007-05-01
1384
+ * @param array $params
1385
+ * @param array $defaults
1386
+ * @return array
1387
+ */
1388
+ public static function _setParams( $params, $defaults=FALSE ) {
1389
+ if( !is_array( $params))
1390
+ $params = array();
1391
+ $input = array();
1392
+ foreach( $params as $paramKey => $paramValue ) {
1393
+ if( is_array( $paramValue )) {
1394
+ foreach( $paramValue as $pkey => $pValue ) {
1395
+ if(( '"' == substr( $pValue, 0, 1 )) && ( '"' == substr( $pValue, -1 )))
1396
+ $paramValue[$pkey] = substr( $pValue, 1, ( strlen( $pValue ) - 2 ));
1397
+ }
1398
+ }
1399
+ elseif(( '"' == substr( $paramValue, 0, 1 )) && ( '"' == substr( $paramValue, -1 )))
1400
+ $paramValue = substr( $paramValue, 1, ( strlen( $paramValue ) - 2 ));
1401
+ if( 'VALUE' == strtoupper( $paramKey ))
1402
+ $input['VALUE'] = strtoupper( $paramValue );
1403
+ else
1404
+ $input[strtoupper( $paramKey )] = $paramValue;
1405
+ }
1406
+ if( is_array( $defaults )) {
1407
+ foreach( $defaults as $paramKey => $paramValue ) {
1408
+ if( !isset( $input[$paramKey] ))
1409
+ $input[$paramKey] = $paramValue;
1410
+ }
1411
+ }
1412
+ return (0 < count( $input )) ? $input : null;
1413
+ }
1414
+ /**
1415
+ * step date, return updated date, array and timpstamp
1416
+ *
1417
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1418
+ * @since 2.4.16 - 2008-10-18
1419
+ * @param array $date, date to step
1420
+ * @param int $timestamp
1421
+ * @param array $step, default array( 'day' => 1 )
1422
+ * @return void
1423
+ */
1424
+ public static function _stepdate( &$date, &$timestamp, $step=array( 'day' => 1 )) {
1425
+ foreach( $step as $stepix => $stepvalue )
1426
+ $date[$stepix] += $stepvalue;
1427
+ $timestamp = iCalUtilityFunctions::_date2timestamp( $date );
1428
+ $date = iCalUtilityFunctions::_timestamp2date( $timestamp, 6 );
1429
+ foreach( $date as $k => $v ) {
1430
+ if( ctype_digit( $v ))
1431
+ $date[$k] = (int) $v;
1432
+ }
1433
+ }
1434
+ /**
1435
+ * convert timestamp to date array
1436
+ *
1437
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1438
+ * @since 2.4.16 - 2008-11-01
1439
+ * @param mixed $timestamp
1440
+ * @param int $parno
1441
+ * @return array
1442
+ */
1443
+ public static function _timestamp2date( $timestamp, $parno=6 ) {
1444
+ if( is_array( $timestamp )) {
1445
+ if(( 7 == $parno ) && !empty( $timestamp['tz'] ))
1446
+ $tz = $timestamp['tz'];
1447
+ $timestamp = $timestamp['timestamp'];
1448
+ }
1449
+ $output = array( 'year' => date( 'Y', $timestamp )
1450
+ , 'month' => date( 'm', $timestamp )
1451
+ , 'day' => date( 'd', $timestamp ));
1452
+ if( 3 != $parno ) {
1453
+ $output['hour'] = date( 'H', $timestamp );
1454
+ $output['min'] = date( 'i', $timestamp );
1455
+ $output['sec'] = date( 's', $timestamp );
1456
+ if( isset( $tz ))
1457
+ $output['tz'] = $tz;
1458
+ }
1459
+ return $output;
1460
+ }
1461
+ /**
1462
+ * convert timestamp to duration in array format
1463
+ *
1464
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1465
+ * @since 2.6.23 - 2010-10-23
1466
+ * @param int $timestamp
1467
+ * @return array, duration format
1468
+ */
1469
+ function _timestamp2duration( $timestamp ) {
1470
+ $dur = array();
1471
+ $dur['week'] = (int) floor( $timestamp / ( 7 * 24 * 60 * 60 ));
1472
+ $timestamp = $timestamp % ( 7 * 24 * 60 * 60 );
1473
+ $dur['day'] = (int) floor( $timestamp / ( 24 * 60 * 60 ));
1474
+ $timestamp = $timestamp % ( 24 * 60 * 60 );
1475
+ $dur['hour'] = (int) floor( $timestamp / ( 60 * 60 ));
1476
+ $timestamp = $timestamp % ( 60 * 60 );
1477
+ $dur['min'] = (int) floor( $timestamp / ( 60 ));
1478
+ $dur['sec'] = (int) $timestamp % ( 60 );
1479
+ return $dur;
1480
+ }
1481
+ /**
1482
+ * convert (numeric) local time offset to seconds correcting localtime to GMT
1483
+ *
1484
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1485
+ * @since 2.4.16 - 2008-10-19
1486
+ * @param string $offset
1487
+ * @return integer
1488
+ */
1489
+ public static function _tz2offset( $tz ) {
1490
+ $tz = trim( (string) $tz );
1491
+ $offset = 0;
1492
+ if((( 5 != strlen( $tz )) && ( 7 != strlen( $tz ))) ||
1493
+ (( '+' != substr( $tz, 0, 1 )) && ( '-' != substr( $tz, 0, 1 ))) ||
1494
+ (( '0000' >= substr( $tz, 1, 4 )) && ( '9999' < substr( $tz, 1, 4 ))) ||
1495
+ (( 7 == strlen( $tz )) && ( '00' > substr( $tz, 5, 2 )) && ( '99' < substr( $tz, 5, 2 ))))
1496
+ return $offset;
1497
+ $hours2sec = (int) substr( $tz, 1, 2 ) * 3600;
1498
+ $min2sec = (int) substr( $tz, 3, 2 ) * 60;
1499
+ $sec = ( 7 == strlen( $tz )) ? (int) substr( $tz, -2 ) : '00';
1500
+ $offset = $hours2sec + $min2sec + $sec;
1501
+ $offset = ('-' == substr( $tz, 0, 1 )) ? $offset : -1 * $offset;
1502
+ return $offset;
1503
+ }
1504
+ }
1505
+ ?>
lib/iCalcreator.class.php ADDED
@@ -0,0 +1,6897 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*********************************************************************************/
3
+ /**
4
+ * iCalcreator class v2.10
5
+ * copyright (c) 2007-2011 Kjell-Inge Gustafsson kigkonsult
6
+ * www.kigkonsult.se/iCalcreator/index.php
7
+ * ical@kigkonsult.se
8
+ *
9
+ * Description:
10
+ * This file is a PHP implementation of RFC 2445.
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
+ /*********************************************************************************/
28
+ /* A little setup */
29
+ /*********************************************************************************/
30
+ /* your local language code */
31
+ // define( 'ICAL_LANG', 'sv' );
32
+ // alt. autosetting
33
+ /*
34
+ $langstr = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
35
+ $pos = strpos( $langstr, ';' );
36
+ if ($pos !== false) {
37
+ $langstr = substr( $langstr, 0, $pos );
38
+ $pos = strpos( $langstr, ',' );
39
+ if ($pos !== false) {
40
+ $pos = strpos( $langstr, ',' );
41
+ $langstr = substr( $langstr, 0, $pos );
42
+ }
43
+ define( 'ICAL_LANG', $langstr );
44
+ }
45
+ */
46
+ /*********************************************************************************/
47
+ /* only for phpversion 5.1 and later, */
48
+ /* date management, default timezone setting */
49
+ /* since 2.6.36 - 2010-12-31 */
50
+ if( substr( phpversion(), 0, 3) >= '5.1' )
51
+ // && ( 'UTC' == date_default_timezone_get()))
52
+ date_default_timezone_set( 'Europe/Stockholm' );
53
+ /*********************************************************************************/
54
+ /* since 2.6.22 - 2010-09-25, do NOT remove!! */
55
+ require_once ( 'iCalUtilityFunctions.class.php' );
56
+ /*********************************************************************************/
57
+ /* version, do NOT remove!! */
58
+ define( 'ICALCREATOR_VERSION', 'iCalcreator 2.10' );
59
+ /*********************************************************************************/
60
+ /*********************************************************************************/
61
+ /**
62
+ * vcalendar class
63
+ *
64
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
65
+ * @since 2.9.6 - 2011-05-14
66
+ */
67
+ class vcalendar {
68
+ // calendar property variables
69
+ var $calscale;
70
+ var $method;
71
+ var $prodid;
72
+ var $version;
73
+ var $xprop;
74
+ // container for calendar components
75
+ var $components;
76
+ // component config variables
77
+ var $allowEmpty;
78
+ var $unique_id;
79
+ var $language;
80
+ var $directory;
81
+ var $filename;
82
+ var $url;
83
+ var $delimiter;
84
+ var $nl;
85
+ var $format;
86
+ var $dtzid;
87
+ // component internal variables
88
+ var $attributeDelimiter;
89
+ var $valueInit;
90
+ // component xCal declaration container
91
+ var $xcaldecl;
92
+ /**
93
+ * constructor for calendar object
94
+ *
95
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
96
+ * @since 2.9.6 - 2011-05-14
97
+ * @param array $config
98
+ * @return void
99
+ */
100
+ function vcalendar ( $config = array()) {
101
+ $this->_makeVersion();
102
+ $this->calscale = null;
103
+ $this->method = null;
104
+ $this->_makeUnique_id();
105
+ $this->prodid = null;
106
+ $this->xprop = array();
107
+ $this->language = null;
108
+ $this->directory = null;
109
+ $this->filename = null;
110
+ $this->url = null;
111
+ $this->dtzid = null;
112
+ /**
113
+ * language = <Text identifying a language, as defined in [RFC 1766]>
114
+ */
115
+ if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
116
+ $config['language'] = ICAL_LANG;
117
+ if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
118
+ if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
119
+ if( !isset( $config['format'] )) $config['format'] = 'iCal';
120
+ if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
121
+ $this->setConfig( $config );
122
+
123
+ $this->xcaldecl = array();
124
+ $this->components = array();
125
+ }
126
+ /*********************************************************************************/
127
+ /**
128
+ * Property Name: CALSCALE
129
+ */
130
+ /**
131
+ * creates formatted output for calendar property calscale
132
+ *
133
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
134
+ * @since 2.4.8 - 2008-10-21
135
+ * @return string
136
+ */
137
+ function createCalscale() {
138
+ if( empty( $this->calscale )) return FALSE;
139
+ switch( $this->format ) {
140
+ case 'xcal':
141
+ return ' calscale="'.$this->calscale.'"'.$this->nl;
142
+ break;
143
+ default:
144
+ return 'CALSCALE:'.$this->calscale.$this->nl;
145
+ break;
146
+ }
147
+ }
148
+ /**
149
+ * set calendar property calscale
150
+ *
151
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
152
+ * @since 2.4.8 - 2008-10-21
153
+ * @param string $value
154
+ * @return void
155
+ */
156
+ function setCalscale( $value ) {
157
+ if( empty( $value )) return FALSE;
158
+ $this->calscale = $value;
159
+ }
160
+ /*********************************************************************************/
161
+ /**
162
+ * Property Name: METHOD
163
+ */
164
+ /**
165
+ * creates formatted output for calendar property method
166
+ *
167
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
168
+ * @since 0.9.7 - 2006-11-20
169
+ * @return string
170
+ */
171
+ function createMethod() {
172
+ if( empty( $this->method )) return FALSE;
173
+ switch( $this->format ) {
174
+ case 'xcal':
175
+ return ' method="'.$this->method.'"'.$this->nl;
176
+ break;
177
+ default:
178
+ return 'METHOD:'.$this->method.$this->nl;
179
+ break;
180
+ }
181
+ }
182
+ /**
183
+ * set calendar property method
184
+ *
185
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
186
+ * @since 2.4.8 - 2008-20-23
187
+ * @param string $value
188
+ * @return bool
189
+ */
190
+ function setMethod( $value ) {
191
+ if( empty( $value )) return FALSE;
192
+ $this->method = $value;
193
+ return TRUE;
194
+ }
195
+ /*********************************************************************************/
196
+ /**
197
+ * Property Name: PRODID
198
+ *
199
+ * The identifier is RECOMMENDED to be the identical syntax to the
200
+ * [RFC 822] addr-spec. A good method to assure uniqueness is to put the
201
+ * domain name or a domain literal IP address of the host on which.. .
202
+ */
203
+ /**
204
+ * creates formatted output for calendar property prodid
205
+ *
206
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
207
+ * @since 0.9.7 - 2006-11-20
208
+ * @return string
209
+ */
210
+ function createProdid() {
211
+ if( !isset( $this->prodid ))
212
+ $this->_makeProdid();
213
+ switch( $this->format ) {
214
+ case 'xcal':
215
+ return ' prodid="'.$this->prodid.'"'.$this->nl;
216
+ break;
217
+ default:
218
+ return 'PRODID:'.$this->prodid.$this->nl;
219
+ break;
220
+ }
221
+ }
222
+ /**
223
+ * make default value for calendar prodid
224
+ *
225
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
226
+ * @since 2.6.8 - 2009-12-30
227
+ * @return void
228
+ */
229
+ function _makeProdid() {
230
+ $this->prodid = '-//'.$this->unique_id.'//NONSGML kigkonsult.se '.ICALCREATOR_VERSION.'//'.strtoupper( $this->language );
231
+ }
232
+ /**
233
+ * Conformance: The property MUST be specified once in an iCalendar object.
234
+ * Description: The vendor of the implementation SHOULD assure that this
235
+ * is a globally unique identifier; using some technique such as an FPI
236
+ * value, as defined in [ISO 9070].
237
+ */
238
+ /**
239
+ * make default unique_id for calendar prodid
240
+ *
241
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
242
+ * @since 0.3.0 - 2006-08-10
243
+ * @return void
244
+ */
245
+ function _makeUnique_id() {
246
+ $this->unique_id = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost';
247
+ }
248
+ /*********************************************************************************/
249
+ /**
250
+ * Property Name: VERSION
251
+ *
252
+ * Description: A value of "2.0" corresponds to this memo.
253
+ */
254
+ /**
255
+ * creates formatted output for calendar property version
256
+
257
+ *
258
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
259
+ * @since 0.9.7 - 2006-11-20
260
+ * @return string
261
+ */
262
+ function createVersion() {
263
+ if( empty( $this->version ))
264
+ $this->_makeVersion();
265
+ switch( $this->format ) {
266
+ case 'xcal':
267
+ return ' version="'.$this->version.'"'.$this->nl;
268
+ break;
269
+ default:
270
+ return 'VERSION:'.$this->version.$this->nl;
271
+ break;
272
+ }
273
+ }
274
+ /**
275
+ * set default calendar version
276
+ *
277
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
278
+ * @since 0.3.0 - 2006-08-10
279
+ * @return void
280
+ */
281
+ function _makeVersion() {
282
+ $this->version = '2.0';
283
+ }
284
+ /**
285
+ * set calendar version
286
+ *
287
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
288
+ * @since 2.4.8 - 2008-10-23
289
+ * @param string $value
290
+ * @return void
291
+ */
292
+ function setVersion( $value ) {
293
+ if( empty( $value )) return FALSE;
294
+ $this->version = $value;
295
+ return TRUE;
296
+ }
297
+ /*********************************************************************************/
298
+ /**
299
+ * Property Name: x-prop
300
+ */
301
+ /**
302
+ * creates formatted output for calendar property x-prop, iCal format only
303
+ *
304
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
305
+ * @since 2.9.3 - 2011-05-14
306
+ * @return string
307
+ */
308
+ function createXprop() {
309
+ if( 'xcal' == $this->format )
310
+ return false;
311
+ if( empty( $this->xprop ) || !is_array( $this->xprop )) return FALSE;
312
+ $output = null;
313
+ $toolbox = new calendarComponent();
314
+ $toolbox->setConfig( $this->getConfig());
315
+ foreach( $this->xprop as $label => $xpropPart ) {
316
+ if( !isset($xpropPart['value']) || ( empty( $xpropPart['value'] ) && !is_numeric( $xpropPart['value'] ))) {
317
+ $output .= $toolbox->_createElement( $label );
318
+ continue;
319
+ }
320
+ $attributes = $toolbox->_createParams( $xpropPart['params'], array( 'LANGUAGE' ));
321
+ if( is_array( $xpropPart['value'] )) {
322
+ foreach( $xpropPart['value'] as $pix => $theXpart )
323
+ $xpropPart['value'][$pix] = $toolbox->_strrep( $theXpart );
324
+ $xpropPart['value'] = implode( ',', $xpropPart['value'] );
325
+ }
326
+ else
327
+ $xpropPart['value'] = $toolbox->_strrep( $xpropPart['value'] );
328
+ $output .= $toolbox->_createElement( $label, $attributes, $xpropPart['value'] );
329
+ }
330
+ return $output;
331
+ }
332
+ /**
333
+ * set calendar property x-prop
334
+ *
335
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
336
+ * @since 2.9.3 - 2011-05-14
337
+ * @param string $label
338
+ * @param string $value
339
+ * @param array $params optional
340
+ * @return bool
341
+ */
342
+ function setXprop( $label, $value, $params=FALSE ) {
343
+ if( empty( $label )) return FALSE;
344
+ if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
345
+ $xprop = array( 'value' => $value );
346
+ $xprop['params'] = iCalUtilityFunctions::_setParams( $params );
347
+ if( !is_array( $this->xprop )) $this->xprop = array();
348
+ $this->xprop[strtoupper( $label )] = $xprop;
349
+ return TRUE;
350
+ }
351
+ /*********************************************************************************/
352
+ /**
353
+ * delete calendar property value
354
+ *
355
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
356
+ * @since 2.8.8 - 2011-03-15
357
+ * @param mixed $propName, bool FALSE => X-property
358
+ * @param int $propix, optional, if specific property is wanted in case of multiply occurences
359
+ * @return bool, if successfull delete
360
+ */
361
+ function deleteProperty( $propName=FALSE, $propix=FALSE ) {
362
+ $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
363
+ if( !$propix )
364
+ $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1;
365
+ $this->propdelix[$propName] = --$propix;
366
+ $return = FALSE;
367
+ switch( $propName ) {
368
+ case 'CALSCALE':
369
+ if( isset( $this->calscale )) {
370
+ $this->calscale = null;
371
+ $return = TRUE;
372
+ }
373
+ break;
374
+ case 'METHOD':
375
+ if( isset( $this->method )) {
376
+ $this->method = null;
377
+ $return = TRUE;
378
+ }
379
+ break;
380
+ default:
381
+ $reduced = array();
382
+ if( $propName != 'X-PROP' ) {
383
+ if( !isset( $this->xprop[$propName] )) { unset( $this->propdelix[$propName] ); return FALSE; }
384
+ foreach( $this->xprop as $k => $a ) {
385
+ if(( $k != $propName ) && !empty( $a ))
386
+ $reduced[$k] = $a;
387
+ }
388
+ }
389
+ else {
390
+ if( count( $this->xprop ) <= $propix ) return FALSE;
391
+ $xpropno = 0;
392
+ foreach( $this->xprop as $xpropkey => $xpropvalue ) {
393
+ if( $propix != $xpropno )
394
+ $reduced[$xpropkey] = $xpropvalue;
395
+ $xpropno++;
396
+ }
397
+ }
398
+ $this->xprop = $reduced;
399
+ if( empty( $this->xprop )) {
400
+ unset( $this->propdelix[$propName] );
401
+ return FALSE;
402
+ }
403
+ return TRUE;
404
+ }
405
+ return $return;
406
+ }
407
+ /**
408
+ * get calendar property value/params
409
+ *
410
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
411
+ * @since 2.8.8 - 2011-04-16
412
+ * @param string $propName, optional
413
+ * @param int $propix, optional, if specific property is wanted in case of multiply occurences
414
+ * @param bool $inclParam=FALSE
415
+ * @return mixed
416
+ */
417
+ function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE ) {
418
+ $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
419
+ if( 'X-PROP' == $propName ) {
420
+ if( !$propix )
421
+ $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1;
422
+ $this->propix[$propName] = --$propix;
423
+ }
424
+ switch( $propName ) {
425
+ case 'ATTENDEE':
426
+ case 'CATEGORIES':
427
+ case 'DTSTART':
428
+ case 'LOCATION':
429
+ case 'ORGANIZER':
430
+ case 'PRIORITY':
431
+ case 'RESOURCES':
432
+ case 'STATUS':
433
+ case 'SUMMARY':
434
+ case 'RECURRENCE-ID-UID':
435
+ case 'R-UID':
436
+ case 'UID':
437
+ $output = array();
438
+ foreach ( $this->components as $cix => $component) {
439
+ if( !in_array( $component->objName, array('vevent', 'vtodo', 'vjournal', 'vfreebusy' )))
440
+ continue;
441
+ if(( 'ATTENDEE' == $propName ) || ( 'CATEGORIES' == $propName ) || ( 'RESOURCES' == $propName )) {
442
+ $component->_getProperties( $propName, $output );
443
+ continue;
444
+ }
445
+ elseif(( 3 < strlen( $propName )) && ( 'UID' == substr( $propName, -3 ))) {
446
+ if( FALSE !== ( $content = $component->getProperty( 'RECURRENCE-ID' )))
447
+ $content = $component->getProperty( 'UID' );
448
+ }
449
+ elseif( FALSE === ( $content = $component->getProperty( $propName )))
450
+ continue;
451
+ if( FALSE === $content )
452
+ continue;
453
+ elseif( is_array( $content )) {
454
+ if( isset( $content['year'] )) {
455
+ $key = sprintf( '%04d%02d%02d', $content['year'], $content['month'], $content['day'] );
456
+ if( !isset( $output[$key] ))
457
+ $output[$key] = 1;
458
+ else
459
+ $output[$key] += 1;
460
+ }
461
+ else {
462
+ foreach( $content as $partValue => $partCount ) {
463
+ if( !isset( $output[$partValue] ))
464
+ $output[$partValue] = $partCount;
465
+ else
466
+ $output[$partValue] += $partCount;
467
+ }
468
+ }
469
+ } // end elseif( is_array( $content )) {
470
+ elseif( !isset( $output[$content] ))
471
+ $output[$content] = 1;
472
+ else
473
+ $output[$content] += 1;
474
+ } // end foreach ( $this->components as $cix => $component)
475
+ if( !empty( $output ))
476
+ ksort( $output );
477
+ return $output;
478
+ break;
479
+
480
+ case 'CALSCALE':
481
+ return ( !empty( $this->calscale )) ? $this->calscale : FALSE;
482
+ break;
483
+ case 'METHOD':
484
+ return ( !empty( $this->method )) ? $this->method : FALSE;
485
+ break;
486
+ case 'PRODID':
487
+ if( empty( $this->prodid ))
488
+ $this->_makeProdid();
489
+ return $this->prodid;
490
+ break;
491
+ case 'VERSION':
492
+ return ( !empty( $this->version )) ? $this->version : FALSE;
493
+ break;
494
+ default:
495
+ if( $propName != 'X-PROP' ) {
496
+ if( !isset( $this->xprop[$propName] )) return FALSE;
497
+ return ( $inclParam ) ? array( $propName, $this->xprop[$propName] )
498
+ : array( $propName, $this->xprop[$propName]['value'] );
499
+ }
500
+ else {
501
+ if( empty( $this->xprop )) return FALSE;
502
+ $xpropno = 0;
503
+ foreach( $this->xprop as $xpropkey => $xpropvalue ) {
504
+ if( $propix == $xpropno )
505
+ return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] )
506
+ : array( $xpropkey, $this->xprop[$xpropkey]['value'] );
507
+ else
508
+ $xpropno++;
509
+ }
510
+ unset( $this->propix[$propName] );
511
+ return FALSE; // not found ??
512
+ }
513
+ }
514
+ return FALSE;
515
+ }
516
+ /**
517
+ * general vcalendar property setting
518
+ *
519
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
520
+ * @since 2.2.13 - 2007-11-04
521
+ * @param mixed $args variable number of function arguments,
522
+ * first argument is ALWAYS component name,
523
+ * second ALWAYS component value!
524
+ * @return bool
525
+ */
526
+ function setProperty () {
527
+ $numargs = func_num_args();
528
+ if( 1 > $numargs )
529
+ return FALSE;
530
+ $arglist = func_get_args();
531
+ $arglist[0] = strtoupper( $arglist[0] );
532
+ switch( $arglist[0] ) {
533
+ case 'CALSCALE':
534
+ return $this->setCalscale( $arglist[1] );
535
+ case 'METHOD':
536
+ return $this->setMethod( $arglist[1] );
537
+ case 'VERSION':
538
+ return $this->setVersion( $arglist[1] );
539
+ default:
540
+ if( !isset( $arglist[1] )) $arglist[1] = null;
541
+ if( !isset( $arglist[2] )) $arglist[2] = null;
542
+ return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] );
543
+ }
544
+ return FALSE;
545
+ }
546
+ /*********************************************************************************/
547
+ /**
548
+ * get vcalendar config values or * calendar components
549
+ *
550
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
551
+ * @since 2.9.6 - 2011-05-14
552
+ * @param mixed $config
553
+ * @return value
554
+ */
555
+ function getConfig( $config = FALSE ) {
556
+ if( !$config ) {
557
+ $return = array();
558
+ $return['ALLOWEMPTY'] = $this->getConfig( 'ALLOWEMPTY' );
559
+ $return['DELIMITER'] = $this->getConfig( 'DELIMITER' );
560
+ $return['DIRECTORY'] = $this->getConfig( 'DIRECTORY' );
561
+ $return['FILENAME'] = $this->getConfig( 'FILENAME' );
562
+ $return['DIRFILE'] = $this->getConfig( 'DIRFILE' );
563
+ $return['FILESIZE'] = $this->getConfig( 'FILESIZE' );
564
+ $return['FORMAT'] = $this->getConfig( 'FORMAT' );
565
+ if( FALSE !== ( $lang = $this->getConfig( 'LANGUAGE' )))
566
+ $return['LANGUAGE'] = $lang;
567
+ $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' );
568
+ $return['UNIQUE_ID'] = $this->getConfig( 'UNIQUE_ID' );
569
+ if( FALSE !== ( $url = $this->getConfig( 'URL' )))
570
+ $return['URL'] = $url;
571
+ $return['TZID'] = $this->getConfig( 'TZID' );
572
+ return $return;
573
+ }
574
+ switch( strtoupper( $config )) {
575
+ case 'ALLOWEMPTY':
576
+ return $this->allowEmpty;
577
+ break;
578
+ case 'COMPSINFO':
579
+ unset( $this->compix );
580
+ $info = array();
581
+ foreach( $this->components as $cix => $component ) {
582
+ if( empty( $component )) continue;
583
+ $info[$cix]['ordno'] = $cix + 1;
584
+ $info[$cix]['type'] = $component->objName;
585
+ $info[$cix]['uid'] = $component->getProperty( 'uid' );
586
+ $info[$cix]['props'] = $component->getConfig( 'propinfo' );
587
+ $info[$cix]['sub'] = $component->getConfig( 'compsinfo' );
588
+ }
589
+ return $info;
590
+ break;
591
+ case 'DELIMITER':
592
+ return $this->delimiter;
593
+ break;
594
+ case 'DIRECTORY':
595
+ if( empty( $this->directory ))
596
+ $this->directory = '.';
597
+ return $this->directory;
598
+ break;
599
+ case 'DIRFILE':
600
+ return $this->getConfig( 'directory' ).$this->getConfig( 'delimiter' ).$this->getConfig( 'filename' );
601
+ break;
602
+ case 'FILEINFO':
603
+ return array( $this->getConfig( 'directory' )
604
+ , $this->getConfig( 'filename' )
605
+ , $this->getConfig( 'filesize' ));
606
+ break;
607
+ case 'FILENAME':
608
+ if( empty( $this->filename )) {
609
+ if( 'xcal' == $this->format )
610
+ $this->filename = date( 'YmdHis' ).'.xml'; // recommended xcs.. .
611
+ else
612
+ $this->filename = date( 'YmdHis' ).'.ics';
613
+ }
614
+ return $this->filename;
615
+ break;
616
+ case 'FILESIZE':
617
+ $size = 0;
618
+ if( empty( $this->url )) {
619
+ $dirfile = $this->getConfig( 'dirfile' );
620
+ if( !is_file( $dirfile ) || ( FALSE === ( $size = filesize( $dirfile ))))
621
+ $size = 0;
622
+ clearstatcache();
623
+ }
624
+ return $size;
625
+ break;
626
+ case 'FORMAT':
627
+ return ( $this->format == 'xcal' ) ? 'xCal' : 'iCal';
628
+ break;
629
+ case 'LANGUAGE':
630
+ /* get language for calendar component as defined in [RFC 1766] */
631
+ return $this->language;
632
+ break;
633
+ case 'NL':
634
+ case 'NEWLINECHAR':
635
+ return $this->nl;
636
+ break;
637
+ case 'TZID':
638
+ return $this->dtzid;
639
+ break;
640
+ case 'UNIQUE_ID':
641
+ return $this->unique_id;
642
+ break;
643
+ case 'URL':
644
+ if( !empty( $this->url ))
645
+ return $this->url;
646
+ else
647
+ return FALSE;
648
+ break;
649
+ }
650
+ }
651
+ /**
652
+ * general vcalendar config setting
653
+ *
654
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
655
+ * @since 2.9.6 - 2011-05-14
656
+ * @param mixed $config
657
+ * @param string $value
658
+ * @return void
659
+ */
660
+ function setConfig( $config, $value = FALSE) {
661
+ if( is_array( $config )) {
662
+ foreach( $config as $cKey => $cValue ) {
663
+ if( FALSE === $this->setConfig( $cKey, $cValue ))
664
+ return FALSE;
665
+ }
666
+ return TRUE;
667
+ }
668
+ $res = FALSE;
669
+ switch( strtoupper( $config )) {
670
+ case 'ALLOWEMPTY':
671
+ $this->allowEmpty = $value;
672
+ $subcfg = array( 'ALLOWEMPTY' => $value );
673
+ $res = TRUE;
674
+ break;
675
+ case 'DELIMITER':
676
+ $this->delimiter = $value;
677
+ return TRUE;
678
+ break;
679
+ case 'DIRECTORY':
680
+ $value = trim( $value );
681
+ $del = $this->getConfig('delimiter');
682
+ if( $del == substr( $value, ( 0 - strlen( $del ))))
683
+ $value = substr( $value, 0, ( strlen( $value ) - strlen( $del )));
684
+ if( is_dir( $value )) {
685
+ /* local directory */
686
+ clearstatcache();
687
+ $this->directory = $value;
688
+ $this->url = null;
689
+ return TRUE;
690
+ }
691
+ else
692
+ return FALSE;
693
+ break;
694
+ case 'FILENAME':
695
+ $value = trim( $value );
696
+ if( !empty( $this->url )) {
697
+ /* remote directory+file -> URL */
698
+ $this->filename = $value;
699
+ return TRUE;
700
+ }
701
+ $dirfile = $this->getConfig( 'directory' ).$this->getConfig( 'delimiter' ).$value;
702
+ if( file_exists( $dirfile )) {
703
+ /* local file exists */
704
+ if( is_readable( $dirfile ) || is_writable( $dirfile )) {
705
+ clearstatcache();
706
+ $this->filename = $value;
707
+ return TRUE;
708
+ }
709
+ else
710
+ return FALSE;
711
+ }
712
+ elseif( is_readable($this->getConfig( 'directory' ) ) || is_writable( $this->getConfig( 'directory' ) )) {
713
+ /* read- or writable directory */
714
+ $this->filename = $value;
715
+ return TRUE;
716
+ }
717
+ else
718
+ return FALSE;
719
+ break;
720
+ case 'FORMAT':
721
+ $value = trim( strtolower( $value ));
722
+ if( 'xcal' == $value ) {
723
+ $this->format = 'xcal';
724
+ $this->attributeDelimiter = $this->nl;
725
+ $this->valueInit = null;
726
+ }
727
+ else {
728
+ $this->format = null;
729
+ $this->attributeDelimiter = ';';
730
+ $this->valueInit = ':';
731
+ }
732
+ $subcfg = array( 'FORMAT' => $value );
733
+ $res = TRUE;
734
+ break;
735
+ case 'LANGUAGE':
736
+ // set language for calendar component as defined in [RFC 1766]
737
+ $value = trim( $value );
738
+ $this->language = $value;
739
+ $subcfg = array( 'LANGUAGE' => $value );
740
+ $res = TRUE;
741
+ break;
742
+ case 'NL':
743
+ case 'NEWLINECHAR':
744
+ $this->nl = $value;
745
+ $subcfg = array( 'NL' => $value );
746
+ $res = TRUE;
747
+ break;
748
+ case 'TZID':
749
+ $this->dtzid = $value;
750
+ $subcfg = array( 'TZID' => $value );
751
+ $res = TRUE;
752
+ break;
753
+ case 'UNIQUE_ID':
754
+ $value = trim( $value );
755
+ $this->unique_id = $value;
756
+ $subcfg = array( 'UNIQUE_ID' => $value );
757
+ $res = TRUE;
758
+ break;
759
+ case 'URL':
760
+ /* remote file - URL */
761
+ $value = trim( $value );
762
+ $value = str_replace( 'HTTP://', 'http://', $value );
763
+ $value = str_replace( 'WEBCAL://', 'http://', $value );
764
+ $value = str_replace( 'webcal://', 'http://', $value );
765
+ $this->url = $value;
766
+ $this->directory = null;
767
+ $parts = pathinfo( $value );
768
+ return $this->setConfig( 'filename', $parts['basename'] );
769
+ break;
770
+ default: // any unvalid config key.. .
771
+ return TRUE;
772
+ }
773
+ if( !$res ) return FALSE;
774
+ if( isset( $subcfg ) && !empty( $this->components )) {
775
+ foreach( $subcfg as $cfgkey => $cfgvalue ) {
776
+ foreach( $this->components as $cix => $component ) {
777
+ $res = $component->setConfig( $cfgkey, $cfgvalue, TRUE );
778
+ if( !$res )
779
+ break 2;
780
+ $this->components[$cix] = $component->copy(); // PHP4 compliant
781
+ }
782
+ }
783
+ }
784
+ return $res;
785
+ }
786
+ /*********************************************************************************/
787
+ /**
788
+ * add calendar component to container
789
+ *
790
+ * alias to setComponent
791
+ *
792
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
793
+ * @since 1.x.x - 2007-04-24
794
+ * @param object $component calendar component
795
+ * @return void
796
+ */
797
+ function addComponent( $component ) {
798
+ $this->setComponent( $component );
799
+ }
800
+ /**
801
+ * delete calendar component from container
802
+ *
803
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
804
+ * @since 2.8.8 - 2011-03-15
805
+ * @param mixed $arg1 ordno / component type / component uid
806
+ * @param mixed $arg2 optional, ordno if arg1 = component type
807
+ * @return void
808
+ */
809
+ function deleteComponent( $arg1, $arg2=FALSE ) {
810
+ $argType = $index = null;
811
+ if ( ctype_digit( (string) $arg1 )) {
812
+ $argType = 'INDEX';
813
+ $index = (int) $arg1 - 1;
814
+ }
815
+ elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
816
+ $argType = strtolower( $arg1 );
817
+ $index = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0;
818
+ }
819
+ $cix1dC = 0;
820
+ foreach ( $this->components as $cix => $component) {
821
+ if( empty( $component )) continue;
822
+ if(( 'INDEX' == $argType ) && ( $index == $cix )) {
823
+ unset( $this->components[$cix] );
824
+ return TRUE;
825
+ }
826
+ elseif( $argType == $component->objName ) {
827
+ if( $index == $cix1dC ) {
828
+ unset( $this->components[$cix] );
829
+ return TRUE;
830
+ }
831
+ $cix1dC++;
832
+ }
833
+ elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) {
834
+ unset( $this->components[$cix] );
835
+ return TRUE;
836
+ }
837
+ }
838
+ return FALSE;
839
+ }
840
+ /**
841
+ * get calendar component from container
842
+ *
843
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
844
+ * @since 2.9.1 - 2011-04-16
845
+ * @param mixed $arg1 optional, ordno/component type/ component uid
846
+ * @param mixed $arg2 optional, ordno if arg1 = component type
847
+ * @return object
848
+ */
849
+ function getComponent( $arg1=FALSE, $arg2=FALSE ) {
850
+ $index = $argType = null;
851
+ if ( !$arg1 ) { // first or next in component chain
852
+ $argType = 'INDEX';
853
+ $index = $this->compix['INDEX'] = ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1;
854
+ }
855
+ elseif ( ctype_digit( (string) $arg1 )) { // specific component in chain
856
+ $argType = 'INDEX';
857
+ $index = (int) $arg1;
858
+ unset( $this->compix );
859
+ }
860
+ elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] )
861
+ $arg2 = implode( '-', array_keys( $arg1 ));
862
+ $index = $this->compix[$arg2] = ( isset( $this->compix[$arg2] )) ? $this->compix[$arg2] + 1 : 1;
863
+ $dateProps = array( 'DTSTART', 'DTEND', 'DUE', 'CREATED', 'COMPLETED', 'DTSTAMP', 'LAST-MODIFIED', 'RECURRENCE-ID' );
864
+ $otherProps = array( 'ATTENDEE', 'CATEGORIES', 'LOCATION', 'ORGANIZER', 'PRIORITY', 'RESOURCES', 'STATUS', 'SUMMARY', 'UID' );
865
+ }
866
+ elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) { // object class name
867
+ unset( $this->compix['INDEX'] );
868
+ $argType = strtolower( $arg1 );
869
+ if( !$arg2 )
870
+ $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1;
871
+ elseif( isset( $arg2 ) && ctype_digit( (string) $arg2 ))
872
+ $index = (int) $arg2;
873
+ }
874
+ elseif(( strlen( $arg1 ) > strlen( 'vfreebusy' )) && ( FALSE !== strpos( $arg1, '@' ))) { // UID as 1st argument
875
+ if( !$arg2 )
876
+ $index = $this->compix[$arg1] = ( isset( $this->compix[$arg1] )) ? $this->compix[$arg1] + 1 : 1;
877
+ elseif( isset( $arg2 ) && ctype_digit( (string) $arg2 ))
878
+ $index = (int) $arg2;
879
+ }
880
+ if( isset( $index ))
881
+ $index -= 1;
882
+ $ckeys = array_keys( $this->components );
883
+ if( !empty( $index) && ( $index > end( $ckeys )))
884
+ return FALSE;
885
+ $cix1gC = 0;
886
+ foreach ( $this->components as $cix => $component) {
887
+ if( empty( $component )) continue;
888
+ if(( 'INDEX' == $argType ) && ( $index == $cix ))
889
+ return $component->copy();
890
+ elseif( $argType == $component->objName ) {
891
+ if( $index == $cix1gC )
892
+ return $component->copy();
893
+ $cix1gC++;
894
+ }
895
+ elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] )
896
+ $hit = FALSE;
897
+ foreach( $arg1 as $pName => $pValue ) {
898
+ $pName = strtoupper( $pName );
899
+ if( !in_array( $pName, $dateProps ) && !in_array( $pName, $otherProps ))
900
+ continue;
901
+ if(( 'ATTENDEE' == $pName ) || ( 'CATEGORIES' == $pName ) || ( 'RESOURCES' == $pName )) { // multiple ocurrence may occur
902
+ $propValues = array();
903
+ $component->_getProperties( $pName, $propValues );
904
+ $propValues = array_keys( $propValues );
905
+ $hit = ( in_array( $pValue, $propValues )) ? TRUE : FALSE;
906
+ continue;
907
+ } // end if(( 'CATEGORIES' == $propName ) || ( 'RESOURCES' == $propName )) { // multiple ocurrence may occur
908
+ if( FALSE === ( $value = $component->getProperty( $pName ))) { // single ocurrency
909
+ $hit = FALSE; // missing property
910
+ continue;
911
+ }
912
+ if( 'SUMMARY' == $pName ) { // exists within (any case)
913
+ $hit = ( FALSE !== stripos( $d, $pValue )) ? TRUE : FALSE;
914
+ continue;
915
+ }
916
+ if( in_array( strtoupper( $pName ), $dateProps )) {
917
+ $valuedate = sprintf( '%04d%02d%02d', $value['year'], $value['month'], $value['day'] );
918
+ if( 8 < strlen( $pValue )) {
919
+ if( isset( $value['hour'] )) {
920
+ if( 'T' == substr( $pValue, 8, 1 ))
921
+ $pValue = str_replace( 'T', '', $pValue );
922
+ $valuedate .= sprintf( '%02d%02d%02d', $value['hour'], $value['min'], $value['sec'] );
923
+ }
924
+ else
925
+ $pValue = substr( $pValue, 0, 8 );
926
+ }
927
+ $hit = ( $pValue == $valuedate ) ? TRUE : FALSE;
928
+ continue;
929
+ }
930
+ elseif( !is_array( $value ))
931
+ $value = array( $value );
932
+ foreach( $value as $part ) {
933
+ $part = ( FALSE !== strpos( $part, ',' )) ? explode( ',', $part ) : array( $part );
934
+ foreach( $part as $subPart ) {
935
+ if( $pValue == $subPart ) {
936
+ $hit = TRUE;
937
+ continue 2;
938
+ }
939
+ }
940
+ }
941
+ $hit = FALSE; // no hit in property
942
+ } // end foreach( $arg1 as $pName => $pValue )
943
+ if( $hit ) {
944
+ if( $index == $cix1gC )
945
+ return $component->copy();
946
+ $cix1gC++;
947
+ }
948
+ } // end elseif( is_array( $arg1 )) { // array( *[propertyName => propertyValue] )
949
+ elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) { // UID
950
+ if( $index == $cix1gC )
951
+ return $component->copy();
952
+ $cix1gC++;
953
+ }
954
+ } // end foreach ( $this->components.. .
955
+ /* not found.. . */
956
+ unset( $this->compix );
957
+ return FALSE;
958
+ }
959
+ /**
960
+ * create new calendar component, already included within calendar
961
+ *
962
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
963
+ * @since 2.6.33 - 2011-01-03
964
+ * @param string $compType component type
965
+ * @return object (reference)
966
+ */
967
+ function & newComponent( $compType ) {
968
+ $config = $this->getConfig();
969
+ $keys = array_keys( $this->components );
970
+ $ix = end( $keys) + 1;
971
+ switch( strtoupper( $compType )) {
972
+ case 'EVENT':
973
+ case 'VEVENT':
974
+ $this->components[$ix] = new vevent( $config );
975
+ break;
976
+ case 'TODO':
977
+ case 'VTODO':
978
+ $this->components[$ix] = new vtodo( $config );
979
+ break;
980
+ case 'JOURNAL':
981
+ case 'VJOURNAL':
982
+ $this->components[$ix] = new vjournal( $config );
983
+ break;
984
+ case 'FREEBUSY':
985
+ case 'VFREEBUSY':
986
+ $this->components[$ix] = new vfreebusy( $config );
987
+ break;
988
+ case 'TIMEZONE':
989
+ case 'VTIMEZONE':
990
+ array_unshift( $this->components, new vtimezone( $config ));
991
+ $ix = 0;
992
+ break;
993
+ default:
994
+ return FALSE;
995
+ }
996
+ return $this->components[$ix];
997
+ }
998
+ /**
999
+ * select components from calendar on date or selectOption basis
1000
+ *
1001
+ * Ensure DTSTART is set for every component.
1002
+ * No date controls occurs.
1003
+ *
1004
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1005
+ * @since 2.9.7 - 2011-06-04
1006
+ * @param mixed $startY optional, start Year, default current Year ALT. array selecOptions
1007
+ * @param int $startM optional, start Month, default current Month
1008
+ * @param int $startD optional, start Day, default current Day
1009
+ * @param int $endY optional, end Year, default $startY
1010
+ * @param int $endY optional, end Month, default $startM
1011
+ * @param int $endY optional, end Day, default $startD
1012
+ * @param mixed $cType optional, calendar component type(-s), default FALSE=all else string/array type(-s)
1013
+ * @param bool $flat optional, FALSE (default) => output : array[Year][Month][Day][]
1014
+ * TRUE => output : array[] (ignores split)
1015
+ * @param bool $any optional, TRUE (default) - select component that take place within period
1016
+ * FALSE - only components that starts within period
1017
+ * @param bool $split optional, TRUE (default) - one component copy every day it take place during the
1018
+ * period (implies flat=FALSE)
1019
+ * FALSE - one occurance of component only in output array
1020
+ * @return array or FALSE
1021
+ */
1022
+ function selectComponents( $startY=FALSE, $startM=FALSE, $startD=FALSE, $endY=FALSE, $endM=FALSE, $endD=FALSE, $cType=FALSE, $flat=FALSE, $any=TRUE, $split=TRUE ) {
1023
+ /* check if empty calendar */
1024
+ if( 0 >= count( $this->components )) return FALSE;
1025
+ if( is_array( $startY ))
1026
+ return $this->selectComponents2( $startY );
1027
+ /* check default dates */
1028
+ if( !$startY ) $startY = date( 'Y' );
1029
+ if( !$startM ) $startM = date( 'm' );
1030
+ if( !$startD ) $startD = date( 'd' );
1031
+ $startDate = mktime( 0, 0, 0, $startM, $startD, $startY );
1032
+ if( !$endY ) $endY = $startY;
1033
+ if( !$endM ) $endM = $startM;
1034
+ if( !$endD ) $endD = $startD;
1035
+ $endDate = mktime( 23, 59, 59, $endM, $endD, $endY );
1036
+ //echo 'selectComp arg='.date( 'Y-m-d H:i:s', $startDate).' -- '.date( 'Y-m-d H:i:s', $endDate)."<br />\n"; $tcnt = 0;// test ###
1037
+ /* check component types */
1038
+ $validTypes = array('vevent', 'vtodo', 'vjournal', 'vfreebusy' );
1039
+ if( is_array( $cType )) {
1040
+ foreach( $cType as $cix => $theType ) {
1041
+ $cType[$cix] = $theType = strtolower( $theType );
1042
+ if( !in_array( $theType, $validTypes ))
1043
+ $cType[$cix] = 'vevent';
1044
+ }
1045
+ $cType = array_unique( $cType );
1046
+ }
1047
+ elseif( !empty( $cType )) {
1048
+ $cType = strtolower( $cType );
1049
+ if( !in_array( $cType, $validTypes ))
1050
+ $cType = array( 'vevent' );
1051
+ else
1052
+ $cType = array( $cType );
1053
+ }
1054
+ else
1055
+ $cType = $validTypes;
1056
+ if( 0 >= count( $cType ))
1057
+ $cType = $validTypes;
1058
+ if(( TRUE === $flat ) && ( TRUE === $split )) // invalid combination
1059
+ $split = FALSE;
1060
+ /* iterate components */
1061
+ $result = array();
1062
+ foreach ( $this->components as $cix => $component ) {
1063
+ if( empty( $component )) continue;
1064
+ unset( $start );
1065
+ /* deselect unvalid type components */
1066
+ if( !in_array( $component->objName, $cType )) continue;
1067
+ $start = $component->getProperty( 'dtstart' );
1068
+ /* select due when dtstart is missing */
1069
+ if( empty( $start ) && ( $component->objName == 'vtodo' ) && ( FALSE === ( $start = $component->getProperty( 'due' ))))
1070
+ continue;
1071
+ $dtendExist = $dueExist = $durationExist = $endAllDayEvent = FALSE;
1072
+ unset( $end, $startWdate, $endWdate, $rdurWsecs, $rdur, $exdatelist, $workstart, $workend, $endDateFormat ); // clean up
1073
+ $startWdate = iCalUtilityFunctions::_date2timestamp( $start );
1074
+ $startDateFormat = ( isset( $start['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
1075
+ /* get end date from dtend/due/duration properties */
1076
+ $end = $component->getProperty( 'dtend' );
1077
+ if( !empty( $end )) {
1078
+ $dtendExist = TRUE;
1079
+ $endDateFormat = ( isset( $end['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
1080
+ }
1081
+ if( empty( $end ) && ( $component->objName == 'vtodo' )) {
1082
+ $end = $component->getProperty( 'due' );
1083
+ if( !empty( $end )) {
1084
+ $dueExist = TRUE;
1085
+ $endDateFormat = ( isset( $end['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
1086
+ }
1087
+ }
1088
+ if( !empty( $end ) && !isset( $end['hour'] )) {
1089
+ /* a DTEND without time part regards an event that ends the day before,
1090
+ for an all-day event DTSTART=20071201 DTEND=20071202 (taking place 20071201!!! */
1091
+ $endAllDayEvent = TRUE;
1092
+ $endWdate = mktime( 23, 59, 59, $end['month'], ($end['day'] - 1), $end['year'] );
1093
+ $end['year'] = date( 'Y', $endWdate );
1094
+ $end['month'] = date( 'm', $endWdate );
1095
+ $end['day'] = date( 'd', $endWdate );
1096
+ $end['hour'] = 23;
1097
+ $end['min'] = $end['sec'] = 59;
1098
+ }
1099
+ if( empty( $end )) {
1100
+ $end = $component->getProperty( 'duration', FALSE, FALSE, TRUE );// in dtend (array) format
1101
+ if( !empty( $end ))
1102
+ $durationExist = TRUE;
1103
+ $endDateFormat = ( isset( $start['hour'] )) ? 'Y-m-d H:i:s' : 'Y-m-d';
1104
+ // if( !empty($end)) echo 'selectComp 4 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
1105
+ }
1106
+ if( empty( $end )) { // assume one day duration if missing end date
1107
+ $end = array( 'year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59 );
1108
+ }
1109
+ // if( isset($end)) echo 'selectComp 5 start='.implode('-',$start).' end='.implode('-',$end)."<br />\n"; // test ###
1110
+ $endWdate = iCalUtilityFunctions::_date2timestamp( $end );
1111
+ if( $endWdate < $startWdate ) { // MUST be after start date!!
1112
+ $end = array( 'year' => $start['year'], 'month' => $start['month'], 'day' => $start['day'], 'hour' => 23, 'min' => 59, 'sec' => 59 );
1113
+ $endWdate = iCalUtilityFunctions::_date2timestamp( $end );
1114
+ }
1115
+ $rdurWsecs = $endWdate - $startWdate; // compute component duration in seconds
1116
+ /* make a list of optional exclude dates for component occurence from exrule and exdate */
1117
+ $exdatelist = array();
1118
+ $workstart = iCalUtilityFunctions::_timestamp2date(( $startDate - $rdurWsecs ), 6);
1119
+ $workend = iCalUtilityFunctions::_timestamp2date(( $endDate + $rdurWsecs ), 6);
1120
+ while( FALSE !== ( $exrule = $component->getProperty( 'exrule' ))) // check exrule
1121
+ iCalUtilityFunctions::_recur2date( $exdatelist, $exrule, $start, $workstart, $workend );
1122
+ while( FALSE !== ( $exdate = $component->getProperty( 'exdate' ))) { // check exdate
1123
+ foreach( $exdate as $theExdate ) {
1124
+ $exWdate = iCalUtilityFunctions::_date2timestamp( $theExdate );
1125
+ $exWdate = mktime( 0, 0, 0, date( 'm', $exWdate ), date( 'd', $exWdate ), date( 'Y', $exWdate ) ); // on a day-basis !!!
1126
+ if((( $startDate - $rdurWsecs ) <= $exWdate ) && ( $endDate >= $exWdate ))
1127
+ $exdatelist[$exWdate] = TRUE;
1128
+ }
1129
+ }
1130
+ /* if 'any' components, check repeating components, removing all excluding dates */
1131
+ if( TRUE === $any ) {
1132
+ /* make a list of optional repeating dates for component occurence, rrule, rdate */
1133
+ $recurlist = array();
1134
+ while( FALSE !== ( $rrule = $component->getProperty( 'rrule' ))) // check rrule
1135
+ iCalUtilityFunctions::_recur2date( $recurlist, $rrule, $start, $workstart, $workend );
1136
+ foreach( $recurlist as $recurkey => $recurvalue ) // key=match date as timestamp
1137
+ $recurlist[$recurkey] = $rdurWsecs; // add duration in seconds
1138
+ while( FALSE !== ( $rdate = $component->getProperty( 'rdate' ))) { // check rdate
1139
+ foreach( $rdate as $theRdate ) {
1140
+ if( is_array( $theRdate ) && ( 2 == count( $theRdate )) && // all days within PERIOD
1141
+ array_key_exists( '0', $theRdate ) && array_key_exists( '1', $theRdate )) {
1142
+ $rstart = iCalUtilityFunctions::_date2timestamp( $theRdate[0] );
1143
+ if(( $rstart < ( $startDate - $rdurWsecs )) || ( $rstart > $endDate ))
1144
+ continue;
1145
+ if( isset( $theRdate[1]['year'] )) // date-date period
1146
+ $rend = iCalUtilityFunctions::_date2timestamp( $theRdate[1] );
1147
+ else { // date-duration period
1148
+ $rend = iCalUtilityFunctions::_duration2date( $theRdate[0], $theRdate[1] );
1149
+ $rend = iCalUtilityFunctions::_date2timestamp( $rend );
1150
+ }
1151
+ while( $rstart < $rend ) {
1152
+ $recurlist[$rstart] = $rdurWsecs; // set start date for recurrence instance + rdate duration in seconds
1153
+ $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day
1154
+ }
1155
+ } // PERIOD end
1156
+ else { // single date
1157
+ $theRdate = iCalUtilityFunctions::_date2timestamp( $theRdate );
1158
+ if((( $startDate - $rdurWsecs ) <= $theRdate ) && ( $endDate >= $theRdate ))
1159
+ $recurlist[$theRdate] = $rdurWsecs; // set start date for recurrence instance + event duration in seconds
1160
+ }
1161
+ }
1162
+ }
1163
+ if( 0 < count( $recurlist )) {
1164
+ ksort( $recurlist );
1165
+ $xRecurrence = 1;
1166
+ foreach( $recurlist as $recurkey => $durvalue ) {
1167
+ // echo "recurKey=".date( 'Y-m-d H:i:s', $recurkey ).' dur='.iCalUtilityFunctions::offsetSec2His( $durvalue )."<br />\n"; // test ###;
1168
+ if((( $startDate - $rdurWsecs ) > $recurkey ) || ( $endDate < $recurkey )) // not within period
1169
+ continue;
1170
+ $checkDate = mktime( 0, 0, 0, date( 'm', $recurkey ), date( 'd', $recurkey ), date( 'Y', $recurkey ) ); // on a day-basis !!!
1171
+ if( isset( $exdatelist[$checkDate] )) // check excluded dates
1172
+ continue;
1173
+ if( $startWdate >= $recurkey ) // exclude component start date
1174
+ continue;
1175
+ $component2 = $component->copy();
1176
+ $rstart = $recurkey;
1177
+ $rend = $recurkey + $durvalue;
1178
+ /* add repeating components within valid dates to output array, only start date set */
1179
+ if( $flat ) {
1180
+ $datestring = date( $startDateFormat, $recurkey );
1181
+ if( isset( $start['tz'] ))
1182
+ $datestring .= ' '.$start['tz'];
1183
+ // echo "X-CURRENT-DTSTART 0 =$datestring tcnt =".++$tcnt."<br />";$component2->setProperty( 'X-CNT', $tcnt ); // test ###
1184
+ $component2->setProperty( 'X-CURRENT-DTSTART', $datestring );
1185
+ if( $dtendExist || $dueExist || $durationExist ) {
1186
+ $datestring = date( $endDateFormat, $recurkey + $durvalue ); // fixa korrekt sluttid
1187
+ if( isset( $end['tz'] ))
1188
+ $datestring .= ' '.$end['tz'];
1189
+ $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
1190
+ $component2->setProperty( $propName, $datestring );
1191
+ } // end if( $dtendExist || $dueExist || $durationExist )
1192
+ $component2->setProperty( 'X-RECURRENCE', ++$xRecurrence );
1193
+ $result[$component2->getProperty( 'UID' )] = $component2->copy(); // copy to output
1194
+ }
1195
+ /* add repeating components within valid dates to output array, one each day */
1196
+ elseif( $split ) {
1197
+ if( $rend > $endDate )
1198
+ $rend = $endDate;
1199
+ $startYMD = date( 'Ymd', $rstart );
1200
+ $endYMD = date( 'Ymd', $rend );
1201
+ // echo "splitStart=".date( 'Y-m-d H:i:s', $rstart ).' end='.date( 'Y-m-d H:i:s', $rend )."<br />\n"; // test ###;
1202
+ while( $rstart <= $rend ) { // iterate.. .
1203
+ $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
1204
+ if( isset( $exdatelist[$checkDate] )) // exclude any recurrence START date, found in exdatelist
1205
+ break;
1206
+ // echo "checking date after startdate=".date( 'Y-m-d H:i:s', $rstart ).' mot '.date( 'Y-m-d H:i:s', $startDate )."<br />"; // test ###;
1207
+ if( $rstart >= $startDate ) { // date after dtstart
1208
+ if( date( 'Ymd', $rstart ) > $startYMD ) // date after dtstart
1209
+ $datestring = date( $startDateFormat, $checkDate );
1210
+ else
1211
+ $datestring = date( $startDateFormat, $rstart );
1212
+ if( isset( $start['tz'] ))
1213
+ $datestring .= ' '.$start['tz'];
1214
+ //echo "X-CURRENT-DTSTART 1 = $datestring xRecurrence=$xRecurrence tcnt =".++$tcnt."<br />";$component2->setProperty( 'X-CNT', $tcnt ); // test ###
1215
+ $component2->setProperty( 'X-CURRENT-DTSTART', $datestring );
1216
+ if( $dtendExist || $dueExist || $durationExist ) {
1217
+ if( date( 'Ymd', $rstart ) < $endYMD ) // not the last day
1218
+ $tend = mktime( 23, 59, 59, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ));
1219
+ else
1220
+ $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
1221
+ $datestring = date( $endDateFormat, $tend );
1222
+ if( isset( $end['tz'] ))
1223
+ $datestring .= ' '.$end['tz'];
1224
+ $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
1225
+ $component2->setProperty( $propName, $datestring );
1226
+ } // end if( $dtendExist || $dueExist || $durationExist )
1227
+ $component2->setProperty( 'X-RECURRENCE', $xRecurrence );
1228
+ $wd = getdate( $rstart );
1229
+ $result[$wd['year']][$wd['mon']][$wd['mday']][$component2->getProperty( 'UID' )] = $component2->copy(); // copy to output
1230
+ } // end if( $checkDate > $startYMD ) { // date after dtstart
1231
+ $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day
1232
+ } // end while( $rstart <= $rend )
1233
+ $xRecurrence += 1;
1234
+ } // end elseif( $split )
1235
+ elseif( $rstart >= $startDate ) { // date within period //* flat=FALSE && split=FALSE *//
1236
+ $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
1237
+ if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist
1238
+ $xRecurrence += 1;
1239
+ $datestring = date( $startDateFormat, $rstart );
1240
+ if( isset( $start['tz'] ))
1241
+ $datestring .= ' '.$start['tz'];
1242
+ //echo "X-CURRENT-DTSTART 2 = $datestring xRecurrence=$xRecurrence tcnt =".++$tcnt."<br />";$component2->setProperty( 'X-CNT', $tcnt ); // test ###
1243
+ $component2->setProperty( 'X-CURRENT-DTSTART', $datestring );
1244
+ if( $dtendExist || $dueExist || $durationExist ) {
1245
+ $rstart += $rdurWsecs;
1246
+ if( date( 'Ymd', $rstart ) < date( 'Ymd', $endWdate ))
1247
+ $tend = mktime( 23, 59, 59, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ));
1248
+ else
1249
+ $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
1250
+ $datestring = date( $endDateFormat, $tend );
1251
+ if( isset( $end['tz'] ))
1252
+ $datestring .= ' '.$end['tz'];
1253
+ $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
1254
+ $component2->setProperty( $propName, $datestring );
1255
+ } // end if( $dtendExist || $dueExist || $durationExist )
1256
+ $component2->setProperty( 'X-RECURRENCE', $xRecurrence );
1257
+ $wd = getdate( $rstart );
1258
+ $result[$wd['year']][$wd['mon']][$wd['mday']][$component2->getProperty( 'UID' )] = $component2->copy(); // copy to output
1259
+ } // end if( !isset( $exdatelist[$checkDate] ))
1260
+ } // end elseif( $rstart >= $startDate )
1261
+ } // end foreach( $recurlist as $recurkey => $durvalue )
1262
+ } // end if( 0 < count( $recurlist ))
1263
+ /* deselect components with startdate/enddate not within period */
1264
+ if(( $endWdate < $startDate ) || ( $startWdate > $endDate ))
1265
+ continue;
1266
+ } // end if( TRUE === $any )
1267
+ /* deselect components with startdate not within period */
1268
+ elseif(( $startWdate < $startDate ) || ( $startWdate > $endDate ))
1269
+ continue;
1270
+ /* add the selected component (WITHIN valid dates) to output array */
1271
+ if( $flat )
1272
+ $result[$component->getProperty( 'UID' )] = $component->copy(); // copy to output;
1273
+ elseif( $split ) { // split the original component
1274
+ if( $endWdate > $endDate )
1275
+ $endWdate = $endDate; // use period end date
1276
+ $rstart = $startWdate;
1277
+ if( $rstart < $startDate )
1278
+ $rstart = $startDate; // use period start date
1279
+ $checkDate = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
1280
+ if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist
1281
+ foreach( array( 'X-CURRENT-DTSTART','X-CURRENT-DTEND','X-CURRENT-DUE','X-RECURRENCE' ) as $propName )
1282
+ $component->deleteProperty( $propName ); // remove any x-props, if set
1283
+ while( $rstart <= $endWdate ) { // iterate
1284
+ if( $rstart > $startWdate ) { // if NOT startdate, set X-properties
1285
+ $datestring = date( $startDateFormat, mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart )));
1286
+ if( isset( $start['tz'] ))
1287
+ $datestring .= ' '.$start['tz'];
1288
+ // echo "X-CURRENT-DTSTART 3 = $datestring xRecurrence=$xRecurrence tcnt =".++$tcnt."<br />";$component->setProperty( 'X-CNT', $tcnt ); // test ###
1289
+ $component->setProperty( 'X-CURRENT-DTSTART', $datestring );
1290
+ if( $dtendExist || $dueExist || $durationExist ) {
1291
+ if( date( 'Ymd', $rstart ) < date( 'Ymd', $endWdate ))
1292
+ $tend = mktime( 0, 0, 0, date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ));
1293
+ else
1294
+ $tend = mktime( date( 'H', $endWdate ), date( 'i', $endWdate ), date( 's', $endWdate ), date( 'm', $rstart ), date( 'd', $rstart ), date( 'Y', $rstart ) ); // on a day-basis !!!
1295
+ $datestring = date( $endDateFormat, $tend );
1296
+ if( isset( $end['tz'] ))
1297
+ $datestring .= ' '.$end['tz'];
1298
+ $propName = ( !$dueExist ) ? 'X-CURRENT-DTEND' : 'X-CURRENT-DUE';
1299
+ $component->setProperty( $propName, $datestring );
1300
+ } // end if( $dtendExist || $dueExist || $durationExist )
1301
+ } // end if( $rstart > $startWdate )
1302
+ $wd = getdate( $rstart );
1303
+ $result[$wd['year']][$wd['mon']][$wd['mday']][$component->getProperty( 'UID' )] = $component->copy(); // copy to output
1304
+ $rstart = mktime( date( 'H', $rstart ), date( 'i', $rstart ), date( 's', $rstart ), date( 'm', $rstart ), date( 'd', $rstart ) + 1, date( 'Y', $rstart ) ); // step one day
1305
+ } // end while( $rstart <= $endWdate )
1306
+ } // end if( !isset( $exdatelist[$checkDate] ))
1307
+ } // end if( $split ) - else use component date
1308
+ elseif( $startWdate >= $startDate ) { // within period
1309
+ $checkDate = mktime( 0, 0, 0, date( 'm', $startWdate ), date( 'd', $startWdate ), date( 'Y', $startWdate ) ); // on a day-basis !!!
1310
+ if( !isset( $exdatelist[$checkDate] )) { // exclude any recurrence START date, found in exdatelist
1311
+ foreach( array( 'X-CURRENT-DTSTART','X-CURRENT-DTEND','X-CURRENT-DUE','X-RECURRENCE' ) as $propName )
1312
+ $component->deleteProperty( $propName ); // remove any x-props, if set
1313
+ $wd = getdate( $startWdate );
1314
+ $result[$wd['year']][$wd['mon']][$wd['mday']][$component->getProperty( 'UID' )] = $component->copy(); // copy to output
1315
+ }
1316
+ }
1317
+ } // end foreach ( $this->components as $cix => $component )
1318
+ if( 0 >= count( $result )) return FALSE;
1319
+ elseif( !$flat ) {
1320
+ foreach( $result as $y => $yeararr ) {
1321
+ foreach( $yeararr as $m => $montharr ) {
1322
+ foreach( $montharr as $d => $dayarr )
1323
+ $result[$y][$m][$d] = array_values( $dayarr ); // skip tricky UID-index
1324
+ ksort( $result[$y][$m] );
1325
+ }
1326
+ ksort( $result[$y] );
1327
+ }
1328
+ ksort( $result );
1329
+ } // end elseif( !$flat )
1330
+ return $result;
1331
+ }
1332
+ /**
1333
+ * select components from calendar on based on Categories, Location, Resources and/or Summary
1334
+ *
1335
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1336
+ * @since 2.8.8 - 2011-05-03
1337
+ * @param array $selectOptions, (string) key => (mixed) value, (key=propertyName)
1338
+ * @return array
1339
+ */
1340
+ function selectComponents2( $selectOptions ) {
1341
+ $output = array();
1342
+ $allowedProperties = array( 'ATTENDEE', 'CATEGORIES', 'LOCATION', 'ORGANIZER', 'RESOURCES', 'PRIORITY', 'STATUS', 'SUMMARY', 'UID' );
1343
+ foreach( $this->components as $cix => $component3 ) {
1344
+ if( !in_array( $component3->objName, array('vevent', 'vtodo', 'vjournal', 'vfreebusy' )))
1345
+ continue;
1346
+ $uid = $component3->getProperty( 'UID' );
1347
+ foreach( $selectOptions as $propName => $pvalue ) {
1348
+ $propName = strtoupper( $propName );
1349
+ if( !in_array( $propName, $allowedProperties ))
1350
+ continue;
1351
+ if( !is_array( $pvalue ))
1352
+ $pvalue = array( $pvalue );
1353
+ if(( 'UID' == $propName ) && in_array( $uid, $pvalue )) {
1354
+ $output[] = $component3->copy();
1355
+ continue;
1356
+ }
1357
+ elseif(( 'ATTENDEE' == $propName ) || ( 'CATEGORIES' == $propName ) || ( 'RESOURCES' == $propName )) {
1358
+ $propValues = array();
1359
+ $component3->_getProperties( $propName, $propValues );
1360
+ $propValues = array_keys( $propValues );
1361
+ foreach( $pvalue as $theValue ) {
1362
+ if( in_array( $theValue, $propValues ) && !isset( $output[$uid] )) {
1363
+ $output[$uid] = $component3->copy();
1364
+ break;
1365
+ }
1366
+ }
1367
+ continue;
1368
+ } // end elseif(( 'ATTENDEE' == $propName ) || ( 'CATEGORIES' == $propName ) || ( 'RESOURCES' == $propName ))
1369
+ elseif( FALSE === ( $d = $component3->getProperty( $propName ))) // single ocurrence
1370
+ continue;
1371
+ if( is_array( $d )) {
1372
+ foreach( $d as $part ) {
1373
+ if( in_array( $part, $pvalue ) && !isset( $output[$uid] ))
1374
+ $output[$uid] = $component3->copy();
1375
+ }
1376
+ }
1377
+ elseif(( 'SUMMARY' == $propName ) && !isset( $output[$uid] )) {
1378
+ foreach( $pvalue as $pval ) {
1379
+ if( FALSE !== stripos( $d, $pval )) {
1380
+ $output[$uid] = $component3->copy();
1381
+ break;
1382
+ }
1383
+ }
1384
+ }
1385
+ elseif( in_array( $d, $pvalue ) && !isset( $output[$uid] ))
1386
+ $output[$uid] = $component3->copy();
1387
+ } // end foreach( $selectOptions as $propName => $pvalue ) {
1388
+ } // end foreach( $this->components as $cix => $component3 ) {
1389
+ if( !empty( $output )) {
1390
+ ksort( $output );
1391
+ $output = array_values( $output );
1392
+ }
1393
+ return $output;
1394
+ }
1395
+ /**
1396
+ * add calendar component to container
1397
+ *
1398
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1399
+ * @since 2.8.8 - 2011-03-15
1400
+ * @param object $component calendar component
1401
+ * @param mixed $arg1 optional, ordno/component type/ component uid
1402
+ * @param mixed $arg2 optional, ordno if arg1 = component type
1403
+ * @return void
1404
+ */
1405
+ function setComponent( $component, $arg1=FALSE, $arg2=FALSE ) {
1406
+ $component->setConfig( $this->getConfig(), FALSE, TRUE );
1407
+ if( !in_array( $component->objName, array( 'valarm', 'vtimezone' ))) {
1408
+ /* make sure dtstamp and uid is set */
1409
+ $dummy1 = $component->getProperty( 'dtstamp' );
1410
+ $dummy2 = $component->getProperty( 'uid' );
1411
+ }
1412
+ if( !$arg1 ) { // plain insert, last in chain
1413
+ $this->components[] = $component->copy();
1414
+ return TRUE;
1415
+ }
1416
+ $argType = $index = null;
1417
+ if ( ctype_digit( (string) $arg1 )) { // index insert/replace
1418
+ $argType = 'INDEX';
1419
+ $index = (int) $arg1 - 1;
1420
+ }
1421
+ elseif( in_array( strtolower( $arg1 ), array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm', 'vtimezone' ))) {
1422
+ $argType = strtolower( $arg1 );
1423
+ $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0;
1424
+ }
1425
+ // else if arg1 is set, arg1 must be an UID
1426
+ $cix1sC = 0;
1427
+ foreach ( $this->components as $cix => $component2) {
1428
+ if( empty( $component2 )) continue;
1429
+ if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace
1430
+ $this->components[$cix] = $component->copy();
1431
+ return TRUE;
1432
+ }
1433
+ elseif( $argType == $component2->objName ) { // component Type index insert/replace
1434
+ if( $index == $cix1sC ) {
1435
+ $this->components[$cix] = $component->copy();
1436
+ return TRUE;
1437
+ }
1438
+ $cix1sC++;
1439
+ }
1440
+ elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace
1441
+ $this->components[$cix] = $component->copy();
1442
+ return TRUE;
1443
+ }
1444
+ }
1445
+ /* arg1=index and not found.. . insert at index .. .*/
1446
+ if( 'INDEX' == $argType ) {
1447
+ $this->components[$index] = $component->copy();
1448
+ ksort( $this->components, SORT_NUMERIC );
1449
+ }
1450
+ else /* not found.. . insert last in chain anyway .. .*/
1451
+ $this->components[] = $component->copy();
1452
+ return TRUE;
1453
+ }
1454
+ /**
1455
+ * sort iCal compoments
1456
+ *
1457
+ * ascending sort on properties (if exist) x-current-dtstart, dtstart,
1458
+ * x-current-dtend, dtend, x-current-due, due, duration, created, dtstamp, uid
1459
+ * if no arguments, otherwise sorting on argument CATEGORIES, LOCATION, SUMMARY or RESOURCES
1460
+ *
1461
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1462
+ * @since 2.8.4 - 2011-06-02
1463
+ * @param string $sortArg, optional
1464
+ * @return void
1465
+ *
1466
+ */
1467
+ function sort( $sortArg=FALSE ) {
1468
+ if( is_array( $this->components )) {
1469
+ if( $sortArg ) {
1470
+ $sortArg = strtoupper( $sortArg );
1471
+ if( !in_array( $sortArg, array( 'ATTENDEE', 'CATEGORIES', 'DTSTAMP', 'LOCATION', 'ORGANIZER', 'RESOURCES', 'PRIORITY', 'STATUS', 'SUMMARY' )))
1472
+ $sortArg = FALSE;
1473
+ }
1474
+ /* set sort parameters for each component */
1475
+ foreach( $this->components as $cix => & $c ) {
1476
+ $c->srtk = array( '0', '0', '0', '0' );
1477
+ if( 'vtimezone' == $c->objName ) {
1478
+ if( FALSE === ( $c->srtk[0] = $c->getProperty( 'tzid' )))
1479
+ $c->srtk[0] = 0;
1480
+ continue;
1481
+ }
1482
+ elseif( $sortArg ) {
1483
+ if(( 'ATTENDEE' == $sortArg ) || ( 'CATEGORIES' == $sortArg ) || ( 'RESOURCES' == $sortArg )) {
1484
+ $propValues = array();
1485
+ $c->_getProperties( $sortArg, $propValues );
1486
+ $c->srtk[0] = reset( array_keys( $propValues ));
1487
+ }
1488
+ elseif( FALSE !== ( $d = $c->getProperty( $sortArg )))
1489
+ $c->srtk[0] = $d;
1490
+ continue;
1491
+ }
1492
+ if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTSTART' )))
1493
+ $c->srtk[0] = iCalUtilityFunctions::_date_time_string( $d[1] );
1494
+ elseif( FALSE === ( $c->srtk[0] = $c->getProperty( 'dtstart' )))
1495
+ $c->srtk[1] = 0; // sortkey 0 : dtstart
1496
+ if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DTEND' )))
1497
+ $c->srtk[1] = iCalUtilityFunctions::_date_time_string( $d[1] ); // sortkey 1 : dtend/due(/dtstart+duration)
1498
+ elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'dtend' ))) {
1499
+ if( FALSE !== ( $d = $c->getProperty( 'X-CURRENT-DUE' )))
1500
+ $c->srtk[1] = iCalUtilityFunctions::_date_time_string( $d[1] );
1501
+ elseif( FALSE === ( $c->srtk[1] = $c->getProperty( 'due' )))
1502
+ if( FALSE === ( $c->srtk[1] = $c->getProperty( 'duration', FALSE, FALSE, TRUE )))
1503
+ $c->srtk[1] = 0;
1504
+ }
1505
+ if( FALSE === ( $c->srtk[2] = $c->getProperty( 'created' ))) // sortkey 2 : created/dtstamp
1506
+ if( FALSE === ( $c->srtk[2] = $c->getProperty( 'dtstamp' )))
1507
+ $c->srtk[2] = 0;
1508
+ if( FALSE === ( $c->srtk[3] = $c->getProperty( 'uid' ))) // sortkey 3 : uid
1509
+ $c->srtk[3] = 0;
1510
+ } // end foreach( $this->components as & $c
1511
+ /* sort */
1512
+ usort( $this->components, array( $this, '_cmpfcn' ));
1513
+ }
1514
+ }
1515
+ function _cmpfcn( $a, $b ) {
1516
+ if( empty( $a )) return -1;
1517
+ if( empty( $b )) return 1;
1518
+ if( 'vtimezone' == $a->objName ) {
1519
+ if( 'vtimezone' != $b->objName ) return -1;
1520
+ elseif( $a->srtk[0] <= $b->srtk[0] ) return -1;
1521
+ else return 1;
1522
+ }
1523
+ elseif( 'vtimezone' == $b->objName ) return 1;
1524
+ $sortkeys = array( 'year', 'month', 'day', 'hour', 'min', 'sec' );
1525
+ for( $k = 0; $k < 4 ; $k++ ) {
1526
+ if( empty( $a->srtk[$k] )) return -1;
1527
+ elseif( empty( $b->srtk[$k] )) return 1;
1528
+ if( is_array( $a->srtk[$k] )) {
1529
+ if( is_array( $b->srtk[$k] )) {
1530
+ foreach( $sortkeys as $key ) {
1531
+ if ( empty( $a->srtk[$k][$key] )) return -1;
1532
+ elseif( empty( $b->srtk[$k][$key] )) return 1;
1533
+ if ( $a->srtk[$k][$key] == $b->srtk[$k][$key])
1534
+ continue;
1535
+ if (( (int) $a->srtk[$k][$key] ) < ((int) $b->srtk[$k][$key] ))
1536
+ return -1;
1537
+ elseif(( (int) $a->srtk[$k][$key] ) > ((int) $b->srtk[$k][$key] ))
1538
+ return 1;
1539
+ }
1540
+ }
1541
+ else return -1;
1542
+ }
1543
+ elseif( is_array( $b->srtk[$k] )) return 1;
1544
+ elseif( $a->srtk[$k] < $b->srtk[$k] ) return -1;
1545
+ elseif( $a->srtk[$k] > $b->srtk[$k] ) return 1;
1546
+ }
1547
+ return 0;
1548
+ }
1549
+ /**
1550
+ * parse iCal text/file into vcalendar, components, properties and parameters
1551
+ *
1552
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1553
+ * @since 2.8.2 - 2011-05-21
1554
+ * @param mixed $unparsedtext, optional, strict rfc2445 formatted, single property string or array of property strings
1555
+ * @return bool FALSE if error occurs during parsing
1556
+ *
1557
+ */
1558
+ function parse( $unparsedtext=FALSE ) {
1559
+ $nl = $this->getConfig( 'nl' );
1560
+ if(( FALSE === $unparsedtext ) || empty( $unparsedtext )) {
1561
+ /* directory+filename is set previously via setConfig directory+filename or url */
1562
+ if( FALSE === ( $filename = $this->getConfig( 'url' )))
1563
+ $filename = $this->getConfig( 'dirfile' );
1564
+ /* READ FILE */
1565
+ if( FALSE === ( $rows = file_get_contents( $filename )))
1566
+ return FALSE; /* err 1 */
1567
+ }
1568
+ elseif( is_array( $unparsedtext ))
1569
+ $rows = implode( '\n'.$nl, $unparsedtext );
1570
+ else
1571
+ $rows = & $unparsedtext;
1572
+ /* identify BEGIN:VCALENDAR, MUST be first row */
1573
+ if( 'BEGIN:VCALENDAR' != strtoupper( substr( $rows, 0, 15 )))
1574
+ return FALSE; /* err 8 */
1575
+ /* fix line folding */
1576
+ $eolchars = array( "\r\n", "\n\r", "\n", "\r" ); // check all line endings
1577
+ $EOLmark = FALSE;
1578
+ foreach( $eolchars as $eolchar ) {
1579
+ if( !$EOLmark && ( FALSE !== strpos( $rows, $eolchar ))) {
1580
+ $rows = str_replace( $eolchar." ", '', $rows );
1581
+ $rows = str_replace( $eolchar."\t", '', $rows );
1582
+ if( $eolchar != $nl )
1583
+ $rows = str_replace( $eolchar, $nl, $rows );
1584
+ $EOLmark = TRUE;
1585
+ }
1586
+ }
1587
+ $tmp = explode( $nl, $rows );
1588
+ $rows = array();
1589
+ foreach( $tmp as $tmpr )
1590
+ if( !empty( $tmpr ))
1591
+ $rows[] = $tmpr;
1592
+ /* skip trailing empty lines */
1593
+ $lix = count( $rows ) - 1;
1594
+ while( empty( $rows[$lix] ) && ( 0 < $lix ))
1595
+ $lix -= 1;
1596
+ /* identify ending END:VCALENDAR row, MUST be last row */
1597
+ if( 'END:VCALENDAR' != strtoupper( substr( $rows[$lix], 0, 13 )))
1598
+ return FALSE; /* err 9 */
1599
+ if( 3 > count( $rows ))
1600
+ return FALSE; /* err 10 */
1601
+ $comp = & $this;
1602
+ $calsync = 0;
1603
+ /* identify components and update unparsed data within component */
1604
+ $config = $this->getConfig();
1605
+ foreach( $rows as $line ) {
1606
+ if( '' == trim( $line ))
1607
+ continue;
1608
+ if( 'BEGIN:VCALENDAR' == strtoupper( substr( $line, 0, 15 ))) {
1609
+ $calsync++;
1610
+ continue;
1611
+ }
1612
+ elseif( 'END:VCALENDAR' == strtoupper( substr( $line, 0, 13 ))) {
1613
+ $calsync--;
1614
+ break;
1615
+ }
1616
+ elseif( 1 != $calsync )
1617
+ return FALSE; /* err 20 */
1618
+ elseif( in_array( strtoupper( substr( $line, 0, 6 )), array( 'END:VE', 'END:VF', 'END:VJ', 'END:VT' ))) {
1619
+ $this->components[] = $comp->copy();
1620
+ continue;
1621
+ }
1622
+
1623
+ if( 'BEGIN:VEVENT' == strtoupper( substr( $line, 0, 12 )))
1624
+ $comp = new vevent( $config );
1625
+ elseif( 'BEGIN:VFREEBUSY' == strtoupper( substr( $line, 0, 15 )))
1626
+ $comp = new vfreebusy( $config );
1627
+ elseif( 'BEGIN:VJOURNAL' == strtoupper( substr( $line, 0, 14 )))
1628
+ $comp = new vjournal( $config );
1629
+ elseif( 'BEGIN:VTODO' == strtoupper( substr( $line, 0, 11 )))
1630
+ $comp = new vtodo( $config );
1631
+ elseif( 'BEGIN:VTIMEZONE' == strtoupper( substr( $line, 0, 15 )))
1632
+ $comp = new vtimezone( $config );
1633
+ else /* update component with unparsed data */
1634
+ $comp->unparsed[] = $line;
1635
+ } // end - foreach( rows.. .
1636
+ unset( $config );
1637
+ /* parse data for calendar (this) object */
1638
+ if( isset( $this->unparsed ) && is_array( $this->unparsed ) && ( 0 < count( $this->unparsed ))) {
1639
+ /* concatenate property values spread over several lines */
1640
+ $lastix = -1;
1641
+ $propnames = array( 'calscale','method','prodid','version','x-' );
1642
+ $proprows = array();
1643
+ foreach( $this->unparsed as $line ) {
1644
+ if( '' == trim( $line ))
1645
+ continue;
1646
+ $newProp = FALSE;
1647
+ foreach ( $propnames as $propname ) {
1648
+ if( $propname == strtolower( substr( $line, 0, strlen( $propname )))) {
1649
+ $newProp = TRUE;
1650
+ break;
1651
+ }
1652
+ }
1653
+ if( $newProp ) {
1654
+ $newProp = FALSE;
1655
+ $lastix++;
1656
+ $proprows[$lastix] = $line;
1657
+ }
1658
+ else
1659
+ $proprows[$lastix] .= '!"#¤%&/()=?'.$line;
1660
+ }
1661
+ foreach( $proprows as $line ) {
1662
+ $line = str_replace( '!"#¤%&/()=? ', '', $line );
1663
+ $line = str_replace( '!"#¤%&/()=?', '', $line );
1664
+ if( '\n' == substr( $line, -2 ))
1665
+ $line = substr( $line, 0, strlen( $line ) - 2 );
1666
+ /* get property name */
1667
+ $cix = $propname = null;
1668
+ for( $cix=0, $clen = strlen( $line ); $cix < $clen; $cix++ ) {
1669
+ if( in_array( $line[$cix], array( ':', ';' )))
1670
+ break;
1671
+ else
1672
+ $propname .= $line[$cix];
1673
+ }
1674
+ /* ignore version/prodid properties */
1675
+ if( in_array( strtoupper( $propname ), array( 'VERSION', 'PRODID' )))
1676
+ continue;
1677
+ $line = substr( $line, $cix);
1678
+ /* separate attributes from value */
1679
+ $attr = array();
1680
+ $attrix = -1;
1681
+ $strlen = strlen( $line );
1682
+ for( $cix=0; $cix < $strlen; $cix++ ) {
1683
+ if(( ':' == $line[$cix] ) &&
1684
+ ( '://' != substr( $line, $cix, 3 )) &&
1685
+ ( !in_array( strtolower( substr( $line, $cix - 3, 4 )), array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' ))) &&
1686
+ ( !in_array( strtolower( substr( $line, $cix - 4, 5 )), array( 'crid:', 'news:', 'pres:' ))) &&
1687
+ ( 'mailto:' != strtolower( substr( $line, $cix - 6, 7 )))) {
1688
+ $attrEnd = TRUE;
1689
+ if(( $cix < ( $strlen - 4 )) &&
1690
+ ctype_digit( substr( $line, $cix+1, 4 ))) { // an URI with a (4pos) portnr??
1691
+ for( $c2ix = $cix; 3 < $c2ix; $c2ix-- ) {
1692
+ if( '://' == substr( $line, $c2ix - 2, 3 )) {
1693
+ $attrEnd = FALSE;
1694
+ break; // an URI with a portnr!!
1695
+ }
1696
+ }
1697
+ }
1698
+ if( $attrEnd) {
1699
+ $line = substr( $line, $cix + 1 );
1700
+ break;
1701
+ }
1702
+ }
1703
+ if( ';' == $line[$cix] )
1704
+ $attr[++$attrix] = null;
1705
+ else
1706
+ $attr[$attrix] .= $line[$cix];
1707
+ }
1708
+
1709
+ /* make attributes in array format */
1710
+ $propattr = array();
1711
+ foreach( $attr as $attribute ) {
1712
+ $attrsplit = explode( '=', $attribute, 2 );
1713
+ if( 1 < count( $attrsplit ))
1714
+ $propattr[$attrsplit[0]] = $attrsplit[1];
1715
+ else
1716
+ $propattr[] = $attribute;
1717
+ }
1718
+ /* update Property */
1719
+ if( FALSE !== strpos( $line, ',' )) {
1720
+ $content = explode( ',', $line );
1721
+ $clen = count( $content );
1722
+ for( $cix = 0; $cix < $clen; $cix++ ) {
1723
+ if( "\\" == substr( $content[$cix], -1 )) {
1724
+ $content[$cix] .= ','.$content[$cix + 1];
1725
+ unset( $content[$cix + 1] );
1726
+ $cix++;
1727
+ }
1728
+ }
1729
+ if( 1 < count( $content )) {
1730
+ foreach( $content as $cix => $contentPart )
1731
+ $content[$cix] = calendarComponent::_strunrep( $contentPart );
1732
+ $this->setProperty( $propname, $content, $propattr );
1733
+ continue;
1734
+ }
1735
+ else
1736
+ $line = reset( $content );
1737
+ $line = calendarComponent::_strunrep( $line );
1738
+ }
1739
+ $this->setProperty( $propname, trim( $line ), $propattr );
1740
+ } // end - foreach( $this->unparsed.. .
1741
+ } // end - if( is_array( $this->unparsed.. .
1742
+ unset( $unparsedtext, $rows, $this->unparsed, $proprows );
1743
+ /* parse Components */
1744
+ if( is_array( $this->components ) && ( 0 < count( $this->components ))) {
1745
+ $ckeys = array_keys( $this->components );
1746
+ foreach( $ckeys as $ckey ) {
1747
+ if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) {
1748
+ $this->components[$ckey]->parse();
1749
+ }
1750
+ }
1751
+ }
1752
+ else
1753
+ return FALSE; /* err 91 or something.. . */
1754
+ return TRUE;
1755
+ }
1756
+ /*********************************************************************************/
1757
+ /**
1758
+ * creates formatted output for calendar object instance
1759
+ *
1760
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1761
+ * @since 2.8.1 - 2011-03-12
1762
+ * @return string
1763
+ */
1764
+ function createCalendar() {
1765
+ $calendarInit1 = $calendarInit2 = $calendarxCaldecl = $calendarStart = $calendar = null;
1766
+ switch( $this->format ) {
1767
+ case 'xcal':
1768
+ $calendarInit1 = '<?xml version="1.0" encoding="UTF-8"?>'.$this->nl.
1769
+ '<!DOCTYPE iCalendar PUBLIC "-//IETF//DTD XCAL/iCalendar XML//EN"'.$this->nl.
1770
+ '"http://www.ietf.org/internet-drafts/draft-ietf-calsch-many-xcal-01.txt"';
1771
+ $calendarInit2 = '>'.$this->nl;
1772
+ $calendarStart = '<vcalendar';
1773
+ break;
1774
+ default:
1775
+ $calendarStart = 'BEGIN:VCALENDAR'.$this->nl;
1776
+ break;
1777
+ }
1778
+ $calendarStart .= $this->createVersion();
1779
+ $calendarStart .= $this->createProdid();
1780
+ $calendarStart .= $this->createCalscale();
1781
+ $calendarStart .= $this->createMethod();
1782
+ switch( $this->format ) {
1783
+ case 'xcal':
1784
+ $nlstrlen = strlen( $this->nl );
1785
+ if( $this->nl == substr( $calendarStart, ( 0 - $nlstrlen )))
1786
+ $calendarStart = substr( $calendarStart, 0, ( strlen( $calendarStart ) - $nlstrlen ));
1787
+ $calendarStart .= '>'.$this->nl;
1788
+ break;
1789
+ default:
1790
+ break;
1791
+ }
1792
+ $calendar .= $this->createXprop();
1793
+ foreach( $this->components as $component ) {
1794
+ if( empty( $component )) continue;
1795
+ $component->setConfig( $this->getConfig(), FALSE, TRUE );
1796
+ $calendar .= $component->createComponent( $this->xcaldecl );
1797
+ }
1798
+ if(( 0 < count( $this->xcaldecl )) && ( 'xcal' == $this->format )) { // xCal only
1799
+ $calendarInit1 .= $this->nl.'['.$this->nl;
1800
+ $old_xcaldecl = array();
1801
+ foreach( $this->xcaldecl as $declix => $declPart ) {
1802
+ if(( 0 < count( $old_xcaldecl)) &&
1803
+ ( in_array( $declPart['uri'], $old_xcaldecl['uri'] )) &&
1804
+ ( in_array( $declPart['external'], $old_xcaldecl['external'] )))
1805
+ continue; // no duplicate uri and ext. references
1806
+ $calendarxCaldecl .= '<!';
1807
+ foreach( $declPart as $declKey => $declValue ) {
1808
+ switch( $declKey ) { // index
1809
+ case 'xmldecl': // no 1
1810
+ $calendarxCaldecl .= $declValue.' ';
1811
+ break;
1812
+ case 'uri': // no 2
1813
+ $calendarxCaldecl .= $declValue.' ';
1814
+ $old_xcaldecl['uri'][] = $declValue;
1815
+ break;
1816
+ case 'ref': // no 3
1817
+ $calendarxCaldecl .= $declValue.' ';
1818
+ break;
1819
+ case 'external': // no 4
1820
+ $calendarxCaldecl .= '"'.$declValue.'" ';
1821
+ $old_xcaldecl['external'][] = $declValue;
1822
+ break;
1823
+ case 'type': // no 5
1824
+ $calendarxCaldecl .= $declValue.' ';
1825
+ break;
1826
+ case 'type2': // no 6
1827
+ $calendarxCaldecl .= $declValue;
1828
+ break;
1829
+ }
1830
+ }
1831
+ $calendarxCaldecl .= '>'.$this->nl;
1832
+ }
1833
+ $calendarInit2 = ']'.$calendarInit2;
1834
+ }
1835
+ switch( $this->format ) {
1836
+ case 'xcal':
1837
+ $calendar .= '</vcalendar>'.$this->nl;
1838
+ break;
1839
+ default:
1840
+ $calendar .= 'END:VCALENDAR'.$this->nl;
1841
+ break;
1842
+ }
1843
+ return $calendarInit1.$calendarxCaldecl.$calendarInit2.$calendarStart.$calendar;
1844
+ }
1845
+ /**
1846
+ * a HTTP redirect header is sent with created, updated and/or parsed calendar
1847
+ *
1848
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1849
+ * @since 2.9.12 - 2011-07-13
1850
+ * @param bool $utf8Encode
1851
+ * @param bool $gzip
1852
+ * @return redirect
1853
+ */
1854
+ function returnCalendar( $utf8Encode=FALSE, $gzip=FALSE ) {
1855
+ $filename = $this->getConfig( 'filename' );
1856
+ $output = $this->createCalendar();
1857
+ if( $utf8Encode )
1858
+ $output = utf8_encode( $output );
1859
+ if( $gzip ) {
1860
+ $output = gzencode( $output, 9 );
1861
+ header( 'Content-Encoding: gzip');
1862
+ header( 'Vary: *');
1863
+ }
1864
+ $filesize = strlen( $output );
1865
+ if( 'xcal' == $this->format )
1866
+ header( 'Content-Type: application/calendar+xml; charset=utf-8' );
1867
+ else
1868
+ header( 'Content-Type: text/calendar; charset=utf-8' );
1869
+ header( 'Content-Length: '.$filesize );
1870
+ header( 'Content-Disposition: attachment; filename="'.$filename.'"' );
1871
+ header( 'Cache-Control: max-age=10' );
1872
+ echo $output;
1873
+ die();
1874
+ }
1875
+ /**
1876
+ * save content in a file
1877
+ *
1878
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1879
+ * @since 2.2.12 - 2007-12-30
1880
+ * @param string $directory optional
1881
+ * @param string $filename optional
1882
+ * @param string $delimiter optional
1883
+ * @return bool
1884
+ */
1885
+ function saveCalendar( $directory=FALSE, $filename=FALSE, $delimiter=FALSE ) {
1886
+ if( $directory )
1887
+ $this->setConfig( 'directory', $directory );
1888
+ if( $filename )
1889
+ $this->setConfig( 'filename', $filename );
1890
+ if( $delimiter && ($delimiter != DIRECTORY_SEPARATOR ))
1891
+ $this->setConfig( 'delimiter', $delimiter );
1892
+ if( FALSE === ( $dirfile = $this->getConfig( 'url' )))
1893
+ $dirfile = $this->getConfig( 'dirfile' );
1894
+ $iCalFile = @fopen( $dirfile, 'w' );
1895
+ if( $iCalFile ) {
1896
+ if( FALSE === fwrite( $iCalFile, $this->createCalendar() ))
1897
+ return FALSE;
1898
+ fclose( $iCalFile );
1899
+ return TRUE;
1900
+ }
1901
+ else
1902
+ return FALSE;
1903
+ }
1904
+ /**
1905
+ * if recent version of calendar file exists (default one hour), an HTTP redirect header is sent
1906
+ * else FALSE is returned
1907
+ *
1908
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1909
+ * @since 2.2.12 - 2007-10-28
1910
+ * @param string $directory optional alt. int timeout
1911
+ * @param string $filename optional
1912
+ * @param string $delimiter optional
1913
+ * @param int timeout optional, default 3600 sec
1914
+ * @return redirect/FALSE
1915
+ */
1916
+ function useCachedCalendar( $directory=FALSE, $filename=FALSE, $delimiter=FALSE, $timeout=3600) {
1917
+ if ( $directory && ctype_digit( (string) $directory ) && !$filename ) {
1918
+ $timeout = (int) $directory;
1919
+ $directory = FALSE;
1920
+ }
1921
+ if( $directory )
1922
+ $this->setConfig( 'directory', $directory );
1923
+ if( $filename )
1924
+ $this->setConfig( 'filename', $filename );
1925
+ if( $delimiter && ( $delimiter != DIRECTORY_SEPARATOR ))
1926
+ $this->setConfig( 'delimiter', $delimiter );
1927
+ $filesize = $this->getConfig( 'filesize' );
1928
+ if( 0 >= $filesize )
1929
+ return FALSE;
1930
+ $dirfile = $this->getConfig( 'dirfile' );
1931
+ if( time() - filemtime( $dirfile ) < $timeout) {
1932
+ clearstatcache();
1933
+ $dirfile = $this->getConfig( 'dirfile' );
1934
+ $filename = $this->getConfig( 'filename' );
1935
+ // if( headers_sent( $filename, $linenum ))
1936
+ // die( "Headers already sent in $filename on line $linenum\n" );
1937
+ if( 'xcal' == $this->format )
1938
+ header( 'Content-Type: application/calendar+xml; charset=utf-8' );
1939
+ else
1940
+ header( 'Content-Type: text/calendar; charset=utf-8' );
1941
+ header( 'Content-Length: '.$filesize );
1942
+ header( 'Content-Disposition: attachment; filename="'.$filename.'"' );
1943
+ header( 'Cache-Control: max-age=10' );
1944
+ $fp = @fopen( $dirfile, 'r' );
1945
+ if( $fp ) {
1946
+ fpassthru( $fp );
1947
+ fclose( $fp );
1948
+ }
1949
+ die();
1950
+ }
1951
+ else
1952
+ return FALSE;
1953
+ }
1954
+ }
1955
+ /*********************************************************************************/
1956
+ /*********************************************************************************/
1957
+ /**
1958
+ * abstract class for calendar components
1959
+ *
1960
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1961
+ * @since 2.9.6 - 2011-05-14
1962
+ */
1963
+ class calendarComponent {
1964
+ // component property variables
1965
+ var $uid;
1966
+ var $dtstamp;
1967
+
1968
+ // component config variables
1969
+ var $allowEmpty;
1970
+ var $language;
1971
+ var $nl;
1972
+ var $unique_id;
1973
+ var $format;
1974
+ var $objName; // created automatically at instance creation
1975
+ var $dtzid; // default (local) timezone
1976
+ // component internal variables
1977
+ var $componentStart1;
1978
+ var $componentStart2;
1979
+ var $componentEnd1;
1980
+ var $componentEnd2;
1981
+ var $elementStart1;
1982
+ var $elementStart2;
1983
+ var $elementEnd1;
1984
+ var $elementEnd2;
1985
+ var $intAttrDelimiter;
1986
+ var $attributeDelimiter;
1987
+ var $valueInit;
1988
+ // component xCal declaration container
1989
+ var $xcaldecl;
1990
+ /**
1991
+ * constructor for calendar component object
1992
+ *
1993
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
1994
+ * @since 2.9.6 - 2011-05-17
1995
+ */
1996
+ function calendarComponent() {
1997
+ $this->objName = ( isset( $this->timezonetype )) ?
1998
+ strtolower( $this->timezonetype ) : get_class ( $this );
1999
+ $this->uid = array();
2000
+ $this->dtstamp = array();
2001
+
2002
+ $this->language = null;
2003
+ $this->nl = null;
2004
+ $this->unique_id = null;
2005
+ $this->format = null;
2006
+ $this->dtzid = null;
2007
+ $this->allowEmpty = TRUE;
2008
+ $this->xcaldecl = array();
2009
+
2010
+ $this->_createFormat();
2011
+ $this->_makeDtstamp();
2012
+ }
2013
+ /*********************************************************************************/
2014
+ /**
2015
+ * Property Name: ACTION
2016
+ */
2017
+ /**
2018
+ * creates formatted output for calendar component property action
2019
+ *
2020
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2021
+ * @since 2.4.8 - 2008-10-22
2022
+ * @return string
2023
+ */
2024
+ function createAction() {
2025
+ if( empty( $this->action )) return FALSE;
2026
+ if( empty( $this->action['value'] ))
2027
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ACTION' ) : FALSE;
2028
+ $attributes = $this->_createParams( $this->action['params'] );
2029
+ return $this->_createElement( 'ACTION', $attributes, $this->action['value'] );
2030
+ }
2031
+ /**
2032
+ * set calendar component property action
2033
+ *
2034
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2035
+ * @since 2.4.8 - 2008-11-04
2036
+ * @param string $value "AUDIO" / "DISPLAY" / "EMAIL" / "PROCEDURE"
2037
+ * @param mixed $params
2038
+ * @return bool
2039
+ */
2040
+ function setAction( $value, $params=FALSE ) {
2041
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2042
+ $this->action = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
2043
+ return TRUE;
2044
+ }
2045
+ /*********************************************************************************/
2046
+ /**
2047
+ * Property Name: ATTACH
2048
+ */
2049
+ /**
2050
+ * creates formatted output for calendar component property attach
2051
+ *
2052
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2053
+ * @since 0.9.7 - 2006-11-23
2054
+ * @return string
2055
+ */
2056
+ function createAttach() {
2057
+ if( empty( $this->attach )) return FALSE;
2058
+ $output = null;
2059
+ foreach( $this->attach as $attachPart ) {
2060
+ if(! empty( $attachPart['value'] )) {
2061
+ $attributes = $this->_createParams( $attachPart['params'] );
2062
+ $output .= $this->_createElement( 'ATTACH', $attributes, $attachPart['value'] );
2063
+ }
2064
+ elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'ATTACH' );
2065
+ }
2066
+ return $output;
2067
+ }
2068
+ /**
2069
+ * set calendar component property attach
2070
+ *
2071
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2072
+ * @since 2.5.1 - 2008-11-06
2073
+ * @param string $value
2074
+ * @param array $params, optional
2075
+ * @param integer $index, optional
2076
+ * @return bool
2077
+ */
2078
+ function setAttach( $value, $params=FALSE, $index=FALSE ) {
2079
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2080
+ iCalUtilityFunctions::_setMval( $this->attach, $value, $params, FALSE, $index );
2081
+ return TRUE;
2082
+ }
2083
+ /*********************************************************************************/
2084
+ /**
2085
+ * Property Name: ATTENDEE
2086
+ */
2087
+ /**
2088
+ * creates formatted output for calendar component property attendee
2089
+ *
2090
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2091
+ * @since 2.9.8 - 2011-05-30
2092
+ * @return string
2093
+ */
2094
+ function createAttendee() {
2095
+ if( empty( $this->attendee )) return FALSE;
2096
+ $output = null;
2097
+ foreach( $this->attendee as $attendeePart ) { // start foreach 1
2098
+ if( empty( $attendeePart['value'] )) {
2099
+ if( $this->getConfig( 'allowEmpty' ))
2100
+ $output .= $this->_createElement( 'ATTENDEE' );
2101
+ continue;
2102
+ }
2103
+ $attendee1 = $attendee2 = null;
2104
+ foreach( $attendeePart as $paramlabel => $paramvalue ) { // start foreach 2
2105
+ if( 'value' == $paramlabel )
2106
+ $attendee2 .= $paramvalue;
2107
+ elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue ))) { // start elseif
2108
+ // set attenddee parameters in rfc2445 order
2109
+ if( isset( $paramvalue['CUTYPE'] ))
2110
+ $attendee1 .= $this->intAttrDelimiter.'CUTYPE='.$paramvalue['CUTYPE'];
2111
+ if( isset( $paramvalue['MEMBER'] )) {
2112
+ $attendee1 .= $this->intAttrDelimiter.'MEMBER=';
2113
+ foreach( $paramvalue['MEMBER'] as $cix => $opv )
2114
+ $attendee1 .= ( $cix ) ? ', "'.$opv.'"' : '"'.$opv.'"' ;
2115
+ }
2116
+ if( isset( $paramvalue['ROLE'] ))
2117
+ $attendee1 .= $this->intAttrDelimiter.'ROLE='.$paramvalue['ROLE'];
2118
+ if( isset( $paramvalue['PARTSTAT'] ))
2119
+ $attendee1 .= $this->intAttrDelimiter.'PARTSTAT='.$paramvalue['PARTSTAT'];
2120
+ if( isset( $paramvalue['RSVP'] ))
2121
+ $attendee1 .= $this->intAttrDelimiter.'RSVP='.$paramvalue['RSVP'];
2122
+ if( isset( $paramvalue['DELEGATED-TO'] )) {
2123
+ $attendee1 .= $this->intAttrDelimiter.'DELEGATED-TO=';
2124
+ foreach( $paramvalue['DELEGATED-TO'] as $cix => $opv )
2125
+ $attendee1 .= ( $cix ) ? ', "'.$opv.'"' : '"'.$opv.'"' ;
2126
+ }
2127
+ if( isset( $paramvalue['DELEGATED-FROM'] )) {
2128
+ $attendee1 .= $this->intAttrDelimiter.'DELEGATED-FROM=';
2129
+ foreach( $paramvalue['DELEGATED-FROM'] as $cix => $opv )
2130
+ $attendee1 .= ( $cix ) ? ', "'.$opv.'"' : '"'.$opv.'"' ;
2131
+ }
2132
+ if( isset( $paramvalue['SENT-BY'] ))
2133
+ $attendee1 .= $this->intAttrDelimiter.'SENT-BY="'.$paramvalue['SENT-BY'].'"';
2134
+ if( isset( $paramvalue['CN'] ))
2135
+ $attendee1 .= $this->intAttrDelimiter.'CN="'.$paramvalue['CN'].'"';
2136
+ if( isset( $paramvalue['DIR'] ))
2137
+ $attendee1 .= $this->intAttrDelimiter.'DIR="'.$paramvalue['DIR'].'"';
2138
+ if( isset( $paramvalue['LANGUAGE'] ))
2139
+ $attendee1 .= $this->intAttrDelimiter.'LANGUAGE='.$paramvalue['LANGUAGE'];
2140
+ $xparams = array();
2141
+ foreach( $paramvalue as $optparamlabel => $optparamvalue ) { // start foreach 3
2142
+ if( ctype_digit( (string) $optparamlabel )) {
2143
+ $xparams[] = $optparamvalue;
2144
+ continue;
2145
+ }
2146
+ if( !in_array( $optparamlabel, array( 'CUTYPE', 'MEMBER', 'ROLE', 'PARTSTAT', 'RSVP', 'DELEGATED-TO', 'DELEGATED-FROM', 'SENT-BY', 'CN', 'DIR', 'LANGUAGE' )))
2147
+ $xparams[$optparamlabel] = $optparamvalue;
2148
+ } // end foreach 3
2149
+ ksort( $xparams, SORT_STRING );
2150
+ foreach( $xparams as $paramKey => $paramValue ) {
2151
+ if( ctype_digit( (string) $paramKey ))
2152
+ $attendee1 .= $this->intAttrDelimiter.$paramValue;
2153
+ else
2154
+ $attendee1 .= $this->intAttrDelimiter."$paramKey=$paramValue";
2155
+ } // end foreach 3
2156
+ } // end elseif(( 'params' == $paramlabel ) && ( is_array( $paramvalue )))
2157
+ } // end foreach 2
2158
+ $output .= $this->_createElement( 'ATTENDEE', $attendee1, $attendee2 );
2159
+ } // end foreach 1
2160
+ return $output;
2161
+ }
2162
+ /**
2163
+ * set calendar component property attach
2164
+ *
2165
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2166
+ * @since 2.6.34 - 2010-12-18
2167
+ * @param string $value
2168
+ * @param array $params, optional
2169
+ * @param integer $index, optional
2170
+ * @return bool
2171
+ */
2172
+ function setAttendee( $value, $params=FALSE, $index=FALSE ) {
2173
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2174
+ // ftp://, http://, mailto:, file://, gopher://, news:, nntp://, telnet://, wais://, prospero:// may exist.. . also in params
2175
+ if( FALSE !== ( $pos = strpos( substr( $value, 0, 9 ), ':' )))
2176
+ $value = strtoupper( substr( $value, 0, $pos )).substr( $value, $pos );
2177
+ elseif( !empty( $value ))
2178
+ $value = 'MAILTO:'.$value;
2179
+ $params2 = array();
2180
+ if( is_array($params )) {
2181
+ $optarrays = array();
2182
+ foreach( $params as $optparamlabel => $optparamvalue ) {
2183
+ $optparamlabel = strtoupper( $optparamlabel );
2184
+ switch( $optparamlabel ) {
2185
+ case 'MEMBER':
2186
+ case 'DELEGATED-TO':
2187
+ case 'DELEGATED-FROM':
2188
+ if( !is_array( $optparamvalue ))
2189
+ $optparamvalue = array( $optparamvalue );
2190
+ foreach( $optparamvalue as $part ) {
2191
+ $part = trim( $part );
2192
+ if(( '"' == substr( $part, 0, 1 )) &&
2193
+ ( '"' == substr( $part, -1 )))
2194
+ $part = substr( $part, 1, ( strlen( $part ) - 2 ));
2195
+ if( 'mailto:' != strtolower( substr( $part, 0, 7 )))
2196
+ $part = "MAILTO:$part";
2197
+ else
2198
+ $part = 'MAILTO:'.substr( $part, 7 );
2199
+ $optarrays[$optparamlabel][] = $part;
2200
+ }
2201
+ break;
2202
+ default:
2203
+ if(( '"' == substr( $optparamvalue, 0, 1 )) &&
2204
+ ( '"' == substr( $optparamvalue, -1 )))
2205
+ $optparamvalue = substr( $optparamvalue, 1, ( strlen( $optparamvalue ) - 2 ));
2206
+ if( 'SENT-BY' == $optparamlabel ) {
2207
+ if( 'mailto:' != strtolower( substr( $optparamvalue, 0, 7 )))
2208
+ $optparamvalue = "MAILTO:$optparamvalue";
2209
+ else
2210
+ $optparamvalue = 'MAILTO:'.substr( $optparamvalue, 7 );
2211
+ }
2212
+ $params2[$optparamlabel] = $optparamvalue;
2213
+ break;
2214
+ } // end switch( $optparamlabel.. .
2215
+ } // end foreach( $optparam.. .
2216
+ foreach( $optarrays as $optparamlabel => $optparams )
2217
+ $params2[$optparamlabel] = $optparams;
2218
+ }
2219
+ // remove defaults
2220
+ iCalUtilityFunctions::_existRem( $params2, 'CUTYPE', 'INDIVIDUAL' );
2221
+ iCalUtilityFunctions::_existRem( $params2, 'PARTSTAT', 'NEEDS-ACTION' );
2222
+ iCalUtilityFunctions::_existRem( $params2, 'ROLE', 'REQ-PARTICIPANT' );
2223
+ iCalUtilityFunctions::_existRem( $params2, 'RSVP', 'FALSE' );
2224
+ // check language setting
2225
+ if( isset( $params2['CN' ] )) {
2226
+ $lang = $this->getConfig( 'language' );
2227
+ if( !isset( $params2['LANGUAGE' ] ) && !empty( $lang ))
2228
+ $params2['LANGUAGE' ] = $lang;
2229
+ }
2230
+ iCalUtilityFunctions::_setMval( $this->attendee, $value, $params2, FALSE, $index );
2231
+ return TRUE;
2232
+ }
2233
+ /*********************************************************************************/
2234
+ /**
2235
+ * Property Name: CATEGORIES
2236
+ */
2237
+ /**
2238
+ * creates formatted output for calendar component property categories
2239
+ *
2240
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2241
+ * @since 2.4.8 - 2008-10-22
2242
+ * @return string
2243
+ */
2244
+ function createCategories() {
2245
+ if( empty( $this->categories )) return FALSE;
2246
+ $output = null;
2247
+ foreach( $this->categories as $category ) {
2248
+ if( empty( $category['value'] )) {
2249
+ if ( $this->getConfig( 'allowEmpty' ))
2250
+ $output .= $this->_createElement( 'CATEGORIES' );
2251
+ continue;
2252
+ }
2253
+ $attributes = $this->_createParams( $category['params'], array( 'LANGUAGE' ));
2254
+ if( is_array( $category['value'] )) {
2255
+ foreach( $category['value'] as $cix => $categoryPart )
2256
+ $category['value'][$cix] = $this->_strrep( $categoryPart );
2257
+ $content = implode( ',', $category['value'] );
2258
+ }
2259
+ else
2260
+ $content = $this->_strrep( $category['value'] );
2261
+ $output .= $this->_createElement( 'CATEGORIES', $attributes, $content );
2262
+ }
2263
+ return $output;
2264
+ }
2265
+ /**
2266
+ * set calendar component property categories
2267
+ *
2268
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2269
+ * @since 2.5.1 - 2008-11-06
2270
+ * @param mixed $value
2271
+ * @param array $params, optional
2272
+ * @param integer $index, optional
2273
+ * @return bool
2274
+ */
2275
+ function setCategories( $value, $params=FALSE, $index=FALSE ) {
2276
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2277
+ iCalUtilityFunctions::_setMval( $this->categories, $value, $params, FALSE, $index );
2278
+ return TRUE;
2279
+ }
2280
+ /*********************************************************************************/
2281
+ /**
2282
+ * Property Name: CLASS
2283
+ */
2284
+ /**
2285
+ * creates formatted output for calendar component property class
2286
+ *
2287
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2288
+ * @since 0.9.7 - 2006-11-20
2289
+ * @return string
2290
+ */
2291
+ function createClass() {
2292
+ if( empty( $this->class )) return FALSE;
2293
+ if( empty( $this->class['value'] ))
2294
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'CLASS' ) : FALSE;
2295
+ $attributes = $this->_createParams( $this->class['params'] );
2296
+ return $this->_createElement( 'CLASS', $attributes, $this->class['value'] );
2297
+ }
2298
+ /**
2299
+ * set calendar component property class
2300
+ *
2301
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2302
+ * @since 2.4.8 - 2008-11-04
2303
+ * @param string $value "PUBLIC" / "PRIVATE" / "CONFIDENTIAL" / iana-token / x-name
2304
+ * @param array $params optional
2305
+ * @return bool
2306
+ */
2307
+ function setClass( $value, $params=FALSE ) {
2308
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2309
+ $this->class = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
2310
+ return TRUE;
2311
+ }
2312
+ /*********************************************************************************/
2313
+ /**
2314
+ * Property Name: COMMENT
2315
+ */
2316
+ /**
2317
+ * creates formatted output for calendar component property comment
2318
+ *
2319
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2320
+ * @since 2.4.8 - 2008-10-22
2321
+ * @return string
2322
+ */
2323
+ function createComment() {
2324
+ if( empty( $this->comment )) return FALSE;
2325
+ $output = null;
2326
+ foreach( $this->comment as $commentPart ) {
2327
+ if( empty( $commentPart['value'] )) {
2328
+ if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'COMMENT' );
2329
+ continue;
2330
+ }
2331
+ $attributes = $this->_createParams( $commentPart['params'], array( 'ALTREP', 'LANGUAGE' ));
2332
+ $content = $this->_strrep( $commentPart['value'] );
2333
+ $output .= $this->_createElement( 'COMMENT', $attributes, $content );
2334
+ }
2335
+ return $output;
2336
+ }
2337
+ /**
2338
+ * set calendar component property comment
2339
+ *
2340
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2341
+ * @since 2.5.1 - 2008-11-06
2342
+ * @param string $value
2343
+ * @param array $params, optional
2344
+ * @param integer $index, optional
2345
+ * @return bool
2346
+ */
2347
+ function setComment( $value, $params=FALSE, $index=FALSE ) {
2348
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2349
+ iCalUtilityFunctions::_setMval( $this->comment, $value, $params, FALSE, $index );
2350
+ return TRUE;
2351
+ }
2352
+ /*********************************************************************************/
2353
+ /**
2354
+ * Property Name: COMPLETED
2355
+ */
2356
+ /**
2357
+ * creates formatted output for calendar component property completed
2358
+ *
2359
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2360
+ * @since 2.4.8 - 2008-10-22
2361
+ * @return string
2362
+ */
2363
+ function createCompleted( ) {
2364
+ if( empty( $this->completed )) return FALSE;
2365
+ if( !isset( $this->completed['value']['year'] ) &&
2366
+ !isset( $this->completed['value']['month'] ) &&
2367
+ !isset( $this->completed['value']['day'] ) &&
2368
+ !isset( $this->completed['value']['hour'] ) &&
2369
+ !isset( $this->completed['value']['min'] ) &&
2370
+ !isset( $this->completed['value']['sec'] ))
2371
+ if( $this->getConfig( 'allowEmpty' ))
2372
+ return $this->_createElement( 'COMPLETED' );
2373
+ else return FALSE;
2374
+ $formatted = iCalUtilityFunctions::_format_date_time( $this->completed['value'], 7 );
2375
+ $attributes = $this->_createParams( $this->completed['params'] );
2376
+ return $this->_createElement( 'COMPLETED', $attributes, $formatted );
2377
+ }
2378
+ /**
2379
+ * set calendar component property completed
2380
+ *
2381
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2382
+ * @since 2.4.8 - 2008-10-23
2383
+ * @param mixed $year
2384
+ * @param mixed $month optional
2385
+ * @param int $day optional
2386
+ * @param int $hour optional
2387
+ * @param int $min optional
2388
+ * @param int $sec optional
2389
+ * @param array $params optional
2390
+ * @return bool
2391
+ */
2392
+ function setCompleted( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
2393
+ if( empty( $year )) {
2394
+ if( $this->getConfig( 'allowEmpty' )) {
2395
+ $this->completed = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
2396
+ return TRUE;
2397
+ }
2398
+ else
2399
+ return FALSE;
2400
+ }
2401
+ $this->completed = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
2402
+ return TRUE;
2403
+ }
2404
+ /*********************************************************************************/
2405
+ /**
2406
+ * Property Name: CONTACT
2407
+ */
2408
+ /**
2409
+ * creates formatted output for calendar component property contact
2410
+ *
2411
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2412
+ * @since 2.4.8 - 2008-10-23
2413
+ * @return string
2414
+ */
2415
+ function createContact() {
2416
+ if( empty( $this->contact )) return FALSE;
2417
+ $output = null;
2418
+ foreach( $this->contact as $contact ) {
2419
+ if( !empty( $contact['value'] )) {
2420
+ $attributes = $this->_createParams( $contact['params'], array( 'ALTREP', 'LANGUAGE' ));
2421
+ $content = $this->_strrep( $contact['value'] );
2422
+ $output .= $this->_createElement( 'CONTACT', $attributes, $content );
2423
+ }
2424
+ elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'CONTACT' );
2425
+ }
2426
+ return $output;
2427
+ }
2428
+ /**
2429
+ * set calendar component property contact
2430
+ *
2431
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2432
+ * @since 2.5.1 - 2008-11-05
2433
+ * @param string $value
2434
+ * @param array $params, optional
2435
+ * @param integer $index, optional
2436
+ * @return bool
2437
+ */
2438
+ function setContact( $value, $params=FALSE, $index=FALSE ) {
2439
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
2440
+ iCalUtilityFunctions::_setMval( $this->contact, $value, $params, FALSE, $index );
2441
+ return TRUE;
2442
+ }
2443
+ /*********************************************************************************/
2444
+ /**
2445
+ * Property Name: CREATED
2446
+ */
2447
+ /**
2448
+ * creates formatted output for calendar component property created
2449
+ *
2450
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2451
+ * @since 2.4.8 - 2008-10-21
2452
+ * @return string
2453
+ */
2454
+ function createCreated() {
2455
+ if( empty( $this->created )) return FALSE;
2456
+ $formatted = iCalUtilityFunctions::_format_date_time( $this->created['value'], 7 );
2457
+ $attributes = $this->_createParams( $this->created['params'] );
2458
+ return $this->_createElement( 'CREATED', $attributes, $formatted );
2459
+ }
2460
+ /**
2461
+ * set calendar component property created
2462
+ *
2463
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2464
+ * @since 2.4.8 - 2008-10-23
2465
+ * @param mixed $year optional
2466
+ * @param mixed $month optional
2467
+ * @param int $day optional
2468
+ * @param int $hour optional
2469
+ * @param int $min optional
2470
+ * @param int $sec optional
2471
+ * @param mixed $params optional
2472
+ * @return bool
2473
+ */
2474
+ function setCreated( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
2475
+ if( !isset( $year )) {
2476
+ $year = date('Ymd\THis', mktime( date( 'H' ), date( 'i' ), date( 's' ) - date( 'Z'), date( 'm' ), date( 'd' ), date( 'Y' )));
2477
+ }
2478
+ $this->created = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
2479
+ return TRUE;
2480
+ }
2481
+ /*********************************************************************************/
2482
+ /**
2483
+ * Property Name: DESCRIPTION
2484
+ */
2485
+ /**
2486
+ * creates formatted output for calendar component property description
2487
+ *
2488
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2489
+ * @since 2.4.8 - 2008-10-22
2490
+ * @return string
2491
+ */
2492
+ function createDescription() {
2493
+ if( empty( $this->description )) return FALSE;
2494
+ $output = null;
2495
+ foreach( $this->description as $description ) {
2496
+ if( !empty( $description['value'] )) {
2497
+ $attributes = $this->_createParams( $description['params'], array( 'ALTREP', 'LANGUAGE' ));
2498
+ $content = $this->_strrep( $description['value'] );
2499
+ $output .= $this->_createElement( 'DESCRIPTION', $attributes, $content );
2500
+ }
2501
+ elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'DESCRIPTION' );
2502
+ }
2503
+ return $output;
2504
+ }
2505
+ /**
2506
+ * set calendar component property description
2507
+ *
2508
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2509
+ * @since 2.6.24 - 2010-11-06
2510
+ * @param string $value
2511
+ * @param array $params, optional
2512
+ * @param integer $index, optional
2513
+ * @return bool
2514
+ */
2515
+ function setDescription( $value, $params=FALSE, $index=FALSE ) {
2516
+ if( empty( $value )) { if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE; }
2517
+ if( 'vjournal' != $this->objName )
2518
+ $index = 1;
2519
+ iCalUtilityFunctions::_setMval( $this->description, $value, $params, FALSE, $index );
2520
+ return TRUE;
2521
+ }
2522
+ /*********************************************************************************/
2523
+ /**
2524
+ * Property Name: DTEND
2525
+ */
2526
+ /**
2527
+ * creates formatted output for calendar component property dtend
2528
+ *
2529
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2530
+ * @since 2.9.6 - 2011-05-14
2531
+ * @return string
2532
+ */
2533
+ function createDtend() {
2534
+ if( empty( $this->dtend )) return FALSE;
2535
+ if( !isset( $this->dtend['value']['year'] ) &&
2536
+ !isset( $this->dtend['value']['month'] ) &&
2537
+ !isset( $this->dtend['value']['day'] ) &&
2538
+ !isset( $this->dtend['value']['hour'] ) &&
2539
+ !isset( $this->dtend['value']['min'] ) &&
2540
+ !isset( $this->dtend['value']['sec'] ))
2541
+ if( $this->getConfig( 'allowEmpty' ))
2542
+ return $this->_createElement( 'DTEND' );
2543
+ else return FALSE;
2544
+ $formatted = iCalUtilityFunctions::_format_date_time( $this->dtend['value'] );
2545
+ if(( FALSE !== ( $tzid = $this->getConfig( 'TZID' ))) &&
2546
+ ( !isset( $this->dtend['params']['VALUE'] ) || ( $this->dtend['params']['VALUE'] != 'DATE' )) &&
2547
+ !isset( $this->dtend['params']['TZID'] ))
2548
+ $this->dtend['params']['TZID'] = $tzid;
2549
+ $attributes = $this->_createParams( $this->dtend['params'] );
2550
+ return $this->_createElement( 'DTEND', $attributes, $formatted );
2551
+ }
2552
+ /**
2553
+ * set calendar component property dtend
2554
+ *
2555
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2556
+ * @since 2.9.6 - 2011-05-14
2557
+ * @param mixed $year
2558
+ * @param mixed $month optional
2559
+ * @param int $day optional
2560
+ * @param int $hour optional
2561
+ * @param int $min optional
2562
+ * @param int $sec optional
2563
+ * @param string $tz optional
2564
+ * @param array params optional
2565
+ * @return bool
2566
+ */
2567
+ function setDtend( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
2568
+ if( empty( $year )) {
2569
+ if( $this->getConfig( 'allowEmpty' )) {
2570
+ $this->dtend = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
2571
+ return TRUE;
2572
+ }
2573
+ else
2574
+ return FALSE;
2575
+ }
2576
+ $this->dtend = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' ));
2577
+ return TRUE;
2578
+ }
2579
+ /*********************************************************************************/
2580
+ /**
2581
+ * Property Name: DTSTAMP
2582
+ */
2583
+ /**
2584
+ * creates formatted output for calendar component property dtstamp
2585
+ *
2586
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2587
+ * @since 2.4.4 - 2008-03-07
2588
+ * @return string
2589
+ */
2590
+ function createDtstamp() {
2591
+ if( !isset( $this->dtstamp['value']['year'] ) &&
2592
+ !isset( $this->dtstamp['value']['month'] ) &&
2593
+ !isset( $this->dtstamp['value']['day'] ) &&
2594
+ !isset( $this->dtstamp['value']['hour'] ) &&
2595
+ !isset( $this->dtstamp['value']['min'] ) &&
2596
+ !isset( $this->dtstamp['value']['sec'] ))
2597
+ $this->_makeDtstamp();
2598
+ $formatted = iCalUtilityFunctions::_format_date_time( $this->dtstamp['value'], 7 );
2599
+ $attributes = $this->_createParams( $this->dtstamp['params'] );
2600
+ return $this->_createElement( 'DTSTAMP', $attributes, $formatted );
2601
+ }
2602
+ /**
2603
+ * computes datestamp for calendar component object instance dtstamp
2604
+ *
2605
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2606
+ * @since 2.6.25 - 2010-11-09
2607
+ * @return void
2608
+ */
2609
+ function _makeDtstamp() {
2610
+ $d = mktime( date('H'), date('m'), (date('s') - date( 'Z' )), date('m'), date('d'), date('Y'));
2611
+ $this->dtstamp['value'] = array( 'year' => date( 'Y', $d )
2612
+ , 'month' => date( 'm', $d )
2613
+ , 'day' => date( 'd', $d )
2614
+ , 'hour' => date( 'H', $d )
2615
+ , 'min' => date( 'i', $d )
2616
+ , 'sec' => date( 's', $d ));
2617
+ $this->dtstamp['params'] = null;
2618
+ }
2619
+ /**
2620
+ * set calendar component property dtstamp
2621
+ *
2622
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2623
+ * @since 2.4.8 - 2008-10-23
2624
+ * @param mixed $year
2625
+ * @param mixed $month optional
2626
+ * @param int $day optional
2627
+ * @param int $hour optional
2628
+ * @param int $min optional
2629
+ * @param int $sec optional
2630
+ * @param array $params optional
2631
+ * @return TRUE
2632
+ */
2633
+ function setDtstamp( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
2634
+ if( empty( $year ))
2635
+ $this->_makeDtstamp();
2636
+ else
2637
+ $this->dtstamp = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
2638
+ return TRUE;
2639
+ }
2640
+ /*********************************************************************************/
2641
+ /**
2642
+ * Property Name: DTSTART
2643
+ */
2644
+ /**
2645
+ * creates formatted output for calendar component property dtstart
2646
+ *
2647
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2648
+ * @since 2.9.6 - 2011-05-15
2649
+ * @return string
2650
+ */
2651
+ function createDtstart() {
2652
+ if( empty( $this->dtstart )) return FALSE;
2653
+ if( !isset( $this->dtstart['value']['year'] ) &&
2654
+ !isset( $this->dtstart['value']['month'] ) &&
2655
+ !isset( $this->dtstart['value']['day'] ) &&
2656
+ !isset( $this->dtstart['value']['hour'] ) &&
2657
+ !isset( $this->dtstart['value']['min'] ) &&
2658
+ !isset( $this->dtstart['value']['sec'] )) {
2659
+ if( $this->getConfig( 'allowEmpty' ))
2660
+ return $this->_createElement( 'DTSTART' );
2661
+ else return FALSE;
2662
+ }
2663
+ if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' )))
2664
+ unset( $this->dtstart['value']['tz'], $this->dtstart['params']['TZID'] );
2665
+ elseif(( FALSE !== ( $tzid = $this->getConfig( 'TZID' ))) &&
2666
+ ( !isset( $this->dtstart['params']['VALUE'] ) || ( $this->dtstart['params']['VALUE'] != 'DATE' )) &&
2667
+ !isset( $this->dtstart['params']['TZID'] ))
2668
+ $this->dtstart['params']['TZID'] = $tzid;
2669
+ $formatted = iCalUtilityFunctions::_format_date_time( $this->dtstart['value'] );
2670
+ $attributes = $this->_createParams( $this->dtstart['params'] );
2671
+ return $this->_createElement( 'DTSTART', $attributes, $formatted );
2672
+ }
2673
+ /**
2674
+ * set calendar component property dtstart
2675
+ *
2676
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2677
+ * @since 2.6.22 - 2010-09-22
2678
+ * @param mixed $year
2679
+ * @param mixed $month optional
2680
+ * @param int $day optional
2681
+ * @param int $hour optional
2682
+ * @param int $min optional
2683
+ * @param int $sec optional
2684
+ * @param string $tz optional
2685
+ * @param array $params optional
2686
+ * @return bool
2687
+ */
2688
+ function setDtstart( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
2689
+ if( empty( $year )) {
2690
+ if( $this->getConfig( 'allowEmpty' )) {
2691
+ $this->dtstart = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
2692
+ return TRUE;
2693
+ }
2694
+ else
2695
+ return FALSE;
2696
+ }
2697
+ $this->dtstart = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, 'dtstart', $this->objName, $this->getConfig( 'TZID' ));
2698
+ return TRUE;
2699
+ }
2700
+ /*********************************************************************************/
2701
+ /**
2702
+ * Property Name: DUE
2703
+ */
2704
+ /**
2705
+ * creates formatted output for calendar component property due
2706
+ *
2707
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2708
+ * @since 2.4.8 - 2008-10-22
2709
+ * @return string
2710
+ */
2711
+ function createDue() {
2712
+ if( empty( $this->due )) return FALSE;
2713
+ if( !isset( $this->due['value']['year'] ) &&
2714
+ !isset( $this->due['value']['month'] ) &&
2715
+ !isset( $this->due['value']['day'] ) &&
2716
+ !isset( $this->due['value']['hour'] ) &&
2717
+ !isset( $this->due['value']['min'] ) &&
2718
+ !isset( $this->due['value']['sec'] )) {
2719
+ if( $this->getConfig( 'allowEmpty' ))
2720
+ return $this->_createElement( 'DUE' );
2721
+ else
2722
+ return FALSE;
2723
+ }
2724
+ $formatted = iCalUtilityFunctions::_format_date_time( $this->due['value'] );
2725
+ if(( FALSE !== ( $tzid = $this->getConfig( 'TZID' ))) &&
2726
+ ( !isset( $this->due['params']['VALUE'] ) || ( $this->due['params']['VALUE'] != 'DATE' )) &&
2727
+ !isset( $this->due['params']['TZID'] ))
2728
+ $this->due['params']['TZID'] = $tzid;
2729
+ $attributes = $this->_createParams( $this->due['params'] );
2730
+ return $this->_createElement( 'DUE', $attributes, $formatted );
2731
+ }
2732
+ /**
2733
+ * set calendar component property due
2734
+ *
2735
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2736
+ * @since 2.4.8 - 2008-11-04
2737
+ * @param mixed $year
2738
+ * @param mixed $month optional
2739
+ * @param int $day optional
2740
+ * @param int $hour optional
2741
+ * @param int $min optional
2742
+ * @param int $sec optional
2743
+ * @param array $params optional
2744
+ * @return bool
2745
+ */
2746
+ function setDue( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
2747
+ if( empty( $year )) {
2748
+ if( $this->getConfig( 'allowEmpty' )) {
2749
+ $this->due = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ));
2750
+ return TRUE;
2751
+ }
2752
+ else
2753
+ return FALSE;
2754
+ }
2755
+ $this->due = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' ));
2756
+ return TRUE;
2757
+ }
2758
+ /*********************************************************************************/
2759
+ /**
2760
+ * Property Name: DURATION
2761
+ */
2762
+ /**
2763
+ * creates formatted output for calendar component property duration
2764
+ *
2765
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2766
+ * @since 2.4.8 - 2008-10-21
2767
+ * @return string
2768
+ */
2769
+ function createDuration() {
2770
+ if( empty( $this->duration )) return FALSE;
2771
+ if( !isset( $this->duration['value']['week'] ) &&
2772
+ !isset( $this->duration['value']['day'] ) &&
2773
+ !isset( $this->duration['value']['hour'] ) &&
2774
+ !isset( $this->duration['value']['min'] ) &&
2775
+ !isset( $this->duration['value']['sec'] ))
2776
+ if( $this->getConfig( 'allowEmpty' ))
2777
+ return $this->_createElement( 'DURATION', array(), null );
2778
+ else return FALSE;
2779
+ $attributes = $this->_createParams( $this->duration['params'] );
2780
+ return $this->_createElement( 'DURATION', $attributes, iCalUtilityFunctions::_format_duration( $this->duration['value'] ));
2781
+ }
2782
+ /**
2783
+ * set calendar component property duration
2784
+ *
2785
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2786
+ * @since 2.4.8 - 2008-11-04
2787
+ * @param mixed $week
2788
+ * @param mixed $day optional
2789
+ * @param int $hour optional
2790
+ * @param int $min optional
2791
+ * @param int $sec optional
2792
+ * @param array $params optional
2793
+ * @return bool
2794
+ */
2795
+ function setDuration( $week, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
2796
+ if( empty( $week )) if( $this->getConfig( 'allowEmpty' )) $week = null; else return FALSE;
2797
+ if( is_array( $week ) && ( 1 <= count( $week )))
2798
+ $this->duration = array( 'value' => iCalUtilityFunctions::_duration_array( $week ), 'params' => iCalUtilityFunctions::_setParams( $day ));
2799
+ elseif( is_string( $week ) && ( 3 <= strlen( trim( $week )))) {
2800
+ $week = trim( $week );
2801
+ if( in_array( substr( $week, 0, 1 ), array( '+', '-' )))
2802
+ $week = substr( $week, 1 );
2803
+ $this->duration = array( 'value' => iCalUtilityFunctions::_duration_string( $week ), 'params' => iCalUtilityFunctions::_setParams( $day ));
2804
+ }
2805
+ elseif( empty( $week ) && empty( $day ) && empty( $hour ) && empty( $min ) && empty( $sec ))
2806
+ return FALSE;
2807
+ else
2808
+ $this->duration = array( 'value' => iCalUtilityFunctions::_duration_array( array( $week, $day, $hour, $min, $sec )), 'params' => iCalUtilityFunctions::_setParams( $params ));
2809
+ return TRUE;
2810
+ }
2811
+ /*********************************************************************************/
2812
+ /**
2813
+ * Property Name: EXDATE
2814
+ */
2815
+ /**
2816
+ * creates formatted output for calendar component property exdate
2817
+ *
2818
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2819
+ * @since 2.4.8 - 2008-10-22
2820
+ * @return string
2821
+ */
2822
+ function createExdate() {
2823
+ if( empty( $this->exdate )) return FALSE;
2824
+ $output = null;
2825
+ foreach( $this->exdate as $ex => $theExdate ) {
2826
+ if( empty( $theExdate['value'] )) {
2827
+ if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'EXDATE' );
2828
+ continue;
2829
+ }
2830
+ $content = $attributes = null;
2831
+ foreach( $theExdate['value'] as $eix => $exdatePart ) {
2832
+ $parno = count( $exdatePart );
2833
+ $formatted = iCalUtilityFunctions::_format_date_time( $exdatePart, $parno );
2834
+ if( isset( $theExdate['params']['TZID'] ))
2835
+ $formatted = str_replace( 'Z', '', $formatted);
2836
+ if( 0 < $eix ) {
2837
+ if( isset( $theExdate['value'][0]['tz'] )) {
2838
+ if( ctype_digit( substr( $theExdate['value'][0]['tz'], -4 )) ||
2839
+ ( 'Z' == $theExdate['value'][0]['tz'] )) {
2840
+ if( 'Z' != substr( $formatted, -1 ))
2841
+ $formatted .= 'Z';
2842
+ }
2843
+ else
2844
+ $formatted = str_replace( 'Z', '', $formatted );
2845
+ }
2846
+ else
2847
+ $formatted = str_replace( 'Z', '', $formatted );
2848
+ }
2849
+ $content .= ( 0 < $eix ) ? ','.$formatted : $formatted;
2850
+ }
2851
+ $attributes .= $this->_createParams( $theExdate['params'] );
2852
+ $output .= $this->_createElement( 'EXDATE', $attributes, $content );
2853
+ }
2854
+ return $output;
2855
+ }
2856
+ /**
2857
+ * set calendar component property exdate
2858
+ *
2859
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2860
+ * @since 2.5.1 - 2008-11-05
2861
+ * @param array exdates
2862
+ * @param array $params, optional
2863
+ * @param integer $index, optional
2864
+ * @return bool
2865
+ */
2866
+ function setExdate( $exdates, $params=FALSE, $index=FALSE ) {
2867
+ if( empty( $exdates )) {
2868
+ if( $this->getConfig( 'allowEmpty' )) {
2869
+ iCalUtilityFunctions::_setMval( $this->exdate, null, $params, FALSE, $index );
2870
+ return TRUE;
2871
+ }
2872
+ else
2873
+ return FALSE;
2874
+ }
2875
+ $input = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )));
2876
+ /* ev. check 1:st date and save ev. timezone **/
2877
+ iCalUtilityFunctions::_chkdatecfg( reset( $exdates ), $parno, $input['params'] );
2878
+ iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default parameter
2879
+ foreach( $exdates as $eix => $theExdate ) {
2880
+ if( iCalUtilityFunctions::_isArrayTimestampDate( $theExdate ))
2881
+ $exdatea = iCalUtilityFunctions::_timestamp2date( $theExdate, $parno );
2882
+ elseif( is_array( $theExdate ))
2883
+ $exdatea = iCalUtilityFunctions::_date_time_array( $theExdate, $parno );
2884
+ elseif( 8 <= strlen( trim( $theExdate ))) // ex. 2006-08-03 10:12:18
2885
+ $exdatea = iCalUtilityFunctions::_date_time_string( $theExdate, $parno );
2886
+ if( 3 == $parno )
2887
+ unset( $exdatea['hour'], $exdatea['min'], $exdatea['sec'], $exdatea['tz'] );
2888
+ elseif( isset( $exdatea['tz'] ))
2889
+ $exdatea['tz'] = (string) $exdatea['tz'];
2890
+ if( isset( $input['params']['TZID'] ) ||
2891
+ ( isset( $exdatea['tz'] ) && !iCalUtilityFunctions::_isOffset( $exdatea['tz'] )) ||
2892
+ ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] ))) ||
2893
+ ( isset( $input['value'][0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value'][0]['tz'] )))
2894
+ unset( $exdatea['tz'] );
2895
+ $input['value'][] = $exdatea;
2896
+ }
2897
+ if( 0 >= count( $input['value'] ))
2898
+ return FALSE;
2899
+ if( 3 == $parno ) {
2900
+ $input['params']['VALUE'] = 'DATE';
2901
+ unset( $input['params']['TZID'] );
2902
+ }
2903
+ iCalUtilityFunctions::_setMval( $this->exdate, $input['value'], $input['params'], FALSE, $index );
2904
+ return TRUE;
2905
+ }
2906
+ /*********************************************************************************/
2907
+ /**
2908
+ * Property Name: EXRULE
2909
+ */
2910
+ /**
2911
+ * creates formatted output for calendar component property exrule
2912
+ *
2913
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2914
+ * @since 2.4.8 - 2008-10-22
2915
+ * @return string
2916
+ */
2917
+ function createExrule() {
2918
+ if( empty( $this->exrule )) return FALSE;
2919
+ return $this->_format_recur( 'EXRULE', $this->exrule );
2920
+ }
2921
+ /**
2922
+ * set calendar component property exdate
2923
+ *
2924
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2925
+ * @since 2.5.1 - 2008-11-05
2926
+ * @param array $exruleset
2927
+ * @param array $params, optional
2928
+ * @param integer $index, optional
2929
+ * @return bool
2930
+ */
2931
+ function setExrule( $exruleset, $params=FALSE, $index=FALSE ) {
2932
+ if( empty( $exruleset )) if( $this->getConfig( 'allowEmpty' )) $exruleset = null; else return FALSE;
2933
+ iCalUtilityFunctions::_setMval( $this->exrule, iCalUtilityFunctions::_setRexrule( $exruleset ), $params, FALSE, $index );
2934
+ return TRUE;
2935
+ }
2936
+ /*********************************************************************************/
2937
+ /**
2938
+ * Property Name: FREEBUSY
2939
+ */
2940
+ /**
2941
+ * creates formatted output for calendar component property freebusy
2942
+ *
2943
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2944
+ * @since 2.4.8 - 2008-10-22
2945
+ * @return string
2946
+ */
2947
+ function createFreebusy() {
2948
+ if( empty( $this->freebusy )) return FALSE;
2949
+ $output = null;
2950
+ foreach( $this->freebusy as $freebusyPart ) {
2951
+ if( empty( $freebusyPart['value'] )) {
2952
+ if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'FREEBUSY' );
2953
+ continue;
2954
+ }
2955
+ $attributes = $content = null;
2956
+ if( isset( $freebusyPart['value']['fbtype'] )) {
2957
+ $attributes .= $this->intAttrDelimiter.'FBTYPE='.$freebusyPart['value']['fbtype'];
2958
+ unset( $freebusyPart['value']['fbtype'] );
2959
+ $freebusyPart['value'] = array_values( $freebusyPart['value'] );
2960
+ }
2961
+ else
2962
+ $attributes .= $this->intAttrDelimiter.'FBTYPE=BUSY';
2963
+ $attributes .= $this->_createParams( $freebusyPart['params'] );
2964
+ $fno = 1;
2965
+ $cnt = count( $freebusyPart['value']);
2966
+ foreach( $freebusyPart['value'] as $periodix => $freebusyPeriod ) {
2967
+ $formatted = iCalUtilityFunctions::_format_date_time( $freebusyPeriod[0] );
2968
+ $content .= $formatted;
2969
+ $content .= '/';
2970
+ $cnt2 = count( $freebusyPeriod[1]);
2971
+ if( array_key_exists( 'year', $freebusyPeriod[1] )) // date-time
2972
+ $cnt2 = 7;
2973
+ elseif( array_key_exists( 'week', $freebusyPeriod[1] )) // duration
2974
+ $cnt2 = 5;
2975
+ if(( 7 == $cnt2 ) && // period= -> date-time
2976
+ isset( $freebusyPeriod[1]['year'] ) &&
2977
+ isset( $freebusyPeriod[1]['month'] ) &&
2978
+ isset( $freebusyPeriod[1]['day'] )) {
2979
+ $content .= iCalUtilityFunctions::_format_date_time( $freebusyPeriod[1] );
2980
+ }
2981
+ else { // period= -> dur-time
2982
+ $content .= iCalUtilityFunctions::_format_duration( $freebusyPeriod[1] );
2983
+ }
2984
+ if( $fno < $cnt )
2985
+ $content .= ',';
2986
+ $fno++;
2987
+ }
2988
+ $output .= $this->_createElement( 'FREEBUSY', $attributes, $content );
2989
+ }
2990
+ return $output;
2991
+ }
2992
+ /**
2993
+ * set calendar component property freebusy
2994
+ *
2995
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
2996
+ * @since 2.8.10 - 2011-03-24
2997
+ * @param string $fbType
2998
+ * @param array $fbValues
2999
+ * @param array $params, optional
3000
+ * @param integer $index, optional
3001
+ * @return bool
3002
+ */
3003
+ function setFreebusy( $fbType, $fbValues, $params=FALSE, $index=FALSE ) {
3004
+ if( empty( $fbValues )) {
3005
+ if( $this->getConfig( 'allowEmpty' )) {
3006
+ iCalUtilityFunctions::_setMval( $this->freebusy, null, $params, FALSE, $index );
3007
+ return TRUE;
3008
+ }
3009
+ else
3010
+ return FALSE;
3011
+ }
3012
+ $fbType = strtoupper( $fbType );
3013
+ if(( !in_array( $fbType, array( 'FREE', 'BUSY', 'BUSY-UNAVAILABLE', 'BUSY-TENTATIVE' ))) &&
3014
+ ( 'X-' != substr( $fbType, 0, 2 )))
3015
+ $fbType = 'BUSY';
3016
+ $input = array( 'fbtype' => $fbType );
3017
+ foreach( $fbValues as $fbPeriod ) { // periods => period
3018
+ if( empty( $fbPeriod ))
3019
+ continue;
3020
+ $freebusyPeriod = array();
3021
+ foreach( $fbPeriod as $fbMember ) { // pairs => singlepart
3022
+ $freebusyPairMember = array();
3023
+ if( is_array( $fbMember )) {
3024
+ if( iCalUtilityFunctions::_isArrayDate( $fbMember )) { // date-time value
3025
+ $freebusyPairMember = iCalUtilityFunctions::_date_time_array( $fbMember, 7 );
3026
+ $freebusyPairMember['tz'] = 'Z';
3027
+ }
3028
+ elseif( iCalUtilityFunctions::_isArrayTimestampDate( $fbMember )) { // timestamp value
3029
+ $freebusyPairMember = iCalUtilityFunctions::_timestamp2date( $fbMember['timestamp'], 7 );
3030
+ $freebusyPairMember['tz'] = 'Z';
3031
+ }
3032
+ else { // array format duration
3033
+ $freebusyPairMember = iCalUtilityFunctions::_duration_array( $fbMember );
3034
+ }
3035
+ }
3036
+ elseif(( 3 <= strlen( trim( $fbMember ))) && // string format duration
3037
+ ( in_array( $fbMember{0}, array( 'P', '+', '-' )))) {
3038
+ if( 'P' != $fbMember{0} )
3039
+ $fbmember = substr( $fbMember, 1 );
3040
+ $freebusyPairMember = iCalUtilityFunctions::_duration_string( $fbMember );
3041
+ }
3042
+ elseif( 8 <= strlen( trim( $fbMember ))) { // text date ex. 2006-08-03 10:12:18
3043
+ $freebusyPairMember = iCalUtilityFunctions::_date_time_string( $fbMember, 7 );
3044
+ $freebusyPairMember['tz'] = 'Z';
3045
+ }
3046
+ $freebusyPeriod[] = $freebusyPairMember;
3047
+ }
3048
+ $input[] = $freebusyPeriod;
3049
+ }
3050
+ iCalUtilityFunctions::_setMval( $this->freebusy, $input, $params, FALSE, $index );
3051
+ return TRUE;
3052
+ }
3053
+ /*********************************************************************************/
3054
+ /**
3055
+ * Property Name: GEO
3056
+ */
3057
+ /**
3058
+ * creates formatted output for calendar component property geo
3059
+ *
3060
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3061
+ * @since 2.4.8 - 2008-10-21
3062
+ * @return string
3063
+ */
3064
+ function createGeo() {
3065
+ if( empty( $this->geo )) return FALSE;
3066
+ if( empty( $this->geo['value'] ))
3067
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'GEO' ) : FALSE;
3068
+ $attributes = $this->_createParams( $this->geo['params'] );
3069
+ $content = null;
3070
+ $content .= number_format( (float) $this->geo['value']['latitude'], 6, '.', '');
3071
+ $content .= ';';
3072
+ $content .= number_format( (float) $this->geo['value']['longitude'], 6, '.', '');
3073
+ return $this->_createElement( 'GEO', $attributes, $content );
3074
+ }
3075
+ /**
3076
+ * set calendar component property geo
3077
+ *
3078
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3079
+ * @since 2.4.8 - 2008-11-04
3080
+ * @param float $latitude
3081
+ * @param float $longitude
3082
+ * @param array $params optional
3083
+ * @return bool
3084
+ */
3085
+ function setGeo( $latitude, $longitude, $params=FALSE ) {
3086
+ if( !empty( $latitude ) && !empty( $longitude )) {
3087
+ if( !is_array( $this->geo )) $this->geo = array();
3088
+ $this->geo['value']['latitude'] = $latitude;
3089
+ $this->geo['value']['longitude'] = $longitude;
3090
+ $this->geo['params'] = iCalUtilityFunctions::_setParams( $params );
3091
+ }
3092
+ elseif( $this->getConfig( 'allowEmpty' ))
3093
+ $this->geo = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ) );
3094
+ else
3095
+ return FALSE;
3096
+ return TRUE;
3097
+ }
3098
+ /*********************************************************************************/
3099
+ /**
3100
+ * Property Name: LAST-MODIFIED
3101
+ */
3102
+ /**
3103
+ * creates formatted output for calendar component property last-modified
3104
+ *
3105
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3106
+ * @since 2.4.8 - 2008-10-21
3107
+ * @return string
3108
+ */
3109
+ function createLastModified() {
3110
+ if( empty( $this->lastmodified )) return FALSE;
3111
+ $attributes = $this->_createParams( $this->lastmodified['params'] );
3112
+ $formatted = iCalUtilityFunctions::_format_date_time( $this->lastmodified['value'], 7 );
3113
+ return $this->_createElement( 'LAST-MODIFIED', $attributes, $formatted );
3114
+ }
3115
+ /**
3116
+ * set calendar component property completed
3117
+ *
3118
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3119
+ * @since 2.4.8 - 2008-10-23
3120
+ * @param mixed $year optional
3121
+ * @param mixed $month optional
3122
+ * @param int $day optional
3123
+ * @param int $hour optional
3124
+ * @param int $min optional
3125
+ * @param int $sec optional
3126
+ * @param array $params optional
3127
+ * @return boll
3128
+ */
3129
+ function setLastModified( $year=FALSE, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $params=FALSE ) {
3130
+ if( empty( $year ))
3131
+ $year = date('Ymd\THis', mktime( date( 'H' ), date( 'i' ), date( 's' ) - date( 'Z'), date( 'm' ), date( 'd' ), date( 'Y' )));
3132
+ $this->lastmodified = iCalUtilityFunctions::_setDate2( $year, $month, $day, $hour, $min, $sec, $params );
3133
+ return TRUE;
3134
+ }
3135
+ /*********************************************************************************/
3136
+ /**
3137
+ * Property Name: LOCATION
3138
+ */
3139
+ /**
3140
+ * creates formatted output for calendar component property location
3141
+ *
3142
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3143
+ * @since 2.4.8 - 2008-10-22
3144
+ * @return string
3145
+ */
3146
+ function createLocation() {
3147
+ if( empty( $this->location )) return FALSE;
3148
+ if( empty( $this->location['value'] ))
3149
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'LOCATION' ) : FALSE;
3150
+ $attributes = $this->_createParams( $this->location['params'], array( 'ALTREP', 'LANGUAGE' ));
3151
+ $content = $this->_strrep( $this->location['value'] );
3152
+ return $this->_createElement( 'LOCATION', $attributes, $content );
3153
+ }
3154
+ /**
3155
+ * set calendar component property location
3156
+ '
3157
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3158
+ * @since 2.4.8 - 2008-11-04
3159
+ * @param string $value
3160
+ * @param array params optional
3161
+ * @return bool
3162
+ */
3163
+ function setLocation( $value, $params=FALSE ) {
3164
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3165
+ $this->location = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3166
+ return TRUE;
3167
+ }
3168
+ /*********************************************************************************/
3169
+ /**
3170
+ * Property Name: ORGANIZER
3171
+ */
3172
+ /**
3173
+ * creates formatted output for calendar component property organizer
3174
+ *
3175
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3176
+ * @since 2.6.33 - 2010-12-17
3177
+ * @return string
3178
+ */
3179
+ function createOrganizer() {
3180
+ if( empty( $this->organizer )) return FALSE;
3181
+ if( empty( $this->organizer['value'] ))
3182
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'ORGANIZER' ) : FALSE;
3183
+ $attributes = $this->_createParams( $this->organizer['params']
3184
+ , array( 'CN', 'DIR', 'SENT-BY', 'LANGUAGE' ));
3185
+ return $this->_createElement( 'ORGANIZER', $attributes, $this->organizer['value'] );
3186
+ }
3187
+ /**
3188
+ * set calendar component property organizer
3189
+ *
3190
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3191
+ * @since 2.6.27 - 2010-11-29
3192
+ * @param string $value
3193
+ * @param array params optional
3194
+ * @return bool
3195
+ */
3196
+ function setOrganizer( $value, $params=FALSE ) {
3197
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3198
+ if( FALSE === ( $pos = strpos( substr( $value, 0, 9 ), ':' )))
3199
+ $value = 'MAILTO:'.$value;
3200
+ else
3201
+ $value = strtolower( substr( $value, 0, $pos )).substr( $value, $pos );
3202
+ $value = str_replace( 'mailto:', 'MAILTO:', $value );
3203
+ $this->organizer = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3204
+ if( isset( $this->organizer['params']['SENT-BY'] )){
3205
+ if( 'mailto:' !== strtolower( substr( $this->organizer['params']['SENT-BY'], 0, 7 )))
3206
+ $this->organizer['params']['SENT-BY'] = 'MAILTO:'.$this->organizer['params']['SENT-BY'];
3207
+ else
3208
+ $this->organizer['params']['SENT-BY'] = 'MAILTO:'.substr( $this->organizer['params']['SENT-BY'], 7 );
3209
+ }
3210
+ return TRUE;
3211
+ }
3212
+ /*********************************************************************************/
3213
+ /**
3214
+ * Property Name: PERCENT-COMPLETE
3215
+ */
3216
+ /**
3217
+ * creates formatted output for calendar component property percent-complete
3218
+ *
3219
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3220
+ * @since 2.9.3 - 2011-05-14
3221
+ * @return string
3222
+ */
3223
+ function createPercentComplete() {
3224
+ if( !isset($this->percentcomplete) || ( empty( $this->percentcomplete ) && !is_numeric( $this->percentcomplete ))) return FALSE;
3225
+ if( !isset( $this->percentcomplete['value'] ) || ( empty( $this->percentcomplete['value'] ) && !is_numeric( $this->percentcomplete['value'] )))
3226
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PERCENT-COMPLETE' ) : FALSE;
3227
+ $attributes = $this->_createParams( $this->percentcomplete['params'] );
3228
+ return $this->_createElement( 'PERCENT-COMPLETE', $attributes, $this->percentcomplete['value'] );
3229
+ }
3230
+ /**
3231
+ * set calendar component property percent-complete
3232
+ *
3233
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3234
+ * @since 2.9.3 - 2011-05-14
3235
+ * @param int $value
3236
+ * @param array $params optional
3237
+ * @return bool
3238
+ */
3239
+ function setPercentComplete( $value, $params=FALSE ) {
3240
+ if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3241
+ $this->percentcomplete = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3242
+ return TRUE;
3243
+ }
3244
+ /*********************************************************************************/
3245
+ /**
3246
+ * Property Name: PRIORITY
3247
+ */
3248
+ /**
3249
+ * creates formatted output for calendar component property priority
3250
+ *
3251
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3252
+ * @since 2.9.3 - 2011-05-14
3253
+ * @return string
3254
+ */
3255
+ function createPriority() {
3256
+ if( !isset($this->priority) || ( empty( $this->priority ) && !is_numeric( $this->priority ))) return FALSE;
3257
+ if( !isset( $this->priority['value'] ) || ( empty( $this->priority['value'] ) && !is_numeric( $this->priority['value'] )))
3258
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'PRIORITY' ) : FALSE;
3259
+ $attributes = $this->_createParams( $this->priority['params'] );
3260
+ return $this->_createElement( 'PRIORITY', $attributes, $this->priority['value'] );
3261
+ }
3262
+ /**
3263
+ * set calendar component property priority
3264
+ *
3265
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3266
+ * @since 2.9.3 - 2011-05-14
3267
+ * @param int $value
3268
+ * @param array $params optional
3269
+ * @return bool
3270
+ */
3271
+ function setPriority( $value, $params=FALSE ) {
3272
+ if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3273
+ $this->priority = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3274
+ return TRUE;
3275
+ }
3276
+ /*********************************************************************************/
3277
+ /**
3278
+ * Property Name: RDATE
3279
+ */
3280
+ /**
3281
+ * creates formatted output for calendar component property rdate
3282
+ *
3283
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3284
+ * @since 2.4.16 - 2008-10-26
3285
+ * @return string
3286
+ */
3287
+ function createRdate() {
3288
+ if( empty( $this->rdate )) return FALSE;
3289
+ $utctime = ( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) ? TRUE : FALSE;
3290
+ $output = null;
3291
+ if( $utctime )
3292
+ unset( $this->rdate['params']['TZID'] );
3293
+ foreach( $this->rdate as $theRdate ) {
3294
+ if( empty( $theRdate['value'] )) {
3295
+ if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RDATE' );
3296
+ continue;
3297
+ }
3298
+ if( $utctime )
3299
+ unset( $theRdate['params']['TZID'] );
3300
+ $attributes = $this->_createParams( $theRdate['params'] );
3301
+ $cnt = count( $theRdate['value'] );
3302
+ $content = null;
3303
+ $rno = 1;
3304
+ foreach( $theRdate['value'] as $rpix => $rdatePart ) {
3305
+ $contentPart = null;
3306
+ if( is_array( $rdatePart ) &&
3307
+ isset( $theRdate['params']['VALUE'] ) && ( 'PERIOD' == $theRdate['params']['VALUE'] )) { // PERIOD
3308
+ if( $utctime )
3309
+ unset( $rdatePart[0]['tz'] );
3310
+ $formatted = iCalUtilityFunctions::_format_date_time( $rdatePart[0]); // PERIOD part 1
3311
+ if( $utctime || !empty( $theRdate['params']['TZID'] ))
3312
+ $formatted = str_replace( 'Z', '', $formatted);
3313
+ if( 0 < $rpix ) {
3314
+ if( !empty( $rdatePart[0]['tz'] ) && iCalUtilityFunctions::_isOffset( $rdatePart[0]['tz'] )) {
3315
+ if( 'Z' != substr( $formatted, -1 )) $formatted .= 'Z';
3316
+ }
3317
+ else
3318
+ $formatted = str_replace( 'Z', '', $formatted );
3319
+ }
3320
+ $contentPart .= $formatted;
3321
+ $contentPart .= '/';
3322
+ $cnt2 = count( $rdatePart[1]);
3323
+ if( array_key_exists( 'year', $rdatePart[1] )) {
3324
+ if( array_key_exists( 'hour', $rdatePart[1] ))
3325
+ $cnt2 = 7; // date-time
3326
+ else
3327
+ $cnt2 = 3; // date
3328
+ }
3329
+ elseif( array_key_exists( 'week', $rdatePart[1] )) // duration
3330
+ $cnt2 = 5;
3331
+ if(( 7 == $cnt2 ) && // period= -> date-time
3332
+ isset( $rdatePart[1]['year'] ) &&
3333
+ isset( $rdatePart[1]['month'] ) &&
3334
+ isset( $rdatePart[1]['day'] )) {
3335
+ if( $utctime )
3336
+ unset( $rdatePart[1]['tz'] );
3337
+ $formatted = iCalUtilityFunctions::_format_date_time( $rdatePart[1] ); // PERIOD part 2
3338
+ if( $utctime || !empty( $theRdate['params']['TZID'] ))
3339
+ $formatted = str_replace( 'Z', '', $formatted);
3340
+ if( !empty( $rdatePart[0]['tz'] ) && iCalUtilityFunctions::_isOffset( $rdatePart[0]['tz'] )) {
3341
+ if( 'Z' != substr( $formatted, -1 )) $formatted .= 'Z';
3342
+ }
3343
+ else
3344
+ $formatted = str_replace( 'Z', '', $formatted );
3345
+ $contentPart .= $formatted;
3346
+ }
3347
+ else { // period= -> dur-time
3348
+ $contentPart .= iCalUtilityFunctions::_format_duration( $rdatePart[1] );
3349
+ }
3350
+ } // PERIOD end
3351
+ else { // SINGLE date start
3352
+ if( $utctime )
3353
+ unset( $rdatePart['tz'] );
3354
+ $formatted = iCalUtilityFunctions::_format_date_time( $rdatePart);
3355
+ if( $utctime || !empty( $theRdate['params']['TZID'] ))
3356
+ $formatted = str_replace( 'Z', '', $formatted);
3357
+ if( !$utctime && ( 0 < $rpix )) {
3358
+ if( !empty( $theRdate['value'][0]['tz'] ) && iCalUtilityFunctions::_isOffset( $theRdate['value'][0]['tz'] )) {
3359
+ if( 'Z' != substr( $formatted, -1 ))
3360
+ $formatted .= 'Z';
3361
+ }
3362
+ else
3363
+ $formatted = str_replace( 'Z', '', $formatted );
3364
+ }
3365
+ $contentPart .= $formatted;
3366
+ }
3367
+ $content .= $contentPart;
3368
+ if( $rno < $cnt )
3369
+ $content .= ',';
3370
+ $rno++;
3371
+ }
3372
+ $output .= $this->_createElement( 'RDATE', $attributes, $content );
3373
+ }
3374
+ return $output;
3375
+ }
3376
+ /**
3377
+ * set calendar component property rdate
3378
+ *
3379
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3380
+ * @since 2.5.1 - 2008-11-07
3381
+ * @param array $rdates
3382
+ * @param array $params, optional
3383
+ * @param integer $index, optional
3384
+ * @return bool
3385
+ */
3386
+ function setRdate( $rdates, $params=FALSE, $index=FALSE ) {
3387
+ if( empty( $rdates )) {
3388
+ if( $this->getConfig( 'allowEmpty' )) {
3389
+ iCalUtilityFunctions::_setMval( $this->rdate, null, $params, FALSE, $index );
3390
+ return TRUE;
3391
+ }
3392
+ else
3393
+ return FALSE;
3394
+ }
3395
+ $input = array( 'params' => iCalUtilityFunctions::_setParams( $params, array( 'VALUE' => 'DATE-TIME' )));
3396
+ if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' ))) {
3397
+ unset( $input['params']['TZID'] );
3398
+ $input['params']['VALUE'] = 'DATE-TIME';
3399
+ }
3400
+ /* check if PERIOD, if not set */
3401
+ if((!isset( $input['params']['VALUE'] ) || !in_array( $input['params']['VALUE'], array( 'DATE', 'PERIOD' ))) &&
3402
+ isset( $rdates[0] ) && is_array( $rdates[0] ) && ( 2 == count( $rdates[0] )) &&
3403
+ isset( $rdates[0][0] ) && isset( $rdates[0][1] ) && !isset( $rdates[0]['timestamp'] ) &&
3404
+ (( is_array( $rdates[0][0] ) && ( isset( $rdates[0][0]['timestamp'] ) ||
3405
+ iCalUtilityFunctions::_isArrayDate( $rdates[0][0] ))) ||
3406
+ ( is_string( $rdates[0][0] ) && ( 8 <= strlen( trim( $rdates[0][0] ))))) &&
3407
+ ( is_array( $rdates[0][1] ) || ( is_string( $rdates[0][1] ) && ( 3 <= strlen( trim( $rdates[0][1] ))))))
3408
+ $input['params']['VALUE'] = 'PERIOD';
3409
+ /* check 1:st date, upd. $parno (opt) and save ev. timezone **/
3410
+ $date = reset( $rdates );
3411
+ if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) // PERIOD
3412
+ $date = reset( $date );
3413
+ iCalUtilityFunctions::_chkdatecfg( $date, $parno, $input['params'] );
3414
+ if( in_array( $this->objName, array( 'vtimezone', 'standard', 'daylight' )))
3415
+ unset( $input['params']['TZID'] );
3416
+ iCalUtilityFunctions::_existRem( $input['params'], 'VALUE', 'DATE-TIME' ); // remove default
3417
+ foreach( $rdates as $rpix => $theRdate ) {
3418
+ $inputa = null;
3419
+ if( is_array( $theRdate )) {
3420
+ if( isset( $input['params']['VALUE'] ) && ( 'PERIOD' == $input['params']['VALUE'] )) { // PERIOD
3421
+ foreach( $theRdate as $rix => $rPeriod ) {
3422
+ if( is_array( $rPeriod )) {
3423
+ if( iCalUtilityFunctions::_isArrayTimestampDate( $rPeriod )) // timestamp
3424
+ $inputab = ( isset( $rPeriod['tz'] )) ? iCalUtilityFunctions::_timestamp2date( $rPeriod, $parno ) : iCalUtilityFunctions::_timestamp2date( $rPeriod, 6 );
3425
+ elseif( iCalUtilityFunctions::_isArrayDate( $rPeriod ))
3426
+ $inputab = ( 3 < count ( $rPeriod )) ? iCalUtilityFunctions::_date_time_array( $rPeriod, $parno ) : iCalUtilityFunctions::_date_time_array( $rPeriod, 6 );
3427
+ elseif (( 1 == count( $rPeriod )) && ( 8 <= strlen( reset( $rPeriod )))) // text-date
3428
+ $inputab = iCalUtilityFunctions::_date_time_string( reset( $rPeriod ), $parno );
3429
+ else // array format duration
3430
+ $inputab = iCalUtilityFunctions::_duration_array( $rPeriod );
3431
+ }
3432
+ elseif(( 3 <= strlen( trim( $rPeriod ))) && // string format duration
3433
+ ( in_array( $rPeriod[0], array( 'P', '+', '-' )))) {
3434
+ if( 'P' != $rPeriod[0] )
3435
+ $rPeriod = substr( $rPeriod, 1 );
3436
+ $inputab = iCalUtilityFunctions::_duration_string( $rPeriod );
3437
+ }
3438
+ elseif( 8 <= strlen( trim( $rPeriod ))) // text date ex. 2006-08-03 10:12:18
3439
+ $inputab = iCalUtilityFunctions::_date_time_string( $rPeriod, $parno );
3440
+ if( isset( $input['params']['TZID'] ) ||
3441
+ ( isset( $inputab['tz'] ) && !iCalUtilityFunctions::_isOffset( $inputab['tz'] )) ||
3442
+ ( isset( $inputa[0] ) && ( !isset( $inputa[0]['tz'] ))) ||
3443
+ ( isset( $inputa[0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $inputa[0]['tz'] )))
3444
+ unset( $inputab['tz'] );
3445
+ $inputa[] = $inputab;
3446
+ }
3447
+ } // PERIOD end
3448
+ elseif ( iCalUtilityFunctions::_isArrayTimestampDate( $theRdate )) // timestamp
3449
+ $inputa = iCalUtilityFunctions::_timestamp2date( $theRdate, $parno );
3450
+ else // date[-time]
3451
+ $inputa = iCalUtilityFunctions::_date_time_array( $theRdate, $parno );
3452
+ }
3453
+ elseif( 8 <= strlen( trim( $theRdate ))) // text date ex. 2006-08-03 10:12:18
3454
+ $inputa = iCalUtilityFunctions::_date_time_string( $theRdate, $parno );
3455
+ if( !isset( $input['params']['VALUE'] ) || ( 'PERIOD' != $input['params']['VALUE'] )) { // no PERIOD
3456
+ if( 3 == $parno )
3457
+ unset( $inputa['hour'], $inputa['min'], $inputa['sec'], $inputa['tz'] );
3458
+ elseif( isset( $inputa['tz'] ))
3459
+ $inputa['tz'] = (string) $inputa['tz'];
3460
+ if( isset( $input['params']['TZID'] ) ||
3461
+ ( isset( $inputa['tz'] ) && !iCalUtilityFunctions::_isOffset( $inputa['tz'] )) ||
3462
+ ( isset( $input['value'][0] ) && ( !isset( $input['value'][0]['tz'] ))) ||
3463
+ ( isset( $input['value'][0]['tz'] ) && !iCalUtilityFunctions::_isOffset( $input['value'][0]['tz'] )))
3464
+ unset( $inputa['tz'] );
3465
+ }
3466
+ $input['value'][] = $inputa;
3467
+ }
3468
+ if( 3 == $parno ) {
3469
+ $input['params']['VALUE'] = 'DATE';
3470
+ unset( $input['params']['TZID'] );
3471
+ }
3472
+ iCalUtilityFunctions::_setMval( $this->rdate, $input['value'], $input['params'], FALSE, $index );
3473
+ return TRUE;
3474
+ }
3475
+ /*********************************************************************************/
3476
+ /**
3477
+ * Property Name: RECURRENCE-ID
3478
+ */
3479
+ /**
3480
+ * creates formatted output for calendar component property recurrence-id
3481
+ *
3482
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3483
+ * @since 2.9.6 - 2011-05-15
3484
+ * @return string
3485
+ */
3486
+ function createRecurrenceid() {
3487
+ if( empty( $this->recurrenceid )) return FALSE;
3488
+ if( empty( $this->recurrenceid['value'] ))
3489
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'RECURRENCE-ID' ) : FALSE;
3490
+ $formatted = iCalUtilityFunctions::_format_date_time( $this->recurrenceid['value'] );
3491
+ if(( FALSE !== ( $tzid = $this->getConfig( 'TZID' ))) &&
3492
+ ( !isset( $this->recurrenceid['params']['VALUE'] ) || ( $this->recurrenceid['params']['VALUE'] != 'DATE' )) &&
3493
+ !isset( $this->recurrenceid['params']['TZID'] ))
3494
+ $this->recurrenceid['params']['TZID'] = $tzid;
3495
+ $attributes = $this->_createParams( $this->recurrenceid['params'] );
3496
+ return $this->_createElement( 'RECURRENCE-ID', $attributes, $formatted );
3497
+ }
3498
+ /**
3499
+ * set calendar component property recurrence-id
3500
+ *
3501
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3502
+ * @since 2.9.6 - 2011-05-15
3503
+ * @param mixed $year
3504
+ * @param mixed $month optional
3505
+ * @param int $day optional
3506
+ * @param int $hour optional
3507
+ * @param int $min optional
3508
+ * @param int $sec optional
3509
+ * @param array $params optional
3510
+ * @return bool
3511
+ */
3512
+ function setRecurrenceid( $year, $month=FALSE, $day=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $tz=FALSE, $params=FALSE ) {
3513
+ if( empty( $year )) {
3514
+ if( $this->getConfig( 'allowEmpty' )) {
3515
+ $this->recurrenceid = array( 'value' => null, 'params' => null );
3516
+ return TRUE;
3517
+ }
3518
+ else
3519
+ return FALSE;
3520
+ }
3521
+ $this->recurrenceid = iCalUtilityFunctions::_setDate( $year, $month, $day, $hour, $min, $sec, $tz, $params, null, null, $this->getConfig( 'TZID' ));
3522
+ return TRUE;
3523
+ }
3524
+ /*********************************************************************************/
3525
+ /**
3526
+ * Property Name: RELATED-TO
3527
+ */
3528
+ /**
3529
+ * creates formatted output for calendar component property related-to
3530
+ *
3531
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3532
+ * @since 2.4.8 - 2008-10-23
3533
+ * @return string
3534
+ */
3535
+ function createRelatedTo() {
3536
+ if( empty( $this->relatedto )) return FALSE;
3537
+ $output = null;
3538
+ foreach( $this->relatedto as $relation ) {
3539
+ if( empty( $relation['value'] )) {
3540
+ if( $this->getConfig( 'allowEmpty' )) $output.= $this->_createElement( 'RELATED-TO', $this->_createParams( $relation['params'] ));
3541
+ continue;
3542
+ }
3543
+ $attributes = $this->_createParams( $relation['params'] );
3544
+ $content = ( 'xcal' != $this->format ) ? '<' : '';
3545
+ $content .= $this->_strrep( $relation['value'] );
3546
+ $content .= ( 'xcal' != $this->format ) ? '>' : '';
3547
+ $output .= $this->_createElement( 'RELATED-TO', $attributes, $content );
3548
+ }
3549
+ return $output;
3550
+ }
3551
+ /**
3552
+ * set calendar component property related-to
3553
+ *
3554
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3555
+ * @since 2.5.1 - 2008-11-07
3556
+ * @param float $relid
3557
+ * @param array $params, optional
3558
+ * @param index $index, optional
3559
+ * @return bool
3560
+ */
3561
+ function setRelatedTo( $value, $params=FALSE, $index=FALSE ) {
3562
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3563
+ if(( '<' == substr( $value, 0, 1 )) && ( '>' == substr( $value, -1 )))
3564
+ $value = substr( $value, 1, ( strlen( $value ) - 2 ));
3565
+ iCalUtilityFunctions::_existRem( $params, 'RELTYPE', 'PARENT', TRUE ); // remove default
3566
+ iCalUtilityFunctions::_setMval( $this->relatedto, $value, $params, FALSE, $index );
3567
+ return TRUE;
3568
+ }
3569
+ /*********************************************************************************/
3570
+ /**
3571
+ * Property Name: REPEAT
3572
+ */
3573
+ /**
3574
+ * creates formatted output for calendar component property repeat
3575
+ *
3576
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3577
+ * @since 2.9.3 - 2011-05-14
3578
+ * @return string
3579
+ */
3580
+ function createRepeat() {
3581
+ if( !isset( $this->repeat ) || ( empty( $this->repeat ) && !is_numeric( $this->repeat ))) return FALSE;
3582
+ if( !isset( $this->repeat['value']) || ( empty( $this->repeat['value'] ) && !is_numeric( $this->repeat['value'] )))
3583
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'REPEAT' ) : FALSE;
3584
+ $attributes = $this->_createParams( $this->repeat['params'] );
3585
+ return $this->_createElement( 'REPEAT', $attributes, $this->repeat['value'] );
3586
+ }
3587
+ /**
3588
+ * set calendar component property transp
3589
+ *
3590
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3591
+ * @since 2.9.3 - 2011-05-14
3592
+ * @param string $value
3593
+ * @param array $params optional
3594
+ * @return void
3595
+ */
3596
+ function setRepeat( $value, $params=FALSE ) {
3597
+ if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3598
+ $this->repeat = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3599
+ return TRUE;
3600
+ }
3601
+ /*********************************************************************************/
3602
+ /**
3603
+ * Property Name: REQUEST-STATUS
3604
+ */
3605
+ /**
3606
+ * creates formatted output for calendar component property request-status
3607
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3608
+ * @since 2.4.8 - 2008-10-23
3609
+ * @return string
3610
+ */
3611
+ function createRequestStatus() {
3612
+ if( empty( $this->requeststatus )) return FALSE;
3613
+ $output = null;
3614
+ foreach( $this->requeststatus as $rstat ) {
3615
+ if( empty( $rstat['value']['statcode'] )) {
3616
+ if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'REQUEST-STATUS' );
3617
+ continue;
3618
+ }
3619
+ $attributes = $this->_createParams( $rstat['params'], array( 'LANGUAGE' ));
3620
+ $content = number_format( (float) $rstat['value']['statcode'], 2, '.', '');
3621
+ $content .= ';'.$this->_strrep( $rstat['value']['text'] );
3622
+ if( isset( $rstat['value']['extdata'] ))
3623
+ $content .= ';'.$this->_strrep( $rstat['value']['extdata'] );
3624
+ $output .= $this->_createElement( 'REQUEST-STATUS', $attributes, $content );
3625
+ }
3626
+ return $output;
3627
+ }
3628
+ /**
3629
+ * set calendar component property request-status
3630
+ *
3631
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3632
+ * @since 2.5.1 - 2008-11-05
3633
+ * @param float $statcode
3634
+ * @param string $text
3635
+ * @param string $extdata, optional
3636
+ * @param array $params, optional
3637
+ * @param integer $index, optional
3638
+ * @return bool
3639
+ */
3640
+ function setRequestStatus( $statcode, $text, $extdata=FALSE, $params=FALSE, $index=FALSE ) {
3641
+ if( empty( $statcode ) || empty( $text )) if( $this->getConfig( 'allowEmpty' )) $statcode = $text = null; else return FALSE;
3642
+ $input = array( 'statcode' => $statcode, 'text' => $text );
3643
+ if( $extdata )
3644
+ $input['extdata'] = $extdata;
3645
+ iCalUtilityFunctions::_setMval( $this->requeststatus, $input, $params, FALSE, $index );
3646
+ return TRUE;
3647
+ }
3648
+ /*********************************************************************************/
3649
+ /**
3650
+ * Property Name: RESOURCES
3651
+ */
3652
+ /**
3653
+ * creates formatted output for calendar component property resources
3654
+ *
3655
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3656
+ * @since 2.4.8 - 2008-10-23
3657
+ * @return string
3658
+ */
3659
+ function createResources() {
3660
+ if( empty( $this->resources )) return FALSE;
3661
+ $output = null;
3662
+ foreach( $this->resources as $resource ) {
3663
+ if( empty( $resource['value'] )) {
3664
+ if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'RESOURCES' );
3665
+ continue;
3666
+ }
3667
+ $attributes = $this->_createParams( $resource['params'], array( 'ALTREP', 'LANGUAGE' ));
3668
+ if( is_array( $resource['value'] )) {
3669
+ foreach( $resource['value'] as $rix => $resourcePart )
3670
+ $resource['value'][$rix] = $this->_strrep( $resourcePart );
3671
+ $content = implode( ',', $resource['value'] );
3672
+ }
3673
+ else
3674
+ $content = $this->_strrep( $resource['value'] );
3675
+ $output .= $this->_createElement( 'RESOURCES', $attributes, $content );
3676
+ }
3677
+ return $output;
3678
+ }
3679
+ /**
3680
+ * set calendar component property recources
3681
+ *
3682
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3683
+ * @since 2.5.1 - 2008-11-05
3684
+ * @param mixed $value
3685
+ * @param array $params, optional
3686
+ * @param integer $index, optional
3687
+ * @return bool
3688
+ */
3689
+ function setResources( $value, $params=FALSE, $index=FALSE ) {
3690
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3691
+ iCalUtilityFunctions::_setMval( $this->resources, $value, $params, FALSE, $index );
3692
+ return TRUE;
3693
+ }
3694
+ /*********************************************************************************/
3695
+ /**
3696
+ * Property Name: RRULE
3697
+ */
3698
+ /**
3699
+ * creates formatted output for calendar component property rrule
3700
+ *
3701
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3702
+ * @since 2.4.8 - 2008-10-21
3703
+ * @return string
3704
+ */
3705
+ function createRrule() {
3706
+ if( empty( $this->rrule )) return FALSE;
3707
+ return $this->_format_recur( 'RRULE', $this->rrule );
3708
+ }
3709
+ /**
3710
+ * set calendar component property rrule
3711
+ *
3712
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3713
+ * @since 2.5.1 - 2008-11-05
3714
+ * @param array $rruleset
3715
+ * @param array $params, optional
3716
+ * @param integer $index, optional
3717
+ * @return void
3718
+ */
3719
+ function setRrule( $rruleset, $params=FALSE, $index=FALSE ) {
3720
+ if( empty( $rruleset )) if( $this->getConfig( 'allowEmpty' )) $rruleset = null; else return FALSE;
3721
+ iCalUtilityFunctions::_setMval( $this->rrule, iCalUtilityFunctions::_setRexrule( $rruleset ), $params, FALSE, $index );
3722
+ return TRUE;
3723
+ }
3724
+ /*********************************************************************************/
3725
+ /**
3726
+ * Property Name: SEQUENCE
3727
+ */
3728
+ /**
3729
+ * creates formatted output for calendar component property sequence
3730
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3731
+ * @since 2.9.3 - 2011-05-14
3732
+ * @return string
3733
+ */
3734
+ function createSequence() {
3735
+ if( !isset( $this->sequence ) || ( empty( $this->sequence ) && !is_numeric( $this->sequence ))) return FALSE;
3736
+ if(( !isset($this->sequence['value'] ) || ( empty( $this->sequence['value'] ) && !is_numeric( $this->sequence['value'] ))) &&
3737
+ ( '0' != $this->sequence['value'] ))
3738
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SEQUENCE' ) : FALSE;
3739
+ $attributes = $this->_createParams( $this->sequence['params'] );
3740
+ return $this->_createElement( 'SEQUENCE', $attributes, $this->sequence['value'] );
3741
+ }
3742
+ /**
3743
+ * set calendar component property sequence
3744
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3745
+ * @since 2.9.3 - 2011-05-14
3746
+ * @param int $value optional
3747
+ * @param array $params optional
3748
+ * @return bool
3749
+ */
3750
+ function setSequence( $value=FALSE, $params=FALSE ) {
3751
+ if(( empty( $value ) && !is_numeric( $value )) && ( '0' != $value ))
3752
+ $value = ( isset( $this->sequence['value'] ) && ( 0 < $this->sequence['value'] )) ? $this->sequence['value'] + 1 : 1;
3753
+ $this->sequence = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3754
+ return TRUE;
3755
+ }
3756
+ /*********************************************************************************/
3757
+ /**
3758
+ * Property Name: STATUS
3759
+ */
3760
+ /**
3761
+ * creates formatted output for calendar component property status
3762
+ *
3763
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3764
+ * @since 2.4.8 - 2008-10-21
3765
+ * @return string
3766
+ */
3767
+ function createStatus() {
3768
+ if( empty( $this->status )) return FALSE;
3769
+ if( empty( $this->status['value'] ))
3770
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'STATUS' ) : FALSE;
3771
+ $attributes = $this->_createParams( $this->status['params'] );
3772
+ return $this->_createElement( 'STATUS', $attributes, $this->status['value'] );
3773
+ }
3774
+ /**
3775
+ * set calendar component property status
3776
+ *
3777
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3778
+ * @since 2.4.8 - 2008-11-04
3779
+ * @param string $value
3780
+ * @param array $params optional
3781
+ * @return bool
3782
+ */
3783
+ function setStatus( $value, $params=FALSE ) {
3784
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3785
+ $this->status = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3786
+ return TRUE;
3787
+ }
3788
+ /*********************************************************************************/
3789
+ /**
3790
+ * Property Name: SUMMARY
3791
+ */
3792
+ /**
3793
+ * creates formatted output for calendar component property summary
3794
+ *
3795
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3796
+ * @since 2.4.8 - 2008-10-21
3797
+ * @return string
3798
+ */
3799
+ function createSummary() {
3800
+ if( empty( $this->summary )) return FALSE;
3801
+ if( empty( $this->summary['value'] ))
3802
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'SUMMARY' ) : FALSE;
3803
+ $attributes = $this->_createParams( $this->summary['params'], array( 'ALTREP', 'LANGUAGE' ));
3804
+ $content = $this->_strrep( $this->summary['value'] );
3805
+ return $this->_createElement( 'SUMMARY', $attributes, $content );
3806
+ }
3807
+ /**
3808
+ * set calendar component property summary
3809
+ *
3810
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3811
+ * @since 2.4.8 - 2008-11-04
3812
+ * @param string $value
3813
+ * @param string $params optional
3814
+ * @return bool
3815
+ */
3816
+ function setSummary( $value, $params=FALSE ) {
3817
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3818
+ $this->summary = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3819
+ return TRUE;
3820
+ }
3821
+ /*********************************************************************************/
3822
+ /**
3823
+ * Property Name: TRANSP
3824
+ */
3825
+ /**
3826
+ * creates formatted output for calendar component property transp
3827
+ *
3828
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3829
+ * @since 2.4.8 - 2008-10-21
3830
+ * @return string
3831
+ */
3832
+ function createTransp() {
3833
+ if( empty( $this->transp )) return FALSE;
3834
+ if( empty( $this->transp['value'] ))
3835
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRANSP' ) : FALSE;
3836
+ $attributes = $this->_createParams( $this->transp['params'] );
3837
+ return $this->_createElement( 'TRANSP', $attributes, $this->transp['value'] );
3838
+ }
3839
+ /**
3840
+ * set calendar component property transp
3841
+ *
3842
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3843
+ * @since 2.4.8 - 2008-11-04
3844
+ * @param string $value
3845
+ * @param string $params optional
3846
+ * @return bool
3847
+ */
3848
+ function setTransp( $value, $params=FALSE ) {
3849
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
3850
+ $this->transp = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
3851
+ return TRUE;
3852
+ }
3853
+ /*********************************************************************************/
3854
+ /**
3855
+ * Property Name: TRIGGER
3856
+ */
3857
+ /**
3858
+ * creates formatted output for calendar component property trigger
3859
+ *
3860
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3861
+ * @since 2.4.16 - 2008-10-21
3862
+ * @return string
3863
+ */
3864
+ function createTrigger() {
3865
+ if( empty( $this->trigger )) return FALSE;
3866
+ if( empty( $this->trigger['value'] ))
3867
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TRIGGER' ) : FALSE;
3868
+ $content = $attributes = null;
3869
+ if( isset( $this->trigger['value']['year'] ) &&
3870
+ isset( $this->trigger['value']['month'] ) &&
3871
+ isset( $this->trigger['value']['day'] ))
3872
+ $content .= iCalUtilityFunctions::_format_date_time( $this->trigger['value'] );
3873
+ else {
3874
+ if( TRUE !== $this->trigger['value']['relatedStart'] )
3875
+ $attributes .= $this->intAttrDelimiter.'RELATED=END';
3876
+ if( $this->trigger['value']['before'] )
3877
+ $content .= '-';
3878
+ $content .= iCalUtilityFunctions::_format_duration( $this->trigger['value'] );
3879
+ }
3880
+ $attributes .= $this->_createParams( $this->trigger['params'] );
3881
+ return $this->_createElement( 'TRIGGER', $attributes, $content );
3882
+ }
3883
+ /**
3884
+ * set calendar component property trigger
3885
+ *
3886
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
3887
+ * @since 2.9.9 - 2011-06-17
3888
+ * @param mixed $year
3889
+ * @param mixed $month optional
3890
+ * @param int $day optional
3891
+ * @param int $week optional
3892
+ * @param int $hour optional
3893
+ * @param int $min optional
3894
+ * @param int $sec optional
3895
+ * @param bool $relatedStart optional
3896
+ * @param bool $before optional
3897
+ * @param array $params optional
3898
+ * @return bool
3899
+ */
3900
+ function setTrigger( $year, $month=null, $day=null, $week=FALSE, $hour=FALSE, $min=FALSE, $sec=FALSE, $relatedStart=TRUE, $before=TRUE, $params=FALSE ) {
3901
+ if( empty( $year ) && empty( $month ) && empty( $day ) && empty( $week ) && empty( $hour ) && empty( $min ) && empty( $sec ))
3902
+ if( $this->getConfig( 'allowEmpty' )) {
3903
+ $this->trigger = array( 'value' => null, 'params' => iCalUtilityFunctions::_setParams( $params ) );
3904
+ return TRUE;
3905
+ }
3906
+ else
3907
+ return FALSE;
3908
+ if( iCalUtilityFunctions::_isArrayTimestampDate( $year )) { // timestamp
3909
+ $params = iCalUtilityFunctions::_setParams( $month );
3910
+ $date = iCalUtilityFunctions::_timestamp2date( $year, 7 );
3911
+ foreach( $date as $k => $v )
3912
+ $$k = $v;
3913
+ }
3914
+ elseif( is_array( $year ) && ( is_array( $month ) || empty( $month ))) {
3915
+ $params = iCalUtilityFunctions::_setParams( $month );
3916
+ if(!(array_key_exists( 'year', $year ) && // exclude date-time
3917
+ array_key_exists( 'month', $year ) &&
3918
+ array_key_exists( 'day', $year ))) { // when this must be a duration
3919
+ if( isset( $params['RELATED'] ) && ( 'END' == strtoupper( $params['RELATED'] )))
3920
+ $relatedStart = FALSE;
3921
+ else
3922
+ $relatedStart = ( array_key_exists( 'relatedStart', $year ) && ( TRUE !== $year['relatedStart'] )) ? FALSE : TRUE;
3923
+ $before = ( array_key_exists( 'before', $year ) && ( TRUE !== $year['before'] )) ? FALSE : TRUE;
3924
+ }
3925
+ $SSYY = ( array_key_exists( 'year', $year )) ? $year['year'] : null;
3926
+ $month = ( array_key_exists( 'month', $year )) ? $year['month'] : null;
3927
+ $day = ( array_key_exists( 'day', $year )) ? $year['day'] : null;
3928
+ $week = ( array_key_exists( 'week', $year )) ? $year['week'] : null;
3929
+ $hour = ( array_key_exists( 'hour', $year )) ? $year['hour'] : 0; //null;
3930
+ $min = ( array_key_exists( 'min', $year )) ? $year['min'] : 0; //null;
3931
+ $sec = ( array_key_exists( 'sec', $year )) ? $year['sec'] : 0; //null;
3932
+ $year = $SSYY;
3933
+ }
3934
+ elseif(is_string( $year ) && ( is_array( $month ) || empty( $month ))) { // duration or date in a string
3935
+ $params = iCalUtilityFunctions::_setParams( $month );
3936
+ if( in_array( $year[0], array( 'P', '+', '-' ))) { // duration
3937
+ $relatedStart = ( isset( $params['RELATED'] ) && ( 'END' == strtoupper( $params['RELATED'] ))) ? FALSE : TRUE;
3938
+ $before = ( '-' == $year[0] ) ? TRUE : FALSE;
3939
+ if( 'P' != $year[0] )
3940
+ $year = substr( $year, 1 );
3941
+ $date = iCalUtilityFunctions::_duration_string( $year);
3942
+ }
3943
+ else // date
3944
+ $date = iCalUtilityFunctions::_date_time_string( $year, 7 );
3945
+ unset( $year, $month, $day );
3946
+ if( empty( $date ))
3947
+ $sec = 0;
3948
+ else
3949
+ foreach( $date as $k => $v )
3950
+ $$k = $v;
3951
+ }
3952
+ else // single values in function input parameters
3953
+ $params = iCalUtilityFunctions::_setParams( $params );
3954
+ if( !empty( $year ) && !empty( $month ) && !empty( $day )) { // date
3955
+ $params['VALUE'] = 'DATE-TIME';
3956
+ $hour = ( $hour ) ? $hour : 0;
3957
+ $min = ( $min ) ? $min : 0;
3958
+ $sec = ( $sec ) ? $sec : 0;
3959
+ $this->trigger = array( 'params' => $params );
3960
+ $this->trigger['value'] = array( 'year' => $year
3961
+ , 'month' => $month
3962
+ , 'day' => $day
3963
+ , 'hour' => $hour
3964
+ , 'min' => $min
3965
+ , 'sec' => $sec
3966
+ , 'tz' => 'Z' );
3967
+ return TRUE;
3968
+ }
3969
+ elseif(( empty( $year ) && empty( $month )) && // duration
3970
+ (( !empty( $week ) || ( 0 == $week )) ||
3971
+ ( !empty( $day ) || ( 0 == $day )) ||
3972
+ ( !empty( $hour ) || ( 0 == $hour )) ||
3973
+ ( !empty( $min ) || ( 0 == $min )) ||
3974
+ ( !empty( $sec ) || ( 0 == $sec )))) {
3975
+ unset( $params['RELATED'] ); // set at output creation (END only)
3976
+ unset( $params['VALUE'] ); // 'DURATION' default
3977
+ $this->trigger = array( 'params' => $params );
3978
+ $this->trigger['value'] = array();
3979
+ if( !empty( $week )) $this->trigger['value']['week'] = $week;
3980
+ if( !empty( $day )) $this->trigger['value']['day'] = $day;
3981
+ if( !empty( $hour )) $this->trigger['value']['hour'] = $hour;
3982
+ if( !empty( $min )) $this->trigger['value']['min'] = $min;
3983
+ if( !empty( $sec )) $this->trigger['value']['sec'] = $sec;
3984
+ if( empty( $this->trigger['value'] )) {
3985
+ $this->trigger['value']['sec'] = 0;
3986
+ $before = FALSE;
3987
+ }
3988
+ $relatedStart = ( FALSE !== $relatedStart ) ? TRUE : FALSE;
3989
+ $before = ( FALSE !== $before ) ? TRUE : FALSE;
3990
+ $this->trigger['value']['relatedStart'] = $relatedStart;
3991
+ $this->trigger['value']['before'] = $before;
3992
+ return TRUE;
3993
+ }
3994
+ return FALSE;
3995
+ }
3996
+ /*********************************************************************************/
3997
+ /**
3998
+ * Property Name: TZID
3999
+ */
4000
+ /**
4001
+ * creates formatted output for calendar component property tzid
4002
+ *
4003
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4004
+ * @since 2.4.8 - 2008-10-21
4005
+ * @return string
4006
+ */
4007
+ function createTzid() {
4008
+ if( empty( $this->tzid )) return FALSE;
4009
+ if( empty( $this->tzid['value'] ))
4010
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZID' ) : FALSE;
4011
+ $attributes = $this->_createParams( $this->tzid['params'] );
4012
+ return $this->_createElement( 'TZID', $attributes, $this->_strrep( $this->tzid['value'] ));
4013
+ }
4014
+ /**
4015
+ * set calendar component property tzid
4016
+ *
4017
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4018
+ * @since 2.4.8 - 2008-11-04
4019
+ * @param string $value
4020
+ * @param array $params optional
4021
+ * @return bool
4022
+ */
4023
+ function setTzid( $value, $params=FALSE ) {
4024
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
4025
+ $this->tzid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
4026
+ return TRUE;
4027
+ }
4028
+ /*********************************************************************************/
4029
+ /**
4030
+ * .. .
4031
+ * Property Name: TZNAME
4032
+ */
4033
+ /**
4034
+ * creates formatted output for calendar component property tzname
4035
+ *
4036
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4037
+ * @since 2.4.8 - 2008-10-21
4038
+ * @return string
4039
+ */
4040
+ function createTzname() {
4041
+ if( empty( $this->tzname )) return FALSE;
4042
+ $output = null;
4043
+ foreach( $this->tzname as $theName ) {
4044
+ if( !empty( $theName['value'] )) {
4045
+ $attributes = $this->_createParams( $theName['params'], array( 'LANGUAGE' ));
4046
+ $output .= $this->_createElement( 'TZNAME', $attributes, $this->_strrep( $theName['value'] ));
4047
+ }
4048
+ elseif( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( 'TZNAME' );
4049
+ }
4050
+ return $output;
4051
+ }
4052
+ /**
4053
+ * set calendar component property tzname
4054
+ *
4055
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4056
+ * @since 2.5.1 - 2008-11-05
4057
+ * @param string $value
4058
+ * @param string $params, optional
4059
+ * @param integer $index, optional
4060
+ * @return bool
4061
+ */
4062
+ function setTzname( $value, $params=FALSE, $index=FALSE ) {
4063
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
4064
+ iCalUtilityFunctions::_setMval( $this->tzname, $value, $params, FALSE, $index );
4065
+ return TRUE;
4066
+ }
4067
+ /*********************************************************************************/
4068
+ /**
4069
+ * Property Name: TZOFFSETFROM
4070
+ */
4071
+ /**
4072
+ * creates formatted output for calendar component property tzoffsetfrom
4073
+ *
4074
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4075
+ * @since 2.4.8 - 2008-10-21
4076
+ * @return string
4077
+ */
4078
+ function createTzoffsetfrom() {
4079
+ if( empty( $this->tzoffsetfrom )) return FALSE;
4080
+ if( empty( $this->tzoffsetfrom['value'] ))
4081
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETFROM' ) : FALSE;
4082
+ $attributes = $this->_createParams( $this->tzoffsetfrom['params'] );
4083
+ return $this->_createElement( 'TZOFFSETFROM', $attributes, $this->tzoffsetfrom['value'] );
4084
+ }
4085
+ /**
4086
+ * set calendar component property tzoffsetfrom
4087
+ *
4088
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4089
+ * @since 2.4.8 - 2008-11-04
4090
+ * @param string $value
4091
+ * @param string $params optional
4092
+ * @return bool
4093
+ */
4094
+ function setTzoffsetfrom( $value, $params=FALSE ) {
4095
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
4096
+ $this->tzoffsetfrom = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
4097
+ return TRUE;
4098
+ }
4099
+ /*********************************************************************************/
4100
+ /**
4101
+ * Property Name: TZOFFSETTO
4102
+ */
4103
+ /**
4104
+ * creates formatted output for calendar component property tzoffsetto
4105
+ *
4106
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4107
+ * @since 2.4.8 - 2008-10-21
4108
+ * @return string
4109
+ */
4110
+ function createTzoffsetto() {
4111
+ if( empty( $this->tzoffsetto )) return FALSE;
4112
+ if( empty( $this->tzoffsetto['value'] ))
4113
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZOFFSETTO' ) : FALSE;
4114
+ $attributes = $this->_createParams( $this->tzoffsetto['params'] );
4115
+ return $this->_createElement( 'TZOFFSETTO', $attributes, $this->tzoffsetto['value'] );
4116
+ }
4117
+ /**
4118
+ * set calendar component property tzoffsetto
4119
+ *
4120
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4121
+ * @since 2.4.8 - 2008-11-04
4122
+ * @param string $value
4123
+ * @param string $params optional
4124
+ * @return bool
4125
+ */
4126
+ function setTzoffsetto( $value, $params=FALSE ) {
4127
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
4128
+ $this->tzoffsetto = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
4129
+ return TRUE;
4130
+ }
4131
+ /*********************************************************************************/
4132
+ /**
4133
+ * Property Name: TZURL
4134
+ */
4135
+ /**
4136
+ * creates formatted output for calendar component property tzurl
4137
+ *
4138
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4139
+ * @since 2.4.8 - 2008-10-21
4140
+ * @return string
4141
+ */
4142
+ function createTzurl() {
4143
+ if( empty( $this->tzurl )) return FALSE;
4144
+ if( empty( $this->tzurl['value'] ))
4145
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'TZURL' ) : FALSE;
4146
+ $attributes = $this->_createParams( $this->tzurl['params'] );
4147
+ return $this->_createElement( 'TZURL', $attributes, $this->tzurl['value'] );
4148
+ }
4149
+ /**
4150
+ * set calendar component property tzurl
4151
+ *
4152
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4153
+ * @since 2.4.8 - 2008-11-04
4154
+ * @param string $value
4155
+ * @param string $params optional
4156
+ * @return boll
4157
+ */
4158
+ function setTzurl( $value, $params=FALSE ) {
4159
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
4160
+ $this->tzurl = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
4161
+ return TRUE;
4162
+ }
4163
+ /*********************************************************************************/
4164
+ /**
4165
+ * Property Name: UID
4166
+ */
4167
+ /**
4168
+ * creates formatted output for calendar component property uid
4169
+ *
4170
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4171
+ * @since 0.9.7 - 2006-11-20
4172
+ * @return string
4173
+ */
4174
+ function createUid() {
4175
+ if( 0 >= count( $this->uid ))
4176
+ $this->_makeuid();
4177
+ $attributes = $this->_createParams( $this->uid['params'] );
4178
+ return $this->_createElement( 'UID', $attributes, $this->uid['value'] );
4179
+ }
4180
+ /**
4181
+ * create an unique id for this calendar component object instance
4182
+ *
4183
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4184
+ * @since 2.2.7 - 2007-09-04
4185
+ * @return void
4186
+ */
4187
+ function _makeUid() {
4188
+ $date = date('Ymd\THisT');
4189
+ $unique = substr(microtime(), 2, 4);
4190
+ $base = 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPrRsStTuUvVxXuUvVwWzZ1234567890';
4191
+ $start = 0;
4192
+ $end = strlen( $base ) - 1;
4193
+ $length = 6;
4194
+ $str = null;
4195
+ for( $p = 0; $p < $length; $p++ )
4196
+ $unique .= $base{mt_rand( $start, $end )};
4197
+ $this->uid = array( 'params' => null );
4198
+ $this->uid['value'] = $date.'-'.$unique.'@'.$this->getConfig( 'unique_id' );
4199
+ }
4200
+ /**
4201
+ * set calendar component property uid
4202
+ *
4203
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4204
+ * @since 2.4.8 - 2008-11-04
4205
+ * @param string $value
4206
+ * @param string $params optional
4207
+ * @return bool
4208
+ */
4209
+ function setUid( $value, $params=FALSE ) {
4210
+ if( empty( $value )) return FALSE; // no allowEmpty check here !!!!
4211
+ $this->uid = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
4212
+ return TRUE;
4213
+ }
4214
+ /*********************************************************************************/
4215
+ /**
4216
+ * Property Name: URL
4217
+ */
4218
+ /**
4219
+ * creates formatted output for calendar component property url
4220
+ *
4221
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4222
+ * @since 2.4.8 - 2008-10-21
4223
+ * @return string
4224
+ */
4225
+ function createUrl() {
4226
+ if( empty( $this->url )) return FALSE;
4227
+ if( empty( $this->url['value'] ))
4228
+ return ( $this->getConfig( 'allowEmpty' )) ? $this->_createElement( 'URL' ) : FALSE;
4229
+ $attributes = $this->_createParams( $this->url['params'] );
4230
+ return $this->_createElement( 'URL', $attributes, $this->url['value'] );
4231
+ }
4232
+ /**
4233
+ * set calendar component property url
4234
+ *
4235
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4236
+ * @since 2.4.8 - 2008-11-04
4237
+ * @param string $value
4238
+ * @param string $params optional
4239
+ * @return bool
4240
+ */
4241
+ function setUrl( $value, $params=FALSE ) {
4242
+ if( empty( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
4243
+ $this->url = array( 'value' => $value, 'params' => iCalUtilityFunctions::_setParams( $params ));
4244
+ return TRUE;
4245
+ }
4246
+ /*********************************************************************************/
4247
+ /**
4248
+ * Property Name: x-prop
4249
+ */
4250
+ /**
4251
+ * creates formatted output for calendar component property x-prop
4252
+ *
4253
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4254
+ * @since 2.9.3 - 2011-05-14
4255
+ * @return string
4256
+ */
4257
+ function createXprop() {
4258
+ if( empty( $this->xprop )) return FALSE;
4259
+ $output = null;
4260
+ foreach( $this->xprop as $label => $xpropPart ) {
4261
+ if( !isset($xpropPart['value']) || ( empty( $xpropPart['value'] ) && !is_numeric( $xpropPart['value'] ))) {
4262
+ if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( $label );
4263
+ continue;
4264
+ }
4265
+ $attributes = $this->_createParams( $xpropPart['params'], array( 'LANGUAGE' ));
4266
+ if( is_array( $xpropPart['value'] )) {
4267
+ foreach( $xpropPart['value'] as $pix => $theXpart )
4268
+ $xpropPart['value'][$pix] = $this->_strrep( $theXpart );
4269
+ $xpropPart['value'] = implode( ',', $xpropPart['value'] );
4270
+ }
4271
+ else
4272
+ $xpropPart['value'] = $this->_strrep( $xpropPart['value'] );
4273
+ $output .= $this->_createElement( $label, $attributes, $xpropPart['value'] );
4274
+ }
4275
+ return $output;
4276
+ }
4277
+ /**
4278
+ * set calendar component property x-prop
4279
+ *
4280
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4281
+ * @since 2.9.3 - 2011-05-14
4282
+ * @param string $label
4283
+ * @param mixed $value
4284
+ * @param array $params optional
4285
+ * @return bool
4286
+ */
4287
+ function setXprop( $label, $value, $params=FALSE ) {
4288
+ if( empty( $label )) return;
4289
+ if( empty( $value ) && !is_numeric( $value )) if( $this->getConfig( 'allowEmpty' )) $value = null; else return FALSE;
4290
+ $xprop = array( 'value' => $value );
4291
+ $xprop['params'] = iCalUtilityFunctions::_setParams( $params );
4292
+ if( !is_array( $this->xprop )) $this->xprop = array();
4293
+ $this->xprop[strtoupper( $label )] = $xprop;
4294
+ return TRUE;
4295
+ }
4296
+ /*********************************************************************************/
4297
+ /*********************************************************************************/
4298
+ /**
4299
+ * create element format parts
4300
+ *
4301
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4302
+ * @since 2.0.6 - 2006-06-20
4303
+ * @return string
4304
+ */
4305
+ function _createFormat() {
4306
+ $objectname = null;
4307
+ switch( $this->format ) {
4308
+ case 'xcal':
4309
+ $objectname = ( isset( $this->timezonetype )) ?
4310
+ strtolower( $this->timezonetype ) : strtolower( $this->objName );
4311
+ $this->componentStart1 = $this->elementStart1 = '<';
4312
+ $this->componentStart2 = $this->elementStart2 = '>';
4313
+ $this->componentEnd1 = $this->elementEnd1 = '</';
4314
+ $this->componentEnd2 = $this->elementEnd2 = '>'.$this->nl;
4315
+ $this->intAttrDelimiter = '<!-- -->';
4316
+ $this->attributeDelimiter = $this->nl;
4317
+ $this->valueInit = null;
4318
+ break;
4319
+ default:
4320
+ $objectname = ( isset( $this->timezonetype )) ?
4321
+ strtoupper( $this->timezonetype ) : strtoupper( $this->objName );
4322
+ $this->componentStart1 = 'BEGIN:';
4323
+ $this->componentStart2 = null;
4324
+ $this->componentEnd1 = 'END:';
4325
+ $this->componentEnd2 = $this->nl;
4326
+ $this->elementStart1 = null;
4327
+ $this->elementStart2 = null;
4328
+ $this->elementEnd1 = null;
4329
+ $this->elementEnd2 = $this->nl;
4330
+ $this->intAttrDelimiter = '<!-- -->';
4331
+ $this->attributeDelimiter = ';';
4332
+ $this->valueInit = ':';
4333
+ break;
4334
+ }
4335
+ return $objectname;
4336
+ }
4337
+ /**
4338
+ * creates formatted output for calendar component property
4339
+ *
4340
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4341
+ * @since 2.6.22 - 2010-12-06
4342
+ * @param string $label property name
4343
+ * @param string $attributes property attributes
4344
+ * @param string $content property content (optional)
4345
+ * @return string
4346
+ */
4347
+ function _createElement( $label, $attributes=null, $content=FALSE ) {
4348
+ switch( $this->format ) {
4349
+ case 'xcal':
4350
+ $label = strtolower( $label );
4351
+ break;
4352
+ default:
4353
+ $label = strtoupper( $label );
4354
+ break;
4355
+ }
4356
+ $output = $this->elementStart1.$label;
4357
+ $categoriesAttrLang = null;
4358
+ $attachInlineBinary = FALSE;
4359
+ $attachfmttype = null;
4360
+ if( !empty( $attributes )) {
4361
+ $attributes = trim( $attributes );
4362
+ if ( 'xcal' == $this->format) {
4363
+ $attributes2 = explode( $this->intAttrDelimiter, $attributes );
4364
+ $attributes = null;
4365
+ foreach( $attributes2 as $attribute ) {
4366
+ $attrKVarr = explode( '=', $attribute );
4367
+ if( empty( $attrKVarr[0] ))
4368
+ continue;
4369
+ if( !isset( $attrKVarr[1] )) {
4370
+ $attrValue = $attrKVarr[0];
4371
+ $attrKey = null;
4372
+ }
4373
+ elseif( 2 == count( $attrKVarr)) {
4374
+ $attrKey = strtolower( $attrKVarr[0] );
4375
+ $attrValue = $attrKVarr[1];
4376
+ }
4377
+ else {
4378
+ $attrKey = strtolower( $attrKVarr[0] );
4379
+ unset( $attrKVarr[0] );
4380
+ $attrValue = implode( '=', $attrKVarr );
4381
+ }
4382
+ if(( 'attach' == $label ) && ( in_array( $attrKey, array( 'fmttype', 'encoding', 'value' )))) {
4383
+ $attachInlineBinary = TRUE;
4384
+ if( 'fmttype' == $attrKey )
4385
+ $attachfmttype = $attrKey.'='.$attrValue;
4386
+ continue;
4387
+ }
4388
+ elseif(( 'categories' == $label ) && ( 'language' == $attrKey ))
4389
+ $categoriesAttrLang = $attrKey.'='.$attrValue;
4390
+ else {
4391
+ $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' ';
4392
+ $attributes .= ( !empty( $attrKey )) ? $attrKey.'=' : null;
4393
+ if(( '"' == substr( $attrValue, 0, 1 )) && ( '"' == substr( $attrValue, -1 ))) {
4394
+ $attrValue = substr( $attrValue, 1, ( strlen( $attrValue ) - 2 ));
4395
+ $attrValue = str_replace( '"', '', $attrValue );
4396
+ }
4397
+ $attributes .= '"'.htmlspecialchars( $attrValue ).'"';
4398
+ }
4399
+ }
4400
+ }
4401
+ else {
4402
+ $attributes = str_replace( $this->intAttrDelimiter, $this->attributeDelimiter, $attributes );
4403
+ }
4404
+ }
4405
+ if(((( 'attach' == $label ) && !$attachInlineBinary ) ||
4406
+ ( in_array( $label, array( 'tzurl', 'url' )))) && ( 'xcal' == $this->format)) {
4407
+ $pos = strrpos($content, "/");
4408
+ $docname = ( $pos !== false) ? substr( $content, (1 - strlen( $content ) + $pos )) : $content;
4409
+ $this->xcaldecl[] = array( 'xmldecl' => 'ENTITY'
4410
+ , 'uri' => $docname
4411
+ , 'ref' => 'SYSTEM'
4412
+ , 'external' => $content
4413
+ , 'type' => 'NDATA'
4414
+ , 'type2' => 'BINERY' );
4415
+ $attributes .= ( empty( $attributes )) ? ' ' : $this->attributeDelimiter.' ';
4416
+ $attributes .= 'uri="'.$docname.'"';
4417
+ $content = null;
4418
+ if( 'attach' == $label ) {
4419
+ $attributes = str_replace( $this->attributeDelimiter, $this->intAttrDelimiter, $attributes );
4420
+ $content = $this->_createElement( 'extref', $attributes, null );
4421
+ $attributes = null;
4422
+ }
4423
+ }
4424
+ elseif(( 'attach' == $label ) && $attachInlineBinary && ( 'xcal' == $this->format)) {
4425
+ $content = $this->nl.$this->_createElement( 'b64bin', $attachfmttype, $content ); // max one attribute
4426
+ }
4427
+ $output .= $attributes;
4428
+ if( !$content && ( '0' != $content )) {
4429
+ switch( $this->format ) {
4430
+ case 'xcal':
4431
+ $output .= ' /';
4432
+ $output .= $this->elementStart2;
4433
+ return $output;
4434
+ break;
4435
+ default:
4436
+ $output .= $this->elementStart2.$this->valueInit;
4437
+ return $this->_size75( $output );
4438
+ break;
4439
+ }
4440
+ }
4441
+ $output .= $this->elementStart2;
4442
+ $output .= $this->valueInit.$content;
4443
+ switch( $this->format ) {
4444
+ case 'xcal':
4445
+ return $output.$this->elementEnd1.$label.$this->elementEnd2;
4446
+ break;
4447
+ default:
4448
+ return $this->_size75( $output );
4449
+ break;
4450
+ }
4451
+ }
4452
+ /**
4453
+ * creates formatted output for calendar component property parameters
4454
+ *
4455
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4456
+ * @since 2.6.33 - 2010-12-18
4457
+ * @param array $params optional
4458
+ * @param array $ctrKeys optional
4459
+ * @return string
4460
+ */
4461
+ function _createParams( $params=array(), $ctrKeys=array() ) {
4462
+ if( !is_array( $params ) || empty( $params ))
4463
+ $params = array();
4464
+ $attrLANG = $attr1 = $attr2 = $lang = null;
4465
+ $CNattrKey = ( in_array( 'CN', $ctrKeys )) ? TRUE : FALSE ;
4466
+ $LANGattrKey = ( in_array( 'LANGUAGE', $ctrKeys )) ? TRUE : FALSE ;
4467
+ $CNattrExist = $LANGattrExist = FALSE;
4468
+ $xparams = array();
4469
+ foreach( $params as $paramKey => $paramValue ) {
4470
+ if( ctype_digit( (string) $paramKey )) {
4471
+ $xparams[] = $paramValue;
4472
+ continue;
4473
+ }
4474
+ $paramKey = strtoupper( $paramKey );
4475
+ if( !in_array( $paramKey, array( 'ALTREP', 'CN', 'DIR', 'ENCODING', 'FMTTYPE', 'LANGUAGE', 'RANGE', 'RELTYPE', 'SENT-BY', 'TZID', 'VALUE' )))
4476
+ $xparams[$paramKey] = $paramValue;
4477
+ else
4478
+ $params[$paramKey] = $paramValue;
4479
+ }
4480
+ ksort( $xparams, SORT_STRING );
4481
+ foreach( $xparams as $paramKey => $paramValue ) {
4482
+ if( ctype_digit( (string) $paramKey ))
4483
+ $attr2 .= $this->intAttrDelimiter.$paramValue;
4484
+ else
4485
+ $attr2 .= $this->intAttrDelimiter."$paramKey=$paramValue";
4486
+ }
4487
+ if( isset( $params['FMTTYPE'] ) && !in_array( 'FMTTYPE', $ctrKeys )) {
4488
+ $attr1 .= $this->intAttrDelimiter.'FMTTYPE='.$params['FMTTYPE'].$attr2;
4489
+ $attr2 = null;
4490
+ }
4491
+ if( isset( $params['ENCODING'] ) && !in_array( 'ENCODING', $ctrKeys )) {
4492
+ if( !empty( $attr2 )) {
4493
+ $attr1 .= $attr2;
4494
+ $attr2 = null;
4495
+ }
4496
+ $attr1 .= $this->intAttrDelimiter.'ENCODING='.$params['ENCODING'];
4497
+ }
4498
+ if( isset( $params['VALUE'] ) && !in_array( 'VALUE', $ctrKeys ))
4499
+ $attr1 .= $this->intAttrDelimiter.'VALUE='.$params['VALUE'];
4500
+ if( isset( $params['TZID'] ) && !in_array( 'TZID', $ctrKeys ))
4501
+ $attr1 .= $this->intAttrDelimiter.'TZID='.$params['TZID'];
4502
+ if( isset( $params['RANGE'] ) && !in_array( 'RANGE', $ctrKeys ))
4503
+ $attr1 .= $this->intAttrDelimiter.'RANGE='.$params['RANGE'];
4504
+ if( isset( $params['RELTYPE'] ) && !in_array( 'RELTYPE', $ctrKeys ))
4505
+ $attr1 .= $this->intAttrDelimiter.'RELTYPE='.$params['RELTYPE'];
4506
+ if( isset( $params['CN'] ) && $CNattrKey ) {
4507
+ $attr1 = $this->intAttrDelimiter.'CN="'.$params['CN'].'"';
4508
+ $CNattrExist = TRUE;
4509
+ }
4510
+ if( isset( $params['DIR'] ) && in_array( 'DIR', $ctrKeys ))
4511
+ $attr1 .= $this->intAttrDelimiter.'DIR="'.$params['DIR'].'"';
4512
+ if( isset( $params['SENT-BY'] ) && in_array( 'SENT-BY', $ctrKeys ))
4513
+ $attr1 .= $this->intAttrDelimiter.'SENT-BY="'.$params['SENT-BY'].'"';
4514
+ if( isset( $params['ALTREP'] ) && in_array( 'ALTREP', $ctrKeys ))
4515
+ $attr1 .= $this->intAttrDelimiter.'ALTREP="'.$params['ALTREP'].'"';
4516
+ if( isset( $params['LANGUAGE'] ) && $LANGattrKey ) {
4517
+ $attrLANG .= $this->intAttrDelimiter.'LANGUAGE='.$params['LANGUAGE'];
4518
+ $LANGattrExist = TRUE;
4519
+ }
4520
+ if( !$LANGattrExist ) {
4521
+ $lang = $this->getConfig( 'language' );
4522
+ if(( $CNattrExist || $LANGattrKey ) && $lang )
4523
+ $attrLANG .= $this->intAttrDelimiter.'LANGUAGE='.$lang;
4524
+ }
4525
+ return $attr1.$attrLANG.$attr2;
4526
+ }
4527
+ /**
4528
+ * creates formatted output for calendar component property data value type recur
4529
+ *
4530
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4531
+ * @since 2.4.8 - 2008-10-22
4532
+ * @param array $recurlabel
4533
+ * @param array $recurdata
4534
+ * @return string
4535
+ */
4536
+ function _format_recur( $recurlabel, $recurdata ) {
4537
+ $output = null;
4538
+ foreach( $recurdata as $therule ) {
4539
+ if( empty( $therule['value'] )) {
4540
+ if( $this->getConfig( 'allowEmpty' )) $output .= $this->_createElement( $recurlabel );
4541
+ continue;
4542
+ }
4543
+ $attributes = ( isset( $therule['params'] )) ? $this->_createParams( $therule['params'] ) : null;
4544
+ $content1 = $content2 = null;
4545
+ foreach( $therule['value'] as $rulelabel => $rulevalue ) {
4546
+ switch( $rulelabel ) {
4547
+ case 'FREQ': {
4548
+ $content1 .= "FREQ=$rulevalue";
4549
+ break;
4550
+ }
4551
+ case 'UNTIL': {
4552
+ $content2 .= ";UNTIL=";
4553
+ $content2 .= iCalUtilityFunctions::_format_date_time( $rulevalue );
4554
+ break;
4555
+ }
4556
+ case 'COUNT':
4557
+ case 'INTERVAL':
4558
+ case 'WKST': {
4559
+ $content2 .= ";$rulelabel=$rulevalue";
4560
+ break;
4561
+ }
4562
+ case 'BYSECOND':
4563
+ case 'BYMINUTE':
4564
+ case 'BYHOUR':
4565
+ case 'BYMONTHDAY':
4566
+ case 'BYYEARDAY':
4567
+ case 'BYWEEKNO':
4568
+ case 'BYMONTH':
4569
+ case 'BYSETPOS': {
4570
+ $content2 .= ";$rulelabel=";
4571
+ if( is_array( $rulevalue )) {
4572
+ foreach( $rulevalue as $vix => $valuePart ) {
4573
+ $content2 .= ( $vix ) ? ',' : null;
4574
+ $content2 .= $valuePart;
4575
+ }
4576
+ }
4577
+ else
4578
+ $content2 .= $rulevalue;
4579
+ break;
4580
+ }
4581
+ case 'BYDAY': {
4582
+ $content2 .= ";$rulelabel=";
4583
+ $bydaycnt = 0;
4584
+ foreach( $rulevalue as $vix => $valuePart ) {
4585
+ $content21 = $content22 = null;
4586
+ if( is_array( $valuePart )) {
4587
+ $content2 .= ( $bydaycnt ) ? ',' : null;
4588
+ foreach( $valuePart as $vix2 => $valuePart2 ) {
4589
+ if( 'DAY' != strtoupper( $vix2 ))
4590
+ $content21 .= $valuePart2;
4591
+ else
4592
+ $content22 .= $valuePart2;
4593
+ }
4594
+ $content2 .= $content21.$content22;
4595
+ $bydaycnt++;
4596
+ }
4597
+ else {
4598
+ $content2 .= ( $bydaycnt ) ? ',' : null;
4599
+ if( 'DAY' != strtoupper( $vix ))
4600
+ $content21 .= $valuePart;
4601
+ else {
4602
+ $content22 .= $valuePart;
4603
+ $bydaycnt++;
4604
+ }
4605
+ $content2 .= $content21.$content22;
4606
+ }
4607
+ }
4608
+ break;
4609
+ }
4610
+ default: {
4611
+ $content2 .= ";$rulelabel=$rulevalue";
4612
+ break;
4613
+ }
4614
+ }
4615
+ }
4616
+ $output .= $this->_createElement( $recurlabel, $attributes, $content1.$content2 );
4617
+ }
4618
+ return $output;
4619
+ }
4620
+ /**
4621
+ * check if property not exists within component
4622
+ *
4623
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4624
+ * @since 2.5.1 - 2008-10-15
4625
+ * @param string $propName
4626
+ * @return bool
4627
+ */
4628
+ function _notExistProp( $propName ) {
4629
+ if( empty( $propName )) return FALSE; // when deleting x-prop, an empty propName may be used=allowed
4630
+ $propName = strtolower( $propName );
4631
+ if( 'last-modified' == $propName ) { if( !isset( $this->lastmodified )) return TRUE; }
4632
+ elseif( 'percent-complete' == $propName ) { if( !isset( $this->percentcomplete )) return TRUE; }
4633
+ elseif( 'recurrence-id' == $propName ) { if( !isset( $this->recurrenceid )) return TRUE; }
4634
+ elseif( 'related-to' == $propName ) { if( !isset( $this->relatedto )) return TRUE; }
4635
+ elseif( 'request-status' == $propName ) { if( !isset( $this->requeststatus )) return TRUE; }
4636
+ elseif(( 'x-' != substr($propName,0,2)) && !isset( $this->$propName )) return TRUE;
4637
+ return FALSE;
4638
+ }
4639
+ /*********************************************************************************/
4640
+ /*********************************************************************************/
4641
+ /**
4642
+ * get general component config variables or info about subcomponents
4643
+ *
4644
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4645
+ * @since 2.9.6 - 2011-05-14
4646
+ * @param mixed $config
4647
+ * @return value
4648
+ */
4649
+ function getConfig( $config = FALSE) {
4650
+ if( !$config ) {
4651
+ $return = array();
4652
+ $return['ALLOWEMPTY'] = $this->getConfig( 'ALLOWEMPTY' );
4653
+ $return['FORMAT'] = $this->getConfig( 'FORMAT' );
4654
+ if( FALSE !== ( $lang = $this->getConfig( 'LANGUAGE' )))
4655
+ $return['LANGUAGE'] = $lang;
4656
+ $return['NEWLINECHAR'] = $this->getConfig( 'NEWLINECHAR' );
4657
+ $return['TZTD'] = $this->getConfig( 'TZID' );
4658
+ $return['UNIQUE_ID'] = $this->getConfig( 'UNIQUE_ID' );
4659
+ return $return;
4660
+ }
4661
+ switch( strtoupper( $config )) {
4662
+ case 'ALLOWEMPTY':
4663
+ return $this->allowEmpty;
4664
+ break;
4665
+ case 'COMPSINFO':
4666
+ unset( $this->compix );
4667
+ $info = array();
4668
+ if( isset( $this->components )) {
4669
+ foreach( $this->components as $cix => $component ) {
4670
+ if( empty( $component )) continue;
4671
+ $info[$cix]['ordno'] = $cix + 1;
4672
+ $info[$cix]['type'] = $component->objName;
4673
+ $info[$cix]['uid'] = $component->getProperty( 'uid' );
4674
+ $info[$cix]['props'] = $component->getConfig( 'propinfo' );
4675
+ $info[$cix]['sub'] = $component->getConfig( 'compsinfo' );
4676
+ }
4677
+ }
4678
+ return $info;
4679
+ break;
4680
+ case 'FORMAT':
4681
+ return $this->format;
4682
+ break;
4683
+ case 'LANGUAGE':
4684
+ // get language for calendar component as defined in [RFC 1766]
4685
+ return $this->language;
4686
+ break;
4687
+ case 'NL':
4688
+ case 'NEWLINECHAR':
4689
+ return $this->nl;
4690
+ break;
4691
+ case 'PROPINFO':
4692
+ $output = array();
4693
+ if( !in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) {
4694
+ if( empty( $this->uid['value'] )) $this->_makeuid();
4695
+ $output['UID'] = 1;
4696
+ }
4697
+ if( !empty( $this->dtstamp )) $output['DTSTAMP'] = 1;
4698
+ if( !empty( $this->summary )) $output['SUMMARY'] = 1;
4699
+ if( !empty( $this->description )) $output['DESCRIPTION'] = count( $this->description );
4700
+ if( !empty( $this->dtstart )) $output['DTSTART'] = 1;
4701
+ if( !empty( $this->dtend )) $output['DTEND'] = 1;
4702
+ if( !empty( $this->due )) $output['DUE'] = 1;
4703
+ if( !empty( $this->duration )) $output['DURATION'] = 1;
4704
+ if( !empty( $this->rrule )) $output['RRULE'] = count( $this->rrule );
4705
+ if( !empty( $this->rdate )) $output['RDATE'] = count( $this->rdate );
4706
+ if( !empty( $this->exdate )) $output['EXDATE'] = count( $this->exdate );
4707
+ if( !empty( $this->exrule )) $output['EXRULE'] = count( $this->exrule );
4708
+ if( !empty( $this->action )) $output['ACTION'] = 1;
4709
+ if( !empty( $this->attach )) $output['ATTACH'] = count( $this->attach );
4710
+ if( !empty( $this->attendee )) $output['ATTENDEE'] = count( $this->attendee );
4711
+ if( !empty( $this->categories )) $output['CATEGORIES'] = count( $this->categories );
4712
+ if( !empty( $this->class )) $output['CLASS'] = 1;
4713
+ if( !empty( $this->comment )) $output['COMMENT'] = count( $this->comment );
4714
+ if( !empty( $this->completed )) $output['COMPLETED'] = 1;
4715
+ if( !empty( $this->contact )) $output['CONTACT'] = count( $this->contact );
4716
+ if( !empty( $this->created )) $output['CREATED'] = 1;
4717
+ if( !empty( $this->freebusy )) $output['FREEBUSY'] = count( $this->freebusy );
4718
+ if( !empty( $this->geo )) $output['GEO'] = 1;
4719
+ if( !empty( $this->lastmodified )) $output['LAST-MODIFIED'] = 1;
4720
+ if( !empty( $this->location )) $output['LOCATION'] = 1;
4721
+ if( !empty( $this->organizer )) $output['ORGANIZER'] = 1;
4722
+ if( !empty( $this->percentcomplete )) $output['PERCENT-COMPLETE'] = 1;
4723
+ if( !empty( $this->priority )) $output['PRIORITY'] = 1;
4724
+ if( !empty( $this->recurrenceid )) $output['RECURRENCE-ID'] = 1;
4725
+ if( !empty( $this->relatedto )) $output['RELATED-TO'] = count( $this->relatedto );
4726
+ if( !empty( $this->repeat )) $output['REPEAT'] = 1;
4727
+ if( !empty( $this->requeststatus )) $output['REQUEST-STATUS'] = count( $this->requeststatus );
4728
+ if( !empty( $this->resources )) $output['RESOURCES'] = count( $this->resources );
4729
+ if( !empty( $this->sequence )) $output['SEQUENCE'] = 1;
4730
+ if( !empty( $this->sequence )) $output['SEQUENCE'] = 1;
4731
+ if( !empty( $this->status )) $output['STATUS'] = 1;
4732
+ if( !empty( $this->transp )) $output['TRANSP'] = 1;
4733
+ if( !empty( $this->trigger )) $output['TRIGGER'] = 1;
4734
+ if( !empty( $this->tzid )) $output['TZID'] = 1;
4735
+ if( !empty( $this->tzname )) $output['TZNAME'] = count( $this->tzname );
4736
+ if( !empty( $this->tzoffsetfrom )) $output['TZOFFSETFROM'] = 1;
4737
+ if( !empty( $this->tzoffsetto )) $output['TZOFFSETTO'] = 1;
4738
+ if( !empty( $this->tzurl )) $output['TZURL'] = 1;
4739
+ if( !empty( $this->url )) $output['URL'] = 1;
4740
+ if( !empty( $this->xprop )) $output['X-PROP'] = count( $this->xprop );
4741
+ return $output;
4742
+ break;
4743
+ case 'TZID':
4744
+ return $this->dtzid;
4745
+ break;
4746
+ case 'UNIQUE_ID':
4747
+ if( empty( $this->unique_id ))
4748
+ $this->unique_id = ( isset( $_SERVER['SERVER_NAME'] )) ? gethostbyname( $_SERVER['SERVER_NAME'] ) : 'localhost';
4749
+ return $this->unique_id;
4750
+ break;
4751
+ }
4752
+ }
4753
+ /**
4754
+ * general component config setting
4755
+ *
4756
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4757
+ * @since 2.9.6 - 2011-05-14
4758
+ * @param mixed $config
4759
+ * @param string $value
4760
+ * @param bool $softUpdate
4761
+ * @return void
4762
+ */
4763
+ function setConfig( $config, $value = FALSE, $softUpdate = FALSE ) {
4764
+ if( is_array( $config )) {
4765
+ foreach( $config as $cKey => $cValue ) {
4766
+ if( FALSE === $this->setConfig( $cKey, $cValue, $softUpdate ))
4767
+ return FALSE;
4768
+ }
4769
+ return TRUE;
4770
+ }
4771
+ $res = FALSE;
4772
+ switch( strtoupper( $config )) {
4773
+ case 'ALLOWEMPTY':
4774
+ $this->allowEmpty = $value;
4775
+ $subcfg = array( 'ALLOWEMPTY' => $value );
4776
+ $res = TRUE;
4777
+ break;
4778
+ case 'FORMAT':
4779
+ $value = trim( strtolower( $value ));
4780
+ $this->format = $value;
4781
+ $this->_createFormat();
4782
+ $subcfg = array( 'FORMAT' => $value );
4783
+ $res = TRUE;
4784
+ break;
4785
+ case 'LANGUAGE':
4786
+ // set language for calendar component as defined in [RFC 1766]
4787
+ $value = trim( $value );
4788
+ if( empty( $this->language ) || !$softUpdate )
4789
+ $this->language = $value;
4790
+ $subcfg = array( 'LANGUAGE' => $value );
4791
+ $res = TRUE;
4792
+ break;
4793
+ case 'NL':
4794
+ case 'NEWLINECHAR':
4795
+ $this->nl = $value;
4796
+ $subcfg = array( 'NL' => $value );
4797
+ $res = TRUE;
4798
+ break;
4799
+ case 'TZID':
4800
+ $this->dtzid = $value;
4801
+ $subcfg = array( 'TZID' => $value );
4802
+ $res = TRUE;
4803
+ break;
4804
+ case 'UNIQUE_ID':
4805
+ $value = trim( $value );
4806
+ $this->unique_id = $value;
4807
+ $subcfg = array( 'UNIQUE_ID' => $value );
4808
+ $res = TRUE;
4809
+ break;
4810
+ default: // any unvalid config key.. .
4811
+ return TRUE;
4812
+ }
4813
+ if( !$res ) return FALSE;
4814
+ if( isset( $subcfg ) && !empty( $this->components )) {
4815
+ foreach( $subcfg as $cfgkey => $cfgvalue ) {
4816
+ foreach( $this->components as $cix => $component ) {
4817
+ $res = $component->setConfig( $cfgkey, $cfgvalue, $softUpdate );
4818
+ if( !$res )
4819
+ break 2;
4820
+ $this->components[$cix] = $component->copy(); // PHP4 compliant
4821
+ }
4822
+ }
4823
+ }
4824
+ return $res;
4825
+ }
4826
+ /*********************************************************************************/
4827
+ /**
4828
+ * delete component property value
4829
+ *
4830
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
4831
+ * @since 2.8.8 - 2011-03-15
4832
+ * @param mixed $propName, bool FALSE => X-property
4833
+ * @param int $propix, optional, if specific property is wanted in case of multiply occurences
4834
+ * @return bool, if successfull delete TRUE
4835
+ */
4836
+ function deleteProperty( $propName=FALSE, $propix=FALSE ) {
4837
+ if( $this->_notExistProp( $propName )) return FALSE;
4838
+ $propName = strtoupper( $propName );
4839
+ if( in_array( $propName, array( 'ATTACH', 'ATTENDEE', 'CATEGORIES', 'COMMENT', 'CONTACT', 'DESCRIPTION', 'EXDATE', 'EXRULE',
4840
+ 'FREEBUSY', 'RDATE', 'RELATED-TO', 'RESOURCES', 'RRULE', 'REQUEST-STATUS', 'TZNAME', 'X-PROP' ))) {
4841
+ if( !$propix )
4842
+ $propix = ( isset( $this->propdelix[$propName] ) && ( 'X-PROP' != $propName )) ? $this->propdelix[$propName] + 2 : 1;
4843
+ $this->propdelix[$propName] = --$propix;
4844
+ }
4845
+ $return = FALSE;
4846
+ switch( $propName ) {
4847
+ case 'ACTION':
4848
+ if( !empty( $this->action )) {
4849
+ $this->action = '';
4850
+ $return = TRUE;
4851
+ }
4852
+ break;
4853
+ case 'ATTACH':
4854
+ return $this->deletePropertyM( $this->attach, $this->propdelix[$propName] );
4855
+ break;
4856
+ case 'ATTENDEE':
4857
+ return $this->deletePropertyM( $this->attendee, $this->propdelix[$propName] );
4858
+ break;
4859
+ case 'CATEGORIES':
4860
+ return $this->deletePropertyM( $this->categories, $this->propdelix[$propName] );
4861
+ break;
4862
+ case 'CLASS':
4863
+ if( !empty( $this->class )) {
4864
+ $this->class = '';
4865
+ $return = TRUE;
4866
+ }
4867
+ break;
4868
+ case 'COMMENT':
4869
+ return $this->deletePropertyM( $this->comment, $this->propdelix[$propName] );
4870
+ break;
4871
+ case 'COMPLETED':
4872
+ if( !empty( $this->completed )) {
4873
+ $this->completed = '';
4874
+ $return = TRUE;
4875
+ }
4876
+ break;
4877
+ case 'CONTACT':
4878
+ return $this->deletePropertyM( $this->contact, $this->propdelix[$propName] );
4879
+ break;
4880
+ case 'CREATED':
4881
+ if( !empty( $this->created )) {
4882
+ $this->created = '';
4883
+ $return = TRUE;
4884
+ }
4885
+ break;
4886
+ case 'DESCRIPTION':
4887
+ return $this->deletePropertyM( $this->description, $this->propdelix[$propName] );
4888
+ break;
4889
+ case 'DTEND':
4890
+ if( !empty( $this->dtend )) {
4891
+ $this->dtend = '';
4892
+ $return = TRUE;
4893
+ }
4894
+ break;
4895
+ case 'DTSTAMP':
4896
+ if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
4897
+ return FALSE;
4898
+ if( !empty( $this->dtstamp )) {
4899
+ $this->dtstamp = '';
4900
+ $return = TRUE;
4901
+ }
4902
+ break;
4903
+ case 'DTSTART':
4904
+ if( !empty( $this->dtstart )) {
4905
+ $this->dtstart = '';
4906
+ $return = TRUE;
4907
+ }
4908
+ break;
4909
+ case 'DUE':
4910
+ if( !empty( $this->due )) {
4911
+ $this->due = '';
4912
+ $return = TRUE;
4913
+ }
4914
+ break;
4915
+ case 'DURATION':
4916
+ if( !empty( $this->duration )) {
4917
+ $this->duration = '';
4918
+ $return = TRUE;
4919
+ }
4920
+ break;
4921
+ case 'EXDATE':
4922
+ return $this->deletePropertyM( $this->exdate, $this->propdelix[$propName] );
4923
+ break;
4924
+ case 'EXRULE':
4925
+ return $this->deletePropertyM( $this->exrule, $this->propdelix[$propName] );
4926
+ break;
4927
+ case 'FREEBUSY':
4928
+ return $this->deletePropertyM( $this->freebusy, $this->propdelix[$propName] );
4929
+ break;
4930
+ case 'GEO':
4931
+ if( !empty( $this->geo )) {
4932
+ $this->geo = '';
4933
+ $return = TRUE;
4934
+ }
4935
+ break;
4936
+ case 'LAST-MODIFIED':
4937
+ if( !empty( $this->lastmodified )) {
4938
+ $this->lastmodified = '';
4939
+ $return = TRUE;
4940
+ }
4941
+ break;
4942
+ case 'LOCATION':
4943
+ if( !empty( $this->location )) {
4944
+ $this->location = '';
4945
+ $return = TRUE;
4946
+ }
4947
+ break;
4948
+ case 'ORGANIZER':
4949
+ if( !empty( $this->organizer )) {
4950
+ $this->organizer = '';
4951
+ $return = TRUE;
4952
+ }
4953
+ break;
4954
+ case 'PERCENT-COMPLETE':
4955
+ if( !empty( $this->percentcomplete )) {
4956
+ $this->percentcomplete = '';
4957
+ $return = TRUE;
4958
+ }
4959
+ break;
4960
+ case 'PRIORITY':
4961
+ if( !empty( $this->priority )) {
4962
+ $this->priority = '';
4963
+ $return = TRUE;
4964
+ }
4965
+ break;
4966
+ case 'RDATE':
4967
+ return $this->deletePropertyM( $this->rdate, $this->propdelix[$propName] );
4968
+ break;
4969
+ case 'RECURRENCE-ID':
4970
+ if( !empty( $this->recurrenceid )) {
4971
+ $this->recurrenceid = '';
4972
+ $return = TRUE;
4973
+ }
4974
+ break;
4975
+ case 'RELATED-TO':
4976
+ return $this->deletePropertyM( $this->relatedto, $this->propdelix[$propName] );
4977
+ break;
4978
+ case 'REPEAT':
4979
+ if( !empty( $this->repeat )) {
4980
+ $this->repeat = '';
4981
+ $return = TRUE;
4982
+ }
4983
+ break;
4984
+ case 'REQUEST-STATUS':
4985
+ return $this->deletePropertyM( $this->requeststatus, $this->propdelix[$propName] );
4986
+ break;
4987
+ case 'RESOURCES':
4988
+ return $this->deletePropertyM( $this->resources, $this->propdelix[$propName] );
4989
+ break;
4990
+ case 'RRULE':
4991
+ return $this->deletePropertyM( $this->rrule, $this->propdelix[$propName] );
4992
+ break;
4993
+ case 'SEQUENCE':
4994
+ if( !empty( $this->sequence )) {
4995
+ $this->sequence = '';
4996
+ $return = TRUE;
4997
+ }
4998
+ break;
4999
+ case 'STATUS':
5000
+ if( !empty( $this->status )) {
5001
+ $this->status = '';
5002
+ $return = TRUE;
5003
+ }
5004
+ break;
5005
+ case 'SUMMARY':
5006
+ if( !empty( $this->summary )) {
5007
+ $this->summary = '';
5008
+ $return = TRUE;
5009
+ }
5010
+ break;
5011
+ case 'TRANSP':
5012
+ if( !empty( $this->transp )) {
5013
+ $this->transp = '';
5014
+ $return = TRUE;
5015
+ }
5016
+ break;
5017
+ case 'TRIGGER':
5018
+ if( !empty( $this->trigger )) {
5019
+ $this->trigger = '';
5020
+ $return = TRUE;
5021
+ }
5022
+ break;
5023
+ case 'TZID':
5024
+ if( !empty( $this->tzid )) {
5025
+ $this->tzid = '';
5026
+ $return = TRUE;
5027
+ }
5028
+ break;
5029
+ case 'TZNAME':
5030
+ return $this->deletePropertyM( $this->tzname, $this->propdelix[$propName] );
5031
+ break;
5032
+ case 'TZOFFSETFROM':
5033
+ if( !empty( $this->tzoffsetfrom )) {
5034
+ $this->tzoffsetfrom = '';
5035
+ $return = TRUE;
5036
+ }
5037
+ break;
5038
+ case 'TZOFFSETTO':
5039
+ if( !empty( $this->tzoffsetto )) {
5040
+ $this->tzoffsetto = '';
5041
+ $return = TRUE;
5042
+ }
5043
+ break;
5044
+ case 'TZURL':
5045
+ if( !empty( $this->tzurl )) {
5046
+ $this->tzurl = '';
5047
+ $return = TRUE;
5048
+ }
5049
+ break;
5050
+ case 'UID':
5051
+ if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
5052
+ return FALSE;
5053
+ if( !empty( $this->uid )) {
5054
+ $this->uid = '';
5055
+ $return = TRUE;
5056
+ }
5057
+ break;
5058
+ case 'URL':
5059
+ if( !empty( $this->url )) {
5060
+ $this->url = '';
5061
+ $return = TRUE;
5062
+ }
5063
+ break;
5064
+ default:
5065
+ $reduced = '';
5066
+ if( $propName != 'X-PROP' ) {
5067
+ if( !isset( $this->xprop[$propName] )) return FALSE;
5068
+ foreach( $this->xprop as $k => $a ) {
5069
+ if(( $k != $propName ) && !empty( $a ))
5070
+ $reduced[$k] = $a;
5071
+ }
5072
+ }
5073
+ else {
5074
+ if( count( $this->xprop ) <= $propix ) { unset( $this->propdelix[$propName] ); return FALSE; }
5075
+ $xpropno = 0;
5076
+ foreach( $this->xprop as $xpropkey => $xpropvalue ) {
5077
+ if( $propix != $xpropno )
5078
+ $reduced[$xpropkey] = $xpropvalue;
5079
+ $xpropno++;
5080
+ }
5081
+ }
5082
+ $this->xprop = $reduced;
5083
+ if( empty( $this->xprop )) {
5084
+ unset( $this->propdelix[$propName] );
5085
+ return FALSE;
5086
+ }
5087
+ return TRUE;
5088
+ }
5089
+ return $return;
5090
+ }
5091
+ /*********************************************************************************/
5092
+ /**
5093
+ * delete component property value, fixing components with multiple occurencies
5094
+ *
5095
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
5096
+ * @since 2.8.8 - 2011-03-15
5097
+ * @param array $multiprop, reference to a component property
5098
+ * @param int $propix, reference to removal counter
5099
+ * @return bool TRUE
5100
+ */
5101
+ function deletePropertyM( & $multiprop, & $propix ) {
5102
+ if( isset( $multiprop[$propix] ))
5103
+ unset( $multiprop[$propix] );
5104
+ if( empty( $multiprop )) {
5105
+ $multiprop = '';
5106
+ unset( $propix );
5107
+ return FALSE;
5108
+ }
5109
+ else
5110
+ return TRUE;
5111
+ }
5112
+ /**
5113
+ * get component property value/params
5114
+ *
5115
+ * if property has multiply values, consequtive function calls are needed
5116
+ *
5117
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
5118
+ * @since 2.9.3 - 2011-05-18
5119
+ * @param string $propName, optional
5120
+ * @param int @propix, optional, if specific property is wanted in case of multiply occurences
5121
+ * @param bool $inclParam=FALSE
5122
+ * @param bool $specform=FALSE
5123
+ * @return mixed
5124
+ */
5125
+ function getProperty( $propName=FALSE, $propix=FALSE, $inclParam=FALSE, $specform=FALSE ) {
5126
+ if( $this->_notExistProp( $propName )) return FALSE;
5127
+ $propName = ( $propName ) ? strtoupper( $propName ) : 'X-PROP';
5128
+ if( in_array( $propName, array( 'ATTACH', 'ATTENDEE', 'CATEGORIES', 'COMMENT', 'CONTACT', 'DESCRIPTION', 'EXDATE', 'EXRULE',
5129
+ 'FREEBUSY', 'RDATE', 'RELATED-TO', 'RESOURCES', 'RRULE', 'REQUEST-STATUS', 'TZNAME', 'X-PROP' ))) {
5130
+ if( !$propix )
5131
+ $propix = ( isset( $this->propix[$propName] )) ? $this->propix[$propName] + 2 : 1;
5132
+ $this->propix[$propName] = --$propix;
5133
+ }
5134
+ switch( $propName ) {
5135
+ case 'ACTION':
5136
+ if( !empty( $this->action['value'] )) return ( $inclParam ) ? $this->action : $this->action['value'];
5137
+ break;
5138
+ case 'ATTACH':
5139
+ while( is_array( $this->attach ) && !isset( $this->attach[$propix] ) && ( 0 < count( $this->attach )) && ( $propix < end( array_keys( $this->attach ))))
5140
+ $propix++;
5141
+ if( !isset( $this->attach[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5142
+ return ( $inclParam ) ? $this->attach[$propix] : $this->attach[$propix]['value'];
5143
+ break;
5144
+ case 'ATTENDEE':
5145
+ while( is_array( $this->attendee ) && !isset( $this->attendee[$propix] ) && ( 0 < count( $this->attendee )) && ( $propix < end( array_keys( $this->attendee ))))
5146
+ $propix++;
5147
+ if( !isset( $this->attendee[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5148
+ return ( $inclParam ) ? $this->attendee[$propix] : $this->attendee[$propix]['value'];
5149
+ break;
5150
+ case 'CATEGORIES':
5151
+ while( is_array( $this->categories ) && !isset( $this->categories[$propix] ) && ( 0 < count( $this->categories )) && ( $propix < end( array_keys( $this->categories ))))
5152
+ $propix++;
5153
+ if( !isset( $this->categories[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5154
+ return ( $inclParam ) ? $this->categories[$propix] : $this->categories[$propix]['value'];
5155
+ break;
5156
+ case 'CLASS':
5157
+ if( !empty( $this->class['value'] )) return ( $inclParam ) ? $this->class : $this->class['value'];
5158
+ break;
5159
+ case 'COMMENT':
5160
+ while( is_array( $this->comment ) && !isset( $this->comment[$propix] ) && ( 0 < count( $this->comment )) && ( $propix < end( array_keys( $this->comment ))))
5161
+ $propix++;
5162
+ if( !isset( $this->comment[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5163
+ return ( $inclParam ) ? $this->comment[$propix] : $this->comment[$propix]['value'];
5164
+ break;
5165
+ case 'COMPLETED':
5166
+ if( !empty( $this->completed['value'] )) return ( $inclParam ) ? $this->completed : $this->completed['value'];
5167
+ break;
5168
+ case 'CONTACT':
5169
+ while( is_array( $this->contact ) && !isset( $this->contact[$propix] ) && ( 0 < count( $this->contact )) && ( $propix < end( array_keys( $this->contact ))))
5170
+ $propix++;
5171
+ if( !isset( $this->contact[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5172
+ return ( $inclParam ) ? $this->contact[$propix] : $this->contact[$propix]['value'];
5173
+ break;
5174
+ case 'CREATED':
5175
+ if( !empty( $this->created['value'] )) return ( $inclParam ) ? $this->created : $this->created['value'];
5176
+ break;
5177
+ case 'DESCRIPTION':
5178
+ while( is_array( $this->description ) && !isset( $this->description[$propix] ) && ( 0 < count( $this->description )) && ( $propix < end( array_keys( $this->description ))))
5179
+ $propix++;
5180
+ if( !isset( $this->description[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5181
+ return ( $inclParam ) ? $this->description[$propix] : $this->description[$propix]['value'];
5182
+ break;
5183
+ case 'DTEND':
5184
+ if( !empty( $this->dtend['value'] )) return ( $inclParam ) ? $this->dtend : $this->dtend['value'];
5185
+ break;
5186
+ case 'DTSTAMP':
5187
+ if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
5188
+ return;
5189
+ if( !isset( $this->dtstamp['value'] ))
5190
+ $this->_makeDtstamp();
5191
+ return ( $inclParam ) ? $this->dtstamp : $this->dtstamp['value'];
5192
+ break;
5193
+ case 'DTSTART':
5194
+ if( !empty( $this->dtstart['value'] )) return ( $inclParam ) ? $this->dtstart : $this->dtstart['value'];
5195
+ break;
5196
+ case 'DUE':
5197
+ if( !empty( $this->due['value'] )) return ( $inclParam ) ? $this->due : $this->due['value'];
5198
+ break;
5199
+ case 'DURATION':
5200
+ if( !isset( $this->duration['value'] )) return FALSE;
5201
+ $value = ( $specform && isset( $this->dtstart['value'] ) && isset( $this->duration['value'] )) ? iCalUtilityFunctions::_duration2date( $this->dtstart['value'], $this->duration['value'] ) : $this->duration['value'];
5202
+ return ( $inclParam ) ? array( 'value' => $value, 'params' => $this->duration['params'] ) : $value;
5203
+ break;
5204
+ case 'EXDATE':
5205
+ while( is_array( $this->exdate ) && !isset( $this->exdate[$propix] ) && ( 0 < count( $this->exdate )) && ( $propix < end( array_keys( $this->exdate ))))
5206
+ $propix++;
5207
+ if( !isset( $this->exdate[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5208
+ return ( $inclParam ) ? $this->exdate[$propix] : $this->exdate[$propix]['value'];
5209
+ break;
5210
+ case 'EXRULE':
5211
+ while( is_array( $this->exrule ) && !isset( $this->exrule[$propix] ) && ( 0 < count( $this->exrule )) && ( $propix < end( array_keys( $this->exrule ))))
5212
+ $propix++;
5213
+ if( !isset( $this->exrule[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5214
+ return ( $inclParam ) ? $this->exrule[$propix] : $this->exrule[$propix]['value'];
5215
+ break;
5216
+ case 'FREEBUSY':
5217
+ while( is_array( $this->freebusy ) && !isset( $this->freebusy[$propix] ) && ( 0 < count( $this->freebusy )) && ( $propix < end( array_keys( $this->freebusy ))))
5218
+ $propix++;
5219
+ if( !isset( $this->freebusy[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5220
+ return ( $inclParam ) ? $this->freebusy[$propix] : $this->freebusy[$propix]['value'];
5221
+ break;
5222
+ case 'GEO':
5223
+ if( !empty( $this->geo['value'] )) return ( $inclParam ) ? $this->geo : $this->geo['value'];
5224
+ break;
5225
+ case 'LAST-MODIFIED':
5226
+ if( !empty( $this->lastmodified['value'] )) return ( $inclParam ) ? $this->lastmodified : $this->lastmodified['value'];
5227
+ break;
5228
+ case 'LOCATION':
5229
+ if( !empty( $this->location['value'] )) return ( $inclParam ) ? $this->location : $this->location['value'];
5230
+ break;
5231
+ case 'ORGANIZER':
5232
+ if( !empty( $this->organizer['value'] )) return ( $inclParam ) ? $this->organizer : $this->organizer['value'];
5233
+ break;
5234
+ case 'PERCENT-COMPLETE':
5235
+ if( !empty( $this->percentcomplete['value'] ) || ( isset( $this->percentcomplete['value'] ) && ( '0' == $this->percentcomplete['value'] ))) return ( $inclParam ) ? $this->percentcomplete : $this->percentcomplete['value'];
5236
+ break;
5237
+ case 'PRIORITY':
5238
+ if( !empty( $this->priority['value'] ) || ( isset( $this->priority['value'] ) && ('0' == $this->priority['value'] ))) return ( $inclParam ) ? $this->priority : $this->priority['value'];
5239
+ break;
5240
+ case 'RDATE':
5241
+ while( is_array( $this->rdate ) && !isset( $this->rdate[$propix] ) && ( 0 < count( $this->rdate )) && ( $propix < end( array_keys( $this->rdate ))))
5242
+ $propix++;
5243
+ if( !isset( $this->rdate[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5244
+ return ( $inclParam ) ? $this->rdate[$propix] : $this->rdate[$propix]['value'];
5245
+ break;
5246
+ case 'RECURRENCE-ID':
5247
+ if( !empty( $this->recurrenceid['value'] )) return ( $inclParam ) ? $this->recurrenceid : $this->recurrenceid['value'];
5248
+ break;
5249
+ case 'RELATED-TO':
5250
+ while( is_array( $this->relatedto ) && !isset( $this->relatedto[$propix] ) && ( 0 < count( $this->relatedto )) && ( $propix < end( array_keys( $this->relatedto ))))
5251
+ $propix++;
5252
+ if( !isset( $this->relatedto[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5253
+ return ( $inclParam ) ? $this->relatedto[$propix] : $this->relatedto[$propix]['value'];
5254
+ break;
5255
+ case 'REPEAT':
5256
+ if( !empty( $this->repeat['value'] ) || ( isset( $this->repeat['value'] ) && ( '0' == $this->repeat['value'] ))) return ( $inclParam ) ? $this->repeat : $this->repeat['value'];
5257
+ break;
5258
+ case 'REQUEST-STATUS':
5259
+ while( is_array( $this->requeststatus ) && !isset( $this->requeststatus[$propix] ) && ( 0 < count( $this->requeststatus )) && ( $propix < end( array_keys( $this->requeststatus ))))
5260
+ $propix++;
5261
+ if( !isset( $this->requeststatus[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5262
+ return ( $inclParam ) ? $this->requeststatus[$propix] : $this->requeststatus[$propix]['value'];
5263
+ break;
5264
+ case 'RESOURCES':
5265
+ while( is_array( $this->resources ) && !isset( $this->resources[$propix] ) && ( 0 < count( $this->resources )) && ( $propix < end( array_keys( $this->resources ))))
5266
+ $propix++;
5267
+ if( !isset( $this->resources[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5268
+ return ( $inclParam ) ? $this->resources[$propix] : $this->resources[$propix]['value'];
5269
+ break;
5270
+ case 'RRULE':
5271
+ while( is_array( $this->rrule ) && !isset( $this->rrule[$propix] ) && ( 0 < count( $this->rrule )) && ( $propix < end( array_keys( $this->rrule ))))
5272
+ $propix++;
5273
+ if( !isset( $this->rrule[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5274
+ return ( $inclParam ) ? $this->rrule[$propix] : $this->rrule[$propix]['value'];
5275
+ break;
5276
+ case 'SEQUENCE':
5277
+ if( isset( $this->sequence['value'] ) && ( isset( $this->sequence['value'] ) && ( '0' <= $this->sequence['value'] ))) return ( $inclParam ) ? $this->sequence : $this->sequence['value'];
5278
+ break;
5279
+ case 'STATUS':
5280
+ if( !empty( $this->status['value'] )) return ( $inclParam ) ? $this->status : $this->status['value'];
5281
+ break;
5282
+ case 'SUMMARY':
5283
+ if( !empty( $this->summary['value'] )) return ( $inclParam ) ? $this->summary : $this->summary['value'];
5284
+ break;
5285
+ case 'TRANSP':
5286
+ if( !empty( $this->transp['value'] )) return ( $inclParam ) ? $this->transp : $this->transp['value'];
5287
+ break;
5288
+ case 'TRIGGER':
5289
+ if( !empty( $this->trigger['value'] )) return ( $inclParam ) ? $this->trigger : $this->trigger['value'];
5290
+ break;
5291
+ case 'TZID':
5292
+ if( !empty( $this->tzid['value'] )) return ( $inclParam ) ? $this->tzid : $this->tzid['value'];
5293
+ break;
5294
+ case 'TZNAME':
5295
+ while( is_array( $this->tzname ) && !isset( $this->tzname[$propix] ) && ( 0 < count( $this->tzname )) && ( $propix < end( array_keys( $this->tzname ))))
5296
+ $propix++;
5297
+ if( !isset( $this->tzname[$propix] )) { unset( $this->propix[$propName] ); return FALSE; }
5298
+ return ( $inclParam ) ? $this->tzname[$propix] : $this->tzname[$propix]['value'];
5299
+ break;
5300
+ case 'TZOFFSETFROM':
5301
+ if( !empty( $this->tzoffsetfrom['value'] )) return ( $inclParam ) ? $this->tzoffsetfrom : $this->tzoffsetfrom['value'];
5302
+ break;
5303
+ case 'TZOFFSETTO':
5304
+ if( !empty( $this->tzoffsetto['value'] )) return ( $inclParam ) ? $this->tzoffsetto : $this->tzoffsetto['value'];
5305
+ break;
5306
+ case 'TZURL':
5307
+ if( !empty( $this->tzurl['value'] )) return ( $inclParam ) ? $this->tzurl : $this->tzurl['value'];
5308
+ break;
5309
+ case 'UID':
5310
+ if( in_array( $this->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' )))
5311
+ return FALSE;
5312
+ if( empty( $this->uid['value'] ))
5313
+ $this->_makeuid();
5314
+ return ( $inclParam ) ? $this->uid : $this->uid['value'];
5315
+ break;
5316
+ case 'URL':
5317
+ if( !empty( $this->url['value'] )) return ( $inclParam ) ? $this->url : $this->url['value'];
5318
+ break;
5319
+ default:
5320
+ if( $propName != 'X-PROP' ) {
5321
+ if( !isset( $this->xprop[$propName] )) return FALSE;
5322
+ return ( $inclParam ) ? array( $propName, $this->xprop[$propName] )
5323
+ : array( $propName, $this->xprop[$propName]['value'] );
5324
+ }
5325
+ else {
5326
+ if( empty( $this->xprop )) return FALSE;
5327
+ $xpropno = 0;
5328
+ foreach( $this->xprop as $xpropkey => $xpropvalue ) {
5329
+ if( $propix == $xpropno )
5330
+ return ( $inclParam ) ? array( $xpropkey, $this->xprop[$xpropkey] )
5331
+ : array( $xpropkey, $this->xprop[$xpropkey]['value'] );
5332
+ else
5333
+ $xpropno++;
5334
+ }
5335
+ return FALSE; // not found ??
5336
+ }
5337
+ }
5338
+ return FALSE;
5339
+ }
5340
+ /**
5341
+ * returns calendar property unique values for 'CATEGORIES', 'RESOURCES' or 'ATTENDEE' and each number of ocurrence
5342
+ *
5343
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
5344
+ * @since 2.8.8 - 2011-04-13
5345
+ * @param string $propName
5346
+ * @param array $output, incremented result array
5347
+ */
5348
+ function _getProperties( $propName, & $output ) {
5349
+ if( !in_array( strtoupper( $propName ), array( 'ATTENDEE', 'CATEGORIES', 'RESOURCES' )))
5350
+ return output;
5351
+ while( FALSE !== ( $content = $this->getProperty( $propName ))) {
5352
+ if( is_array( $content )) {
5353
+ foreach( $content as $part ) {
5354
+ if( FALSE !== strpos( $part, ',' )) {
5355
+ $part = explode( ',', $part );
5356
+ foreach( $part as $thePart ) {
5357
+ $thePart = trim( $thePart );
5358
+ if( !empty( $thePart )) {
5359
+ if( !isset( $output[$thePart] ))
5360
+ $output[$thePart] = 1;
5361
+ else
5362
+ $output[$thePart] += 1;
5363
+ }
5364
+ }
5365
+ }
5366
+ else {
5367
+ $part = trim( $part );
5368
+ if( !isset( $output[$part] ))
5369
+ $output[$part] = 1;
5370
+ else
5371
+ $output[$part] += 1;
5372
+ }
5373
+ }
5374
+ }
5375
+ elseif( FALSE !== strpos( $content, ',' )) {
5376
+ $content = explode( ',', $content );
5377
+ foreach( $content as $thePart ) {
5378
+ $thePart = trim( $thePart );
5379
+ if( !empty( $thePart )) {
5380
+ if( !isset( $output[$thePart] ))
5381
+ $output[$thePart] = 1;
5382
+ else
5383
+ $output[$thePart] += 1;
5384
+ }
5385
+ }
5386
+ }
5387
+ else {
5388
+ $content = trim( $content );
5389
+ if( !empty( $content )) {
5390
+ if( !isset( $output[$content] ))
5391
+ $output[$content] = 1;
5392
+ else
5393
+ $output[$content] += 1;
5394
+ }
5395
+ }
5396
+ }
5397
+ ksort( $output );
5398
+ return $output;
5399
+ }
5400
+ /**
5401
+ * general component property setting
5402
+ *
5403
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
5404
+ * @since 2.5.1 - 2008-11-05
5405
+ * @param mixed $args variable number of function arguments,
5406
+ * first argument is ALWAYS component name,
5407
+ * second ALWAYS component value!
5408
+ * @return void
5409
+ */
5410
+ function setProperty() {
5411
+ $numargs = func_num_args();
5412
+ if( 1 > $numargs ) return FALSE;
5413
+ $arglist = func_get_args();
5414
+ if( $this->_notExistProp( $arglist[0] )) return FALSE;
5415
+ if( !$this->getConfig( 'allowEmpty' ) && ( !isset( $arglist[1] ) || empty( $arglist[1] )))
5416
+ return FALSE;
5417
+ $arglist[0] = strtoupper( $arglist[0] );
5418
+ for( $argix=$numargs; $argix < 12; $argix++ ) {
5419
+ if( !isset( $arglist[$argix] ))
5420
+ $arglist[$argix] = null;
5421
+ }
5422
+ switch( $arglist[0] ) {
5423
+ case 'ACTION':
5424
+ return $this->setAction( $arglist[1], $arglist[2] );
5425
+ case 'ATTACH':
5426
+ return $this->setAttach( $arglist[1], $arglist[2], $arglist[3] );
5427
+ case 'ATTENDEE':
5428
+ return $this->setAttendee( $arglist[1], $arglist[2], $arglist[3] );
5429
+ case 'CATEGORIES':
5430
+ return $this->setCategories( $arglist[1], $arglist[2], $arglist[3] );
5431
+ case 'CLASS':
5432
+ return $this->setClass( $arglist[1], $arglist[2] );
5433
+ case 'COMMENT':
5434
+ return $this->setComment( $arglist[1], $arglist[2], $arglist[3] );
5435
+ case 'COMPLETED':
5436
+ return $this->setCompleted( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
5437
+ case 'CONTACT':
5438
+ return $this->setContact( $arglist[1], $arglist[2], $arglist[3] );
5439
+ case 'CREATED':
5440
+ return $this->setCreated( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
5441
+ case 'DESCRIPTION':
5442
+ return $this->setDescription( $arglist[1], $arglist[2], $arglist[3] );
5443
+ case 'DTEND':
5444
+ return $this->setDtend( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
5445
+ case 'DTSTAMP':
5446
+ return $this->setDtstamp( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
5447
+ case 'DTSTART':
5448
+ return $this->setDtstart( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
5449
+ case 'DUE':
5450
+ return $this->setDue( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
5451
+ case 'DURATION':
5452
+ return $this->setDuration( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6] );
5453
+ case 'EXDATE':
5454
+ return $this->setExdate( $arglist[1], $arglist[2], $arglist[3] );
5455
+ case 'EXRULE':
5456
+ return $this->setExrule( $arglist[1], $arglist[2], $arglist[3] );
5457
+ case 'FREEBUSY':
5458
+ return $this->setFreebusy( $arglist[1], $arglist[2], $arglist[3], $arglist[4] );
5459
+ case 'GEO':
5460
+ return $this->setGeo( $arglist[1], $arglist[2], $arglist[3] );
5461
+ case 'LAST-MODIFIED':
5462
+ return $this->setLastModified( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7] );
5463
+ case 'LOCATION':
5464
+ return $this->setLocation( $arglist[1], $arglist[2] );
5465
+ case 'ORGANIZER':
5466
+ return $this->setOrganizer( $arglist[1], $arglist[2] );
5467
+ case 'PERCENT-COMPLETE':
5468
+ return $this->setPercentComplete( $arglist[1], $arglist[2] );
5469
+ case 'PRIORITY':
5470
+ return $this->setPriority( $arglist[1], $arglist[2] );
5471
+ case 'RDATE':
5472
+ return $this->setRdate( $arglist[1], $arglist[2], $arglist[3] );
5473
+ case 'RECURRENCE-ID':
5474
+ return $this->setRecurrenceid( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5], $arglist[6], $arglist[7], $arglist[8] );
5475
+ case 'RELATED-TO':
5476
+ return $this->setRelatedTo( $arglist[1], $arglist[2], $arglist[3] );
5477
+ case 'REPEAT':
5478
+ return $this->setRepeat( $arglist[1], $arglist[2] );
5479
+ case 'REQUEST-STATUS':
5480
+ return $this->setRequestStatus( $arglist[1], $arglist[2], $arglist[3], $arglist[4], $arglist[5] );
5481
+ case 'RESOURCES':
5482
+ return $this->setResources( $arglist[1], $arglist[2], $arglist[3] );
5483
+ case 'RRULE':
5484
+ return $this->setRrule( $arglist[1], $arglist[2], $arglist[3] );
5485
+ case 'SEQUENCE':
5486
+ return $this->setSequence( $arglist[1], $arglist[2] );
5487
+ case 'STATUS':
5488
+ return $this->setStatus( $arglist[1], $arglist[2] );
5489
+ case 'SUMMARY':
5490
+ return $this->setSummary( $arglist[1], $arglist[2] );
5491
+ case 'TRANSP':
5492
+ return $this->setTransp( $arglist[1], $arglist[2] );
5493
+ case 'TRIGGER':
5494
+ 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] );
5495
+ case 'TZID':
5496
+ return $this->setTzid( $arglist[1], $arglist[2] );
5497
+ case 'TZNAME':
5498
+ return $this->setTzname( $arglist[1], $arglist[2], $arglist[3] );
5499
+ case 'TZOFFSETFROM':
5500
+ return $this->setTzoffsetfrom( $arglist[1], $arglist[2] );
5501
+ case 'TZOFFSETTO':
5502
+ return $this->setTzoffsetto( $arglist[1], $arglist[2] );
5503
+ case 'TZURL':
5504
+ return $this->setTzurl( $arglist[1], $arglist[2] );
5505
+ case 'UID':
5506
+ return $this->setUid( $arglist[1], $arglist[2] );
5507
+ case 'URL':
5508
+ return $this->setUrl( $arglist[1], $arglist[2] );
5509
+ default:
5510
+ return $this->setXprop( $arglist[0], $arglist[1], $arglist[2] );
5511
+ }
5512
+ return FALSE;
5513
+ }
5514
+ /*********************************************************************************/
5515
+ /**
5516
+ * parse component unparsed data into properties
5517
+ *
5518
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
5519
+ * @since 2.8.2 - 2011-05-21
5520
+ * @param mixed $unparsedtext, optional, strict rfc2445 formatted, single property string or array of strings
5521
+ * @return bool FALSE if error occurs during parsing
5522
+ *
5523
+ */
5524
+ function parse( $unparsedtext=null ) {
5525
+ if( !empty( $unparsedtext )) {
5526
+ $nl = $this->getConfig( 'nl' );
5527
+ if( is_array( $unparsedtext ))
5528
+ $unparsedtext = implode( '\n'.$nl, $unparsedtext );
5529
+ /* fix line folding */
5530
+ $eolchars = array( "\r\n", "\n\r", "\n", "\r" ); // check all line endings
5531
+ $EOLmark = FALSE;
5532
+ foreach( $eolchars as $eolchar ) {
5533
+ if( !$EOLmark && ( FALSE !== strpos( $unparsedtext, $eolchar ))) {
5534
+ $unparsedtext = str_replace( $eolchar." ", '', $unparsedtext );
5535
+ $unparsedtext = str_replace( $eolchar."\t", '', $unparsedtext );
5536
+ if( $eolchar != $nl )
5537
+ $unparsedtext = str_replace( $eolchar, $nl, $unparsedtext );
5538
+ $EOLmark = TRUE;
5539
+ }
5540
+ }
5541
+ $tmp = explode( $nl, $unparsedtext );
5542
+ $unparsedtext = array();
5543
+ foreach( $tmp as $tmpr )
5544
+ if( !empty( $tmpr ))
5545
+ $unparsedtext[] = $tmpr;
5546
+ }
5547
+ elseif( !isset( $this->unparsed ))
5548
+ $unparsedtext = array();
5549
+ else
5550
+ $unparsedtext = $this->unparsed;
5551
+ $this->unparsed = array();
5552
+ $comp = & $this;
5553
+ $config = $this->getConfig();
5554
+ foreach ( $unparsedtext as $line ) {
5555
+ // echo $comp->objName.": $line<br />"; // test ###
5556
+ if( in_array( strtoupper( substr( $line, 0, 6 )), array( 'END:VA', 'END:ST', 'END:DA' )))
5557
+ $this->components[] = $comp->copy();
5558
+ elseif( 'END:' == strtoupper( substr( $line, 0, 4 )))
5559
+ break;
5560
+ elseif( 'BEGIN:VALARM' == strtoupper( substr( $line, 0, 12 )))
5561
+ $comp = new valarm( $config);
5562
+ elseif( 'BEGIN:STANDARD' == strtoupper( substr( $line, 0, 14 )))
5563
+ $comp = new vtimezone( 'standard', $config );
5564
+ elseif( 'BEGIN:DAYLIGHT' == strtoupper( substr( $line, 0, 14 )))
5565
+ $comp = new vtimezone( 'daylight', $config );
5566
+ elseif( 'BEGIN:' == strtoupper( substr( $line, 0, 6 )))
5567
+ continue;
5568
+ else {
5569
+ $comp->unparsed[] = $line;
5570
+ // echo $comp->objName.": $line<br />\n"; // test ###
5571
+ }
5572
+ }
5573
+ unset( $config );
5574
+ // echo $this->objName.'<br />'.var_export( $this->unparsed, TRUE )."<br />\n"; // test ###
5575
+ /* concatenate property values spread over several lines */
5576
+ $lastix = -1;
5577
+ $propnames = array( 'action', 'attach', 'attendee', 'categories', 'comment', 'completed'
5578
+ , 'contact', 'class', 'created', 'description', 'dtend', 'dtstart'
5579
+ , 'dtstamp', 'due', 'duration', 'exdate', 'exrule', 'freebusy', 'geo'
5580
+ , 'last-modified', 'location', 'organizer', 'percent-complete'
5581
+ , 'priority', 'rdate', 'recurrence-id', 'related-to', 'repeat'
5582
+ , 'request-status', 'resources', 'rrule', 'sequence', 'status'
5583
+ , 'summary', 'transp', 'trigger', 'tzid', 'tzname', 'tzoffsetfrom'
5584
+ , 'tzoffsetto', 'tzurl', 'uid', 'url', 'x-' );
5585
+ $proprows = array();
5586
+ foreach( $this->unparsed as $line ) {
5587
+ $newProp = FALSE;
5588
+ foreach ( $propnames as $propname ) {
5589
+ if( $propname == strtolower( substr( $line, 0, strlen( $propname )))) {
5590
+ $newProp = TRUE;
5591
+ break;
5592
+ }
5593
+ }
5594
+ if( $newProp ) {
5595
+ if( -1 < $lastix )
5596
+ $proprows[$lastix] = $proprows[$lastix];
5597
+ $newProp = FALSE;
5598
+ $lastix++;
5599
+ $proprows[$lastix] = $line;
5600
+ }
5601
+ else
5602
+ $proprows[$lastix] .= '!"#¤%&/()=?'.$line;
5603
+ }
5604
+ /* parse each property 'line' */
5605
+ foreach( $proprows as $line ) {
5606
+ $line = str_replace( '!"#¤%&/()=? ', '', $line );
5607
+ $line = str_replace( '!"#¤%&/()=?', '', $line );
5608
+ if( '\n' == substr( $line, -2 ))
5609
+ $line = substr( $line, 0, strlen( $line ) - 2 );
5610
+ /* get propname, (problem with x-properties, otherwise in previous loop) */
5611
+ $cix = $propname = null;
5612
+ for( $cix=0, $clen = strlen( $line ); $cix < $clen; $cix++ ) {
5613
+ if( in_array( $line[$cix], array( ':', ';' )))
5614
+ break;
5615
+ else {
5616
+ $propname .= $line[$cix];
5617
+ }
5618
+ }
5619
+ if(( 'x-' == substr( $propname, 0, 2 )) || ( 'X-' == substr( $propname, 0, 2 ))) {
5620
+ $propname2 = $propname;
5621
+ $propname = 'X-';
5622
+ }
5623
+ /* rest of the line is opt.params and value */
5624
+ $line = substr( $line, $cix );
5625
+ /* separate attributes from value */
5626
+ $attr = array();
5627
+ $attrix = -1;
5628
+ $clen = strlen( $line );
5629
+ for( $cix=0; $cix < $clen; $cix++ ) {
5630
+ if(( ':' == $line[$cix] ) &&
5631
+ ( '://' != substr( $line, $cix, 3 )) &&
5632
+ ( !in_array( strtolower( substr( $line, $cix - 3, 4 )), array( 'fax:', 'cid:', 'sms:', 'tel:', 'urn:' ))) &&
5633
+ ( !in_array( strtolower( substr( $line, $cix - 4, 5 )), array( 'crid:', 'news:', 'pres:' ))) &&
5634
+ ( 'mailto:' != strtolower( substr( $line, $cix - 6, 7 )))) {
5635
+ $attrEnd = TRUE;
5636
+ if(( $cix < ( $clen - 4 )) &&
5637
+ ctype_digit( substr( $line, $cix+1, 4 ))) { // an URI with a (4pos) portnr??
5638
+ for( $c2ix = $cix; 3 < $c2ix; $c2ix-- ) {
5639
+ if( '://' == substr( $line, $c2ix - 2, 3 )) {
5640
+ $attrEnd = FALSE;
5641
+ break; // an URI with a portnr!!
5642
+ }
5643
+ }
5644
+ }
5645
+ if( $attrEnd) {
5646
+ $line = substr( $line, $cix + 1 );
5647
+ break;
5648
+ }
5649
+ }
5650
+ if( ';' == $line[$cix] )
5651
+ $attr[++$attrix] = null;
5652
+ else
5653
+ $attr[$attrix] .= $line[$cix];
5654
+ }
5655
+ /* make attributes in array format */
5656
+ $propattr = array();
5657
+ foreach( $attr as $attribute ) {
5658
+ $attrsplit = explode( '=', $attribute, 2 );
5659
+ if( 1 < count( $attrsplit ))
5660
+ $propattr[$attrsplit[0]] = $attrsplit[1];
5661
+ else
5662
+ $propattr[] = $attribute;
5663
+ }
5664
+ /* call setProperty( $propname.. . */
5665
+ switch( strtoupper( $propname )) {
5666
+ case 'ATTENDEE':
5667
+ foreach( $propattr as $pix => $attr ) {
5668
+ $attr2 = explode( ',', $attr );
5669
+ if( 1 < count( $attr2 ))
5670
+ $propattr[$pix] = $attr2;
5671
+ }
5672
+ $this->setProperty( $propname, $line, $propattr );
5673
+ break;
5674
+ case 'CATEGORIES':
5675
+ case 'RESOURCES':
5676
+ if( FALSE !== strpos( $line, ',' )) {
5677
+ $content = explode( ',', $line );
5678
+ $clen = count( $content );
5679
+ for( $cix = 0; $cix < $clen; $cix++ ) {
5680
+ if( "\\" == substr($content[$cix], -1)) {
5681
+ $content[$cix] .= ','.$content[$cix + 1];
5682
+ unset($content[$cix + 1]);
5683
+ $cix++;
5684
+ }
5685
+ }
5686
+ if( 1 < count( $content )) {
5687
+ $content = array_values( $content );
5688
+ foreach( $content as $cix => $contentPart )
5689
+ $content[$cix] = calendarComponent::_strunrep( $contentPart );
5690
+ $this->setProperty( $propname, $content, $propattr );
5691
+ break;
5692
+ }
5693
+ else
5694
+ $line = reset( $content );
5695
+ }
5696
+ case 'X-':
5697
+ $propname = ( isset( $propname2 )) ? $propname2 : $propname;
5698
+ case 'COMMENT':
5699
+ case 'CONTACT':
5700
+ case 'DESCRIPTION':
5701
+ case 'LOCATION':
5702
+ case 'SUMMARY':
5703
+ if( empty( $line ))
5704
+ $propattr = null;
5705
+ $this->setProperty( $propname, calendarComponent::_strunrep( $line ), $propattr );
5706
+ unset( $propname2 );
5707
+ break;
5708
+ case 'REQUEST-STATUS':
5709
+ $values = explode( ';', $line, 3 );
5710
+ $values[1] = ( !isset( $values[1] )) ? null : calendarComponent::_strunrep( $values[1] );
5711
+ $values[2] = ( !isset( $values[2] )) ? null : calendarComponent::_strunrep( $values[2] );
5712
+ $this->setProperty( $propname
5713
+ , $values[0] // statcode
5714
+ , $values[1] // statdesc
5715
+ , $values[2] // extdata
5716
+ , $propattr );
5717
+ break;
5718
+ case 'FREEBUSY':
5719
+ $fbtype = ( isset( $propattr['FBTYPE'] )) ? $propattr['FBTYPE'] : ''; // force setting default, if missing
5720
+ unset( $propattr['FBTYPE'] );
5721
+ $values = explode( ',', $line );
5722
+ foreach( $values as $vix => $value ) {
5723
+ $value2 = explode( '/', $value );
5724
+ if( 1 < count( $value2 ))
5725
+ $values[$vix] = $value2;
5726
+ }
5727
+ $this->setProperty( $propname, $fbtype, $values, $propattr );
5728
+ break;
5729
+ case 'GEO':
5730
+ $value = explode( ';', $line, 2 );
5731
+ if( 2 > count( $value ))
5732
+ $value[1] = null;
5733
+ $this->setProperty( $propname, $value[0], $value[1], $propattr );
5734
+ break;
5735
+ case 'EXDATE':
5736
+ $values = ( !empty( $line )) ? explode( ',', $line ) : null;
5737
+ $this->setProperty( $propname, $values, $propattr );
5738
+ break;
5739
+ case 'RDATE':
5740
+ if( empty( $line )) {
5741
+ $this->setProperty( $propname, $line, $propattr );
5742
+ break;
5743
+ }
5744
+ $values = explode( ',', $line );
5745
+ foreach( $values as $vix => $value ) {
5746
+ $value2 = explode( '/', $value );
5747
+ if( 1 < count( $value2 ))
5748
+ $values[$vix] = $value2;
5749
+ }
5750
+ $this->setProperty( $propname, $values, $propattr );
5751
+ break;
5752
+ case 'EXRULE':
5753
+ case 'RRULE':
5754
+ $values = explode( ';', $line );
5755
+ $recur = array();
5756
+ foreach( $values as $value2 ) {
5757
+ if( empty( $value2 ))
5758
+ continue; // ;-char in ending position ???
5759
+ $value3 = explode( '=', $value2, 2 );
5760
+ $rulelabel = strtoupper( $value3[0] );
5761
+ switch( $rulelabel ) {
5762
+ case 'BYDAY': {
5763
+ $value4 = explode( ',', $value3[1] );
5764
+ if( 1 < count( $value4 )) {
5765
+ foreach( $value4 as $v5ix => $value5 ) {
5766
+ $value6 = array();
5767
+ $dayno = $dayname = null;
5768
+ $value5 = trim( (string) $value5 );
5769
+ if(( ctype_alpha( substr( $value5, -1 ))) &&
5770
+ ( ctype_alpha( substr( $value5, -2, 1 )))) {
5771
+ $dayname = substr( $value5, -2, 2 );
5772
+ if( 2 < strlen( $value5 ))
5773
+ $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 ));
5774
+ }
5775
+ if( $dayno )
5776
+ $value6[] = $dayno;
5777
+ if( $dayname )
5778
+ $value6['DAY'] = $dayname;
5779
+ $value4[$v5ix] = $value6;
5780
+ }
5781
+ }
5782
+ else {
5783
+ $value4 = array();
5784
+ $dayno = $dayname = null;
5785
+ $value5 = trim( (string) $value3[1] );
5786
+ if(( ctype_alpha( substr( $value5, -1 ))) &&
5787
+ ( ctype_alpha( substr( $value5, -2, 1 )))) {
5788
+ $dayname = substr( $value5, -2, 2 );
5789
+ if( 2 < strlen( $value5 ))
5790
+ $dayno = substr( $value5, 0, ( strlen( $value5 ) - 2 ));
5791
+ }
5792
+ if( $dayno )
5793
+ $value4[] = $dayno;
5794
+ if( $dayname )
5795
+ $value4['DAY'] = $dayname;
5796
+ }
5797
+ $recur[$rulelabel] = $value4;
5798
+ break;
5799
+ }
5800
+ default: {
5801
+ $value4 = explode( ',', $value3[1] );
5802
+ if( 1 < count( $value4 ))
5803
+ $value3[1] = $value4;
5804
+ $recur[$rulelabel] = $value3[1];
5805
+ break;
5806
+ }
5807
+ } // end - switch $rulelabel
5808
+ } // end - foreach( $values.. .
5809
+ $this->setProperty( $propname, $recur, $propattr );
5810
+ break;
5811
+ case 'ACTION':
5812
+ case 'CLASSIFICATION':
5813
+ case 'STATUS':
5814
+ case 'TRANSP':
5815
+ case 'UID':
5816
+ case 'TZID':
5817
+ case 'RELATED-TO':
5818
+ case 'TZNAME':
5819
+ $line = calendarComponent::_strunrep( $line );
5820
+ default:
5821
+ $this->setProperty( $propname, $line, $propattr );
5822
+ break;
5823
+ } // end switch( $propname.. .
5824
+ } // end - foreach( $proprows.. .
5825
+ unset( $unparsedtext, $this->unparsed, $proprows );
5826
+ if( isset( $this->components ) && is_array( $this->components ) && ( 0 < count( $this->components ))) {
5827
+ $ckeys = array_keys( $this->components );
5828
+ foreach( $ckeys as $ckey ) {
5829
+ if( !empty( $this->components[$ckey] ) && !empty( $this->components[$ckey]->unparsed )) {
5830
+ $this->components[$ckey]->parse();
5831
+ }
5832
+ }
5833
+ }
5834
+ return TRUE;
5835
+ }
5836
+ /*********************************************************************************/
5837
+ /*********************************************************************************/
5838
+ /**
5839
+ * return a copy of this component
5840
+ *
5841
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
5842
+ * @since 2.8.8 - 2011-03-15
5843
+ * @return object
5844
+ */
5845
+ function copy() {
5846
+ $serialized_contents = serialize( $this );
5847
+ $copy = unserialize( $serialized_contents );
5848
+ return $copy;
5849
+ }
5850
+ /*********************************************************************************/
5851
+ /*********************************************************************************/
5852
+ /**
5853
+ * delete calendar subcomponent from component container
5854
+ *
5855
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
5856
+ * @since 2.8.8 - 2011-03-15
5857
+ * @param mixed $arg1 ordno / component type / component uid
5858
+ * @param mixed $arg2 optional, ordno if arg1 = component type
5859
+ * @return void
5860
+ */
5861
+ function deleteComponent( $arg1, $arg2=FALSE ) {
5862
+ if( !isset( $this->components )) return FALSE;
5863
+ $argType = $index = null;
5864
+ if ( ctype_digit( (string) $arg1 )) {
5865
+ $argType = 'INDEX';
5866
+ $index = (int) $arg1 - 1;
5867
+ }
5868
+ elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
5869
+ $argType = strtolower( $arg1 );
5870
+ $index = ( !empty( $arg2 ) && ctype_digit( (string) $arg2 )) ? (( int ) $arg2 - 1 ) : 0;
5871
+ }
5872
+ $cix2dC = 0;
5873
+ foreach ( $this->components as $cix => $component) {
5874
+ if( empty( $component )) continue;
5875
+ if(( 'INDEX' == $argType ) && ( $index == $cix )) {
5876
+ unset( $this->components[$cix] );
5877
+ return TRUE;
5878
+ }
5879
+ elseif( $argType == $component->objName ) {
5880
+ if( $index == $cix2dC ) {
5881
+ unset( $this->components[$cix] );
5882
+ return TRUE;
5883
+ }
5884
+ $cix2dC++;
5885
+ }
5886
+ elseif( !$argType && ($arg1 == $component->getProperty( 'uid' ))) {
5887
+ unset( $this->components[$cix] );
5888
+ return TRUE;
5889
+ }
5890
+ }
5891
+ return FALSE;
5892
+ }
5893
+ /**
5894
+ * get calendar component subcomponent from component container
5895
+ *
5896
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
5897
+ * @since 2.8.8 - 2011-03-15
5898
+ * @param mixed $arg1 optional, ordno/component type/ component uid
5899
+ * @param mixed $arg2 optional, ordno if arg1 = component type
5900
+ * @return object
5901
+ */
5902
+ function getComponent ( $arg1=FALSE, $arg2=FALSE ) {
5903
+ if( !isset( $this->components )) return FALSE;
5904
+ $index = $argType = null;
5905
+ if ( !$arg1 ) {
5906
+ $argType = 'INDEX';
5907
+ $index = $this->compix['INDEX'] =
5908
+ ( isset( $this->compix['INDEX'] )) ? $this->compix['INDEX'] + 1 : 1;
5909
+ }
5910
+ elseif ( ctype_digit( (string) $arg1 )) {
5911
+ $argType = 'INDEX';
5912
+ $index = (int) $arg1;
5913
+ unset( $this->compix );
5914
+ }
5915
+ elseif(( strlen( $arg1 ) <= strlen( 'vfreebusy' )) && ( FALSE === strpos( $arg1, '@' ))) {
5916
+ unset( $this->compix['INDEX'] );
5917
+ $argType = strtolower( $arg1 );
5918
+ if( !$arg2 )
5919
+ $index = $this->compix[$argType] = ( isset( $this->compix[$argType] )) ? $this->compix[$argType] + 1 : 1;
5920
+ else
5921
+ $index = (int) $arg2;
5922
+ }
5923
+ $index -= 1;
5924
+ $ckeys = array_keys( $this->components );
5925
+ if( !empty( $index) && ( $index > end( $ckeys )))
5926
+ return FALSE;
5927
+ $cix2gC = 0;
5928
+ foreach( $this->components as $cix => $component ) {
5929
+ if( empty( $component )) continue;
5930
+ if(( 'INDEX' == $argType ) && ( $index == $cix ))
5931
+ return $component->copy();
5932
+ elseif( $argType == $component->objName ) {
5933
+ if( $index == $cix2gC )
5934
+ return $component->copy();
5935
+ $cix2gC++;
5936
+ }
5937
+ elseif( !$argType && ( $arg1 == $component->getProperty( 'uid' )))
5938
+ return $component->copy();
5939
+ }
5940
+ /* not found.. . */
5941
+ unset( $this->compix );
5942
+ return false;
5943
+ }
5944
+ /**
5945
+ * add calendar component as subcomponent to container for subcomponents
5946
+ *
5947
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
5948
+ * @since 1.x.x - 2007-04-24
5949
+ * @param object $component calendar component
5950
+ * @return void
5951
+ */
5952
+ function addSubComponent ( $component ) {
5953
+ $this->setComponent( $component );
5954
+ }
5955
+ /**
5956
+ * create new calendar component subcomponent, already included within component
5957
+ *
5958
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
5959
+ * @since 2.6.33 - 2011-01-03
5960
+ * @param string $compType subcomponent type
5961
+ * @return object (reference)
5962
+ */
5963
+ function & newComponent( $compType ) {
5964
+ $config = $this->getConfig();
5965
+ $keys = array_keys( $this->components );
5966
+ $ix = end( $keys) + 1;
5967
+ switch( strtoupper( $compType )) {
5968
+ case 'ALARM':
5969
+ case 'VALARM':
5970
+ $this->components[$ix] = new valarm( $config );
5971
+ break;
5972
+ case 'STANDARD':
5973
+ array_unshift( $this->components, new vtimezone( 'STANDARD', $config ));
5974
+ $ix = 0;
5975
+ break;
5976
+ case 'DAYLIGHT':
5977
+ $this->components[$ix] = new vtimezone( 'DAYLIGHT', $config );
5978
+ break;
5979
+ default:
5980
+ return FALSE;
5981
+ }
5982
+ return $this->components[$ix];
5983
+ }
5984
+ /**
5985
+ * add calendar component as subcomponent to container for subcomponents
5986
+ *
5987
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
5988
+ * @since 2.8.8 - 2011-03-15
5989
+ * @param object $component calendar component
5990
+ * @param mixed $arg1 optional, ordno/component type/ component uid
5991
+ * @param mixed $arg2 optional, ordno if arg1 = component type
5992
+ * @return bool
5993
+ */
5994
+ function setComponent( $component, $arg1=FALSE, $arg2=FALSE ) {
5995
+ if( !isset( $this->components )) return FALSE;
5996
+ $component->setConfig( $this->getConfig(), FALSE, TRUE );
5997
+ if( !in_array( $component->objName, array( 'valarm', 'vtimezone', 'standard', 'daylight' ))) {
5998
+ /* make sure dtstamp and uid is set */
5999
+ $dummy = $component->getProperty( 'dtstamp' );
6000
+ $dummy = $component->getProperty( 'uid' );
6001
+ }
6002
+ if( !$arg1 ) { // plain insert, last in chain
6003
+ $this->components[] = $component->copy();
6004
+ return TRUE;
6005
+ }
6006
+ $argType = $index = null;
6007
+ if ( ctype_digit( (string) $arg1 )) { // index insert/replace
6008
+ $argType = 'INDEX';
6009
+ $index = (int) $arg1 - 1;
6010
+ }
6011
+ elseif( in_array( strtolower( $arg1 ), array( 'vevent', 'vtodo', 'vjournal', 'vfreebusy', 'valarm', 'vtimezone' ))) {
6012
+ $argType = strtolower( $arg1 );
6013
+ $index = ( ctype_digit( (string) $arg2 )) ? ((int) $arg2) - 1 : 0;
6014
+ }
6015
+ // else if arg1 is set, arg1 must be an UID
6016
+ $cix2sC = 0;
6017
+ foreach ( $this->components as $cix => $component2 ) {
6018
+ if( empty( $component2 )) continue;
6019
+ if(( 'INDEX' == $argType ) && ( $index == $cix )) { // index insert/replace
6020
+ $this->components[$cix] = $component->copy();
6021
+ return TRUE;
6022
+ }
6023
+ elseif( $argType == $component2->objName ) { // component Type index insert/replace
6024
+ if( $index == $cix2sC ) {
6025
+ $this->components[$cix] = $component->copy();
6026
+ return TRUE;
6027
+ }
6028
+ $cix2sC++;
6029
+ }
6030
+ elseif( !$argType && ( $arg1 == $component2->getProperty( 'uid' ))) { // UID insert/replace
6031
+ $this->components[$cix] = $component->copy();
6032
+ return TRUE;
6033
+ }
6034
+ }
6035
+ /* arg1=index and not found.. . insert at index .. .*/
6036
+ if( 'INDEX' == $argType ) {
6037
+ $this->components[$index] = $component->copy();
6038
+ ksort( $this->components, SORT_NUMERIC );
6039
+ }
6040
+ else /* not found.. . insert last in chain anyway .. .*/
6041
+ $this->components[] = $component->copy();
6042
+ return TRUE;
6043
+ }
6044
+ /**
6045
+ * creates formatted output for subcomponents
6046
+ *
6047
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6048
+ * @since 2.6.27 - 2010-12-12
6049
+ * @return string
6050
+ */
6051
+ function createSubComponent() {
6052
+ $output = null;
6053
+ foreach( $this->components as $component ) {
6054
+ if( empty( $component )) continue;
6055
+ $component->setConfig( $this->getConfig(), FALSE, TRUE );
6056
+ $output .= $component->createComponent( $this->xcaldecl );
6057
+ }
6058
+ return $output;
6059
+ }
6060
+ /********************************************************************************/
6061
+ /**
6062
+ * break lines at pos 75
6063
+ *
6064
+ * Lines of text SHOULD NOT be longer than 75 octets, excluding the line
6065
+ * break. Long content lines SHOULD be split into a multiple line
6066
+ * representations using a line "folding" technique. That is, a long
6067
+ * line can be split between any two characters by inserting a CRLF
6068
+ * immediately followed by a single linear white space character (i.e.,
6069
+ * SPACE, US-ASCII decimal 32 or HTAB, US-ASCII decimal 9). Any sequence
6070
+ * of CRLF followed immediately by a single linear white space character
6071
+ * is ignored (i.e., removed) when processing the content type.
6072
+ *
6073
+ * Edited 2007-08-26 by Anders Litzell, anders@litzell.se to fix bug where
6074
+ * the reserved expression "\n" in the arg $string could be broken up by the
6075
+ * folding of lines, causing ambiguity in the return string.
6076
+ * Fix uses var $breakAtChar=75 and breaks the line at $breakAtChar-1 if need be.
6077
+ *
6078
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6079
+ * @since 2.6.13 - 2010-12-06
6080
+ * @param string $value
6081
+ * @return string
6082
+ */
6083
+ function _size75( $string ) {
6084
+ $tmp = $string;
6085
+ $string = null;
6086
+ /* if PHP is config with mb_string.. . */
6087
+ if( defined( MB_OVERLOAD_STRING )) {
6088
+ $strlen = mb_strlen( $tmp );
6089
+ while( $strlen > 75 ) {
6090
+ $breakAtChar = 75;
6091
+ if( substr( $tmp, ( $breakAtChar - 1 ), strlen( '\n' )) == '\n' )
6092
+ $breakAtChar = $breakAtChar - 1;
6093
+ $string .= mb_substr( $tmp, 0, $breakAtChar );
6094
+ if( '\n' == substr( $string, ( 0 - strlen( '\n' ))))
6095
+ $string = substr( $string, 0, ( strlen( $string ) - strlen( '\n' )));
6096
+ if( $this->nl != mb_substr( $string, ( 0 - strlen( $this->nl ))))
6097
+ $string .= $this->nl;
6098
+ $tmp = ' '.mb_substr( $tmp, $breakAtChar );
6099
+ $strlen = mb_strlen( $tmp );
6100
+ } // end while
6101
+ if( 0 < $strlen ) {
6102
+ $string .= $tmp; // the rest
6103
+ if( '\n' == substr( $string, ( 0 - strlen( '\n' ))))
6104
+ $string = substr( $string, 0, ( strlen( $string ) - strlen( '\n' )));
6105
+ if( $this->nl != mb_substr( $string, ( 0 - strlen( $this->nl ))))
6106
+ $string .= $this->nl;
6107
+ }
6108
+ return $string;
6109
+ }
6110
+ /* if PHP is not config with mb_string.. . */
6111
+ $eolcharlen = strlen( '\n' );
6112
+ while( TRUE ) {
6113
+ $bytecnt = strlen( $tmp );
6114
+ $charCnt = $ix = 0;
6115
+ for( $ix = 0; $ix < $bytecnt; $ix++ ) {
6116
+ if(( 73 < $charCnt ) && ( '\n' == substr( $tmp, $ix, $eolcharlen ))) {
6117
+ $ix += $eolcharlen;
6118
+ break; // break when '\n' and eol
6119
+ }
6120
+ elseif( 74 < $charCnt )
6121
+ break; // always break for-loop here
6122
+ else {
6123
+ $byte = ord( $tmp[$ix] );
6124
+ if ($byte <= 127) { // add a one byte character
6125
+ $string .= substr( $tmp, $ix, 1 );
6126
+ $charCnt += 1;
6127
+ }
6128
+ else if ($byte >= 194 && $byte <= 223) { // start byte in two byte character
6129
+ $string .= substr( $tmp, $ix, 2 ); // add a two bytes character
6130
+ $charCnt += 1;
6131
+ }
6132
+ else if ($byte >= 224 && $byte <= 239) { // start byte in three bytes character
6133
+ $string .= substr( $tmp, $ix, 3 ); // add a three bytes character
6134
+ $charCnt += 1;
6135
+ }
6136
+ else if ($byte >= 240 && $byte <= 244) { // start byte in four bytes character
6137
+ $string .= substr( $tmp, $ix, 4 ); // add a four bytes character
6138
+ $charCnt += 1;
6139
+ }
6140
+ }
6141
+ } // end for
6142
+ if( '\n' == substr( $string, ( 0 - strlen( '\n' ))))
6143
+ $string = substr( $string, 0, ( strlen( $string ) - strlen( '\n' )));
6144
+ if( $this->nl != substr( $string, ( 0 - strlen( $this->nl ))))
6145
+ $string .= $this->nl;
6146
+ $tmp = substr( $tmp, $ix );
6147
+ if( empty( $tmp ))
6148
+ break; // while-loop breakes here
6149
+ else
6150
+ $tmp = ' '.$tmp;
6151
+ } // end while
6152
+ if( !empty( $tmp )) {
6153
+ if( '\n' == substr( $string, ( 0 - strlen( '\n' ))))
6154
+ $string = substr( $string, 0, ( strlen( $string ) - strlen( '\n' ))).$this->nl;
6155
+ if( $this->nl != substr( $string, ( 0 - strlen( $this->nl ))))
6156
+ $string .= $this->nl;
6157
+ }
6158
+ return $string;
6159
+ }
6160
+ /**
6161
+ * special characters management output
6162
+ *
6163
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6164
+ * @since 2.6.15 - 2010-09-24
6165
+ * @param string $string
6166
+ * @return string
6167
+ */
6168
+ function _strrep( $string ) {
6169
+ switch( $this->format ) {
6170
+ case 'xcal':
6171
+ $string = str_replace( '\n', $this->nl, $string);
6172
+ $string = htmlspecialchars( strip_tags( stripslashes( urldecode ( $string ))));
6173
+ break;
6174
+ default:
6175
+ $pos = 0;
6176
+ $specChars = array( 'n', 'N', 'r', ',', ';' );
6177
+ while( $pos <= strlen( $string )) {
6178
+ $pos = strpos( $string, "\\", $pos );
6179
+ if( FALSE === $pos )
6180
+ break;
6181
+ if( !in_array( substr( $string, $pos, 1 ), $specChars )) {
6182
+ $string = substr( $string, 0, $pos )."\\".substr( $string, ( $pos + 1 ));
6183
+ $pos += 1;
6184
+ }
6185
+ $pos += 1;
6186
+ }
6187
+ if( FALSE !== strpos( $string, '"' ))
6188
+ $string = str_replace('"', "'", $string);
6189
+ if( FALSE !== strpos( $string, ',' ))
6190
+ $string = str_replace(',', '\,', $string);
6191
+ if( FALSE !== strpos( $string, ';' ))
6192
+ $string = str_replace(';', '\;', $string);
6193
+
6194
+ if( FALSE !== strpos( $string, "\r\n" ))
6195
+ $string = str_replace( "\r\n", '\n', $string);
6196
+ elseif( FALSE !== strpos( $string, "\r" ))
6197
+ $string = str_replace( "\r", '\n', $string);
6198
+
6199
+ elseif( FALSE !== strpos( $string, "\n" ))
6200
+ $string = str_replace( "\n", '\n', $string);
6201
+
6202
+ if( FALSE !== strpos( $string, '\N' ))
6203
+ $string = str_replace( '\N', '\n', $string);
6204
+ // if( FALSE !== strpos( $string, $this->nl ))
6205
+ $string = str_replace( $this->nl, '\n', $string);
6206
+ break;
6207
+ }
6208
+ return $string;
6209
+ }
6210
+ /**
6211
+ * special characters management input (from iCal file)
6212
+ *
6213
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6214
+ * @since 2.6.22 - 2010-10-17
6215
+ * @param string $string
6216
+ * @return string
6217
+ */
6218
+ static function _strunrep( $string ) {
6219
+ $string = str_replace( '\\\\', '\\', $string);
6220
+ $string = str_replace( '\,', ',', $string);
6221
+ $string = str_replace( '\;', ';', $string);
6222
+ // $string = str_replace( '\n', $this->nl, $string); // ??
6223
+ return $string;
6224
+ }
6225
+ }
6226
+ /*********************************************************************************/
6227
+ /*********************************************************************************/
6228
+ /**
6229
+ * class for calendar component VEVENT
6230
+ *
6231
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6232
+ * @since 2.5.1 - 2008-10-12
6233
+ */
6234
+ class vevent extends calendarComponent {
6235
+ var $attach;
6236
+ var $attendee;
6237
+ var $categories;
6238
+ var $comment;
6239
+ var $contact;
6240
+ var $class;
6241
+ var $created;
6242
+ var $description;
6243
+ var $dtend;
6244
+ var $dtstart;
6245
+ var $duration;
6246
+ var $exdate;
6247
+ var $exrule;
6248
+ var $geo;
6249
+ var $lastmodified;
6250
+ var $location;
6251
+ var $organizer;
6252
+ var $priority;
6253
+ var $rdate;
6254
+ var $recurrenceid;
6255
+ var $relatedto;
6256
+ var $requeststatus;
6257
+ var $resources;
6258
+ var $rrule;
6259
+ var $sequence;
6260
+ var $status;
6261
+ var $summary;
6262
+ var $transp;
6263
+ var $url;
6264
+ var $xprop;
6265
+ // component subcomponents container
6266
+ var $components;
6267
+ /**
6268
+ * constructor for calendar component VEVENT object
6269
+ *
6270
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6271
+ * @since 2.8.2 - 2011-05-01
6272
+ * @param array $config
6273
+ * @return void
6274
+ */
6275
+ function vevent( $config = array()) {
6276
+ $this->calendarComponent();
6277
+
6278
+ $this->attach = '';
6279
+ $this->attendee = '';
6280
+ $this->categories = '';
6281
+ $this->class = '';
6282
+ $this->comment = '';
6283
+ $this->contact = '';
6284
+ $this->created = '';
6285
+ $this->description = '';
6286
+ $this->dtstart = '';
6287
+ $this->dtend = '';
6288
+ $this->duration = '';
6289
+ $this->exdate = '';
6290
+ $this->exrule = '';
6291
+ $this->geo = '';
6292
+ $this->lastmodified = '';
6293
+ $this->location = '';
6294
+ $this->organizer = '';
6295
+ $this->priority = '';
6296
+ $this->rdate = '';
6297
+ $this->recurrenceid = '';
6298
+ $this->relatedto = '';
6299
+ $this->requeststatus = '';
6300
+ $this->resources = '';
6301
+ $this->rrule = '';
6302
+ $this->sequence = '';
6303
+ $this->status = '';
6304
+ $this->summary = '';
6305
+ $this->transp = '';
6306
+ $this->url = '';
6307
+ $this->xprop = '';
6308
+
6309
+ $this->components = array();
6310
+
6311
+ if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
6312
+ $config['language'] = ICAL_LANG;
6313
+ if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
6314
+ if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
6315
+ if( !isset( $config['format'] )) $config['format'] = 'iCal';
6316
+ if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
6317
+ $this->setConfig( $config );
6318
+
6319
+ }
6320
+ /**
6321
+ * create formatted output for calendar component VEVENT object instance
6322
+ *
6323
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6324
+ * @since 2.5.1 - 2008-11-07
6325
+ * @param array $xcaldecl
6326
+ * @return string
6327
+ */
6328
+ function createComponent( &$xcaldecl ) {
6329
+ $objectname = $this->_createFormat();
6330
+ $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
6331
+ $component .= $this->createUid();
6332
+ $component .= $this->createDtstamp();
6333
+ $component .= $this->createAttach();
6334
+ $component .= $this->createAttendee();
6335
+ $component .= $this->createCategories();
6336
+ $component .= $this->createComment();
6337
+ $component .= $this->createContact();
6338
+ $component .= $this->createClass();
6339
+ $component .= $this->createCreated();
6340
+ $component .= $this->createDescription();
6341
+ $component .= $this->createDtstart();
6342
+ $component .= $this->createDtend();
6343
+ $component .= $this->createDuration();
6344
+ $component .= $this->createExdate();
6345
+ $component .= $this->createExrule();
6346
+ $component .= $this->createGeo();
6347
+ $component .= $this->createLastModified();
6348
+ $component .= $this->createLocation();
6349
+ $component .= $this->createOrganizer();
6350
+ $component .= $this->createPriority();
6351
+ $component .= $this->createRdate();
6352
+ $component .= $this->createRrule();
6353
+ $component .= $this->createRelatedTo();
6354
+ $component .= $this->createRequestStatus();
6355
+ $component .= $this->createRecurrenceid();
6356
+ $component .= $this->createResources();
6357
+ $component .= $this->createSequence();
6358
+ $component .= $this->createStatus();
6359
+ $component .= $this->createSummary();
6360
+ $component .= $this->createTransp();
6361
+ $component .= $this->createUrl();
6362
+ $component .= $this->createXprop();
6363
+ $component .= $this->createSubComponent();
6364
+ $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
6365
+ if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
6366
+ foreach( $this->xcaldecl as $localxcaldecl )
6367
+ $xcaldecl[] = $localxcaldecl;
6368
+ }
6369
+ return $component;
6370
+ }
6371
+ }
6372
+ /*********************************************************************************/
6373
+ /*********************************************************************************/
6374
+ /**
6375
+ * class for calendar component VTODO
6376
+ *
6377
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6378
+ * @since 2.5.1 - 2008-10-12
6379
+ */
6380
+ class vtodo extends calendarComponent {
6381
+ var $attach;
6382
+ var $attendee;
6383
+ var $categories;
6384
+ var $comment;
6385
+ var $completed;
6386
+ var $contact;
6387
+ var $class;
6388
+ var $created;
6389
+ var $description;
6390
+ var $dtstart;
6391
+ var $due;
6392
+ var $duration;
6393
+ var $exdate;
6394
+ var $exrule;
6395
+ var $geo;
6396
+ var $lastmodified;
6397
+ var $location;
6398
+ var $organizer;
6399
+ var $percentcomplete;
6400
+ var $priority;
6401
+ var $rdate;
6402
+ var $recurrenceid;
6403
+ var $relatedto;
6404
+ var $requeststatus;
6405
+ var $resources;
6406
+ var $rrule;
6407
+ var $sequence;
6408
+ var $status;
6409
+ var $summary;
6410
+ var $url;
6411
+ var $xprop;
6412
+ // component subcomponents container
6413
+ var $components;
6414
+ /**
6415
+ * constructor for calendar component VTODO object
6416
+ *
6417
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6418
+ * @since 2.8.2 - 2011-05-01
6419
+ * @param array $config
6420
+ * @return void
6421
+ */
6422
+ function vtodo( $config = array()) {
6423
+ $this->calendarComponent();
6424
+
6425
+ $this->attach = '';
6426
+ $this->attendee = '';
6427
+ $this->categories = '';
6428
+ $this->class = '';
6429
+ $this->comment = '';
6430
+ $this->completed = '';
6431
+ $this->contact = '';
6432
+ $this->created = '';
6433
+ $this->description = '';
6434
+ $this->dtstart = '';
6435
+ $this->due = '';
6436
+ $this->duration = '';
6437
+ $this->exdate = '';
6438
+ $this->exrule = '';
6439
+ $this->geo = '';
6440
+ $this->lastmodified = '';
6441
+ $this->location = '';
6442
+ $this->organizer = '';
6443
+ $this->percentcomplete = '';
6444
+ $this->priority = '';
6445
+ $this->rdate = '';
6446
+ $this->recurrenceid = '';
6447
+ $this->relatedto = '';
6448
+ $this->requeststatus = '';
6449
+ $this->resources = '';
6450
+ $this->rrule = '';
6451
+ $this->sequence = '';
6452
+ $this->status = '';
6453
+ $this->summary = '';
6454
+ $this->url = '';
6455
+ $this->xprop = '';
6456
+
6457
+ $this->components = array();
6458
+
6459
+ if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
6460
+ $config['language'] = ICAL_LANG;
6461
+ if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
6462
+ if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
6463
+ if( !isset( $config['format'] )) $config['format'] = 'iCal';
6464
+ if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
6465
+ $this->setConfig( $config );
6466
+
6467
+ }
6468
+ /**
6469
+ * create formatted output for calendar component VTODO object instance
6470
+ *
6471
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6472
+ * @since 2.5.1 - 2008-11-07
6473
+ * @param array $xcaldecl
6474
+ * @return string
6475
+ */
6476
+ function createComponent( &$xcaldecl ) {
6477
+ $objectname = $this->_createFormat();
6478
+ $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
6479
+ $component .= $this->createUid();
6480
+ $component .= $this->createDtstamp();
6481
+ $component .= $this->createAttach();
6482
+ $component .= $this->createAttendee();
6483
+ $component .= $this->createCategories();
6484
+ $component .= $this->createClass();
6485
+ $component .= $this->createComment();
6486
+ $component .= $this->createCompleted();
6487
+ $component .= $this->createContact();
6488
+ $component .= $this->createCreated();
6489
+ $component .= $this->createDescription();
6490
+ $component .= $this->createDtstart();
6491
+ $component .= $this->createDue();
6492
+ $component .= $this->createDuration();
6493
+ $component .= $this->createExdate();
6494
+ $component .= $this->createExrule();
6495
+ $component .= $this->createGeo();
6496
+ $component .= $this->createLastModified();
6497
+ $component .= $this->createLocation();
6498
+ $component .= $this->createOrganizer();
6499
+ $component .= $this->createPercentComplete();
6500
+ $component .= $this->createPriority();
6501
+ $component .= $this->createRdate();
6502
+ $component .= $this->createRelatedTo();
6503
+ $component .= $this->createRequestStatus();
6504
+ $component .= $this->createRecurrenceid();
6505
+ $component .= $this->createResources();
6506
+ $component .= $this->createRrule();
6507
+ $component .= $this->createSequence();
6508
+ $component .= $this->createStatus();
6509
+ $component .= $this->createSummary();
6510
+ $component .= $this->createUrl();
6511
+ $component .= $this->createXprop();
6512
+ $component .= $this->createSubComponent();
6513
+ $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
6514
+ if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
6515
+ foreach( $this->xcaldecl as $localxcaldecl )
6516
+ $xcaldecl[] = $localxcaldecl;
6517
+ }
6518
+ return $component;
6519
+ }
6520
+ }
6521
+ /*********************************************************************************/
6522
+ /*********************************************************************************/
6523
+ /**
6524
+ * class for calendar component VJOURNAL
6525
+ *
6526
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6527
+ * @since 2.5.1 - 2008-10-12
6528
+ */
6529
+ class vjournal extends calendarComponent {
6530
+ var $attach;
6531
+ var $attendee;
6532
+ var $categories;
6533
+ var $comment;
6534
+ var $contact;
6535
+ var $class;
6536
+ var $created;
6537
+ var $description;
6538
+ var $dtstart;
6539
+ var $exdate;
6540
+ var $exrule;
6541
+ var $lastmodified;
6542
+ var $organizer;
6543
+ var $rdate;
6544
+ var $recurrenceid;
6545
+ var $relatedto;
6546
+ var $requeststatus;
6547
+ var $rrule;
6548
+ var $sequence;
6549
+ var $status;
6550
+ var $summary;
6551
+ var $url;
6552
+ var $xprop;
6553
+ /**
6554
+ * constructor for calendar component VJOURNAL object
6555
+ *
6556
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6557
+ * @since 2.8.2 - 2011-05-01
6558
+ * @param array $config
6559
+ * @return void
6560
+ */
6561
+ function vjournal( $config = array()) {
6562
+ $this->calendarComponent();
6563
+
6564
+ $this->attach = '';
6565
+ $this->attendee = '';
6566
+ $this->categories = '';
6567
+ $this->class = '';
6568
+ $this->comment = '';
6569
+ $this->contact = '';
6570
+ $this->created = '';
6571
+ $this->description = '';
6572
+ $this->dtstart = '';
6573
+ $this->exdate = '';
6574
+ $this->exrule = '';
6575
+ $this->lastmodified = '';
6576
+ $this->organizer = '';
6577
+ $this->rdate = '';
6578
+ $this->recurrenceid = '';
6579
+ $this->relatedto = '';
6580
+ $this->requeststatus = '';
6581
+ $this->rrule = '';
6582
+ $this->sequence = '';
6583
+ $this->status = '';
6584
+ $this->summary = '';
6585
+ $this->url = '';
6586
+ $this->xprop = '';
6587
+
6588
+ if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
6589
+ $config['language'] = ICAL_LANG;
6590
+ if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
6591
+ if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
6592
+ if( !isset( $config['format'] )) $config['format'] = 'iCal';
6593
+ if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
6594
+ $this->setConfig( $config );
6595
+
6596
+ }
6597
+ /**
6598
+ * create formatted output for calendar component VJOURNAL object instance
6599
+ *
6600
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6601
+ * @since 2.5.1 - 2008-10-12
6602
+ * @param array $xcaldecl
6603
+ * @return string
6604
+ */
6605
+ function createComponent( &$xcaldecl ) {
6606
+ $objectname = $this->_createFormat();
6607
+ $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
6608
+ $component .= $this->createUid();
6609
+ $component .= $this->createDtstamp();
6610
+ $component .= $this->createAttach();
6611
+ $component .= $this->createAttendee();
6612
+ $component .= $this->createCategories();
6613
+ $component .= $this->createClass();
6614
+ $component .= $this->createComment();
6615
+ $component .= $this->createContact();
6616
+ $component .= $this->createCreated();
6617
+ $component .= $this->createDescription();
6618
+ $component .= $this->createDtstart();
6619
+ $component .= $this->createExdate();
6620
+ $component .= $this->createExrule();
6621
+ $component .= $this->createLastModified();
6622
+ $component .= $this->createOrganizer();
6623
+ $component .= $this->createRdate();
6624
+ $component .= $this->createRequestStatus();
6625
+ $component .= $this->createRecurrenceid();
6626
+ $component .= $this->createRelatedTo();
6627
+ $component .= $this->createRrule();
6628
+ $component .= $this->createSequence();
6629
+ $component .= $this->createStatus();
6630
+ $component .= $this->createSummary();
6631
+ $component .= $this->createUrl();
6632
+ $component .= $this->createXprop();
6633
+ $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
6634
+ if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
6635
+ foreach( $this->xcaldecl as $localxcaldecl )
6636
+ $xcaldecl[] = $localxcaldecl;
6637
+ }
6638
+ return $component;
6639
+ }
6640
+ }
6641
+ /*********************************************************************************/
6642
+ /*********************************************************************************/
6643
+ /**
6644
+ * class for calendar component VFREEBUSY
6645
+ *
6646
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6647
+ * @since 2.5.1 - 2008-10-12
6648
+ */
6649
+ class vfreebusy extends calendarComponent {
6650
+ var $attendee;
6651
+ var $comment;
6652
+ var $contact;
6653
+ var $dtend;
6654
+ var $dtstart;
6655
+ var $duration;
6656
+ var $freebusy;
6657
+ var $organizer;
6658
+ var $requeststatus;
6659
+ var $url;
6660
+ var $xprop;
6661
+ // component subcomponents container
6662
+ var $components;
6663
+ /**
6664
+ * constructor for calendar component VFREEBUSY object
6665
+ *
6666
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6667
+ * @since 2.8.2 - 2011-05-01
6668
+ * @param array $config
6669
+ * @return void
6670
+ */
6671
+ function vfreebusy( $config = array()) {
6672
+ $this->calendarComponent();
6673
+
6674
+ $this->attendee = '';
6675
+ $this->comment = '';
6676
+ $this->contact = '';
6677
+ $this->dtend = '';
6678
+ $this->dtstart = '';
6679
+ $this->duration = '';
6680
+ $this->freebusy = '';
6681
+ $this->organizer = '';
6682
+ $this->requeststatus = '';
6683
+ $this->url = '';
6684
+ $this->xprop = '';
6685
+
6686
+ if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
6687
+ $config['language'] = ICAL_LANG;
6688
+ if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
6689
+ if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
6690
+ if( !isset( $config['format'] )) $config['format'] = 'iCal';
6691
+ if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
6692
+ $this->setConfig( $config );
6693
+
6694
+ }
6695
+ /**
6696
+ * create formatted output for calendar component VFREEBUSY object instance
6697
+ *
6698
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6699
+ * @since 2.3.1 - 2007-11-19
6700
+ * @param array $xcaldecl
6701
+ * @return string
6702
+ */
6703
+ function createComponent( &$xcaldecl ) {
6704
+ $objectname = $this->_createFormat();
6705
+ $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
6706
+ $component .= $this->createUid();
6707
+ $component .= $this->createDtstamp();
6708
+ $component .= $this->createAttendee();
6709
+ $component .= $this->createComment();
6710
+ $component .= $this->createContact();
6711
+ $component .= $this->createDtstart();
6712
+ $component .= $this->createDtend();
6713
+ $component .= $this->createDuration();
6714
+ $component .= $this->createFreebusy();
6715
+ $component .= $this->createOrganizer();
6716
+ $component .= $this->createRequestStatus();
6717
+ $component .= $this->createUrl();
6718
+ $component .= $this->createXprop();
6719
+ $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
6720
+ if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
6721
+ foreach( $this->xcaldecl as $localxcaldecl )
6722
+ $xcaldecl[] = $localxcaldecl;
6723
+ }
6724
+ return $component;
6725
+ }
6726
+ }
6727
+ /*********************************************************************************/
6728
+ /*********************************************************************************/
6729
+ /**
6730
+ * class for calendar component VALARM
6731
+ *
6732
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6733
+ * @since 2.5.1 - 2008-10-12
6734
+ */
6735
+ class valarm extends calendarComponent {
6736
+ var $action;
6737
+ var $attach;
6738
+ var $attendee;
6739
+ var $description;
6740
+ var $duration;
6741
+ var $repeat;
6742
+ var $summary;
6743
+ var $trigger;
6744
+ var $xprop;
6745
+ /**
6746
+ * constructor for calendar component VALARM object
6747
+ *
6748
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6749
+ * @since 2.8.2 - 2011-05-01
6750
+ * @param array $config
6751
+ * @return void
6752
+ */
6753
+ function valarm( $config = array()) {
6754
+ $this->calendarComponent();
6755
+
6756
+ $this->action = '';
6757
+ $this->attach = '';
6758
+ $this->attendee = '';
6759
+ $this->description = '';
6760
+ $this->duration = '';
6761
+ $this->repeat = '';
6762
+ $this->summary = '';
6763
+ $this->trigger = '';
6764
+ $this->xprop = '';
6765
+
6766
+ if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
6767
+ $config['language'] = ICAL_LANG;
6768
+ if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
6769
+ if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
6770
+ if( !isset( $config['format'] )) $config['format'] = 'iCal';
6771
+ if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
6772
+ $this->setConfig( $config );
6773
+
6774
+ }
6775
+ /**
6776
+ * create formatted output for calendar component VALARM object instance
6777
+ *
6778
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6779
+ * @since 2.5.1 - 2008-10-22
6780
+ * @param array $xcaldecl
6781
+ * @return string
6782
+ */
6783
+ function createComponent( &$xcaldecl ) {
6784
+ $objectname = $this->_createFormat();
6785
+ $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
6786
+ $component .= $this->createAction();
6787
+ $component .= $this->createAttach();
6788
+ $component .= $this->createAttendee();
6789
+ $component .= $this->createDescription();
6790
+ $component .= $this->createDuration();
6791
+ $component .= $this->createRepeat();
6792
+ $component .= $this->createSummary();
6793
+ $component .= $this->createTrigger();
6794
+ $component .= $this->createXprop();
6795
+ $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
6796
+ return $component;
6797
+ }
6798
+ }
6799
+ /**********************************************************************************
6800
+ /*********************************************************************************/
6801
+ /**
6802
+ * class for calendar component VTIMEZONE
6803
+ *
6804
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6805
+ * @since 2.5.1 - 2008-10-12
6806
+ */
6807
+ class vtimezone extends calendarComponent {
6808
+ var $timezonetype;
6809
+
6810
+ var $comment;
6811
+ var $dtstart;
6812
+ var $lastmodified;
6813
+ var $rdate;
6814
+ var $rrule;
6815
+ var $tzid;
6816
+ var $tzname;
6817
+ var $tzoffsetfrom;
6818
+ var $tzoffsetto;
6819
+ var $tzurl;
6820
+ var $xprop;
6821
+ // component subcomponents container
6822
+ var $components;
6823
+ /**
6824
+ * constructor for calendar component VTIMEZONE object
6825
+ *
6826
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6827
+ * @since 2.8.2 - 2011-05-01
6828
+ * @param mixed $timezonetype optional, default FALSE ( STANDARD / DAYLIGHT )
6829
+ * @param array $config
6830
+ * @return void
6831
+ */
6832
+ function vtimezone( $timezonetype=FALSE, $config = array()) {
6833
+ if( is_array( $timezonetype )) {
6834
+ $config = $timezonetype;
6835
+ $timezonetype = FALSE;
6836
+ }
6837
+ if( !$timezonetype )
6838
+ $this->timezonetype = 'VTIMEZONE';
6839
+ else
6840
+ $this->timezonetype = strtoupper( $timezonetype );
6841
+ $this->calendarComponent();
6842
+
6843
+ $this->comment = '';
6844
+ $this->dtstart = '';
6845
+ $this->lastmodified = '';
6846
+ $this->rdate = '';
6847
+ $this->rrule = '';
6848
+ $this->tzid = '';
6849
+ $this->tzname = '';
6850
+ $this->tzoffsetfrom = '';
6851
+ $this->tzoffsetto = '';
6852
+ $this->tzurl = '';
6853
+ $this->xprop = '';
6854
+
6855
+ $this->components = array();
6856
+
6857
+ if( defined( 'ICAL_LANG' ) && !isset( $config['language'] ))
6858
+ $config['language'] = ICAL_LANG;
6859
+ if( !isset( $config['allowEmpty'] )) $config['allowEmpty'] = TRUE;
6860
+ if( !isset( $config['nl'] )) $config['nl'] = "\r\n";
6861
+ if( !isset( $config['format'] )) $config['format'] = 'iCal';
6862
+ if( !isset( $config['delimiter'] )) $config['delimiter'] = DIRECTORY_SEPARATOR;
6863
+ $this->setConfig( $config );
6864
+
6865
+ }
6866
+ /**
6867
+ * create formatted output for calendar component VTIMEZONE object instance
6868
+ *
6869
+ * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
6870
+ * @since 2.5.1 - 2008-10-25
6871
+ * @param array $xcaldecl
6872
+ * @return string
6873
+ */
6874
+ function createComponent( &$xcaldecl ) {
6875
+ $objectname = $this->_createFormat();
6876
+ $component = $this->componentStart1.$objectname.$this->componentStart2.$this->nl;
6877
+ $component .= $this->createTzid();
6878
+ $component .= $this->createLastModified();
6879
+ $component .= $this->createTzurl();
6880
+ $component .= $this->createDtstart();
6881
+ $component .= $this->createTzoffsetfrom();
6882
+ $component .= $this->createTzoffsetto();
6883
+ $component .= $this->createComment();
6884
+ $component .= $this->createRdate();
6885
+ $component .= $this->createRrule();
6886
+ $component .= $this->createTzname();
6887
+ $component .= $this->createXprop();
6888
+ $component .= $this->createSubComponent();
6889
+ $component .= $this->componentEnd1.$objectname.$this->componentEnd2;
6890
+ if( is_array( $this->xcaldecl ) && ( 0 < count( $this->xcaldecl ))) {
6891
+ foreach( $this->xcaldecl as $localxcaldecl )
6892
+ $xcaldecl[] = $localxcaldecl;
6893
+ }
6894
+ return $component;
6895
+ }
6896
+ }
6897
+ ?>
lib/lgpl.txt ADDED
@@ -0,0 +1,504 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU LESSER GENERAL PUBLIC LICENSE
2
+ Version 2.1, February 1999
3
+
4
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
5
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ [This is the first released version of the Lesser GPL. It also counts
10
+ as the successor of the GNU Library Public License, version 2, hence
11
+ the version number 2.1.]
12
+
13
+ Preamble
14
+
15
+ The licenses for most software are designed to take away your
16
+ freedom to share and change it. By contrast, the GNU General Public
17
+ Licenses are intended to guarantee your freedom to share and change
18
+ free software--to make sure the software is free for all its users.
19
+
20
+ This license, the Lesser General Public License, applies to some
21
+ specially designated software packages--typically libraries--of the
22
+ Free Software Foundation and other authors who decide to use it. You
23
+ can use it too, but we suggest you first think carefully about whether
24
+ this license or the ordinary General Public License is the better
25
+ strategy to use in any particular case, based on the explanations below.
26
+
27
+ When we speak of free software, we are referring to freedom of use,
28
+ not price. Our General Public Licenses are designed to make sure that
29
+ you have the freedom to distribute copies of free software (and charge
30
+ for this service if you wish); that you receive source code or can get
31
+ it if you want it; that you can change the software and use pieces of
32
+ it in new free programs; and that you are informed that you can do
33
+ these things.
34
+
35
+ To protect your rights, we need to make restrictions that forbid
36
+ distributors to deny you these rights or to ask you to surrender these
37
+ rights. These restrictions translate to certain responsibilities for
38
+ you if you distribute copies of the library or if you modify it.
39
+
40
+ For example, if you distribute copies of the library, whether gratis
41
+ or for a fee, you must give the recipients all the rights that we gave
42
+ you. You must make sure that they, too, receive or can get the source
43
+ code. If you link other code with the library, you must provide
44
+ complete object files to the recipients, so that they can relink them
45
+ with the library after making changes to the library and recompiling
46
+ it. And you must show them these terms so they know their rights.
47
+
48
+ We protect your rights with a two-step method: (1) we copyright the
49
+ library, and (2) we offer you this license, which gives you legal
50
+ permission to copy, distribute and/or modify the library.
51
+
52
+ To protect each distributor, we want to make it very clear that
53
+ there is no warranty for the free library. Also, if the library is
54
+ modified by someone else and passed on, the recipients should know
55
+ that what they have is not the original version, so that the original
56
+ author's reputation will not be affected by problems that might be
57
+ introduced by others.
58
+
59
+ Finally, software patents pose a constant threat to the existence of
60
+ any free program. We wish to make sure that a company cannot
61
+ effectively restrict the users of a free program by obtaining a
62
+ restrictive license from a patent holder. Therefore, we insist that
63
+ any patent license obtained for a version of the library must be
64
+ consistent with the full freedom of use specified in this license.
65
+
66
+ Most GNU software, including some libraries, is covered by the
67
+ ordinary GNU General Public License. This license, the GNU Lesser
68
+ General Public License, applies to certain designated libraries, and
69
+ is quite different from the ordinary General Public License. We use
70
+ this license for certain libraries in order to permit linking those
71
+ libraries into non-free programs.
72
+
73
+ When a program is linked with a library, whether statically or using
74
+ a shared library, the combination of the two is legally speaking a
75
+ combined work, a derivative of the original library. The ordinary
76
+ General Public License therefore permits such linking only if the
77
+ entire combination fits its criteria of freedom. The Lesser General
78
+ Public License permits more lax criteria for linking other code with
79
+ the library.
80
+
81
+ We call this license the "Lesser" General Public License because it
82
+ does Less to protect the user's freedom than the ordinary General
83
+ Public License. It also provides other free software developers Less
84
+ of an advantage over competing non-free programs. These disadvantages
85
+ are the reason we use the ordinary General Public License for many
86
+ libraries. However, the Lesser license provides advantages in certain
87
+ special circumstances.
88
+
89
+ For example, on rare occasions, there may be a special need to
90
+ encourage the widest possible use of a certain library, so that it becomes
91
+ a de-facto standard. To achieve this, non-free programs must be
92
+ allowed to use the library. A more frequent case is that a free
93
+ library does the same job as widely used non-free libraries. In this
94
+ case, there is little to gain by limiting the free library to free
95
+ software only, so we use the Lesser General Public License.
96
+
97
+ In other cases, permission to use a particular library in non-free
98
+ programs enables a greater number of people to use a large body of
99
+ free software. For example, permission to use the GNU C Library in
100
+ non-free programs enables many more people to use the whole GNU
101
+ operating system, as well as its variant, the GNU/Linux operating
102
+ system.
103
+
104
+ Although the Lesser General Public License is Less protective of the
105
+ users' freedom, it does ensure that the user of a program that is
106
+ linked with the Library has the freedom and the wherewithal to run
107
+ that program using a modified version of the Library.
108
+
109
+ The precise terms and conditions for copying, distribution and
110
+ modification follow. Pay close attention to the difference between a
111
+ "work based on the library" and a "work that uses the library". The
112
+ former contains code derived from the library, whereas the latter must
113
+ be combined with the library in order to run.
114
+
115
+ GNU LESSER GENERAL PUBLIC LICENSE
116
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
117
+
118
+ 0. This License Agreement applies to any software library or other
119
+ program which contains a notice placed by the copyright holder or
120
+ other authorized party saying it may be distributed under the terms of
121
+ this Lesser General Public License (also called "this License").
122
+ Each licensee is addressed as "you".
123
+
124
+ A "library" means a collection of software functions and/or data
125
+ prepared so as to be conveniently linked with application programs
126
+ (which use some of those functions and data) to form executables.
127
+
128
+ The "Library", below, refers to any such software library or work
129
+ which has been distributed under these terms. A "work based on the
130
+ Library" means either the Library or any derivative work under
131
+ copyright law: that is to say, a work containing the Library or a
132
+ portion of it, either verbatim or with modifications and/or translated
133
+ straightforwardly into another language. (Hereinafter, translation is
134
+ included without limitation in the term "modification".)
135
+
136
+ "Source code" for a work means the preferred form of the work for
137
+ making modifications to it. For a library, complete source code means
138
+ all the source code for all modules it contains, plus any associated
139
+ interface definition files, plus the scripts used to control compilation
140
+ and installation of the library.
141
+
142
+ Activities other than copying, distribution and modification are not
143
+ covered by this License; they are outside its scope. The act of
144
+ running a program using the Library is not restricted, and output from
145
+ such a program is covered only if its contents constitute a work based
146
+ on the Library (independent of the use of the Library in a tool for
147
+ writing it). Whether that is true depends on what the Library does
148
+ and what the program that uses the Library does.
149
+
150
+ 1. You may copy and distribute verbatim copies of the Library's
151
+ complete source code as you receive it, in any medium, provided that
152
+ you conspicuously and appropriately publish on each copy an
153
+ appropriate copyright notice and disclaimer of warranty; keep intact
154
+ all the notices that refer to this License and to the absence of any
155
+ warranty; and distribute a copy of this License along with the
156
+ Library.
157
+
158
+ You may charge a fee for the physical act of transferring a copy,
159
+ and you may at your option offer warranty protection in exchange for a
160
+ fee.
161
+
162
+ 2. You may modify your copy or copies of the Library or any portion
163
+ of it, thus forming a work based on the Library, and copy and
164
+ distribute such modifications or work under the terms of Section 1
165
+ above, provided that you also meet all of these conditions:
166
+
167
+ a) The modified work must itself be a software library.
168
+
169
+ b) You must cause the files modified to carry prominent notices
170
+ stating that you changed the files and the date of any change.
171
+
172
+ c) You must cause the whole of the work to be licensed at no
173
+ charge to all third parties under the terms of this License.
174
+
175
+ d) If a facility in the modified Library refers to a function or a
176
+ table of data to be supplied by an application program that uses
177
+ the facility, other than as an argument passed when the facility
178
+ is invoked, then you must make a good faith effort to ensure that,
179
+ in the event an application does not supply such function or
180
+ table, the facility still operates, and performs whatever part of
181
+ its purpose remains meaningful.
182
+
183
+ (For example, a function in a library to compute square roots has
184
+ a purpose that is entirely well-defined independent of the
185
+ application. Therefore, Subsection 2d requires that any
186
+ application-supplied function or table used by this function must
187
+ be optional: if the application does not supply it, the square
188
+ root function must still compute square roots.)
189
+
190
+ These requirements apply to the modified work as a whole. If
191
+ identifiable sections of that work are not derived from the Library,
192
+ and can be reasonably considered independent and separate works in
193
+ themselves, then this License, and its terms, do not apply to those
194
+ sections when you distribute them as separate works. But when you
195
+ distribute the same sections as part of a whole which is a work based
196
+ on the Library, the distribution of the whole must be on the terms of
197
+ this License, whose permissions for other licensees extend to the
198
+ entire whole, and thus to each and every part regardless of who wrote
199
+ it.
200
+
201
+ Thus, it is not the intent of this section to claim rights or contest
202
+ your rights to work written entirely by you; rather, the intent is to
203
+ exercise the right to control the distribution of derivative or
204
+ collective works based on the Library.
205
+
206
+ In addition, mere aggregation of another work not based on the Library
207
+ with the Library (or with a work based on the Library) on a volume of
208
+ a storage or distribution medium does not bring the other work under
209
+ the scope of this License.
210
+
211
+ 3. You may opt to apply the terms of the ordinary GNU General Public
212
+ License instead of this License to a given copy of the Library. To do
213
+ this, you must alter all the notices that refer to this License, so
214
+ that they refer to the ordinary GNU General Public License, version 2,
215
+ instead of to this License. (If a newer version than version 2 of the
216
+ ordinary GNU General Public License has appeared, then you can specify
217
+ that version instead if you wish.) Do not make any other change in
218
+ these notices.
219
+
220
+ Once this change is made in a given copy, it is irreversible for
221
+ that copy, so the ordinary GNU General Public License applies to all
222
+ subsequent copies and derivative works made from that copy.
223
+
224
+ This option is useful when you wish to copy part of the code of
225
+ the Library into a program that is not a library.
226
+
227
+ 4. You may copy and distribute the Library (or a portion or
228
+ derivative of it, under Section 2) in object code or executable form
229
+ under the terms of Sections 1 and 2 above provided that you accompany
230
+ it with the complete corresponding machine-readable source code, which
231
+ must be distributed under the terms of Sections 1 and 2 above on a
232
+ medium customarily used for software interchange.
233
+
234
+ If distribution of object code is made by offering access to copy
235
+ from a designated place, then offering equivalent access to copy the
236
+ source code from the same place satisfies the requirement to
237
+ distribute the source code, even though third parties are not
238
+ compelled to copy the source along with the object code.
239
+
240
+ 5. A program that contains no derivative of any portion of the
241
+ Library, but is designed to work with the Library by being compiled or
242
+ linked with it, is called a "work that uses the Library". Such a
243
+ work, in isolation, is not a derivative work of the Library, and
244
+ therefore falls outside the scope of this License.
245
+
246
+ However, linking a "work that uses the Library" with the Library
247
+ creates an executable that is a derivative of the Library (because it
248
+ contains portions of the Library), rather than a "work that uses the
249
+ library". The executable is therefore covered by this License.
250
+ Section 6 states terms for distribution of such executables.
251
+
252
+ When a "work that uses the Library" uses material from a header file
253
+ that is part of the Library, the object code for the work may be a
254
+ derivative work of the Library even though the source code is not.
255
+ Whether this is true is especially significant if the work can be
256
+ linked without the Library, or if the work is itself a library. The
257
+ threshold for this to be true is not precisely defined by law.
258
+
259
+ If such an object file uses only numerical parameters, data
260
+ structure layouts and accessors, and small macros and small inline
261
+ functions (ten lines or less in length), then the use of the object
262
+ file is unrestricted, regardless of whether it is legally a derivative
263
+ work. (Executables containing this object code plus portions of the
264
+ Library will still fall under Section 6.)
265
+
266
+ Otherwise, if the work is a derivative of the Library, you may
267
+ distribute the object code for the work under the terms of Section 6.
268
+ Any executables containing that work also fall under Section 6,
269
+ whether or not they are linked directly with the Library itself.
270
+
271
+ 6. As an exception to the Sections above, you may also combine or
272
+ link a "work that uses the Library" with the Library to produce a
273
+ work containing portions of the Library, and distribute that work
274
+ under terms of your choice, provided that the terms permit
275
+ modification of the work for the customer's own use and reverse
276
+ engineering for debugging such modifications.
277
+
278
+ You must give prominent notice with each copy of the work that the
279
+ Library is used in it and that the Library and its use are covered by
280
+ this License. You must supply a copy of this License. If the work
281
+ during execution displays copyright notices, you must include the
282
+ copyright notice for the Library among them, as well as a reference
283
+ directing the user to the copy of this License. Also, you must do one
284
+ of these things:
285
+
286
+ a) Accompany the work with the complete corresponding
287
+ machine-readable source code for the Library including whatever
288
+ changes were used in the work (which must be distributed under
289
+ Sections 1 and 2 above); and, if the work is an executable linked
290
+ with the Library, with the complete machine-readable "work that
291
+ uses the Library", as object code and/or source code, so that the
292
+ user can modify the Library and then relink to produce a modified
293
+ executable containing the modified Library. (It is understood
294
+ that the user who changes the contents of definitions files in the
295
+ Library will not necessarily be able to recompile the application
296
+ to use the modified definitions.)
297
+
298
+ b) Use a suitable shared library mechanism for linking with the
299
+ Library. A suitable mechanism is one that (1) uses at run time a
300
+ copy of the library already present on the user's computer system,
301
+ rather than copying library functions into the executable, and (2)
302
+ will operate properly with a modified version of the library, if
303
+ the user installs one, as long as the modified version is
304
+ interface-compatible with the version that the work was made with.
305
+
306
+ c) Accompany the work with a written offer, valid for at
307
+ least three years, to give the same user the materials
308
+ specified in Subsection 6a, above, for a charge no more
309
+ than the cost of performing this distribution.
310
+
311
+ d) If distribution of the work is made by offering access to copy
312
+ from a designated place, offer equivalent access to copy the above
313
+ specified materials from the same place.
314
+
315
+ e) Verify that the user has already received a copy of these
316
+ materials or that you have already sent this user a copy.
317
+
318
+ For an executable, the required form of the "work that uses the
319
+ Library" must include any data and utility programs needed for
320
+ reproducing the executable from it. However, as a special exception,
321
+ the materials to be distributed need not include anything that is
322
+ normally distributed (in either source or binary form) with the major
323
+ components (compiler, kernel, and so on) of the operating system on
324
+ which the executable runs, unless that component itself accompanies
325
+ the executable.
326
+
327
+ It may happen that this requirement contradicts the license
328
+ restrictions of other proprietary libraries that do not normally
329
+ accompany the operating system. Such a contradiction means you cannot
330
+ use both them and the Library together in an executable that you
331
+ distribute.
332
+
333
+ 7. You may place library facilities that are a work based on the
334
+ Library side-by-side in a single library together with other library
335
+ facilities not covered by this License, and distribute such a combined
336
+ library, provided that the separate distribution of the work based on
337
+ the Library and of the other library facilities is otherwise
338
+ permitted, and provided that you do these two things:
339
+
340
+ a) Accompany the combined library with a copy of the same work
341
+ based on the Library, uncombined with any other library
342
+ facilities. This must be distributed under the terms of the
343
+ Sections above.
344
+
345
+ b) Give prominent notice with the combined library of the fact
346
+ that part of it is a work based on the Library, and explaining
347
+ where to find the accompanying uncombined form of the same work.
348
+
349
+ 8. You may not copy, modify, sublicense, link with, or distribute
350
+ the Library except as expressly provided under this License. Any
351
+ attempt otherwise to copy, modify, sublicense, link with, or
352
+ distribute the Library is void, and will automatically terminate your
353
+ rights under this License. However, parties who have received copies,
354
+ or rights, from you under this License will not have their licenses
355
+ terminated so long as such parties remain in full compliance.
356
+
357
+ 9. You are not required to accept this License, since you have not
358
+ signed it. However, nothing else grants you permission to modify or
359
+ distribute the Library or its derivative works. These actions are
360
+ prohibited by law if you do not accept this License. Therefore, by
361
+ modifying or distributing the Library (or any work based on the
362
+ Library), you indicate your acceptance of this License to do so, and
363
+ all its terms and conditions for copying, distributing or modifying
364
+ the Library or works based on it.
365
+
366
+ 10. Each time you redistribute the Library (or any work based on the
367
+ Library), the recipient automatically receives a license from the
368
+ original licensor to copy, distribute, link with or modify the Library
369
+ subject to these terms and conditions. You may not impose any further
370
+ restrictions on the recipients' exercise of the rights granted herein.
371
+ You are not responsible for enforcing compliance by third parties with
372
+ this License.
373
+
374
+ 11. If, as a consequence of a court judgment or allegation of patent
375
+ infringement or for any other reason (not limited to patent issues),
376
+ conditions are imposed on you (whether by court order, agreement or
377
+ otherwise) that contradict the conditions of this License, they do not
378
+ excuse you from the conditions of this License. If you cannot
379
+ distribute so as to satisfy simultaneously your obligations under this
380
+ License and any other pertinent obligations, then as a consequence you
381
+ may not distribute the Library at all. For example, if a patent
382
+ license would not permit royalty-free redistribution of the Library by
383
+ all those who receive copies directly or indirectly through you, then
384
+ the only way you could satisfy both it and this License would be to
385
+ refrain entirely from distribution of the Library.
386
+
387
+ If any portion of this section is held invalid or unenforceable under any
388
+ particular circumstance, the balance of the section is intended to apply,
389
+ and the section as a whole is intended to apply in other circumstances.
390
+
391
+ It is not the purpose of this section to induce you to infringe any
392
+ patents or other property right claims or to contest validity of any
393
+ such claims; this section has the sole purpose of protecting the
394
+ integrity of the free software distribution system which is
395
+ implemented by public license practices. Many people have made
396
+ generous contributions to the wide range of software distributed
397
+ through that system in reliance on consistent application of that
398
+ system; it is up to the author/donor to decide if he or she is willing
399
+ to distribute software through any other system and a licensee cannot
400
+ impose that choice.
401
+
402
+ This section is intended to make thoroughly clear what is believed to
403
+ be a consequence of the rest of this License.
404
+
405
+ 12. If the distribution and/or use of the Library is restricted in
406
+ certain countries either by patents or by copyrighted interfaces, the
407
+ original copyright holder who places the Library under this License may add
408
+ an explicit geographical distribution limitation excluding those countries,
409
+ so that distribution is permitted only in or among countries not thus
410
+ excluded. In such case, this License incorporates the limitation as if
411
+ written in the body of this License.
412
+
413
+ 13. The Free Software Foundation may publish revised and/or new
414
+ versions of the Lesser General Public License from time to time.
415
+ Such new versions will be similar in spirit to the present version,
416
+ but may differ in detail to address new problems or concerns.
417
+
418
+ Each version is given a distinguishing version number. If the Library
419
+ specifies a version number of this License which applies to it and
420
+ "any later version", you have the option of following the terms and
421
+ conditions either of that version or of any later version published by
422
+ the Free Software Foundation. If the Library does not specify a
423
+ license version number, you may choose any version ever published by
424
+ the Free Software Foundation.
425
+
426
+ 14. If you wish to incorporate parts of the Library into other free
427
+ programs whose distribution conditions are incompatible with these,
428
+ write to the author to ask for permission. For software which is
429
+ copyrighted by the Free Software Foundation, write to the Free
430
+ Software Foundation; we sometimes make exceptions for this. Our
431
+ decision will be guided by the two goals of preserving the free status
432
+ of all derivatives of our free software and of promoting the sharing
433
+ and reuse of software generally.
434
+
435
+ NO WARRANTY
436
+
437
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
438
+ WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
439
+ EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
440
+ OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
441
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
442
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
443
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
444
+ LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
445
+ THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
446
+
447
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
448
+ WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
449
+ AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
450
+ FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
451
+ CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
452
+ LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
453
+ RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
454
+ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
455
+ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
456
+ DAMAGES.
457
+
458
+ END OF TERMS AND CONDITIONS
459
+
460
+ How to Apply These Terms to Your New Libraries
461
+
462
+ If you develop a new library, and you want it to be of the greatest
463
+ possible use to the public, we recommend making it free software that
464
+ everyone can redistribute and change. You can do so by permitting
465
+ redistribution under these terms (or, alternatively, under the terms of the
466
+ ordinary General Public License).
467
+
468
+ To apply these terms, attach the following notices to the library. It is
469
+ safest to attach them to the start of each source file to most effectively
470
+ convey the exclusion of warranty; and each file should have at least the
471
+ "copyright" line and a pointer to where the full notice is found.
472
+
473
+ <one line to give the library's name and a brief idea of what it does.>
474
+ Copyright (C) <year> <name of author>
475
+
476
+ This library is free software; you can redistribute it and/or
477
+ modify it under the terms of the GNU Lesser General Public
478
+ License as published by the Free Software Foundation; either
479
+ version 2.1 of the License, or (at your option) any later version.
480
+
481
+ This library is distributed in the hope that it will be useful,
482
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
483
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
484
+ Lesser General Public License for more details.
485
+
486
+ You should have received a copy of the GNU Lesser General Public
487
+ License along with this library; if not, write to the Free Software
488
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
489
+
490
+ Also add information on how to contact you by electronic and paper mail.
491
+
492
+ You should also get your employer (if you work as a programmer) or your
493
+ school, if any, to sign a "copyright disclaimer" for the library, if
494
+ necessary. Here is a sample; alter the names:
495
+
496
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
497
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
498
+
499
+ <signature of Ty Coon>, 1 April 1990
500
+ Ty Coon, President of Vice
501
+
502
+ That's all there is to it!
503
+
504
+
readme.txt ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === All-in-One Events Calendar ===
2
+ Contributors: theseed, hubrik, vtowel, yani.iliev
3
+ Donate link: http://theseedstudio.com/software/all-in-one-events-calendar-wordpress/
4
+ Tags: calendar, event, events, ics, ics calendar, ical-feed, ics feed, wordpress ics importer, wordpress ical importer, upcoming events, todo, notes, journal, freebusy, availability, web calendar, web events, webcal, google calendar, ical, iCalendar, all-in-one, ai1ec, google calendar sync, ical sync, events sync, holiday calendar, calendar 2011, events 2011
5
+ Requires at least: 3.1.3
6
+ Tested up to: 3.2.1
7
+ Stable tag: 1.0
8
+
9
+ An events calendar system with month and agenda calendar views, color-coded categories, recurring events, and import/export of iCalendar (.ics) feeds.
10
+
11
+ == Description ==
12
+
13
+ Welcome to the All-in-One Events Calendar Plugin, a new way to list your events in WordPress and easily share them with the rest of the world.
14
+
15
+ Our new calendar system combines a clean visual design, solid architectural patterns and rigorous testing with a powerful set of features to create the most advanced calendar system available for WordPress and one of the most powerful website calendar systems in the world. Best of all: it’s completely free.
16
+
17
+ = Calendar Features For Users =
18
+
19
+ This plugin has many features we hope will prove useful to users, including:
20
+
21
+ * Recurring* events
22
+ * Filtering by event category or tag
23
+ * Easy sharing with Google Calendar, Apple iCal, MS Outlook and any other system that accepts iCalendar (.ics) feeds
24
+ * Embedded Google Maps
25
+ * Color-coded events based on category
26
+ * Event-registration ready
27
+ * Month and agenda views
28
+ * Links to filtered calendar views
29
+
30
+ (* Limited recurrence patterns available in first release. Full support to come in the next release.)
31
+
32
+ = Features for Website and Blog Owners =
33
+
34
+ * Import other calendars automatically to display in your calendar
35
+ * Categorize and tag imported calendar feeds automatically
36
+ * Events from [The Events Calendar](http://wordpress.org/extend/plugins/the-events-calendar/) plugin can also be easily imported
37
+
38
+ Importing and exporting iCalendar (.ics) feeds is one of the strongest features of the All-in-One Events Calendar system. Enter an event on one site and you can have it appear automatically in another website's calendar. You can even send events from a specific category or tag (or combination of categories and tags).
39
+
40
+ Why is this cool? It allows event creators to create one event and have it displayed on a few or thousands of calendars with no extra work. And it also allows calendar owners to populate their calendar from other calendar feeds without having to go through the hassle of creating new events. For example, a soccer league can send its game schedule to a community sports calendar, which, in turn, can send only featured games (from all the sports leagues it aggragates) to a community calendar, which features sports as just one category.
41
+
42
+ = Additional Features =
43
+
44
+ The All-in-One Events Calendar Plugin also has a few features that will prove useful for website and blog owners:
45
+
46
+ * Each event is SEO-optimized
47
+ * Each event links to the original calendar
48
+ * Your calendar can be embedded into a WordPress page without needing to create template files or modify the theme
49
+
50
+ [Check out the demo »](http://thenelsonpost.ca/calendar/)
51
+
52
+ == Installation ==
53
+
54
+ 1. Upload `all-in-one-events-calendar` to the `/wp-content/plugins/` directory.
55
+ 1. Activate the plugin through the **Plugins** menu item in the WordPress Dashboard.
56
+ 1. Once the plugin is activated, follow the instructions in the notice to configure it.
57
+
58
+ == Screenshots ==
59
+
60
+ 1. Add new event - part 1
61
+ 2. Add new event - part 2
62
+ 3. Event categories
63
+ 4. Event categories with color picker
64
+ 5. Front-end: Month view of calendar
65
+ 6. Front-end: Month view of calendar with mouse cursor hovering over event
66
+ 7. Front-end: Month view of calendar with active category filter
67
+ 8. Front-end: Month view of calendar with active tag filter
68
+ 9. Front-end: Agenda view of calendar
69
+ 10. Settings page
screenshot-1.png ADDED
Binary file
screenshot-10.png ADDED
Binary file
screenshot-2.png ADDED
Binary file
screenshot-3.png ADDED
Binary file
screenshot-4.png ADDED
Binary file
screenshot-5.png ADDED
Binary file
screenshot-6.png ADDED
Binary file
screenshot-7.png ADDED
Binary file
screenshot-8.png ADDED
Binary file
screenshot-9.png ADDED
Binary file