All-in-One Event Calendar - Version 1.2

Version Description

Download this release

Release Info

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

Code changes from version 1.1.3 to 1.2

all-in-one-event-calendar.php CHANGED
@@ -2,11 +2,11 @@
2
  /**
3
  * Plugin Name: All-in-One Event Calendar Plugin
4
  * Plugin URI: http://theseednetwork.com/software/all-in-one-event-calendar-wordpress/
5
- * Description: An event calendar system with month and agenda views, upcoming events widget, color-coded categories, recurrence, and import/export of .ics feeds.
6
- * Version: 1.1.3
7
  * Author: The Seed Studio
8
  * Author URI: http://theseednetwork.com/
9
- */
 
10
  @set_time_limit( 0 );
11
  @ini_set( "memory_limit", "256M" );
12
  @ini_set( "max_input_time", "-1" );
2
  /**
3
  * Plugin Name: All-in-One Event Calendar Plugin
4
  * Plugin URI: http://theseednetwork.com/software/all-in-one-event-calendar-wordpress/
5
+ * Description: An event calendar system with month, week & agenda views, upcoming events widget, color-coded categories, recurrence, and import/export of .ics feeds.
 
6
  * Author: The Seed Studio
7
  * Author URI: http://theseednetwork.com/
8
+ * Version: 1.2
9
+ */ // NOTE: When updating version number also update first line of app/view/calendar.php
10
  @set_time_limit( 0 );
11
  @ini_set( "memory_limit", "256M" );
12
  @ini_set( "max_input_time", "-1" );
app/controller/class-ai1ec-calendar-controller.php CHANGED
@@ -47,10 +47,13 @@ class Ai1ec_Calendar_Controller {
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
  }
@@ -72,36 +75,47 @@ class Ai1ec_Calendar_Controller {
72
  // object
73
  $this->request['action'] = isset( $_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
- isset( $_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'] = isset( $_REQUEST['ai1ec_active_event'] ) ? intval( $_REQUEST['ai1ec_active_event'] ) : 0;
86
  // Category/tag filter parameters
87
- $this->request['ai1ec_cat_ids'] = isset( $_REQUEST['ai1ec_cat_ids'] ) ? $_REQUEST['ai1ec_cat_ids'] : 0;
88
- $this->request['ai1ec_tag_ids'] = isset( $_REQUEST['ai1ec_tag_ids'] ) ? $_REQUEST['ai1ec_tag_ids'] : 0;
 
 
 
 
 
 
 
 
 
 
 
 
89
  break;
90
 
91
  case 'ai1ec_agenda':
92
  $this->request['ai1ec_page_offset'] =
93
- isset( $_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'] = isset( $_REQUEST['ai1ec_active_event'] ) ? intval( $_REQUEST['ai1ec_active_event'] ) : 0;
97
  // Category/tag filter parameters
98
- $this->request['ai1ec_cat_ids'] = isset( $_REQUEST['ai1ec_cat_ids'] ) ? $_REQUEST['ai1ec_cat_ids'] : 0;
99
- $this->request['ai1ec_tag_ids'] = isset( $_REQUEST['ai1ec_tag_ids'] ) ? $_REQUEST['ai1ec_tag_ids'] : 0;
 
100
  break;
101
 
102
  case 'ai1ec_term_filter':
103
- $this->request['ai1ec_post_ids'] = isset( $_REQUEST['ai1ec_post_ids'] ) ? $_REQUEST['ai1ec_post_ids'] : 0;
104
- $this->request['ai1ec_term_ids'] = isset( $_REQUEST['ai1ec_term_ids'] ) ? $_REQUEST['ai1ec_term_ids'] : 0;
105
  break;
106
  }
107
  }
@@ -142,9 +156,11 @@ class Ai1ec_Calendar_Controller {
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
@@ -155,6 +171,11 @@ class Ai1ec_Calendar_Controller {
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 );
@@ -166,9 +187,10 @@ class Ai1ec_Calendar_Controller {
166
  else
167
  $create_event_url = false;
168
 
169
- // Validate preselected category/tag IDs
170
- $cat_ids = join( ',', array_filter( explode( ',', $this->request['ai1ec_cat_ids'] ), 'is_numeric' ) );
171
- $tag_ids = join( ',', array_filter( explode( ',', $this->request['ai1ec_tag_ids'] ), 'is_numeric' ) );
 
172
 
173
  $categories = get_terms( 'events_categories', array( 'orderby' => 'name' ) );
174
  foreach( $categories as &$cat ) {
@@ -182,6 +204,7 @@ class Ai1ec_Calendar_Controller {
182
  'tags' => get_terms( 'events_tags', array( 'orderby' => 'name' ) ),
183
  'selected_cat_ids' => $cat_ids,
184
  'selected_tag_ids' => $tag_ids,
 
185
  'show_subscribe_buttons' => ! $ai1ec_settings->turn_off_subscription_buttons
186
  );
187
 
@@ -204,6 +227,8 @@ class Ai1ec_Calendar_Controller {
204
  * event category slugs
205
  * array tags => restrict events returned to the given set of
206
  * event tag names
 
 
207
  *
208
  * @return string returns string of view output
209
  **/
@@ -215,12 +240,13 @@ class Ai1ec_Calendar_Controller {
215
 
216
  $defaults = array(
217
  'month_offset' => 0,
218
- 'active_event' => 0,
219
  'categories' => array(),
220
- 'tags' => array()
 
221
  );
222
  $args = wp_parse_args( $args, $defaults );
223
-
224
  extract( $args );
225
 
226
  // Get components of localized time
@@ -228,20 +254,86 @@ class Ai1ec_Calendar_Controller {
228
  // Use first day of the month as reference timestamp, and apply month offset
229
  $timestamp = gmmktime( 0, 0, 0, $bits['mon'] + $month_offset, 1, $bits['year'] );
230
 
231
- $days_events = $ai1ec_calendar_helper->get_events_for_month( $timestamp, $categories, $tags );
 
232
  $cell_array = $ai1ec_calendar_helper->get_month_cell_array( $timestamp, $days_events );
233
  $pagination_links = $ai1ec_calendar_helper->get_month_pagination_links( $month_offset );
234
 
235
  $view_args = array(
236
- 'title' => date_i18n( 'F Y', $timestamp, true ),
237
- 'weekdays' => $ai1ec_calendar_helper->get_weekdays(),
238
- 'cell_array' => $cell_array,
239
  'pagination_links' => $pagination_links,
240
- 'active_event' => $active_event,
 
241
  );
242
  return apply_filters( 'ai1ec_get_month_view', $ai1ec_view_helper->get_view( 'month.php', $view_args ), $view_args );
243
  }
244
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  /**
246
  * get_agenda_view function
247
  *
@@ -275,7 +367,8 @@ class Ai1ec_Calendar_Controller {
275
  $event_results = $ai1ec_calendar_helper->get_events_relative_to(
276
  $timestamp,
277
  $ai1ec_settings->agenda_events_per_page,
278
- $page_offset
 
279
  );
280
  $dates = $ai1ec_calendar_helper->get_agenda_date_array( $event_results['events'] );
281
 
@@ -290,7 +383,8 @@ class Ai1ec_Calendar_Controller {
290
  'page_offset' => $page_offset,
291
  'pagination_links' => $pagination_links,
292
  'active_event' => $active_event,
293
- 'expanded' => $ai1ec_settings->agenda_events_expanded
 
294
  );
295
  return apply_filters( 'ai1ec_get_agenda_view', $ai1ec_view_helper->get_view( 'agenda.php', $args ), $args );
296
  }
@@ -301,7 +395,7 @@ class Ai1ec_Calendar_Controller {
301
  * AJAX request handler for month view.
302
  *
303
  * @return void
304
- **/
305
  function ajax_month() {
306
  global $ai1ec_view_helper;
307
 
@@ -311,6 +405,7 @@ class Ai1ec_Calendar_Controller {
311
  $args = array(
312
  'month_offset' => $this->request['ai1ec_month_offset'],
313
  'active_event' => $this->request['ai1ec_active_event'],
 
314
  );
315
 
316
  // Return this data structure to the client
@@ -321,6 +416,33 @@ class Ai1ec_Calendar_Controller {
321
  $ai1ec_view_helper->json_response( $data );
322
  }
323
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
  /**
325
  * ajax_agenda function
326
  *
@@ -335,8 +457,9 @@ class Ai1ec_Calendar_Controller {
335
 
336
  // View arguments
337
  $args = array(
338
- 'page_offset' => $this->request['ai1ec_page_offset'],
339
  'active_event' => $this->request['ai1ec_active_event'],
 
340
  );
341
 
342
  // Return this data structure to the client
@@ -448,12 +571,13 @@ class Ai1ec_Calendar_Controller {
448
  {
449
  global $ai1ec_settings;
450
 
451
- // Include scrollTo jQuery plugin
452
  wp_enqueue_script( 'jquery.scrollTo', AI1EC_JS_URL . '/jquery.scrollTo-min.js', array( 'jquery' ), 1 );
 
453
  // Include element selector function
454
  wp_enqueue_script( 'ai1ec-element-selector', AI1EC_JS_URL . '/element-selector.js', array( 'jquery', 'jquery.scrollTo' ), 1 );
455
  // Include custom script
456
- wp_enqueue_script( 'ai1ec-calendar', AI1EC_JS_URL . '/calendar.js', array( 'jquery', 'jquery.scrollTo' ), 1 );
457
 
458
  $data = array(
459
  // Point script to AJAX URL
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_week', array( &$this, 'ajax_week' ) );
51
  add_action( 'wp_ajax_ai1ec_agenda', array( &$this, 'ajax_agenda' ) );
52
+ add_action( 'wp_ajax_ai1ec_term_filter', array( &$this, 'ajax_term_filter' ) );
53
+
54
  add_action( 'wp_ajax_nopriv_ai1ec_month', array( &$this, 'ajax_month' ) );
55
+ add_action( 'wp_ajax_nopriv_ai1ec_week', array( &$this, 'ajax_week' ) );
56
  add_action( 'wp_ajax_nopriv_ai1ec_agenda', array( &$this, 'ajax_agenda' ) );
 
57
  add_action( 'wp_ajax_nopriv_ai1ec_term_filter', array( &$this, 'ajax_term_filter' ) );
58
  }
59
  }
75
  // object
76
  $this->request['action'] = isset( $_REQUEST['action'] ) ? $_REQUEST['action'] : '';
77
  if( ! in_array( $this->request['action'],
78
+ array( 'ai1ec_month', 'ai1ec_week', 'ai1ec_agenda', 'ai1ec_term_filter' ) ) )
79
  $this->request['action'] = 'ai1ec_' . $ai1ec_settings->default_calendar_view;
80
 
81
  switch( $this->request['action'] )
82
  {
83
  case 'ai1ec_month':
84
  $this->request['ai1ec_month_offset'] =
85
+ isset( $_REQUEST['ai1ec_month_offset'] ) ? intval( $_REQUEST['ai1ec_month_offset'] ) : 0;
 
86
  // Parse active event parameter as an integer ID
87
+ $this->request['ai1ec_active_event'] = isset( $_REQUEST['ai1ec_active_event'] ) ? intval( $_REQUEST['ai1ec_active_event'] ) : null;
88
  // Category/tag filter parameters
89
+ $this->request['ai1ec_cat_ids'] = isset( $_REQUEST['ai1ec_cat_ids'] ) ? $_REQUEST['ai1ec_cat_ids'] : null;
90
+ $this->request['ai1ec_tag_ids'] = isset( $_REQUEST['ai1ec_tag_ids'] ) ? $_REQUEST['ai1ec_tag_ids'] : null;
91
+ $this->request['ai1ec_post_ids'] = isset( $_REQUEST['ai1ec_post_ids'] ) ? $_REQUEST['ai1ec_post_ids'] : null;
92
+ break;
93
+
94
+ case 'ai1ec_week':
95
+ $this->request['ai1ec_week_offset'] =
96
+ isset( $_REQUEST['ai1ec_week_offset'] ) ? intval( $_REQUEST['ai1ec_week_offset'] ) : 0;
97
+ // Parse active event parameter as an integer ID
98
+ $this->request['ai1ec_active_event'] = isset( $_REQUEST['ai1ec_active_event'] ) ? intval( $_REQUEST['ai1ec_active_event'] ) : null;
99
+ // Category/tag filter parameters
100
+ $this->request['ai1ec_cat_ids'] = isset( $_REQUEST['ai1ec_cat_ids'] ) ? $_REQUEST['ai1ec_cat_ids'] : null;
101
+ $this->request['ai1ec_tag_ids'] = isset( $_REQUEST['ai1ec_tag_ids'] ) ? $_REQUEST['ai1ec_tag_ids'] : null;
102
+ $this->request['ai1ec_post_ids'] = isset( $_REQUEST['ai1ec_post_ids'] ) ? $_REQUEST['ai1ec_post_ids'] : null;
103
  break;
104
 
105
  case 'ai1ec_agenda':
106
  $this->request['ai1ec_page_offset'] =
107
+ isset( $_REQUEST['ai1ec_page_offset'] ) ? intval( $_REQUEST['ai1ec_page_offset'] ) : 0;
 
108
  // Parse active event parameter as an integer ID
109
+ $this->request['ai1ec_active_event'] = isset( $_REQUEST['ai1ec_active_event'] ) ? intval( $_REQUEST['ai1ec_active_event'] ) : null;
110
  // Category/tag filter parameters
111
+ $this->request['ai1ec_cat_ids'] = isset( $_REQUEST['ai1ec_cat_ids'] ) ? $_REQUEST['ai1ec_cat_ids'] : null;
112
+ $this->request['ai1ec_tag_ids'] = isset( $_REQUEST['ai1ec_tag_ids'] ) ? $_REQUEST['ai1ec_tag_ids'] : null;
113
+ $this->request['ai1ec_post_ids'] = isset( $_REQUEST['ai1ec_post_ids'] ) ? $_REQUEST['ai1ec_post_ids'] : null;
114
  break;
115
 
116
  case 'ai1ec_term_filter':
117
+ $this->request['ai1ec_post_ids'] = isset( $_REQUEST['ai1ec_post_ids'] ) ? $_REQUEST['ai1ec_post_ids'] : null;
118
+ $this->request['ai1ec_term_ids'] = isset( $_REQUEST['ai1ec_term_ids'] ) ? $_REQUEST['ai1ec_term_ids'] : null;
119
  break;
120
  }
121
  }
156
  $this->load_css();
157
  $this->load_js();
158
 
159
+ $post_ids = array_filter( explode( ',', $this->request['ai1ec_post_ids'] ), 'is_numeric' );
160
  // Define arguments for specific calendar sub-view (month, agenda, etc.)
161
  $args = array(
162
  'active_event' => $this->request['ai1ec_active_event'],
163
+ 'post_ids' => $post_ids,
164
  );
165
 
166
  // Find out which view of the calendar page was requested
171
  $view = $this->get_month_view( $args );
172
  break;
173
 
174
+ case 'ai1ec_week':
175
+ $args['week_offset'] = $this->request['ai1ec_week_offset'];
176
+ $view = $this->get_week_view( $args );
177
+ break;
178
+
179
  case 'ai1ec_agenda':
180
  $args['page_offset'] = $this->request['ai1ec_page_offset'];
181
  $view = $this->get_agenda_view( $args );
187
  else
188
  $create_event_url = false;
189
 
190
+ // Validate preselected category/tag/post IDs
191
+ $cat_ids = join( ',', array_filter( explode( ',', $this->request['ai1ec_cat_ids'] ), 'is_numeric' ) );
192
+ $tag_ids = join( ',', array_filter( explode( ',', $this->request['ai1ec_tag_ids'] ), 'is_numeric' ) );
193
+ $post_ids = join( ',', $post_ids );
194
 
195
  $categories = get_terms( 'events_categories', array( 'orderby' => 'name' ) );
196
  foreach( $categories as &$cat ) {
204
  'tags' => get_terms( 'events_tags', array( 'orderby' => 'name' ) ),
205
  'selected_cat_ids' => $cat_ids,
206
  'selected_tag_ids' => $tag_ids,
207
+ 'selected_post_ids' => $post_ids,
208
  'show_subscribe_buttons' => ! $ai1ec_settings->turn_off_subscription_buttons
209
  );
210
 
227
  * event category slugs
228
  * array tags => restrict events returned to the given set of
229
  * event tag names
230
+ * array post_ids => restrict events returned to the given set of
231
+ * post IDs
232
  *
233
  * @return string returns string of view output
234
  **/
240
 
241
  $defaults = array(
242
  'month_offset' => 0,
243
+ 'active_event' => null,
244
  'categories' => array(),
245
+ 'tags' => array(),
246
+ 'post_ids' => array(),
247
  );
248
  $args = wp_parse_args( $args, $defaults );
249
+
250
  extract( $args );
251
 
252
  // Get components of localized time
254
  // Use first day of the month as reference timestamp, and apply month offset
255
  $timestamp = gmmktime( 0, 0, 0, $bits['mon'] + $month_offset, 1, $bits['year'] );
256
 
257
+ $days_events = $ai1ec_calendar_helper->get_events_for_month( $timestamp,
258
+ array( 'cat_ids' => $categories, 'cat_ids' => $tags, 'post_ids' => $post_ids ) );
259
  $cell_array = $ai1ec_calendar_helper->get_month_cell_array( $timestamp, $days_events );
260
  $pagination_links = $ai1ec_calendar_helper->get_month_pagination_links( $month_offset );
261
 
262
  $view_args = array(
263
+ 'title' => date_i18n( 'F Y', $timestamp, true ),
264
+ 'weekdays' => $ai1ec_calendar_helper->get_weekdays(),
265
+ 'cell_array' => $cell_array,
266
  'pagination_links' => $pagination_links,
267
+ 'active_event' => $active_event,
268
+ 'post_ids' => join( ',', $post_ids ),
269
  );
270
  return apply_filters( 'ai1ec_get_month_view', $ai1ec_view_helper->get_view( 'month.php', $view_args ), $view_args );
271
  }
272
 
273
+ /**
274
+ * get_week_view function
275
+ *
276
+ * Return the embedded week view of the calendar, optionally filtered by
277
+ * event categories and tags.
278
+ *
279
+ * @param array $args associative array with any of these elements:
280
+ * int week_offset => specifies which week to display relative to the
281
+ * current week
282
+ * int active_event => specifies which event to make visible when
283
+ * page is loaded
284
+ * array categories => restrict events returned to the given set of
285
+ * event category slugs
286
+ * array tags => restrict events returned to the given set of
287
+ * event tag names
288
+ * array post_ids => restrict events returned to the given set of
289
+ * post IDs
290
+ *
291
+ * @return string returns string of view output
292
+ */
293
+ function get_week_view( $args )
294
+ {
295
+ global $ai1ec_view_helper,
296
+ $ai1ec_events_helper,
297
+ $ai1ec_calendar_helper;
298
+
299
+ $defaults = array(
300
+ 'week_offset' => 0,
301
+ 'active_event' => null,
302
+ 'categories' => array(),
303
+ 'tags' => array(),
304
+ 'post_ids' => array(),
305
+ );
306
+ $args = wp_parse_args( $args, $defaults );
307
+
308
+ extract( $args );
309
+
310
+ // Get components of localized time
311
+ $bits = $ai1ec_events_helper->gmgetdate( $ai1ec_events_helper->gmt_to_local( time() ) );
312
+ // Day shift is initially the first day of the week according to settings
313
+ $day_shift = $ai1ec_events_helper->get_week_start_day_offset( $bits['wday'] );
314
+ // Then apply week offset
315
+ $day_shift += $args['week_offset'] * 7;
316
+
317
+ // Now apply to reference timestamp
318
+ $timestamp = gmmktime( 0, 0, 0, $bits['mon'], $bits['mday'] + $day_shift, $bits['year'] );
319
+
320
+ $cell_array = $ai1ec_calendar_helper->get_week_cell_array( $timestamp,
321
+ array( 'cat_ids' => $categories, 'cat_ids' => $tags, 'post_ids' => $post_ids ) );
322
+ $pagination_links = $ai1ec_calendar_helper->get_week_pagination_links( $week_offset );
323
+
324
+ $view_args = array(
325
+ 'title' => sprintf( __( 'Week of %s', AI1EC_PLUGIN_NAME ), date_i18n( __( 'F j' ), $timestamp, true ) ),
326
+ 'cell_array' => $cell_array,
327
+ 'now_top' => $bits['hours'] * 60 + $bits['minutes'],
328
+ 'pagination_links' => $pagination_links,
329
+ 'active_event' => $active_event,
330
+ 'post_ids' => join( ',', $post_ids ),
331
+ 'done_allday_label' => false,
332
+ 'done_grid' => false
333
+ );
334
+ return apply_filters( 'ai1ec_get_week_view', $ai1ec_view_helper->get_view( 'week.php', $view_args ), $view_args );
335
+ }
336
+
337
  /**
338
  * get_agenda_view function
339
  *
367
  $event_results = $ai1ec_calendar_helper->get_events_relative_to(
368
  $timestamp,
369
  $ai1ec_settings->agenda_events_per_page,
370
+ $page_offset,
371
+ array( 'post_ids' => $post_ids )
372
  );
373
  $dates = $ai1ec_calendar_helper->get_agenda_date_array( $event_results['events'] );
374
 
383
  'page_offset' => $page_offset,
384
  'pagination_links' => $pagination_links,
385
  'active_event' => $active_event,
386
+ 'expanded' => $ai1ec_settings->agenda_events_expanded,
387
+ 'post_ids' => join( ',', $post_ids ),
388
  );
389
  return apply_filters( 'ai1ec_get_agenda_view', $ai1ec_view_helper->get_view( 'agenda.php', $args ), $args );
390
  }
395
  * AJAX request handler for month view.
396
  *
397
  * @return void
398
+ */
399
  function ajax_month() {
400
  global $ai1ec_view_helper;
401
 
405
  $args = array(
406
  'month_offset' => $this->request['ai1ec_month_offset'],
407
  'active_event' => $this->request['ai1ec_active_event'],
408
+ 'post_ids' => array_filter( explode( ',', $this->request['ai1ec_post_ids'] ), 'is_numeric' ),
409
  );
410
 
411
  // Return this data structure to the client
416
  $ai1ec_view_helper->json_response( $data );
417
  }
418
 
419
+ /**
420
+ * ajax_week function
421
+ *
422
+ * AJAX request handler for week view.
423
+ *
424
+ * @return void
425
+ */
426
+ function ajax_week() {
427
+ global $ai1ec_view_helper;
428
+
429
+ $this->process_request();
430
+
431
+ // View arguments
432
+ $args = array(
433
+ 'week_offset' => $this->request['ai1ec_week_offset'],
434
+ 'active_event' => $this->request['ai1ec_active_event'],
435
+ 'post_ids' => array_filter( explode( ',', $this->request['ai1ec_post_ids'] ), 'is_numeric' ),
436
+ );
437
+
438
+ // Return this data structure to the client
439
+ $data = array(
440
+ 'body_class' => join( ' ', $this->body_class() ),
441
+ 'html' => $this->get_week_view( $args ),
442
+ );
443
+ $ai1ec_view_helper->json_response( $data );
444
+ }
445
+
446
  /**
447
  * ajax_agenda function
448
  *
457
 
458
  // View arguments
459
  $args = array(
460
+ 'page_offset' => $this->request['ai1ec_page_offset'],
461
  'active_event' => $this->request['ai1ec_active_event'],
462
+ 'post_ids' => array_filter( explode( ',', $this->request['ai1ec_post_ids'] ), 'is_numeric' ),
463
  );
464
 
465
  // Return this data structure to the client
571
  {
572
  global $ai1ec_settings;
573
 
574
+ // Include dependent jQuery plugins
575
  wp_enqueue_script( 'jquery.scrollTo', AI1EC_JS_URL . '/jquery.scrollTo-min.js', array( 'jquery' ), 1 );
576
+ wp_enqueue_script( 'jquery.tableScroll', AI1EC_JS_URL . '/jquery.tablescroll.js', array( 'jquery' ), 1 );
577
  // Include element selector function
578
  wp_enqueue_script( 'ai1ec-element-selector', AI1EC_JS_URL . '/element-selector.js', array( 'jquery', 'jquery.scrollTo' ), 1 );
579
  // Include custom script
580
+ wp_enqueue_script( 'ai1ec-calendar', AI1EC_JS_URL . '/calendar.js', array( 'jquery', 'jquery.scrollTo', 'jquery.tableScroll' ), 1 );
581
 
582
  $data = array(
583
  // Point script to AJAX URL
app/helper/class-ai1ec-app-helper.php CHANGED
@@ -570,7 +570,9 @@ class Ai1ec_App_Helper {
570
  if( $term->term_id == AI1EC_FAKE_CATEGORY_ID )
571
  $link = $ai1ec_calendar_helper->get_calendar_url( null );
572
  else
573
- $link = $ai1ec_calendar_helper->get_calendar_url( null, array( $term->term_id ) );
 
 
574
  }
575
 
576
  return $link;
570
  if( $term->term_id == AI1EC_FAKE_CATEGORY_ID )
571
  $link = $ai1ec_calendar_helper->get_calendar_url( null );
572
  else
573
+ $link = $ai1ec_calendar_helper->get_calendar_url( null,
574
+ array( 'cat_ids' => array( $term->term_id ) )
575
+ );
576
  }
577
 
578
  return $link;
app/helper/class-ai1ec-calendar-helper.php CHANGED
@@ -47,17 +47,19 @@ class Ai1ec_Calendar_Helper {
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
 
@@ -66,29 +68,30 @@ class Ai1ec_Calendar_Helper {
66
  $bits = $ai1ec_events_helper->gmgetdate( $time );
67
  $last_day = gmdate( 't', $time );
68
 
69
- $start_time = gmmktime( 0, 0, 0, $bits['mon'], 1, $bits['year'] );
70
- $end_time = gmmktime( 0, 0, 0, $bits['mon'], $last_day + 1, $bits['year'] );
71
-
72
- $events_between = $this->get_events_between( $start_time, $end_time, 'publish', $categories, $tags );
73
 
74
  // ==========================================
75
  // = Iterate through each date of the month =
76
  // ==========================================
77
  for( $day = 1; $day <= $last_day; $day++ )
78
- {
79
- $_events = array();
80
  $start_time = gmmktime( 0, 0, 0, $bits['mon'], $day, $bits['year'] );
81
  $end_time = gmmktime( 0, 0, 0, $bits['mon'], $day + 1, $bits['year'] );
82
-
83
- foreach( $events_between as $event ) {
84
- if( $ai1ec_events_helper->gmt_to_local( $event->start ) >= $start_time && $ai1ec_events_helper->gmt_to_local( $event->start ) < $end_time ) {
85
- $_events[] = $event;
86
- }
87
- }
88
- $days_events[$day] = $_events;
 
89
  }
90
 
91
- return apply_filters( 'ai1ec_get_events_for_month', $days_events, $time, $categories, $tags );
92
  }
93
 
94
  /**
@@ -96,7 +99,8 @@ class Ai1ec_Calendar_Helper {
96
  *
97
  * Return an array of weeks, each containing an array of days, each
98
  * containing the date for the day ['date'] (if inside the month) and
99
- * the events ['events'] (if any) for the day.
 
100
  *
101
  * @param int $timestamp UNIX timestamp of the 1st day of the desired
102
  * month to display
@@ -142,9 +146,9 @@ class Ai1ec_Calendar_Helper {
142
  'today' =>
143
  $bits['year'] == $today['year'] &&
144
  $bits['mon'] == $today['mon'] &&
145
- $i == $today['mday'],
146
  'events' => $days_events[$i]
147
- );
148
  // If reached the end of the week, increment week
149
  if( count( $weeks[$week] ) == 7 )
150
  $week++;
@@ -158,69 +162,172 @@ class Ai1ec_Calendar_Helper {
158
  return $weeks;
159
  }
160
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  /**
162
  * get_events_between function
163
  *
164
  * Return all events starting after the given start time and before the
165
- * given end time. If there are any all-day events spanning this period,
166
- * then return those as well. All-day events are returned first.
167
- *
168
- * @param int $start_time limit to events starting after this (local) UNIX time
169
- * @param int $end_time limit to events starting before this (local) UNIX time
170
- * @param string $post_status limit to events matching this post_status
171
- * (null for no restriction)
172
- *
173
- * @return array list of matching event objects
 
 
 
 
174
  **/
175
- function get_events_between( $start_time, $end_time, $post_status = 'publish' ) {
176
- global $wpdb, $ai1ec_events_helper, $current_user;
 
177
 
178
  // Convert timestamps to MySQL format in GMT time
179
  $start_time = $ai1ec_events_helper->local_to_gmt( $start_time );
180
  $end_time = $ai1ec_events_helper->local_to_gmt( $end_time );
181
 
182
  // Query arguments
183
- $args = array(
184
- $start_time,
185
- $end_time,
186
- );
187
-
188
- if( current_user_can( 'administrator' ) || current_user_can( 'editor' ) ) {
189
- $post_status = "AND ( post_status = %s OR post_status = %s ) ";
190
- $args[] = 'publish';
191
- $args[] = 'private';
192
- }
193
- else if( is_user_logged_in() ) {
194
- // get user info
195
- get_currentuserinfo();
196
-
197
- /**
198
- * include post_status = published
199
- * or
200
- * post_status = private and author = logged in user
201
- */
202
- $post_status = "AND " .
203
- "( " .
204
- "post_status = %s " .
205
-
206
- "OR " .
207
-
208
- "( " .
209
- "post_status = %s " .
210
-
211
- "AND " .
212
-
213
- "post_author = %d " .
214
- ") " .
215
- ") ";
216
-
217
- $args[] = 'publish';
218
- $args[] = 'private';
219
- $args[] = $current_user->ID;
220
- } else {
221
- $post_status = "AND post_status = %s ";
222
- $args[] = 'publish';
223
- }
224
 
225
  $query = $wpdb->prepare(
226
  "SELECT p.*, e.post_id, i.id AS instance_id, " .
@@ -235,10 +342,13 @@ class Ai1ec_Calendar_Helper {
235
  "FROM {$wpdb->prefix}ai1ec_events e " .
236
  "INNER JOIN $wpdb->posts p ON p.ID = e.post_id " .
237
  "INNER JOIN {$wpdb->prefix}ai1ec_event_instances i ON e.post_id = i.post_id " .
 
238
  "WHERE post_type = '" . AI1EC_POST_TYPE . "' " .
239
- "AND i.start >= FROM_UNIXTIME( %d ) " .
240
- "AND i.start < FROM_UNIXTIME( %d ) " .
241
- $post_status .
 
 
242
  "ORDER BY allday DESC, i.start ASC, post_title ASC",
243
  $args );
244
 
@@ -261,9 +371,10 @@ class Ai1ec_Calendar_Helper {
261
  * @param int $time limit to events starting after this (local) UNIX time
262
  * @param int $limit return a maximum of this number of items
263
  * @param int $page_offset offset the result set by $limit times this number
264
- * @param array $filter Array of filters for the events returned.
265
- * ['cat_ids'] => non-associatative array of category IDs
266
- * ['tag_ids'] => non-associatative array of tag IDs
 
267
  *
268
  * @return array three-element array:
269
  * ['events'] an array of matching event objects
@@ -271,7 +382,8 @@ class Ai1ec_Calendar_Helper {
271
  * ['next'] true if more next events
272
  **/
273
  function get_events_relative_to( $time, $limit = 0, $page_offset = 0, $filter = array() ) {
274
- global $wpdb, $ai1ec_events_helper, $current_user;
 
275
 
276
  // Figure out what the beginning of the day is to properly query all-day
277
  // events; then convert to GMT time
@@ -287,47 +399,14 @@ class Ai1ec_Calendar_Helper {
287
  $first_record = $page_offset * $limit;
288
  else
289
  $first_record = ( -$page_offset - 1 ) * $limit;
290
-
291
- // administrators and editors can see private posts
292
- if( current_user_can( 'administrator' ) || current_user_can( 'editor' ) ) {
293
- $post_status = "AND ( post_status = %s OR post_status = %s ) ";
294
- $args[] = 'publish';
295
- $args[] = 'private';
296
- }
297
- else if( is_user_logged_in() ) {
298
- // get user info
299
- get_currentuserinfo();
300
-
301
- /**
302
- * include post_status = published
303
- * or
304
- * post_status = private and author = logged in user
305
- */
306
- $post_status = "AND " .
307
- "( " .
308
- "post_status = %s " .
309
-
310
- "OR " .
311
-
312
- "( " .
313
- "post_status = %s " .
314
 
315
- "AND " .
 
316
 
317
- "post_author = %d " .
318
- ") " .
319
- ") ";
320
 
321
- $args[] = 'publish';
322
- $args[] = 'private';
323
- $args[] = $current_user->ID;
324
- } else {
325
- $post_status = "AND post_status = %s ";
326
- $args[] = 'publish';
327
- }
328
-
329
- // Get the Join (filter_join) and Where (filter_where) statements based on $filter elements specified
330
- $filter = $this->_get_filter_sql( $filter );
331
  $query = $wpdb->prepare(
332
  "SELECT SQL_CALC_FOUND_ROWS p.*, e.post_id, i.id AS instance_id, " .
333
  "UNIX_TIMESTAMP( i.start ) AS start, " .
@@ -348,7 +427,7 @@ class Ai1ec_Calendar_Helper {
348
  : "i.start < FROM_UNIXTIME( %d ) "
349
  ) .
350
  $filter['filter_where'] .
351
- $post_status .
352
  // Reverse order when viewing negative pages, to get correct set of
353
  // records. Then reverse results later to order them properly.
354
  "ORDER BY i.start " . ( $page_offset >= 0 ? 'ASC' : 'DESC' ) .
@@ -358,7 +437,7 @@ class Ai1ec_Calendar_Helper {
358
 
359
  $events = $wpdb->get_results( $query, ARRAY_A );
360
 
361
- // Re-order records if in negative page offset
362
  if( $page_offset < 0 ) $events = array_reverse( $events );
363
 
364
  foreach( $events as &$event ) {
@@ -383,11 +462,13 @@ class Ai1ec_Calendar_Helper {
383
  $query = $wpdb->prepare(
384
  "SELECT COUNT(*) " .
385
  "FROM {$wpdb->prefix}ai1ec_events e " .
386
- "INNER JOIN {$wpdb->prefix}ai1ec_event_instances i ON e.post_id = i.post_id " .
387
- "INNER JOIN $wpdb->posts p ON e.post_id = p.ID " .
 
388
  "WHERE post_type = '" . AI1EC_POST_TYPE . "' " .
389
  "AND i.start < FROM_UNIXTIME( %d ) " .
390
- ( $post_status == null ? '' : "AND post_status = %s " ),
 
391
  $args );
392
  $prev = $wpdb->get_var( $query );
393
  $next = $more;
@@ -445,16 +526,17 @@ class Ai1ec_Calendar_Helper {
445
  *
446
  * Returns the URL of the configured calendar page in the default view,
447
  * optionally preloaded at the month containing the given event (rather than
448
- * today's date), and optionally prefiltered by the given category IDs and/or
449
- * tag IDs.
450
  *
451
- * @param object|null $event The event to focus the calendar on
452
- * @param array $cat_ids The category IDs to filter the calendar by
453
- * @param array $tag_ids The tag IDs to filter the calendar by
 
 
454
  *
455
  * @return string The URL for this calendar
456
  **/
457
- function get_calendar_url( $event = null, $cat_ids = array(), $tag_ids = array() ) {
458
  global $ai1ec_settings, $ai1ec_events_helper, $ai1ec_app_helper, $wpdb;
459
 
460
  $url = get_permalink( $ai1ec_settings->calendar_page_id );
@@ -470,12 +552,25 @@ class Ai1ec_Calendar_Helper {
470
  $today = $ai1ec_events_helper->gmgetdate( $ai1ec_events_helper->gmt_to_local( time() ) );
471
  $desired = $ai1ec_events_helper->gmgetdate( $ai1ec_events_helper->gmt_to_local( $event->start ) );
472
  $month_offset =
473
- ( $desired['year'] - $today['year'] ) * 12 +
474
  $desired['mon'] - $today['mon'];
475
 
476
  $url .= "ai1ec_month_offset=$month_offset";
477
  break;
478
 
 
 
 
 
 
 
 
 
 
 
 
 
 
479
  case 'agenda':
480
  // Find out how many event instances are between today's first
481
  // instance and the desired event's instance
@@ -505,12 +600,13 @@ class Ai1ec_Calendar_Helper {
505
  $url .= "&ai1ec_active_event=$event->post_id";
506
  }
507
 
508
- if( $cat_ids )
509
- $url .= $ai1ec_app_helper->get_param_delimiter_char( $url ) .
510
- 'ai1ec_cat_ids=' . join( ',', $cat_ids );
511
- if( $tag_ids )
512
- $url .= $ai1ec_app_helper->get_param_delimiter_char( $url ) .
513
- 'ai1ec_tag_ids=' . join( ',', $tag_ids );
 
514
 
515
  return $url;
516
  }
@@ -528,7 +624,7 @@ class Ai1ec_Calendar_Helper {
528
  static $weekdays;
529
 
530
  if( ! isset( $weekdays ) )
531
- {
532
  $time = strtotime( 'next Sunday' );
533
  $time = strtotime( "+{$ai1ec_settings->week_start_day} days", $time );
534
 
@@ -544,7 +640,7 @@ class Ai1ec_Calendar_Helper {
544
  /**
545
  * get_month_pagination_links function
546
  *
547
- * Returns an associative array of four links for the month view of the
548
  * calendar:
549
  * previous year, previous month, next month, and next year, in that order.
550
  * Each element's key is an associative array containing the link's ID
@@ -589,6 +685,53 @@ class Ai1ec_Calendar_Helper {
589
  return $links;
590
  }
591
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
592
  /**
593
  * get_agenda_pagination_links function
594
  *
@@ -627,29 +770,83 @@ class Ai1ec_Calendar_Helper {
627
  return $links;
628
  }
629
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
630
  /**
631
  * _get_filter_sql function
632
  *
633
  * Takes an array of filtering options and turns it into JOIN and WHERE statements
634
  * for running an SQL query limited to the specified options
635
  *
636
- * @param array $filter Array of filters for the events returned.
637
  * ['cat_ids'] => non-associatative array of category IDs
638
  * ['tag_ids'] => non-associatative array of tag IDs
639
  * ['post_ids'] => non-associatative array of event post IDs
640
- *
641
- * @return array Returns the same $filter array modified to have:
642
  * ['filter_join'] the Join statements for the SQL
643
  * ['filter_where'] the Where statements for the SQL
644
- **/
645
- function _get_filter_sql( $filter ) {
 
 
646
  global $wpdb;
647
 
648
  // Set up the filter join and where strings
649
  $filter['filter_join'] = '';
650
  $filter['filter_where'] = '';
651
 
652
- // By default open the Where with an AND ( .. ) to group all statements. Later, set it to OR to join statements together.
 
653
  // TODO - make this cleaner by supporting the choice of AND/OR logic
654
  $where_logic = ' AND (';
655
 
@@ -681,11 +878,9 @@ class Ai1ec_Calendar_Helper {
681
  }
682
 
683
  // Close the Where statement bracket if any Where statements were set
684
- if( $filter['filter_where'] != '') {
685
  $filter['filter_where'] .= ' ) ';
686
  }
687
-
688
- return $filter;
689
- }
690
  }
691
  // END class
47
  /**
48
  * get_events_for_month function
49
  *
50
+ * Return an array of all dates for the given 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 $filter Array of filters for the events returned:
56
+ * ['cat_ids'] => non-associatative array of category IDs
57
+ * ['tag_ids'] => non-associatative array of tag IDs
58
+ * ['post_ids'] => non-associatative array of post IDs
59
  *
60
  * @return array array of arrays as per function description
61
  **/
62
+ function get_events_for_month( $time, $filter = array() )
63
  {
64
  global $ai1ec_events_helper;
65
 
68
  $bits = $ai1ec_events_helper->gmgetdate( $time );
69
  $last_day = gmdate( 't', $time );
70
 
71
+ $start_time = gmmktime( 0, 0, 0, $bits['mon'], 1, $bits['year'] );
72
+ $end_time = gmmktime( 0, 0, 0, $bits['mon'], $last_day + 1, $bits['year'] );
73
+
74
+ $month_events = $this->get_events_between( $start_time, $end_time, $filter );
75
 
76
  // ==========================================
77
  // = Iterate through each date of the month =
78
  // ==========================================
79
  for( $day = 1; $day <= $last_day; $day++ )
80
+ {
81
+ $_events = array();
82
  $start_time = gmmktime( 0, 0, 0, $bits['mon'], $day, $bits['year'] );
83
  $end_time = gmmktime( 0, 0, 0, $bits['mon'], $day + 1, $bits['year'] );
84
+
85
+ // Itemize events that fall under the current day
86
+ foreach( $month_events as $event ) {
87
+ $event_start = $ai1ec_events_helper->gmt_to_local( $event->start );
88
+ if( $event_start >= $start_time && $event_start < $end_time )
89
+ $_events[] = $event;
90
+ }
91
+ $days_events[$day] = $_events;
92
  }
93
 
94
+ return apply_filters( 'ai1ec_get_events_for_month', $days_events, $time, $filter );
95
  }
96
 
97
  /**
99
  *
100
  * Return an array of weeks, each containing an array of days, each
101
  * containing the date for the day ['date'] (if inside the month) and
102
+ * the events ['events'] (if any) for the day, and a boolean ['today']
103
+ * indicating whether that day is today.
104
  *
105
  * @param int $timestamp UNIX timestamp of the 1st day of the desired
106
  * month to display
146
  'today' =>
147
  $bits['year'] == $today['year'] &&
148
  $bits['mon'] == $today['mon'] &&
149
+ $i == $today['mday'],
150
  'events' => $days_events[$i]
151
+ );
152
  // If reached the end of the week, increment week
153
  if( count( $weeks[$week] ) == 7 )
154
  $week++;
162
  return $weeks;
163
  }
164
 
165
+ /**
166
+ * get_week_cell_array function
167
+ *
168
+ * Return an associative array of weekdays, indexed by the day's date,
169
+ * starting the day given by $timestamp, each element an associative array
170
+ * containing three elements:
171
+ * ['today'] => whether the day is today
172
+ * ['allday'] => non-associative ordered array of events that are all-day
173
+ * ['notallday'] => non-associative ordered array of non-all-day events to
174
+ * display for that day, each element another associative
175
+ * array like so:
176
+ * ['top'] => how many minutes offset from the start of the day
177
+ * ['height'] => how many minutes this event spans
178
+ * ['indent'] => how much to indent this event to accommodate multiple
179
+ * events occurring at the same time (0, 1, 2, etc., to
180
+ * be multiplied by whatever desired px/em amount)
181
+ * ['event'] => event data object
182
+ *
183
+ * @param int $timestamp the UNIX timestamp of the first day of the week
184
+ * @param array $filter Array of filters for the events returned:
185
+ * ['cat_ids'] => non-associatative array of category IDs
186
+ * ['tag_ids'] => non-associatative array of tag IDs
187
+ * ['post_ids'] => non-associatative array of post IDs
188
+ *
189
+ * @return array array of arrays as per function description
190
+ **/
191
+ function get_week_cell_array( $timestamp, $filter = array() )
192
+ {
193
+ global $ai1ec_events_helper, $ai1ec_settings;
194
+
195
+ // Decompose given date and current time into components, used below
196
+ $bits = $ai1ec_events_helper->gmgetdate( $timestamp );
197
+ $now = $ai1ec_events_helper->gmgetdate( $ai1ec_events_helper->gmt_to_local( time() ) );
198
+
199
+ // Do one SQL query to find all events for the week, including spanning
200
+ $week_events = $this->get_events_between(
201
+ $timestamp,
202
+ gmmktime( 0, 0, 0, $bits['mon'], $bits['mday'] + 7, $bits['year'] ),
203
+ $filter,
204
+ true );
205
+
206
+ // Split up events on a per-day basis
207
+ $all_events = array();
208
+ foreach( $week_events as $evt ) {
209
+ $evt_start = $ai1ec_events_helper->gmt_to_local( $evt->start );
210
+ $evt_end = $ai1ec_events_helper->gmt_to_local( $evt->end );
211
+
212
+ // Iterate through each day of the week and generate new event object
213
+ // based on this one for each day that it spans
214
+ for( $day = $bits['mday']; $day < $bits['mday'] + 7; $day++ ) {
215
+ $day_start = gmmktime( 0, 0, 0, $bits['mon'], $day, $bits['year'] );
216
+ $day_end = gmmktime( 0, 0, 0, $bits['mon'], $day + 1, $bits['year'] );
217
+
218
+ // If event falls on this day, make a copy.
219
+ if( $evt_end > $day_start && $evt_start < $day_end ) {
220
+ $_evt = clone $evt;
221
+ if( $evt_start < $day_start ) {
222
+ // If event starts before this day, adjust copy's start time
223
+ $_evt->start = $ai1ec_events_helper->local_to_gmt( $day_start );
224
+ $_evt->start_truncated = true;
225
+ }
226
+ if( $evt_end > $day_end ) {
227
+ // If event ends after this day, adjust copy's end time
228
+ $_evt->end = $ai1ec_events_helper->local_to_gmt( $day_end );
229
+ $_evt->end_truncated = true;
230
+ }
231
+
232
+ // Place copy of event in appropriate category
233
+ if( $_evt->allday )
234
+ $all_events[$day_start]['allday'][] = $_evt;
235
+ else
236
+ $all_events[$day_start]['notallday'][] = $_evt;
237
+ }
238
+ }
239
+ }
240
+
241
+ // This will store the returned array
242
+ $days = array();
243
+ // =========================================
244
+ // = Iterate through each date of the week =
245
+ // =========================================
246
+ for( $day = $bits['mday']; $day < $bits['mday'] + 7; $day++ )
247
+ {
248
+ $day_date = gmmktime( 0, 0, 0, $bits['mon'], $day, $bits['year'] );
249
+ // Re-fetch date bits, since $bits['mday'] + 7 might be in the next month
250
+ $day_bits = $ai1ec_events_helper->gmgetdate( $day_date );
251
+
252
+ // Initialize empty arrays for this day if no events to minimize warnings
253
+ if( ! isset( $all_events[$day_date]['allday'] ) ) $all_events[$day_date]['allday'] = array();
254
+ if( ! isset( $all_events[$day_date]['notallday'] ) ) $all_events[$day_date]['notallday'] = array();
255
+
256
+ $notallday = array();
257
+ $evt_stack = array( 0 ); // Stack to keep track of indentation
258
+ foreach( $all_events[$day_date]['notallday'] as $evt )
259
+ {
260
+ $start_bits = $ai1ec_events_helper->gmgetdate( $ai1ec_events_helper->gmt_to_local( $evt->start ) );
261
+
262
+ // Calculate top and bottom edges of current event
263
+ $top = $start_bits['hours'] * 60 + $start_bits['minutes'];
264
+ $bottom = min( $top + $evt->getDuration() / 60, 1440 );
265
+
266
+ // While there's more than one event in the stack and this event's top
267
+ // position is beyond the last event's bottom, pop the stack
268
+ while( count( $evt_stack ) > 1 && $top >= end( $evt_stack ) )
269
+ array_pop( $evt_stack );
270
+ // Indentation is number of stacked events minus 1
271
+ $indent = count( $evt_stack ) - 1;
272
+ // Push this event onto the top of the stack
273
+ array_push( $evt_stack, $bottom );
274
+
275
+ $notallday[] = array(
276
+ 'top' => $top,
277
+ 'height' => $bottom - $top,
278
+ 'indent' => $indent,
279
+ 'event' => $evt,
280
+ );
281
+ }
282
+
283
+ $days[$day_date] = array(
284
+ 'today' =>
285
+ $day_bits['year'] == $now['year'] &&
286
+ $day_bits['mon'] == $now['mon'] &&
287
+ $day_bits['mday'] == $now['mday'],
288
+ 'allday' => $all_events[$day_date]['allday'],
289
+ 'notallday' => $notallday,
290
+ );
291
+ }
292
+
293
+ return apply_filters( 'ai1ec_get_week_cell_array', $days, $timestamp, $filter );
294
+ }
295
+
296
  /**
297
  * get_events_between function
298
  *
299
  * Return all events starting after the given start time and before the
300
+ * given end time that the currently logged in user has permission to view.
301
+ * If $spanning is true, then also include events that span this
302
+ * period. All-day events are returned first.
303
+ *
304
+ * @param int $start_time limit to events starting after this (local) UNIX time
305
+ * @param int $end_time limit to events starting before this (local) UNIX time
306
+ * @param array $filter Array of filters for the events returned:
307
+ * ['cat_ids'] => non-associatative array of category IDs
308
+ * ['tag_ids'] => non-associatative array of tag IDs
309
+ * ['post_ids'] => non-associatative array of post IDs
310
+ * @param bool $spanning also include events that span this period
311
+ *
312
+ * @return array list of matching event objects
313
  **/
314
+ function get_events_between( $start_time, $end_time, $filter, $spanning = false ) {
315
+
316
+ global $wpdb, $ai1ec_events_helper;
317
 
318
  // Convert timestamps to MySQL format in GMT time
319
  $start_time = $ai1ec_events_helper->local_to_gmt( $start_time );
320
  $end_time = $ai1ec_events_helper->local_to_gmt( $end_time );
321
 
322
  // Query arguments
323
+ $args = array( $start_time, $end_time );
324
+
325
+ // Get post status Where snippet and associated SQL arguments
326
+ $this->_get_post_status_sql( $post_status_where = '', $args );
327
+
328
+ // Get the Join (filter_join) and Where (filter_where) statements based on
329
+ // $filter elements specified
330
+ $this->_get_filter_sql( $filter );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
331
 
332
  $query = $wpdb->prepare(
333
  "SELECT p.*, e.post_id, i.id AS instance_id, " .
342
  "FROM {$wpdb->prefix}ai1ec_events e " .
343
  "INNER JOIN $wpdb->posts p ON p.ID = e.post_id " .
344
  "INNER JOIN {$wpdb->prefix}ai1ec_event_instances i ON e.post_id = i.post_id " .
345
+ $filter['filter_join'] .
346
  "WHERE post_type = '" . AI1EC_POST_TYPE . "' " .
347
+ "AND " .
348
+ ( $spanning ? "i.end > FROM_UNIXTIME( %d ) AND i.start < FROM_UNIXTIME( %d ) "
349
+ : "i.start >= FROM_UNIXTIME( %d ) AND i.start < FROM_UNIXTIME( %d ) " ) .
350
+ $filter['filter_where'] .
351
+ $post_status_where .
352
  "ORDER BY allday DESC, i.start ASC, post_title ASC",
353
  $args );
354
 
371
  * @param int $time limit to events starting after this (local) UNIX time
372
  * @param int $limit return a maximum of this number of items
373
  * @param int $page_offset offset the result set by $limit times this number
374
+ * @param array $filter Array of filters for the events returned.
375
+ * ['cat_ids'] => non-associatative array of category IDs
376
+ * ['tag_ids'] => non-associatative array of tag IDs
377
+ * ['post_ids'] => non-associatative array of post IDs
378
  *
379
  * @return array three-element array:
380
  * ['events'] an array of matching event objects
382
  * ['next'] true if more next events
383
  **/
384
  function get_events_relative_to( $time, $limit = 0, $page_offset = 0, $filter = array() ) {
385
+
386
+ global $wpdb, $ai1ec_events_helper;
387
 
388
  // Figure out what the beginning of the day is to properly query all-day
389
  // events; then convert to GMT time
399
  $first_record = $page_offset * $limit;
400
  else
401
  $first_record = ( -$page_offset - 1 ) * $limit;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
402
 
403
+ // Get post status Where snippet and associated SQL arguments
404
+ $this->_get_post_status_sql( $post_status_where = '', $args );
405
 
406
+ // Get the Join (filter_join) and Where (filter_where) statements based on
407
+ // $filter elements specified
408
+ $this->_get_filter_sql( $filter );
409
 
 
 
 
 
 
 
 
 
 
 
410
  $query = $wpdb->prepare(
411
  "SELECT SQL_CALC_FOUND_ROWS p.*, e.post_id, i.id AS instance_id, " .
412
  "UNIX_TIMESTAMP( i.start ) AS start, " .
427
  : "i.start < FROM_UNIXTIME( %d ) "
428
  ) .
429
  $filter['filter_where'] .
430
+ $post_status_where .
431
  // Reverse order when viewing negative pages, to get correct set of
432
  // records. Then reverse results later to order them properly.
433
  "ORDER BY i.start " . ( $page_offset >= 0 ? 'ASC' : 'DESC' ) .
437
 
438
  $events = $wpdb->get_results( $query, ARRAY_A );
439
 
440
+ // Reorder records if in negative page offset
441
  if( $page_offset < 0 ) $events = array_reverse( $events );
442
 
443
  foreach( $events as &$event ) {
462
  $query = $wpdb->prepare(
463
  "SELECT COUNT(*) " .
464
  "FROM {$wpdb->prefix}ai1ec_events e " .
465
+ "INNER JOIN {$wpdb->prefix}ai1ec_event_instances i ON e.post_id = i.post_id " .
466
+ "INNER JOIN $wpdb->posts p ON e.post_id = p.ID " .
467
+ $filter['filter_join'] .
468
  "WHERE post_type = '" . AI1EC_POST_TYPE . "' " .
469
  "AND i.start < FROM_UNIXTIME( %d ) " .
470
+ $filter['filter_where'] .
471
+ $post_status_where,
472
  $args );
473
  $prev = $wpdb->get_var( $query );
474
  $next = $more;
526
  *
527
  * Returns the URL of the configured calendar page in the default view,
528
  * optionally preloaded at the month containing the given event (rather than
529
+ * today's date), and optionally prefiltered by the given filters.
 
530
  *
531
+ * @param object|null $event The event to focus the calendar on
532
+ * @param array $filter Array of filters for the events returned.
533
+ * ['cat_ids'] => non-associatative array of category IDs
534
+ * ['tag_ids'] => non-associatative array of tag IDs
535
+ * ['post_ids'] => non-associatative array of post IDs
536
  *
537
  * @return string The URL for this calendar
538
  **/
539
+ function get_calendar_url( $event = null, $filter = array() ) {
540
  global $ai1ec_settings, $ai1ec_events_helper, $ai1ec_app_helper, $wpdb;
541
 
542
  $url = get_permalink( $ai1ec_settings->calendar_page_id );
552
  $today = $ai1ec_events_helper->gmgetdate( $ai1ec_events_helper->gmt_to_local( time() ) );
553
  $desired = $ai1ec_events_helper->gmgetdate( $ai1ec_events_helper->gmt_to_local( $event->start ) );
554
  $month_offset =
555
+ ( $desired['year'] - $today['year'] ) * 12 +
556
  $desired['mon'] - $today['mon'];
557
 
558
  $url .= "ai1ec_month_offset=$month_offset";
559
  break;
560
 
561
+ case 'week':
562
+ // Get components of localized timstamps and calculate week offset
563
+ /* TODO - code this; first need to find out first day of week based on week start day,
564
+ then calculate how many weeks off we are from that one
565
+ $today = $ai1ec_events_helper->gmgetdate( $ai1ec_events_helper->gmt_to_local( time() ) );
566
+ $desired = $ai1ec_events_helper->gmgetdate( $ai1ec_events_helper->gmt_to_local( $event->start ) );
567
+ $week_offset =
568
+ ( $desired['year'] - $today['year'] ) * 12 +
569
+ $desired['mon'] - $today['mon'];
570
+
571
+ $url .= "ai1ec_week_offset=$week_offset";*/
572
+ break;
573
+
574
  case 'agenda':
575
  // Find out how many event instances are between today's first
576
  // instance and the desired event's instance
600
  $url .= "&ai1ec_active_event=$event->post_id";
601
  }
602
 
603
+ // Add filter parameters
604
+ foreach( $filter as $key => $val ) {
605
+ if( $val ) {
606
+ $url .= $ai1ec_app_helper->get_param_delimiter_char( $url ) .
607
+ "ai1ec_$key=" . join( ',', $val );
608
+ }
609
+ }
610
 
611
  return $url;
612
  }
624
  static $weekdays;
625
 
626
  if( ! isset( $weekdays ) )
627
+ {
628
  $time = strtotime( 'next Sunday' );
629
  $time = strtotime( "+{$ai1ec_settings->week_start_day} days", $time );
630
 
640
  /**
641
  * get_month_pagination_links function
642
  *
643
+ * Returns a non-associative array of four links for the month view of the
644
  * calendar:
645
  * previous year, previous month, next month, and next year, in that order.
646
  * Each element's key is an associative array containing the link's ID
685
  return $links;
686
  }
687
 
688
+ /**
689
+ * get_week_pagination_links function
690
+ *
691
+ * Returns a non-associative array of two links for the week view of the
692
+ * calendar:
693
+ * previous week, next week, in that order.
694
+ * Each element's key is an associative array containing the link's ID
695
+ * ['id'], text ['text'] and value to assign to link's href ['href'].
696
+ *
697
+ * @param int $cur_offset week offset of current week, needed for hrefs
698
+ *
699
+ * @return array array of link information as described above
700
+ **/
701
+ function get_week_pagination_links( $cur_offset ) {
702
+ global $ai1ec_events_helper;
703
+
704
+ $links = array();
705
+
706
+ // Base timestamp on offset week
707
+ $bits = $ai1ec_events_helper->gmgetdate( $ai1ec_events_helper->gmt_to_local( time() ) );
708
+ $bits['mday'] += $ai1ec_events_helper->get_week_start_day_offset( $bits['wday'] );
709
+ $bits['mday'] += $cur_offset * 7;
710
+
711
+ $links[] = array(
712
+ 'id' => 'ai1ec-prev-week',
713
+ 'text' =>
714
+ '‹ ' .
715
+ sprintf(
716
+ __( 'Week of %s', AI1EC_PLUGIN_NAME ),
717
+ date_i18n( __( 'M j' ), gmmktime( 0, 0, 0, $bits['mon'], $bits['mday'] - 7, $bits['year'] ), true )
718
+ ),
719
+ 'href' => '#action=ai1ec_week&ai1ec_week_offset=' . ( $cur_offset - 1 ),
720
+ );
721
+ $links[] = array(
722
+ 'id' => 'ai1ec-next-week',
723
+ 'text' =>
724
+ sprintf(
725
+ __( 'Week of %s', AI1EC_PLUGIN_NAME ),
726
+ date_i18n( __( 'M j' ), gmmktime( 0, 0, 0, $bits['mon'], $bits['mday'] + 7, $bits['year'] ), true )
727
+ )
728
+ . ' ›',
729
+ 'href' => '#action=ai1ec_week&ai1ec_week_offset=' . ( $cur_offset + 1 ),
730
+ );
731
+
732
+ return $links;
733
+ }
734
+
735
  /**
736
  * get_agenda_pagination_links function
737
  *
770
  return $links;
771
  }
772
 
773
+ /**
774
+ * _get_post_status_sql function
775
+ *
776
+ * Returns SQL snippet for properly matching event posts, as well as array
777
+ * of arguments to pass to $wpdb->prepare, in function argument references.
778
+ * Nothing is returned by the function.
779
+ *
780
+ * @param string &$sql The variable to store the SQL snippet into
781
+ * @param array &$args The variable to store the SQL arguments into
782
+ *
783
+ * @return void
784
+ */
785
+ function _get_post_status_sql( &$sql, &$args )
786
+ {
787
+ global $current_user;
788
+
789
+ // Query the correct post status
790
+ if( current_user_can( 'administrator' ) || current_user_can( 'editor' ) )
791
+ {
792
+ // User has privilege of seeing all published and private posts
793
+
794
+ $post_status = "AND ( post_status = %s OR post_status = %s ) ";
795
+ $args[] = 'publish';
796
+ $args[] = 'private';
797
+ }
798
+ elseif( is_user_logged_in() )
799
+ {
800
+ // User has privilege of seeing all published and only their own private
801
+ // posts.
802
+
803
+ // get user info
804
+ get_currentuserinfo();
805
+
806
+ // include post_status = published
807
+ // OR
808
+ // post_status = private AND author = logged-in user
809
+ $post_status =
810
+ "AND ( " .
811
+ "post_status = %s " .
812
+ "OR ( post_status = %s AND post_author = %d ) " .
813
+ ") ";
814
+
815
+ $args[] = 'publish';
816
+ $args[] = 'private';
817
+ $args[] = $current_user->ID;
818
+ } else {
819
+ // User can only see published posts.
820
+ $post_status = "AND post_status = %s ";
821
+ $args[] = 'publish';
822
+ }
823
+ }
824
+
825
  /**
826
  * _get_filter_sql function
827
  *
828
  * Takes an array of filtering options and turns it into JOIN and WHERE statements
829
  * for running an SQL query limited to the specified options
830
  *
831
+ * @param array &$filter Array of filters for the events returned.
832
  * ['cat_ids'] => non-associatative array of category IDs
833
  * ['tag_ids'] => non-associatative array of tag IDs
834
  * ['post_ids'] => non-associatative array of event post IDs
835
+ * This array is modified to have:
 
836
  * ['filter_join'] the Join statements for the SQL
837
  * ['filter_where'] the Where statements for the SQL
838
+ *
839
+ * @return void
840
+ */
841
+ function _get_filter_sql( &$filter ) {
842
  global $wpdb;
843
 
844
  // Set up the filter join and where strings
845
  $filter['filter_join'] = '';
846
  $filter['filter_where'] = '';
847
 
848
+ // By default open the Where with an AND ( .. ) to group all statements.
849
+ // Later, set it to OR to join statements together.
850
  // TODO - make this cleaner by supporting the choice of AND/OR logic
851
  $where_logic = ' AND (';
852
 
878
  }
879
 
880
  // Close the Where statement bracket if any Where statements were set
881
+ if( $filter['filter_where'] != '' ) {
882
  $filter['filter_where'] .= ' ) ';
883
  }
884
+ }
 
 
885
  }
886
  // END class
app/helper/class-ai1ec-events-helper.php CHANGED
@@ -55,11 +55,11 @@ class Ai1ec_Events_Helper {
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
- // try to get the event instance id, if it is not set get the post id
62
- $instance_id = isset( $_REQUEST["instance_id"] ) ? (int) $_REQUEST["instance_id"] : false;
63
  $event = new Ai1ec_Event( $post_id, $instance_id );
64
 
65
  if( ! $event->post_id )
@@ -95,7 +95,7 @@ class Ai1ec_Events_Helper {
95
  "WHERE ical_feed_url = %s " .
96
  "AND ical_uid = %s " .
97
  "AND start = FROM_UNIXTIME( %d ) " .
98
- ( $has_recurrence ? 'AND NOT ' : 'AND ' ) .
99
  "( recurrence_rules IS NULL OR recurrence_rules = '' )";
100
  $args = array( $feed, $uid, $start );
101
  if( ! is_null( $exclude_post_id ) ) {
@@ -161,7 +161,7 @@ class Ai1ec_Events_Helper {
161
 
162
  $freq->firstOccurrence();
163
  while( ( $next = $freq->nextOccurrence( $start ) ) > 0 &&
164
- $count < 1000 )
165
  {
166
  $count++;
167
  $start = $next;
@@ -205,12 +205,12 @@ class Ai1ec_Events_Helper {
205
  // If event spans a day and end time is not midnight, or spans more than
206
  // a day, then create instance for each spanning day
207
  if( ( $start['mday'] != $end['mday'] &&
208
- ( $end['hours'] || $end['minutes'] || $end['seconds'] ) )
209
- || $e['end'] - $e['start'] > 60 * 60 * 24 ) {
210
- $this->create_cache_table_entries( $e );
211
  // Else cache single instance of event
212
  } else {
213
- $this->insert_event_in_cache_table( $e );
214
  }
215
  */
216
  $this->insert_event_in_cache_table( $e );
@@ -228,42 +228,42 @@ class Ai1ec_Events_Helper {
228
  * @return void
229
  **/
230
  function insert_event_in_cache_table( $event ) {
231
- global $wpdb;
232
-
233
- // Return the start/end times to GMT zone
234
- $event['start'] = $this->local_to_gmt( $event['start'] ) + date( 'Z', $event['start'] );
235
- $event['end'] = $this->local_to_gmt( $event['end'] ) + date( 'Z', $event['end'] );
236
-
237
- $wpdb->query(
238
- $wpdb->prepare(
239
- "INSERT INTO {$wpdb->prefix}ai1ec_event_instances " .
240
- " ( post_id, start, end ) " .
241
- "VALUES ( %d, FROM_UNIXTIME( %d ), FROM_UNIXTIME( %d ) )",
242
- $event
243
- )
244
- );
245
  }
246
 
247
  /**
248
- * create_cache_table_entries function
249
- *
250
- * Create a new entry for each day that the event spans.
251
- *
252
- * @param array $e Event array
253
- *
254
- * @return void
255
- **/
256
- function create_cache_table_entries( $e )
257
- {
258
- global $ai1ec_events_helper;
259
-
260
- // Decompose start dates into components
261
- $start_bits = getdate( $e['start'] );
262
-
263
- // ============================================
264
- // = Calculate the time for event's first day =
265
- // ============================================
266
- // Start time is event's original start time
267
  $event_start = $e['start'];
268
  // End time is beginning of next day
269
  $event_end = mktime(
@@ -286,9 +286,9 @@ class Ai1ec_Events_Helper {
286
  $event_end += 60 * 60 * 24;
287
  // Cache intermediate days
288
  while( $event_end < $e['end'] ) {
289
- $this->insert_event_in_cache_table( array( 'post_id' => $e['post_id'], 'start' => $event_start, 'end' => $event_end ) );
290
- $event_start = $event_end; // Start time is previous end time
291
- $event_end += 24 * 60 * 60; // Increment end time by 1 day
292
  }
293
 
294
  // ===========================================
@@ -301,7 +301,7 @@ class Ai1ec_Events_Helper {
301
  if( $event_end > $event_start )
302
  // Cache last day
303
  $this->insert_event_in_cache_table( array( 'post_id' => $e['post_id'], 'start' => $event_start, 'end' => $event_end ) );
304
- }
305
 
306
  /**
307
  * Returns the various preset recurrence options available (e.g.,
@@ -311,23 +311,23 @@ class Ai1ec_Events_Helper {
311
  * equivalents
312
  */
313
  function get_repeat_patterns() {
314
- // Calling functions when creating an array does not seem to work when
315
- // the assigned to variable is static. This is a workaround.
316
- static $options;
317
- if( !isset( $options ) ) {
318
- $temp = array(
319
- ' ' => __( 'No repeat', AI1EC_PLUGIN_NAME ),
320
- '1' => __( 'Every day', AI1EC_PLUGIN_NAME ),
321
- '2' => __( 'Every week', AI1EC_PLUGIN_NAME ),
322
- '3' => __( 'Every month', AI1EC_PLUGIN_NAME ),
323
- '4' => __( 'Every year', AI1EC_PLUGIN_NAME ),
324
- '5' => '-----------',
325
- '6' => __( 'Custom...', AI1EC_PLUGIN_NAME ),
326
- );
327
- $options = $temp;
328
- }
329
- return $options;
330
- }
331
 
332
  /**
333
  * Generates and returns repeat dropdown
@@ -337,16 +337,16 @@ class Ai1ec_Events_Helper {
337
  * @return String Repeat dropdown
338
  */
339
  function create_repeat_dropdown( $selected = null ) {
340
- $options = array(
341
- ' ' => __( 'No repeat', AI1EC_PLUGIN_NAME ),
342
- 1 => __( 'Every day', AI1EC_PLUGIN_NAME ),
343
- 2 => __( 'Every week', AI1EC_PLUGIN_NAME ),
344
- 3 => __( 'Every month', AI1EC_PLUGIN_NAME ),
345
- 4 => __( 'Every year', AI1EC_PLUGIN_NAME ),
346
- 5 => '-----------',
347
- 6 => __( 'Custom...', AI1EC_PLUGIN_NAME ),
348
- );
349
- return $this->create_select_element( 'ai1ec_repeat', $options, $selected, array( 5 ) );
350
  }
351
 
352
  /**
@@ -402,9 +402,9 @@ class Ai1ec_Events_Helper {
402
  $until += date( 'Z', $until ); // Add timezone offset
403
  $end = 2;
404
  } elseif( $count )
405
- $end = 1;
406
- else
407
- $end = 0;
408
  }
409
  }
410
  return array(
@@ -432,7 +432,7 @@ class Ai1ec_Events_Helper {
432
  <?php
433
  return ob_get_clean();
434
  }
435
-
436
  /**
437
  * create_select_element function
438
  *
@@ -441,7 +441,7 @@ class Ai1ec_Events_Helper {
441
  * @return void
442
  **/
443
  function create_select_element( $name, $options = array(), $selected = false, $disabled_keys = array() ) {
444
- ob_start();
445
  ?>
446
  <select name="<?php echo $name ?>" id="<?php echo $name ?>">
447
  <?php foreach( $options as $key => $val ): ?>
@@ -453,7 +453,7 @@ class Ai1ec_Events_Helper {
453
  <?php
454
  return ob_get_clean();
455
  }
456
-
457
  /**
458
  * create_on_the_select function
459
  *
@@ -462,35 +462,35 @@ class Ai1ec_Events_Helper {
462
  * @return void
463
  **/
464
  function create_on_the_select( $f_selected = false, $s_selected = false ) {
465
- $ret = "";
466
-
467
- $first_options = array(
468
- '0' => __( 'first', AI1EC_PLUGIN_NAME ),
469
- '1' => __( 'second', AI1EC_PLUGIN_NAME ),
470
- '2' => __( 'third', AI1EC_PLUGIN_NAME ),
471
- '3' => __( 'fourth', AI1EC_PLUGIN_NAME ),
472
- '4' => '------',
473
- '5' => __( 'last', AI1EC_PLUGIN_NAME )
474
- );
475
- $ret = $this->create_select_element( 'ai1ec_monthly_each_select', $first_options, $f_selected, array( 4 ) );
476
-
477
- $second_options = array(
478
- '0' => __( 'Sunday', AI1EC_PLUGIN_NAME ),
479
- '1' => __( 'Monday', AI1EC_PLUGIN_NAME ),
480
- '2' => __( 'Tuesday', AI1EC_PLUGIN_NAME ),
481
- '3' => __( 'Wednesday', AI1EC_PLUGIN_NAME ),
482
- '4' => __( 'Thursday', AI1EC_PLUGIN_NAME ),
483
- '5' => __( 'Friday', AI1EC_PLUGIN_NAME ),
484
- '6' => __( 'Saturday', AI1EC_PLUGIN_NAME ),
485
- '7' => '--------',
486
- '8' => __( 'day', AI1EC_PLUGIN_NAME ),
487
- '9' => __( 'weekday', AI1EC_PLUGIN_NAME ),
488
- '10' => __( 'weekend day', AI1EC_PLUGIN_NAME )
489
- );
490
-
491
- return $ret . $this->create_select_element( 'ai1ec_monthly_on_the_select', $second_options, $s_selected, array( 7 ) );
492
  }
493
-
494
  /**
495
  * undocumented function
496
  *
@@ -499,21 +499,21 @@ class Ai1ec_Events_Helper {
499
  * @return void
500
  **/
501
  function create_list_element( $name, $options = array(), $selected = array() ) {
502
- ob_start();
503
  ?>
504
  <ul class="ai1ec_date_select <?php echo $name?>" id="<?php echo $name?>">
505
- <?php foreach( $options as $key => $val ): ?>
506
- <li<?php echo in_array( $key, $selected ) ? 'class="ai1ec_selected"' : '' ?>>
507
- <?php echo $val ?>
508
- <input type="hidden" name="<?php echo $name . '_' . $key ?>" value="<?php echo $key ?>" />
509
- </li>
510
  <?php endforeach ?>
511
  </ul>
512
  <input type="hidden" name="<?php echo $name ?>" value="<?php echo implode( ',', $selected ) ?>" />
513
  <?php
514
  return ob_get_clean();
515
  }
516
-
517
  /**
518
  * create_montly_date_select function
519
  *
@@ -522,14 +522,14 @@ class Ai1ec_Events_Helper {
522
  * @return void
523
  **/
524
  function create_montly_date_select( $selected = array() ) {
525
- $options = array();
526
-
527
- for( $i = 1; $i <= 31; ++$i )
528
- $options[$i] = $i;
529
-
530
- return $this->create_list_element( 'ai1ec_montly_date_select', $options, $selected );
531
  }
532
-
533
  /**
534
  * create_yearly_date_select function
535
  *
@@ -538,27 +538,27 @@ class Ai1ec_Events_Helper {
538
  * @return void
539
  **/
540
  function create_yearly_date_select( $selected = array() ) {
541
- global $wp_locale;
542
- $options = array();
543
-
544
- for( $i = 1; $i <= 12; ++$i ) {
545
- $x = $i < 10 ? 0 . $i : $i;
546
- $options[$i] = $wp_locale->month_abbrev[$wp_locale->month[$x]];
547
- }
548
-
549
- return $this->create_list_element( 'ai1ec_yearly_date_select', $options, $selected );
550
  }
551
-
552
  function get_frequency( $index ) {
553
- $frequency = array(
554
- 0 => __( 'Daily', AI1EC_PLUGIN_NAME ),
555
- 1 => __( 'Weekly', AI1EC_PLUGIN_NAME ),
556
- 2 => __( 'Monthly', AI1EC_PLUGIN_NAME ),
557
- 3 => __( 'Yearly', AI1EC_PLUGIN_NAME ),
558
- );
559
- return $frequency[$index];
560
  }
561
-
562
  /**
563
  * row_frequency function
564
  *
@@ -567,22 +567,22 @@ class Ai1ec_Events_Helper {
567
  * @return void
568
  **/
569
  function row_frequency( $visible = false, $selected = false ) {
570
- global $ai1ec_view_helper;
571
-
572
- $frequency = array(
573
- 0 => __( 'Daily', AI1EC_PLUGIN_NAME ),
574
- 1 => __( 'Weekly', AI1EC_PLUGIN_NAME ),
575
- 2 => __( 'Monthly', AI1EC_PLUGIN_NAME ),
576
- 3 => __( 'Yearly', AI1EC_PLUGIN_NAME ),
577
- );
578
-
579
- $args = array(
580
- 'visible' => $visible,
581
- 'frequency' => $this->create_select_element( 'ai1ec_frequency', $frequency, $selected )
582
- );
583
- return $ai1ec_view_helper->get_view( 'row_frequency.php', $args );
584
  }
585
-
586
  /**
587
  * row_daily function
588
  *
@@ -591,15 +591,15 @@ class Ai1ec_Events_Helper {
591
  * @return void
592
  **/
593
  function row_daily( $visible = false, $selected = 1 ) {
594
- global $ai1ec_view_helper;
595
-
596
- $args = array(
597
- 'visible' => $visible,
598
- 'count' => $this->create_count_input( 'ai1ec_daily_count', $selected, 365 ) . __( 'day(s)', AI1EC_PLUGIN_NAME )
599
- );
600
- return $ai1ec_view_helper->get_view( 'row_daily.php', $args );
601
  }
602
-
603
  /**
604
  * row_weekly function
605
  *
@@ -608,28 +608,28 @@ class Ai1ec_Events_Helper {
608
  * @return void
609
  **/
610
  function row_weekly( $visible = false, $count = 1, $selected = array() ) {
611
- global $ai1ec_view_helper, $wp_locale;
612
- $start_of_week = get_option( 'start_of_week', 1 );
613
-
614
- $options = array();
615
- // get days from start_of_week until the last day
616
- for( $i = $start_of_week; $i <= 6; ++$i )
617
- $options[$this->get_weekday_by_id( $i )] = $wp_locale->weekday_initial[$wp_locale->weekday[$i]];
618
-
619
- // get days from 0 until start_of_week
620
- if( $start_of_week > 0 ) {
621
- for( $i = 0; $i < $start_of_week; $i++ )
622
- $options[$this->get_weekday_by_id( $i )] = $wp_locale->weekday_initial[$wp_locale->weekday[$i]];
623
- }
624
-
625
- $args = array(
626
- 'visible' => $visible,
627
- 'count' => $this->create_count_input( 'ai1ec_weekly_count', $count, 52 ) . __( 'week(s)', AI1EC_PLUGIN_NAME ),
628
- 'week_days' => $this->create_list_element( 'ai1ec_weekly_date_select', $options, $selected )
629
- );
630
- return $ai1ec_view_helper->get_view( 'row_weekly.php', $args );
631
  }
632
-
633
  /**
634
  * get_weekday_by_id function
635
  *
@@ -640,30 +640,30 @@ class Ai1ec_Events_Helper {
640
  * @return string
641
  **/
642
  function get_weekday_by_id( $day_id, $by_value = false ) {
643
- // do not translate this !!!
644
- $week_days = array(
645
- 0 => 'SU',
646
- 1 => 'MO',
647
- 2 => 'TU',
648
- 3 => 'WE',
649
- 4 => 'TH',
650
- 5 => 'FR',
651
- 6 => 'SA'
652
- );
653
-
654
- if( $by_value ) {
655
- while( $_name = current( $week_days ) ) {
656
- if( $_name == $day_id ) {
657
- return key( $week_days );
658
- }
659
- next( $week_days );
660
- }
661
- return false;
662
- }
663
- else
664
- return $week_days[$day_id];
665
  }
666
-
667
  /**
668
  * row_monthly function
669
  *
@@ -672,19 +672,19 @@ class Ai1ec_Events_Helper {
672
  * @return void
673
  **/
674
  function row_monthly( $visible = false, $count = 1, $ai1ec_monthly_each = 0, $ai1ec_monthly_on_the = 0, $month = array(), $first = false, $second = false ) {
675
- global $ai1ec_view_helper;
676
-
677
- $args = array(
678
- 'visible' => $visible,
679
- 'count' => $this->create_count_input( 'ai1ec_monthly_count', $count, 12 ) . __( 'month(s)', AI1EC_PLUGIN_NAME ),
680
- 'ai1ec_monthly_each' => $ai1ec_monthly_each,
681
- 'ai1ec_monthly_on_the' => $ai1ec_monthly_on_the,
682
- 'month' => $this->create_montly_date_select( $month ),
683
- 'on_the_select' => $this->create_on_the_select( $first, $second )
684
- );
685
- return $ai1ec_view_helper->get_view( 'row_monthly.php', $args );
686
  }
687
-
688
  /**
689
  * row_yearly function
690
  *
@@ -693,15 +693,15 @@ class Ai1ec_Events_Helper {
693
  * @return void
694
  **/
695
  function row_yearly( $visible = false, $count = 1, $year = array(), $first = false, $second = false ) {
696
- global $ai1ec_view_helper;
697
-
698
- $args = array(
699
- 'visible' => $visible,
700
- 'count' => $this->create_count_input( 'ai1ec_yearly_count', $count, 10 ) . __( 'year(s)', AI1EC_PLUGIN_NAME ),
701
- 'year' => $this->create_yearly_date_select( $year ),
702
- 'on_the_select' => $this->create_on_the_select( $first, $second )
703
- );
704
- return $ai1ec_view_helper->get_view( 'row_yearly.php', $args );
705
  }
706
 
707
  /**
@@ -726,7 +726,7 @@ class Ai1ec_Events_Helper {
726
  *
727
  * @param int | bool $start Events start before this (GMT) time
728
  * @param int | bool $end Events end before this (GMT) time
729
- * @param array $filter Array of filters for the events returned.
730
  * ['cat_ids'] => non-associatative array of category IDs
731
  * ['tag_ids'] => non-associatative array of tag IDs
732
  * ['post_ids'] => non-associatative array of post IDs
@@ -734,40 +734,40 @@ class Ai1ec_Events_Helper {
734
  * @return array Matching events
735
  **/
736
  function get_matching_events( $start = false, $end = false, $filter = array() ) {
737
- global $wpdb, $ai1ec_calendar_helper;
738
 
739
  // holds event_categories sql
740
- $c_sql = '';
741
- $c_where_sql = '';
742
- // holds event_tags sql
743
- $t_sql = '';
744
- $t_where_sql ='';
745
- // holds posts sql
746
- $p_where_sql = '';
747
- // holds start sql
748
- $start_where_sql = '';
749
- // holds end sql
750
- $end_where_sql = '';
751
- // hold escape values
752
- $args = array();
753
-
754
- // =============================
755
- // = Generating start date sql =
756
- // =============================
757
- if( $start !== false ) {
758
- $start_where_sql = "AND (e.start >= FROM_UNIXTIME( %d ) OR e.recurrence_rules != '')";
759
- $args[] = $start;
760
- }
761
-
762
- // ===========================
763
- // = Generating end date sql =
764
- // ===========================
765
- if( $end !== false ) {
766
- $end_where_sql = "AND (e.end <= FROM_UNIXTIME( %d ) OR e.recurrence_rules != '')";
767
- $args[] = $end;
768
- }
769
-
770
- // Get the Join (filter_join) and Where (filter_where) statements based on $filter elements specified
771
  $filter = $ai1ec_calendar_helper->_get_filter_sql( $filter );
772
 
773
  $query = $wpdb->prepare(
@@ -776,39 +776,39 @@ class Ai1ec_Events_Helper {
776
  e.show_map, e.contact_name, e.contact_phone, e.contact_email, e.cost, e.ical_feed_url, e.ical_source_url,
777
  e.ical_organizer, e.ical_contact, e.ical_uid " .
778
  "FROM $wpdb->posts " .
779
- "INNER JOIN {$wpdb->prefix}ai1ec_events AS e ON e.post_id = ID " .
780
  $filter['filter_join'] .
781
  "WHERE post_type = '" . AI1EC_POST_TYPE . "' " .
782
- "AND post_status = 'publish' " .
783
- $filter['filter_where'] .
784
- $start_where_sql .
785
- $end_where_sql,
786
  $args );
787
 
788
  $events = $wpdb->get_results( $query, ARRAY_A );
789
 
790
- foreach( $events as &$event ) {
791
- try{
792
- $event = new Ai1ec_Event( $event );
793
- } catch( Ai1ec_Event_Not_Found $n ) {
794
- unset( $event );
795
- // The event is not found, continue to the next event
796
- continue;
797
- }
798
 
799
- // if there are recurrence rules, include the event, else...
800
  if( empty( $event->recurrence_rules ) ) {
801
  // if start time is set, and event start time is before the range
802
  // it, continue to the next event
803
  if( $start !== false && $event->start < $start ) {
804
- unset( $event );
805
- continue;
806
  }
807
  // if end time is set, and event end time is after
808
  // it, continue to the next event
809
  if( $end !== false && $ev->end < $end ) {
810
- unset( $event );
811
- continue;
812
  }
813
  }
814
  }
@@ -844,7 +844,7 @@ class Ai1ec_Events_Helper {
844
  * @return string
845
  **/
846
  function get_short_time( $timestamp, $convert_from_gmt = true ) {
847
- $time_format = get_option( 'time_format', 'g:ia' );
848
  if( $convert_from_gmt )
849
  $timestamp = $this->gmt_to_local( $timestamp );
850
  return date_i18n( $time_format, $timestamp, true );
@@ -878,7 +878,7 @@ class Ai1ec_Events_Helper {
878
  * @return string
879
  **/
880
  function get_medium_time( $timestamp, $convert_from_gmt = true ) {
881
- $time_format = get_option( 'time_format', 'g:ia' );
882
  if( $convert_from_gmt )
883
  $timestamp = $this->gmt_to_local( $timestamp );
884
  return date_i18n( $time_format, $timestamp, true );
@@ -896,8 +896,8 @@ class Ai1ec_Events_Helper {
896
  * @return string
897
  **/
898
  function get_long_time( $timestamp, $convert_from_gmt = true ) {
899
- $date_format = get_option( 'date_format', 'D, F j' );
900
- $time_format = get_option( 'time_format', 'g:i' );
901
  if( $convert_from_gmt )
902
  $timestamp = $this->gmt_to_local( $timestamp );
903
  return date_i18n( $date_format, $timestamp, true ) . ' @ ' . date_i18n( $time_format, $timestamp, true );
@@ -915,7 +915,7 @@ class Ai1ec_Events_Helper {
915
  * @return string
916
  **/
917
  function get_long_date( $timestamp, $convert_from_gmt = true ) {
918
- $date_format = get_option( 'date_format', 'D, F j' );
919
  if( $convert_from_gmt )
920
  $timestamp = $this->gmt_to_local( $timestamp );
921
  return date_i18n( $date_format, $timestamp, true );
@@ -933,12 +933,12 @@ class Ai1ec_Events_Helper {
933
  function gmt_to_local( $timestamp ) {
934
  $offset = get_option( 'gmt_offset' );
935
  $tz = get_option( 'timezone_string', 'America/Los_Angeles' );
936
-
937
  $offset = $this->get_timezone_offset( 'UTC', $tz, $timestamp );
938
 
939
  if( ! $offset )
940
  $offset = get_option( 'gmt_offset' ) * 3600;
941
-
942
  return $timestamp + $offset;
943
  }
944
 
@@ -954,15 +954,15 @@ class Ai1ec_Events_Helper {
954
  function local_to_gmt( $timestamp ) {
955
  $offset = get_option( 'gmt_offset' );
956
  $tz = get_option( 'timezone_string', 'America/Los_Angeles' );
957
-
958
  $offset = $this->get_timezone_offset( 'UTC', $tz, $timestamp );
959
 
960
  if( ! $offset )
961
  $offset = get_option( 'gmt_offset' ) * 3600;
962
-
963
  return $timestamp - $offset;
964
  }
965
-
966
  /**
967
  * get_timezone_offset function
968
  *
@@ -978,7 +978,7 @@ class Ai1ec_Events_Helper {
978
  if( $origin_tz === null )
979
  if( ! is_string( $origin_tz = date_default_timezone_get() ) )
980
  return false; // A UTC timestamp was returned -- bail out!
981
-
982
  try {
983
  $origin_dtz = new DateTimeZone( $origin_tz );
984
  $remote_dtz = new DateTimeZone( $remote_tz );
@@ -989,7 +989,7 @@ class Ai1ec_Events_Helper {
989
  } catch( Exception $e ) {
990
  return false;
991
  }
992
-
993
  return $offset;
994
  }
995
 
@@ -1034,10 +1034,10 @@ class Ai1ec_Events_Helper {
1034
  function get_gmap_url( &$event ) {
1035
  $location_arg = urlencode( $event->address );
1036
  $lang = $this->get_lang();
1037
-
1038
  return "http://www.google.com/maps?f=q&hl=" . $lang . "&source=embed&q=" . $location_arg;
1039
  }
1040
-
1041
  /**
1042
  * get_lang function
1043
  *
@@ -1051,7 +1051,7 @@ class Ai1ec_Events_Helper {
1051
 
1052
  return ( isset( $locale[0] ) && $locale[0] != '' ) ? $locale[0] : 'en';
1053
  }
1054
-
1055
  /**
1056
  * get_region function
1057
  *
@@ -1062,7 +1062,7 @@ class Ai1ec_Events_Helper {
1062
  **/
1063
  function get_region() {
1064
  $locale = explode( '_', get_locale() );
1065
-
1066
  $region = ( isset( $locale[1] ) && $locale[1] != '' ) ? strtolower( $locale[1] ) : '';
1067
 
1068
  // Primary ccTLD for United Kingdom is uk.
@@ -1081,7 +1081,7 @@ class Ai1ec_Events_Helper {
1081
  * @return string The excerpt.
1082
  **/
1083
  function trim_excerpt( $text )
1084
- {
1085
  $raw_excerpt = $text;
1086
 
1087
  $text = strip_shortcodes( $text );
@@ -1123,14 +1123,14 @@ class Ai1ec_Events_Helper {
1123
  // = Sanitize provided IDs against SQL injection =
1124
  // ===============================================
1125
  if( ! is_array( $post_ids ) )
1126
- $post_ids = explode( ',', $post_ids );
1127
  foreach( $post_ids as &$post_id ) {
1128
  $post_id = intval( $post_id );
1129
  }
1130
  $post_ids = join( ',', $post_ids );
1131
 
1132
  if( ! is_array( $term_ids ) )
1133
- $term_ids = explode( ',', $term_ids );
1134
  foreach( $term_ids as &$term_id ) {
1135
  $term_id = intval( $term_id );
1136
  }
@@ -1156,12 +1156,12 @@ class Ai1ec_Events_Helper {
1156
  * @return string
1157
  */
1158
  function get_category_color( $term_id ) {
1159
- global $wpdb;
1160
 
1161
- $term_id = (int) $term_id;
1162
- $table_name = $wpdb->prefix . 'ai1ec_event_category_colors';
1163
- $color = $wpdb->get_var( "SELECT term_color FROM {$table_name} WHERE term_id = {$term_id}" );
1164
- return $color;
1165
  }
1166
 
1167
  /**
@@ -1174,12 +1174,12 @@ class Ai1ec_Events_Helper {
1174
  * @return string
1175
  **/
1176
  function get_category_color_square( $term_id ) {
1177
- $color = $this->get_category_color( $term_id );
1178
- $cat = get_term( $term_id, 'events_categories' );
1179
- if( ! is_null( $color ) && ! empty( $color ) )
1180
- return '<div class="ai1ec-category-color" style="background:' . $color . '" title="' . esc_attr( $cat->name ) . '"></div>';
1181
 
1182
- return '';
1183
  }
1184
 
1185
  /**
@@ -1192,15 +1192,48 @@ class Ai1ec_Events_Helper {
1192
  * @return string
1193
  **/
1194
  function get_event_category_color_style( $term_id, $allday = false ) {
1195
- $color = $this->get_category_color( $term_id );
1196
- if( ! is_null( $color ) && ! empty( $color ) ) {
1197
- if( $allday )
1198
- return ' style="background:' . $color . '"';
1199
- else
1200
- return ' style="color:' . $color . ' !important"';
1201
- }
1202
-
1203
- return '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1204
  }
1205
 
1206
  /**
@@ -1212,15 +1245,15 @@ class Ai1ec_Events_Helper {
1212
  * @return string
1213
  **/
1214
  function get_event_category_colors( $cats ) {
1215
- $sqrs = '';
1216
 
1217
- foreach( $cats as $cat ) {
1218
- $tmp = $this->get_category_color_square( $cat->term_id );
1219
- if( ! empty( $tmp ) )
1220
- $sqrs .= $tmp;
1221
- }
1222
 
1223
- return $sqrs;
1224
  }
1225
 
1226
  /**
@@ -1232,12 +1265,12 @@ class Ai1ec_Events_Helper {
1232
  * @return void
1233
  **/
1234
  function create_end_dropdown( $selected = null ) {
1235
- ob_start();
1236
 
1237
  $options = array(
1238
- 0 => __( 'Never', AI1EC_PLUGIN_NAME ),
1239
- 1 => __( 'After', AI1EC_PLUGIN_NAME ),
1240
- 2 => __( 'On date', AI1EC_PLUGIN_NAME )
1241
  );
1242
 
1243
  ?>
@@ -1255,7 +1288,7 @@ class Ai1ec_Events_Helper {
1255
 
1256
  return $output;
1257
  }
1258
-
1259
  /**
1260
  * rrule_to_text function
1261
  *
@@ -1264,34 +1297,34 @@ class Ai1ec_Events_Helper {
1264
  * @return void
1265
  **/
1266
  function rrule_to_text( $rrule = '') {
1267
- $txt = '';
1268
- $rc = new SG_iCal_Recurrence( new SG_iCal_Line( 'RRULE:' . $rrule ) );
1269
- switch( $rc->getFreq() ) {
1270
- case 'DAILY':
1271
- $this->_get_interval( $txt, 'daily', $rc->getInterval() );
1272
- $this->_ending_sentence( $txt, $rc );
1273
- break;
1274
- case 'WEEKLY':
1275
- $this->_get_interval( $txt, 'weekly', $rc->getInterval() );
1276
- $this->_get_sentence_by( $txt, 'weekly', $rc );
1277
- $this->_ending_sentence( $txt, $rc );
1278
- break;
1279
- case 'MONTHLY':
1280
- $this->_get_interval( $txt, 'monthly', $rc->getInterval() );
1281
- $this->_get_sentence_by( $txt, 'monthly', $rc );
1282
- $this->_ending_sentence( $txt, $rc );
1283
- break;
1284
- case 'YEARLY':
1285
- $this->_get_interval( $txt, 'yearly', $rc->getInterval() );
1286
- $this->_get_sentence_by( $txt, 'yearly', $rc );
1287
- $this->_ending_sentence( $txt, $rc );
1288
- break;
1289
- default:
1290
- $txt = $rrule;
1291
- }
1292
- return $txt;
1293
  }
1294
-
1295
  /**
1296
  * _get_sentence_by function
1297
  *
@@ -1300,101 +1333,101 @@ class Ai1ec_Events_Helper {
1300
  * @return void
1301
  **/
1302
  function _get_sentence_by( &$txt, $freq, $rc ) {
1303
- global $wp_locale;
1304
-
1305
- switch( $freq ) {
1306
- case 'weekly':
1307
- if( $rc->getByDay() ) {
1308
- if( count( $rc->getByDay() ) > 1 ) {
1309
- // if there are more than 3 days
1310
- // use days's abbr
1311
- if( count( $rc->getByDay() ) > 2 ) {
1312
- $_days = '';
1313
- foreach( $rc->getByDay() as $d ) {
1314
- $day = $this->get_weekday_by_id( $d, true );
1315
- $_days .= ' ' . $wp_locale->weekday_abbrev[$wp_locale->weekday[$day]] . ',';
1316
- }
1317
- // remove the last ' and'
1318
- $_days = substr( $_days, 0, -1 );
1319
- $txt .= ' ' . _x( 'on', 'Recurrence editor - weekly tab', AI1EC_PLUGIN_NAME ) . $_days;
1320
- } else {
1321
- $_days = '';
1322
- foreach( $rc->getByDay() as $d ) {
1323
- $day = $this->get_weekday_by_id( $d, true );
1324
- $_days .= ' ' . $wp_locale->weekday[$day] . ' ' . __( 'and', AI1EC_PLUGIN_NAME );
1325
- }
1326
- // remove the last ' and'
1327
- $_days = substr( $_days, 0, -4 );
1328
- $txt .= ' ' . _x( 'on', 'Recurrence editor - weekly tab', AI1EC_PLUGIN_NAME ) . $_days;
1329
- }
1330
- } else {
1331
- $_days = '';
1332
- foreach( $rc->getByDay() as $d ) {
1333
- $day = $this->get_weekday_by_id( $d, true );
1334
- $_days .= ' ' . $wp_locale->weekday[$day];
1335
- }
1336
- $txt .= ' ' . _x( 'on', 'Recurrence editor - weekly tab', AI1EC_PLUGIN_NAME ) . $_days;
1337
- }
1338
- }
1339
- break;
1340
- case 'monthly':
1341
- if( $rc->getByMonthDay() ) {
1342
- // if there are more than 2 days
1343
- if( count( $rc->getByMonthDay() ) > 2 ) {
1344
- $_days = '';
1345
- foreach( $rc->getByMonthDay() as $m_day ) {
1346
- $_days .= ' ' . $this->_ordinal( $m_day ) . ',';
1347
- }
1348
- $_days = substr( $_days, 0, -1 );
1349
- $txt .= ' ' . _x( 'on', 'Recurrence editor - monthly tab', AI1EC_PLUGIN_NAME ) . $_days . ' ' . __( 'of the month', AI1EC_PLUGIN_NAME );
1350
- } else if( count( $rc->getByMonthDay() ) > 1 ) {
1351
- $_days = '';
1352
- foreach( $rc->getByMonthDay() as $m_day ) {
1353
- $_days .= ' ' . $this->_ordinal( $m_day ) . ' ' . __( 'and', AI1EC_PLUGIN_NAME );
1354
- }
1355
- $_days = substr( $_days, 0, -4 );
1356
- $txt .= ' ' . _x( 'on', 'Recurrence editor - monthly tab', AI1EC_PLUGIN_NAME ) . $_days . ' ' . __( 'of the month', AI1EC_PLUGIN_NAME );
1357
- } else {
1358
- $_days = '';
1359
- foreach( $rc->getByMonthDay() as $m_day ) {
1360
- $_days .= ' ' . $this->_ordinal( $m_day );
1361
- }
1362
- $txt .= ' ' . _x( 'on', 'Recurrence editor - monthly tab', AI1EC_PLUGIN_NAME ) . $_days . ' ' . __( 'of the month', AI1EC_PLUGIN_NAME );
1363
- }
1364
- }
1365
- break;
1366
- case 'yearly':
1367
- if( $rc->getByMonth() ) {
1368
- // if there are more than 2 months
1369
- if( count( $rc->getByMonth() ) > 2 ) {
1370
- $_months = '';
1371
- foreach( $rc->getByMonth() as $_m ) {
1372
- $_m = $_m < 10 ? 0 . $_m : $_m;
1373
- $_months .= ' ' . $wp_locale->month_abbrev[$wp_locale->month[$_m]] . ',';
1374
- }
1375
- $_months = substr( $_months, 0, -1 );
1376
- $txt .= ' ' . _x( 'on', 'Recurrence editor - yearly tab', AI1EC_PLUGIN_NAME ) . $_months;
1377
- } else if( count( $rc->getByMonth() ) > 1 ) {
1378
- $_months = '';
1379
- foreach( $rc->getByMonth() as $_m ) {
1380
- $_m = $_m < 10 ? 0 . $_m : $_m;
1381
- $_months .= ' ' . $wp_locale->month[$_m] . ' ' . __( 'and', AI1EC_PLUGIN_NAME );
1382
- }
1383
- $_months = substr( $_months, 0, -4 );
1384
- $txt .= ' ' . _x( 'on', 'Recurrence editor - yearly tab', AI1EC_PLUGIN_NAME ) . $_months;
1385
- } else {
1386
- $_months = '';
1387
- foreach( $rc->getByMonth() as $_m ) {
1388
- $_m = $_m < 10 ? 0 . $_m : $_m;
1389
- $_months .= ' ' . $wp_locale->month[$_m];
1390
- }
1391
- $txt .= ' ' . _x( 'on', 'Recurrence editor - yearly tab', AI1EC_PLUGIN_NAME ) . $_months;
1392
- }
1393
- }
1394
- break;
1395
- }
1396
  }
1397
-
1398
  /**
1399
  * _ordinal function
1400
  *
@@ -1404,17 +1437,17 @@ class Ai1ec_Events_Helper {
1404
  **/
1405
  function _ordinal( $cdnl ) {
1406
  $locale = explode( '_', get_locale() );
1407
-
1408
  if( isset( $locale[0] ) && $locale[0] != 'en' )
1409
  return $cdnl;
1410
-
1411
- $test_c = abs($cdnl) % 10;
1412
- $ext = ( ( abs( $cdnl ) % 100 < 21 && abs( $cdnl ) % 100 > 4 ) ? 'th'
1413
- : ( ( $test_c < 4 ) ? ( $test_c < 3 ) ? ( $test_c < 2 ) ? ( $test_c < 1 )
1414
- ? 'th' : 'st' : 'nd' : 'rd' : 'th' ) );
1415
- return $cdnl.$ext;
1416
- }
1417
-
1418
  /**
1419
  * _get_interval function
1420
  *
@@ -1423,58 +1456,58 @@ class Ai1ec_Events_Helper {
1423
  * @return void
1424
  **/
1425
  function _get_interval( &$txt, $freq, $interval ) {
1426
- switch( $freq ) {
1427
- case 'daily':
1428
- // check if interval is set
1429
- if( ! $interval || $interval == 1 ) {
1430
- $txt = __( 'Daily', AI1EC_PLUGIN_NAME );
1431
- } else {
1432
- if( $interval == 2 ) {
1433
- $txt = __( 'Every other day', AI1EC_PLUGIN_NAME );
1434
- } else {
1435
- $txt = sprintf( __( 'Every %d days', AI1EC_PLUGIN_NAME ), $interval );
1436
- }
1437
- }
1438
- break;
1439
- case 'weekly':
1440
- // check if interval is set
1441
- if( ! $interval || $interval == 1 ) {
1442
- $txt = __( 'Weekly', AI1EC_PLUGIN_NAME );
1443
- } else {
1444
- if( $interval == 2 ) {
1445
- $txt = __( 'Every other week', AI1EC_PLUGIN_NAME );
1446
- } else {
1447
- $txt = sprintf( __( 'Every %d weeks', AI1EC_PLUGIN_NAME ), $interval );
1448
- }
1449
- }
1450
- break;
1451
- case 'monthly':
1452
- // check if interval is set
1453
- if( ! $interval || $interval == 1 ) {
1454
- $txt = __( 'Monthly', AI1EC_PLUGIN_NAME );
1455
- } else {
1456
- if( $interval == 2 ) {
1457
- $txt = __( 'Every other month', AI1EC_PLUGIN_NAME );
1458
- } else {
1459
- $txt = sprintf( __( 'Every %d months', AI1EC_PLUGIN_NAME ), $interval );
1460
- }
1461
- }
1462
- break;
1463
- case 'yearly':
1464
- // check if interval is set
1465
- if( ! $interval || $interval == 1 ) {
1466
- $txt = __( 'Yearly', AI1EC_PLUGIN_NAME );
1467
- } else {
1468
- if( $interval == 2 ) {
1469
- $txt = __( 'Every other year', AI1EC_PLUGIN_NAME );
1470
- } else {
1471
- $txt = sprintf( __( 'Every %d years', AI1EC_PLUGIN_NAME ), $interval );
1472
- }
1473
- }
1474
- break;
1475
- }
1476
  }
1477
-
1478
  /**
1479
  * _ending_sentence function
1480
  *
@@ -1495,7 +1528,7 @@ class Ai1ec_Events_Helper {
1495
  else
1496
  $txt .= ' - ' . __( 'forever', AI1EC_PLUGIN_NAME );
1497
  }
1498
-
1499
  /**
1500
  * undocumented function
1501
  *
@@ -1504,25 +1537,25 @@ class Ai1ec_Events_Helper {
1504
  * @return void
1505
  **/
1506
  function convert_rrule_to_text() {
1507
- $error = false;
1508
- // check to see if RRULE is set
1509
- if( isset( $_REQUEST["rrule"] ) ) {
1510
-
1511
- // check to see if rrule is empty
1512
- if( empty( $_REQUEST["rrule"] ) ) {
1513
- $error = true;
1514
- $message = 'Recurrence rule cannot be empty!';
1515
- } else {
1516
- // convert rrule to text
1517
- $message = $this->rrule_to_text( $_REQUEST["rrule"] );
1518
- }
1519
-
1520
- } else {
1521
- $error = true;
1522
- $message = 'Recurrence rule is not provided!';
1523
- }
1524
-
1525
- $output = array(
1526
  "error" => $error,
1527
  "message" => stripslashes( $message )
1528
  );
@@ -1530,7 +1563,7 @@ class Ai1ec_Events_Helper {
1530
  echo json_encode( $output );
1531
  exit();
1532
  }
1533
-
1534
  /**
1535
  * post_type_link function
1536
  *
@@ -1540,13 +1573,29 @@ class Ai1ec_Events_Helper {
1540
  **/
1541
  function post_type_link( $permalink, $post, $leavename ) {
1542
  global $ai1ec_app_helper;
1543
-
1544
  if( $post->post_type == AI1EC_POST_TYPE ) {
1545
  $delimiter = $ai1ec_app_helper->get_param_delimiter_char( $permalink );
1546
  return $permalink . $delimiter . 'instance_id=';
1547
  }
1548
-
1549
  return $permalink;
1550
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1551
  }
1552
  // END class
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
+ // try to get the event instance id, if it is not set get the post id
62
+ $instance_id = isset( $_REQUEST["instance_id"] ) ? (int) $_REQUEST["instance_id"] : false;
63
  $event = new Ai1ec_Event( $post_id, $instance_id );
64
 
65
  if( ! $event->post_id )
95
  "WHERE ical_feed_url = %s " .
96
  "AND ical_uid = %s " .
97
  "AND start = FROM_UNIXTIME( %d ) " .
98
+ ( $has_recurrence ? 'AND NOT ' : 'AND ' ) .
99
  "( recurrence_rules IS NULL OR recurrence_rules = '' )";
100
  $args = array( $feed, $uid, $start );
101
  if( ! is_null( $exclude_post_id ) ) {
161
 
162
  $freq->firstOccurrence();
163
  while( ( $next = $freq->nextOccurrence( $start ) ) > 0 &&
164
+ $count < 1000 )
165
  {
166
  $count++;
167
  $start = $next;
205
  // If event spans a day and end time is not midnight, or spans more than
206
  // a day, then create instance for each spanning day
207
  if( ( $start['mday'] != $end['mday'] &&
208
+ ( $end['hours'] || $end['minutes'] || $end['seconds'] ) )
209
+ || $e['end'] - $e['start'] > 60 * 60 * 24 ) {
210
+ $this->create_cache_table_entries( $e );
211
  // Else cache single instance of event
212
  } else {
213
+ $this->insert_event_in_cache_table( $e );
214
  }
215
  */
216
  $this->insert_event_in_cache_table( $e );
228
  * @return void
229
  **/
230
  function insert_event_in_cache_table( $event ) {
231
+ global $wpdb;
232
+
233
+ // Return the start/end times to GMT zone
234
+ $event['start'] = $this->local_to_gmt( $event['start'] ) + date( 'Z', $event['start'] );
235
+ $event['end'] = $this->local_to_gmt( $event['end'] ) + date( 'Z', $event['end'] );
236
+
237
+ $wpdb->query(
238
+ $wpdb->prepare(
239
+ "INSERT INTO {$wpdb->prefix}ai1ec_event_instances " .
240
+ " ( post_id, start, end ) " .
241
+ "VALUES ( %d, FROM_UNIXTIME( %d ), FROM_UNIXTIME( %d ) )",
242
+ $event
243
+ )
244
+ );
245
  }
246
 
247
  /**
248
+ * create_cache_table_entries function
249
+ *
250
+ * Create a new entry for each day that the event spans.
251
+ *
252
+ * @param array $e Event array
253
+ *
254
+ * @return void
255
+ **/
256
+ function create_cache_table_entries( $e )
257
+ {
258
+ global $ai1ec_events_helper;
259
+
260
+ // Decompose start dates into components
261
+ $start_bits = getdate( $e['start'] );
262
+
263
+ // ============================================
264
+ // = Calculate the time for event's first day =
265
+ // ============================================
266
+ // Start time is event's original start time
267
  $event_start = $e['start'];
268
  // End time is beginning of next day
269
  $event_end = mktime(
286
  $event_end += 60 * 60 * 24;
287
  // Cache intermediate days
288
  while( $event_end < $e['end'] ) {
289
+ $this->insert_event_in_cache_table( array( 'post_id' => $e['post_id'], 'start' => $event_start, 'end' => $event_end ) );
290
+ $event_start = $event_end; // Start time is previous end time
291
+ $event_end += 24 * 60 * 60; // Increment end time by 1 day
292
  }
293
 
294
  // ===========================================
301
  if( $event_end > $event_start )
302
  // Cache last day
303
  $this->insert_event_in_cache_table( array( 'post_id' => $e['post_id'], 'start' => $event_start, 'end' => $event_end ) );
304
+ }
305
 
306
  /**
307
  * Returns the various preset recurrence options available (e.g.,
311
  * equivalents
312
  */
313
  function get_repeat_patterns() {
314
+ // Calling functions when creating an array does not seem to work when
315
+ // the assigned to variable is static. This is a workaround.
316
+ static $options;
317
+ if( !isset( $options ) ) {
318
+ $temp = array(
319
+ ' ' => __( 'No repeat', AI1EC_PLUGIN_NAME ),
320
+ '1' => __( 'Every day', AI1EC_PLUGIN_NAME ),
321
+ '2' => __( 'Every week', AI1EC_PLUGIN_NAME ),
322
+ '3' => __( 'Every month', AI1EC_PLUGIN_NAME ),
323
+ '4' => __( 'Every year', AI1EC_PLUGIN_NAME ),
324
+ '5' => '-----------',
325
+ '6' => __( 'Custom...', AI1EC_PLUGIN_NAME ),
326
+ );
327
+ $options = $temp;
328
+ }
329
+ return $options;
330
+ }
331
 
332
  /**
333
  * Generates and returns repeat dropdown
337
  * @return String Repeat dropdown
338
  */
339
  function create_repeat_dropdown( $selected = null ) {
340
+ $options = array(
341
+ ' ' => __( 'No repeat', AI1EC_PLUGIN_NAME ),
342
+ 1 => __( 'Every day', AI1EC_PLUGIN_NAME ),
343
+ 2 => __( 'Every week', AI1EC_PLUGIN_NAME ),
344
+ 3 => __( 'Every month', AI1EC_PLUGIN_NAME ),
345
+ 4 => __( 'Every year', AI1EC_PLUGIN_NAME ),
346
+ 5 => '-----------',
347
+ 6 => __( 'Custom...', AI1EC_PLUGIN_NAME ),
348
+ );
349
+ return $this->create_select_element( 'ai1ec_repeat', $options, $selected, array( 5 ) );
350
  }
351
 
352
  /**
402
  $until += date( 'Z', $until ); // Add timezone offset
403
  $end = 2;
404
  } elseif( $count )
405
+ $end = 1;
406
+ else
407
+ $end = 0;
408
  }
409
  }
410
  return array(
432
  <?php
433
  return ob_get_clean();
434
  }
435
+
436
  /**
437
  * create_select_element function
438
  *
441
  * @return void
442
  **/
443
  function create_select_element( $name, $options = array(), $selected = false, $disabled_keys = array() ) {
444
+ ob_start();
445
  ?>
446
  <select name="<?php echo $name ?>" id="<?php echo $name ?>">
447
  <?php foreach( $options as $key => $val ): ?>
453
  <?php
454
  return ob_get_clean();
455
  }
456
+
457
  /**
458
  * create_on_the_select function
459
  *
462
  * @return void
463
  **/
464
  function create_on_the_select( $f_selected = false, $s_selected = false ) {
465
+ $ret = "";
466
+
467
+ $first_options = array(
468
+ '0' => __( 'first', AI1EC_PLUGIN_NAME ),
469
+ '1' => __( 'second', AI1EC_PLUGIN_NAME ),
470
+ '2' => __( 'third', AI1EC_PLUGIN_NAME ),
471
+ '3' => __( 'fourth', AI1EC_PLUGIN_NAME ),
472
+ '4' => '------',
473
+ '5' => __( 'last', AI1EC_PLUGIN_NAME )
474
+ );
475
+ $ret = $this->create_select_element( 'ai1ec_monthly_each_select', $first_options, $f_selected, array( 4 ) );
476
+
477
+ $second_options = array(
478
+ '0' => __( 'Sunday', AI1EC_PLUGIN_NAME ),
479
+ '1' => __( 'Monday', AI1EC_PLUGIN_NAME ),
480
+ '2' => __( 'Tuesday', AI1EC_PLUGIN_NAME ),
481
+ '3' => __( 'Wednesday', AI1EC_PLUGIN_NAME ),
482
+ '4' => __( 'Thursday', AI1EC_PLUGIN_NAME ),
483
+ '5' => __( 'Friday', AI1EC_PLUGIN_NAME ),
484
+ '6' => __( 'Saturday', AI1EC_PLUGIN_NAME ),
485
+ '7' => '--------',
486
+ '8' => __( 'day', AI1EC_PLUGIN_NAME ),
487
+ '9' => __( 'weekday', AI1EC_PLUGIN_NAME ),
488
+ '10' => __( 'weekend day', AI1EC_PLUGIN_NAME )
489
+ );
490
+
491
+ return $ret . $this->create_select_element( 'ai1ec_monthly_on_the_select', $second_options, $s_selected, array( 7 ) );
492
  }
493
+
494
  /**
495
  * undocumented function
496
  *
499
  * @return void
500
  **/
501
  function create_list_element( $name, $options = array(), $selected = array() ) {
502
+ ob_start();
503
  ?>
504
  <ul class="ai1ec_date_select <?php echo $name?>" id="<?php echo $name?>">
505
+ <?php foreach( $options as $key => $val ): ?>
506
+ <li<?php echo in_array( $key, $selected ) ? 'class="ai1ec_selected"' : '' ?>>
507
+ <?php echo $val ?>
508
+ <input type="hidden" name="<?php echo $name . '_' . $key ?>" value="<?php echo $key ?>" />
509
+ </li>
510
  <?php endforeach ?>
511
  </ul>
512
  <input type="hidden" name="<?php echo $name ?>" value="<?php echo implode( ',', $selected ) ?>" />
513
  <?php
514
  return ob_get_clean();
515
  }
516
+
517
  /**
518
  * create_montly_date_select function
519
  *
522
  * @return void
523
  **/
524
  function create_montly_date_select( $selected = array() ) {
525
+ $options = array();
526
+
527
+ for( $i = 1; $i <= 31; ++$i )
528
+ $options[$i] = $i;
529
+
530
+ return $this->create_list_element( 'ai1ec_montly_date_select', $options, $selected );
531
  }
532
+
533
  /**
534
  * create_yearly_date_select function
535
  *
538
  * @return void
539
  **/
540
  function create_yearly_date_select( $selected = array() ) {
541
+ global $wp_locale;
542
+ $options = array();
543
+
544
+ for( $i = 1; $i <= 12; ++$i ) {
545
+ $x = $i < 10 ? 0 . $i : $i;
546
+ $options[$i] = $wp_locale->month_abbrev[$wp_locale->month[$x]];
547
+ }
548
+
549
+ return $this->create_list_element( 'ai1ec_yearly_date_select', $options, $selected );
550
  }
551
+
552
  function get_frequency( $index ) {
553
+ $frequency = array(
554
+ 0 => __( 'Daily', AI1EC_PLUGIN_NAME ),
555
+ 1 => __( 'Weekly', AI1EC_PLUGIN_NAME ),
556
+ 2 => __( 'Monthly', AI1EC_PLUGIN_NAME ),
557
+ 3 => __( 'Yearly', AI1EC_PLUGIN_NAME ),
558
+ );
559
+ return $frequency[$index];
560
  }
561
+
562
  /**
563
  * row_frequency function
564
  *
567
  * @return void
568
  **/
569
  function row_frequency( $visible = false, $selected = false ) {
570
+ global $ai1ec_view_helper;
571
+
572
+ $frequency = array(
573
+ 0 => __( 'Daily', AI1EC_PLUGIN_NAME ),
574
+ 1 => __( 'Weekly', AI1EC_PLUGIN_NAME ),
575
+ 2 => __( 'Monthly', AI1EC_PLUGIN_NAME ),
576
+ 3 => __( 'Yearly', AI1EC_PLUGIN_NAME ),
577
+ );
578
+
579
+ $args = array(
580
+ 'visible' => $visible,
581
+ 'frequency' => $this->create_select_element( 'ai1ec_frequency', $frequency, $selected )
582
+ );
583
+ return $ai1ec_view_helper->get_view( 'row_frequency.php', $args );
584
  }
585
+
586
  /**
587
  * row_daily function
588
  *
591
  * @return void
592
  **/
593
  function row_daily( $visible = false, $selected = 1 ) {
594
+ global $ai1ec_view_helper;
595
+
596
+ $args = array(
597
+ 'visible' => $visible,
598
+ 'count' => $this->create_count_input( 'ai1ec_daily_count', $selected, 365 ) . __( 'day(s)', AI1EC_PLUGIN_NAME )
599
+ );
600
+ return $ai1ec_view_helper->get_view( 'row_daily.php', $args );
601
  }
602
+
603
  /**
604
  * row_weekly function
605
  *
608
  * @return void
609
  **/
610
  function row_weekly( $visible = false, $count = 1, $selected = array() ) {
611
+ global $ai1ec_view_helper, $wp_locale;
612
+ $start_of_week = get_option( 'start_of_week', 1 );
613
+
614
+ $options = array();
615
+ // get days from start_of_week until the last day
616
+ for( $i = $start_of_week; $i <= 6; ++$i )
617
+ $options[$this->get_weekday_by_id( $i )] = $wp_locale->weekday_initial[$wp_locale->weekday[$i]];
618
+
619
+ // get days from 0 until start_of_week
620
+ if( $start_of_week > 0 ) {
621
+ for( $i = 0; $i < $start_of_week; $i++ )
622
+ $options[$this->get_weekday_by_id( $i )] = $wp_locale->weekday_initial[$wp_locale->weekday[$i]];
623
+ }
624
+
625
+ $args = array(
626
+ 'visible' => $visible,
627
+ 'count' => $this->create_count_input( 'ai1ec_weekly_count', $count, 52 ) . __( 'week(s)', AI1EC_PLUGIN_NAME ),
628
+ 'week_days' => $this->create_list_element( 'ai1ec_weekly_date_select', $options, $selected )
629
+ );
630
+ return $ai1ec_view_helper->get_view( 'row_weekly.php', $args );
631
  }
632
+
633
  /**
634
  * get_weekday_by_id function
635
  *
640
  * @return string
641
  **/
642
  function get_weekday_by_id( $day_id, $by_value = false ) {
643
+ // do not translate this !!!
644
+ $week_days = array(
645
+ 0 => 'SU',
646
+ 1 => 'MO',
647
+ 2 => 'TU',
648
+ 3 => 'WE',
649
+ 4 => 'TH',
650
+ 5 => 'FR',
651
+ 6 => 'SA'
652
+ );
653
+
654
+ if( $by_value ) {
655
+ while( $_name = current( $week_days ) ) {
656
+ if( $_name == $day_id ) {
657
+ return key( $week_days );
658
+ }
659
+ next( $week_days );
660
+ }
661
+ return false;
662
+ }
663
+ else
664
+ return $week_days[$day_id];
665
  }
666
+
667
  /**
668
  * row_monthly function
669
  *
672
  * @return void
673
  **/
674
  function row_monthly( $visible = false, $count = 1, $ai1ec_monthly_each = 0, $ai1ec_monthly_on_the = 0, $month = array(), $first = false, $second = false ) {
675
+ global $ai1ec_view_helper;
676
+
677
+ $args = array(
678
+ 'visible' => $visible,
679
+ 'count' => $this->create_count_input( 'ai1ec_monthly_count', $count, 12 ) . __( 'month(s)', AI1EC_PLUGIN_NAME ),
680
+ 'ai1ec_monthly_each' => $ai1ec_monthly_each,
681
+ 'ai1ec_monthly_on_the' => $ai1ec_monthly_on_the,
682
+ 'month' => $this->create_montly_date_select( $month ),
683
+ 'on_the_select' => $this->create_on_the_select( $first, $second )
684
+ );
685
+ return $ai1ec_view_helper->get_view( 'row_monthly.php', $args );
686
  }
687
+
688
  /**
689
  * row_yearly function
690
  *
693
  * @return void
694
  **/
695
  function row_yearly( $visible = false, $count = 1, $year = array(), $first = false, $second = false ) {
696
+ global $ai1ec_view_helper;
697
+
698
+ $args = array(
699
+ 'visible' => $visible,
700
+ 'count' => $this->create_count_input( 'ai1ec_yearly_count', $count, 10 ) . __( 'year(s)', AI1EC_PLUGIN_NAME ),
701
+ 'year' => $this->create_yearly_date_select( $year ),
702
+ 'on_the_select' => $this->create_on_the_select( $first, $second )
703
+ );
704
+ return $ai1ec_view_helper->get_view( 'row_yearly.php', $args );
705
  }
706
 
707
  /**
726
  *
727
  * @param int | bool $start Events start before this (GMT) time
728
  * @param int | bool $end Events end before this (GMT) time
729
+ * @param array $filter Array of filters for the events returned.
730
  * ['cat_ids'] => non-associatative array of category IDs
731
  * ['tag_ids'] => non-associatative array of tag IDs
732
  * ['post_ids'] => non-associatative array of post IDs
734
  * @return array Matching events
735
  **/
736
  function get_matching_events( $start = false, $end = false, $filter = array() ) {
737
+ global $wpdb, $ai1ec_calendar_helper;
738
 
739
  // holds event_categories sql
740
+ $c_sql = '';
741
+ $c_where_sql = '';
742
+ // holds event_tags sql
743
+ $t_sql = '';
744
+ $t_where_sql ='';
745
+ // holds posts sql
746
+ $p_where_sql = '';
747
+ // holds start sql
748
+ $start_where_sql = '';
749
+ // holds end sql
750
+ $end_where_sql = '';
751
+ // hold escape values
752
+ $args = array();
753
+
754
+ // =============================
755
+ // = Generating start date sql =
756
+ // =============================
757
+ if( $start !== false ) {
758
+ $start_where_sql = "AND (e.start >= FROM_UNIXTIME( %d ) OR e.recurrence_rules != '')";
759
+ $args[] = $start;
760
+ }
761
+
762
+ // ===========================
763
+ // = Generating end date sql =
764
+ // ===========================
765
+ if( $end !== false ) {
766
+ $end_where_sql = "AND (e.end <= FROM_UNIXTIME( %d ) OR e.recurrence_rules != '')";
767
+ $args[] = $end;
768
+ }
769
+
770
+ // Get the Join (filter_join) and Where (filter_where) statements based on $filter elements specified
771
  $filter = $ai1ec_calendar_helper->_get_filter_sql( $filter );
772
 
773
  $query = $wpdb->prepare(
776
  e.show_map, e.contact_name, e.contact_phone, e.contact_email, e.cost, e.ical_feed_url, e.ical_source_url,
777
  e.ical_organizer, e.ical_contact, e.ical_uid " .
778
  "FROM $wpdb->posts " .
779
+ "INNER JOIN {$wpdb->prefix}ai1ec_events AS e ON e.post_id = ID " .
780
  $filter['filter_join'] .
781
  "WHERE post_type = '" . AI1EC_POST_TYPE . "' " .
782
+ "AND post_status = 'publish' " .
783
+ $filter['filter_where'] .
784
+ $start_where_sql .
785
+ $end_where_sql,
786
  $args );
787
 
788
  $events = $wpdb->get_results( $query, ARRAY_A );
789
 
790
+ foreach( $events as &$event ) {
791
+ try{
792
+ $event = new Ai1ec_Event( $event );
793
+ } catch( Ai1ec_Event_Not_Found $n ) {
794
+ unset( $event );
795
+ // The event is not found, continue to the next event
796
+ continue;
797
+ }
798
 
799
+ // if there are recurrence rules, include the event, else...
800
  if( empty( $event->recurrence_rules ) ) {
801
  // if start time is set, and event start time is before the range
802
  // it, continue to the next event
803
  if( $start !== false && $event->start < $start ) {
804
+ unset( $event );
805
+ continue;
806
  }
807
  // if end time is set, and event end time is after
808
  // it, continue to the next event
809
  if( $end !== false && $ev->end < $end ) {
810
+ unset( $event );
811
+ continue;
812
  }
813
  }
814
  }
844
  * @return string
845
  **/
846
  function get_short_time( $timestamp, $convert_from_gmt = true ) {
847
+ $time_format = get_option( 'time_format', 'g:ia' );
848
  if( $convert_from_gmt )
849
  $timestamp = $this->gmt_to_local( $timestamp );
850
  return date_i18n( $time_format, $timestamp, true );
878
  * @return string
879
  **/
880
  function get_medium_time( $timestamp, $convert_from_gmt = true ) {
881
+ $time_format = get_option( 'time_format', 'g:ia' );
882
  if( $convert_from_gmt )
883
  $timestamp = $this->gmt_to_local( $timestamp );
884
  return date_i18n( $time_format, $timestamp, true );
896
  * @return string
897
  **/
898
  function get_long_time( $timestamp, $convert_from_gmt = true ) {
899
+ $date_format = get_option( 'date_format', 'D, F j' );
900
+ $time_format = get_option( 'time_format', 'g:i' );
901
  if( $convert_from_gmt )
902
  $timestamp = $this->gmt_to_local( $timestamp );
903
  return date_i18n( $date_format, $timestamp, true ) . ' @ ' . date_i18n( $time_format, $timestamp, true );
915
  * @return string
916
  **/
917
  function get_long_date( $timestamp, $convert_from_gmt = true ) {
918
+ $date_format = get_option( 'date_format', 'D, F j' );
919
  if( $convert_from_gmt )
920
  $timestamp = $this->gmt_to_local( $timestamp );
921
  return date_i18n( $date_format, $timestamp, true );
933
  function gmt_to_local( $timestamp ) {
934
  $offset = get_option( 'gmt_offset' );
935
  $tz = get_option( 'timezone_string', 'America/Los_Angeles' );
936
+
937
  $offset = $this->get_timezone_offset( 'UTC', $tz, $timestamp );
938
 
939
  if( ! $offset )
940
  $offset = get_option( 'gmt_offset' ) * 3600;
941
+
942
  return $timestamp + $offset;
943
  }
944
 
954
  function local_to_gmt( $timestamp ) {
955
  $offset = get_option( 'gmt_offset' );
956
  $tz = get_option( 'timezone_string', 'America/Los_Angeles' );
957
+
958
  $offset = $this->get_timezone_offset( 'UTC', $tz, $timestamp );
959
 
960
  if( ! $offset )
961
  $offset = get_option( 'gmt_offset' ) * 3600;
962
+
963
  return $timestamp - $offset;
964
  }
965
+
966
  /**
967
  * get_timezone_offset function
968
  *
978
  if( $origin_tz === null )
979
  if( ! is_string( $origin_tz = date_default_timezone_get() ) )
980
  return false; // A UTC timestamp was returned -- bail out!
981
+
982
  try {
983
  $origin_dtz = new DateTimeZone( $origin_tz );
984
  $remote_dtz = new DateTimeZone( $remote_tz );
989
  } catch( Exception $e ) {
990
  return false;
991
  }
992
+
993
  return $offset;
994
  }
995
 
1034
  function get_gmap_url( &$event ) {
1035
  $location_arg = urlencode( $event->address );
1036
  $lang = $this->get_lang();
1037
+
1038
  return "http://www.google.com/maps?f=q&hl=" . $lang . "&source=embed&q=" . $location_arg;
1039
  }
1040
+
1041
  /**
1042
  * get_lang function
1043
  *
1051
 
1052
  return ( isset( $locale[0] ) && $locale[0] != '' ) ? $locale[0] : 'en';
1053
  }
1054
+
1055
  /**
1056
  * get_region function
1057
  *
1062
  **/
1063
  function get_region() {
1064
  $locale = explode( '_', get_locale() );
1065
+
1066
  $region = ( isset( $locale[1] ) && $locale[1] != '' ) ? strtolower( $locale[1] ) : '';
1067
 
1068
  // Primary ccTLD for United Kingdom is uk.
1081
  * @return string The excerpt.
1082
  **/
1083
  function trim_excerpt( $text )
1084
+ {
1085
  $raw_excerpt = $text;
1086
 
1087
  $text = strip_shortcodes( $text );
1123
  // = Sanitize provided IDs against SQL injection =
1124
  // ===============================================
1125
  if( ! is_array( $post_ids ) )
1126
+ $post_ids = explode( ',', $post_ids );
1127
  foreach( $post_ids as &$post_id ) {
1128
  $post_id = intval( $post_id );
1129
  }
1130
  $post_ids = join( ',', $post_ids );
1131
 
1132
  if( ! is_array( $term_ids ) )
1133
+ $term_ids = explode( ',', $term_ids );
1134
  foreach( $term_ids as &$term_id ) {
1135
  $term_id = intval( $term_id );
1136
  }
1156
  * @return string
1157
  */
1158
  function get_category_color( $term_id ) {
1159
+ global $wpdb;
1160
 
1161
+ $term_id = (int) $term_id;
1162
+ $table_name = $wpdb->prefix . 'ai1ec_event_category_colors';
1163
+ $color = $wpdb->get_var( "SELECT term_color FROM {$table_name} WHERE term_id = {$term_id}" );
1164
+ return $color;
1165
  }
1166
 
1167
  /**
1174
  * @return string
1175
  **/
1176
  function get_category_color_square( $term_id ) {
1177
+ $color = $this->get_category_color( $term_id );
1178
+ $cat = get_term( $term_id, 'events_categories' );
1179
+ if( ! is_null( $color ) && ! empty( $color ) )
1180
+ return '<div class="ai1ec-category-color" style="background:' . $color . '" title="' . esc_attr( $cat->name ) . '"></div>';
1181
 
1182
+ return '';
1183
  }
1184
 
1185
  /**
1192
  * @return string
1193
  **/
1194
  function get_event_category_color_style( $term_id, $allday = false ) {
1195
+ $color = $this->get_category_color( $term_id );
1196
+ if( ! is_null( $color ) && ! empty( $color ) ) {
1197
+ if( $allday )
1198
+ return 'background: ' . $color . ';';
1199
+ else
1200
+ return 'color: ' . $color . ' !important;';
1201
+ }
1202
+
1203
+ return '';
1204
+ }
1205
+
1206
+ /**
1207
+ * get_event_category_faded_color function
1208
+ *
1209
+ * Returns a faded version of the event's category color in hex format.
1210
+ *
1211
+ * @param int $term_id The Event Category's term ID
1212
+ * @return string
1213
+ **/
1214
+ function get_event_category_faded_color( $term_id ) {
1215
+ $color = $this->get_category_color( $term_id );
1216
+ if( ! is_null( $color ) && ! empty( $color ) ) {
1217
+
1218
+ $color1 = substr( $color, 1 );
1219
+ $color2 = 'ffffff';
1220
+
1221
+ $c1_p1 = hexdec( substr( $color1, 0, 2 ) );
1222
+ $c1_p2 = hexdec( substr( $color1, 2, 2 ) );
1223
+ $c1_p3 = hexdec( substr( $color1, 4, 2 ) );
1224
+
1225
+ $c2_p1 = hexdec( substr( $color2, 0, 2 ) );
1226
+ $c2_p2 = hexdec( substr( $color2, 2, 2 ) );
1227
+ $c2_p3 = hexdec( substr( $color2, 4, 2 ) );
1228
+
1229
+ $m_p1 = dechex( round( $c1_p1 * 0.3 + $c2_p1 * 0.7 ) );
1230
+ $m_p2 = dechex( round( $c1_p2 * 0.3 + $c2_p2 * 0.7 ) );
1231
+ $m_p3 = dechex( round( $c1_p3 * 0.3 + $c2_p3 * 0.7 ) );
1232
+
1233
+ return '#' . $m_p1 . $m_p2 . $m_p3;
1234
+ }
1235
+
1236
+ return '';
1237
  }
1238
 
1239
  /**
1245
  * @return string
1246
  **/
1247
  function get_event_category_colors( $cats ) {
1248
+ $sqrs = '';
1249
 
1250
+ foreach( $cats as $cat ) {
1251
+ $tmp = $this->get_category_color_square( $cat->term_id );
1252
+ if( ! empty( $tmp ) )
1253
+ $sqrs .= $tmp;
1254
+ }
1255
 
1256
+ return $sqrs;
1257
  }
1258
 
1259
  /**
1265
  * @return void
1266
  **/
1267
  function create_end_dropdown( $selected = null ) {
1268
+ ob_start();
1269
 
1270
  $options = array(
1271
+ 0 => __( 'Never', AI1EC_PLUGIN_NAME ),
1272
+ 1 => __( 'After', AI1EC_PLUGIN_NAME ),
1273
+ 2 => __( 'On date', AI1EC_PLUGIN_NAME )
1274
  );
1275
 
1276
  ?>
1288
 
1289
  return $output;
1290
  }
1291
+
1292
  /**
1293
  * rrule_to_text function
1294
  *
1297
  * @return void
1298
  **/
1299
  function rrule_to_text( $rrule = '') {
1300
+ $txt = '';
1301
+ $rc = new SG_iCal_Recurrence( new SG_iCal_Line( 'RRULE:' . $rrule ) );
1302
+ switch( $rc->getFreq() ) {
1303
+ case 'DAILY':
1304
+ $this->_get_interval( $txt, 'daily', $rc->getInterval() );
1305
+ $this->_ending_sentence( $txt, $rc );
1306
+ break;
1307
+ case 'WEEKLY':
1308
+ $this->_get_interval( $txt, 'weekly', $rc->getInterval() );
1309
+ $this->_get_sentence_by( $txt, 'weekly', $rc );
1310
+ $this->_ending_sentence( $txt, $rc );
1311
+ break;
1312
+ case 'MONTHLY':
1313
+ $this->_get_interval( $txt, 'monthly', $rc->getInterval() );
1314
+ $this->_get_sentence_by( $txt, 'monthly', $rc );
1315
+ $this->_ending_sentence( $txt, $rc );
1316
+ break;
1317
+ case 'YEARLY':
1318
+ $this->_get_interval( $txt, 'yearly', $rc->getInterval() );
1319
+ $this->_get_sentence_by( $txt, 'yearly', $rc );
1320
+ $this->_ending_sentence( $txt, $rc );
1321
+ break;
1322
+ default:
1323
+ $txt = $rrule;
1324
+ }
1325
+ return $txt;
1326
  }
1327
+
1328
  /**
1329
  * _get_sentence_by function
1330
  *
1333
  * @return void
1334
  **/
1335
  function _get_sentence_by( &$txt, $freq, $rc ) {
1336
+ global $wp_locale;
1337
+
1338
+ switch( $freq ) {
1339
+ case 'weekly':
1340
+ if( $rc->getByDay() ) {
1341
+ if( count( $rc->getByDay() ) > 1 ) {
1342
+ // if there are more than 3 days
1343
+ // use days's abbr
1344
+ if( count( $rc->getByDay() ) > 2 ) {
1345
+ $_days = '';
1346
+ foreach( $rc->getByDay() as $d ) {
1347
+ $day = $this->get_weekday_by_id( $d, true );
1348
+ $_days .= ' ' . $wp_locale->weekday_abbrev[$wp_locale->weekday[$day]] . ',';
1349
+ }
1350
+ // remove the last ' and'
1351
+ $_days = substr( $_days, 0, -1 );
1352
+ $txt .= ' ' . _x( 'on', 'Recurrence editor - weekly tab', AI1EC_PLUGIN_NAME ) . $_days;
1353
+ } else {
1354
+ $_days = '';
1355
+ foreach( $rc->getByDay() as $d ) {
1356
+ $day = $this->get_weekday_by_id( $d, true );
1357
+ $_days .= ' ' . $wp_locale->weekday[$day] . ' ' . __( 'and', AI1EC_PLUGIN_NAME );
1358
+ }
1359
+ // remove the last ' and'
1360
+ $_days = substr( $_days, 0, -4 );
1361
+ $txt .= ' ' . _x( 'on', 'Recurrence editor - weekly tab', AI1EC_PLUGIN_NAME ) . $_days;
1362
+ }
1363
+ } else {
1364
+ $_days = '';
1365
+ foreach( $rc->getByDay() as $d ) {
1366
+ $day = $this->get_weekday_by_id( $d, true );
1367
+ $_days .= ' ' . $wp_locale->weekday[$day];
1368
+ }
1369
+ $txt .= ' ' . _x( 'on', 'Recurrence editor - weekly tab', AI1EC_PLUGIN_NAME ) . $_days;
1370
+ }
1371
+ }
1372
+ break;
1373
+ case 'monthly':
1374
+ if( $rc->getByMonthDay() ) {
1375
+ // if there are more than 2 days
1376
+ if( count( $rc->getByMonthDay() ) > 2 ) {
1377
+ $_days = '';
1378
+ foreach( $rc->getByMonthDay() as $m_day ) {
1379
+ $_days .= ' ' . $this->_ordinal( $m_day ) . ',';
1380
+ }
1381
+ $_days = substr( $_days, 0, -1 );
1382
+ $txt .= ' ' . _x( 'on', 'Recurrence editor - monthly tab', AI1EC_PLUGIN_NAME ) . $_days . ' ' . __( 'of the month', AI1EC_PLUGIN_NAME );
1383
+ } else if( count( $rc->getByMonthDay() ) > 1 ) {
1384
+ $_days = '';
1385
+ foreach( $rc->getByMonthDay() as $m_day ) {
1386
+ $_days .= ' ' . $this->_ordinal( $m_day ) . ' ' . __( 'and', AI1EC_PLUGIN_NAME );
1387
+ }
1388
+ $_days = substr( $_days, 0, -4 );
1389
+ $txt .= ' ' . _x( 'on', 'Recurrence editor - monthly tab', AI1EC_PLUGIN_NAME ) . $_days . ' ' . __( 'of the month', AI1EC_PLUGIN_NAME );
1390
+ } else {
1391
+ $_days = '';
1392
+ foreach( $rc->getByMonthDay() as $m_day ) {
1393
+ $_days .= ' ' . $this->_ordinal( $m_day );
1394
+ }
1395
+ $txt .= ' ' . _x( 'on', 'Recurrence editor - monthly tab', AI1EC_PLUGIN_NAME ) . $_days . ' ' . __( 'of the month', AI1EC_PLUGIN_NAME );
1396
+ }
1397
+ }
1398
+ break;
1399
+ case 'yearly':
1400
+ if( $rc->getByMonth() ) {
1401
+ // if there are more than 2 months
1402
+ if( count( $rc->getByMonth() ) > 2 ) {
1403
+ $_months = '';
1404
+ foreach( $rc->getByMonth() as $_m ) {
1405
+ $_m = $_m < 10 ? 0 . $_m : $_m;
1406
+ $_months .= ' ' . $wp_locale->month_abbrev[$wp_locale->month[$_m]] . ',';
1407
+ }
1408
+ $_months = substr( $_months, 0, -1 );
1409
+ $txt .= ' ' . _x( 'on', 'Recurrence editor - yearly tab', AI1EC_PLUGIN_NAME ) . $_months;
1410
+ } else if( count( $rc->getByMonth() ) > 1 ) {
1411
+ $_months = '';
1412
+ foreach( $rc->getByMonth() as $_m ) {
1413
+ $_m = $_m < 10 ? 0 . $_m : $_m;
1414
+ $_months .= ' ' . $wp_locale->month[$_m] . ' ' . __( 'and', AI1EC_PLUGIN_NAME );
1415
+ }
1416
+ $_months = substr( $_months, 0, -4 );
1417
+ $txt .= ' ' . _x( 'on', 'Recurrence editor - yearly tab', AI1EC_PLUGIN_NAME ) . $_months;
1418
+ } else {
1419
+ $_months = '';
1420
+ foreach( $rc->getByMonth() as $_m ) {
1421
+ $_m = $_m < 10 ? 0 . $_m : $_m;
1422
+ $_months .= ' ' . $wp_locale->month[$_m];
1423
+ }
1424
+ $txt .= ' ' . _x( 'on', 'Recurrence editor - yearly tab', AI1EC_PLUGIN_NAME ) . $_months;
1425
+ }
1426
+ }
1427
+ break;
1428
+ }
1429
  }
1430
+
1431
  /**
1432
  * _ordinal function
1433
  *
1437
  **/
1438
  function _ordinal( $cdnl ) {
1439
  $locale = explode( '_', get_locale() );
1440
+
1441
  if( isset( $locale[0] ) && $locale[0] != 'en' )
1442
  return $cdnl;
1443
+
1444
+ $test_c = abs($cdnl) % 10;
1445
+ $ext = ( ( abs( $cdnl ) % 100 < 21 && abs( $cdnl ) % 100 > 4 ) ? 'th'
1446
+ : ( ( $test_c < 4 ) ? ( $test_c < 3 ) ? ( $test_c < 2 ) ? ( $test_c < 1 )
1447
+ ? 'th' : 'st' : 'nd' : 'rd' : 'th' ) );
1448
+ return $cdnl.$ext;
1449
+ }
1450
+
1451
  /**
1452
  * _get_interval function
1453
  *
1456
  * @return void
1457
  **/
1458
  function _get_interval( &$txt, $freq, $interval ) {
1459
+ switch( $freq ) {
1460
+ case 'daily':
1461
+ // check if interval is set
1462
+ if( ! $interval || $interval == 1 ) {
1463
+ $txt = __( 'Daily', AI1EC_PLUGIN_NAME );
1464
+ } else {
1465
+ if( $interval == 2 ) {
1466
+ $txt = __( 'Every other day', AI1EC_PLUGIN_NAME );
1467
+ } else {
1468
+ $txt = sprintf( __( 'Every %d days', AI1EC_PLUGIN_NAME ), $interval );
1469
+ }
1470
+ }
1471
+ break;
1472
+ case 'weekly':
1473
+ // check if interval is set
1474
+ if( ! $interval || $interval == 1 ) {
1475
+ $txt = __( 'Weekly', AI1EC_PLUGIN_NAME );
1476
+ } else {
1477
+ if( $interval == 2 ) {
1478
+ $txt = __( 'Every other week', AI1EC_PLUGIN_NAME );
1479
+ } else {
1480
+ $txt = sprintf( __( 'Every %d weeks', AI1EC_PLUGIN_NAME ), $interval );
1481
+ }
1482
+ }
1483
+ break;
1484
+ case 'monthly':
1485
+ // check if interval is set
1486
+ if( ! $interval || $interval == 1 ) {
1487
+ $txt = __( 'Monthly', AI1EC_PLUGIN_NAME );
1488
+ } else {
1489
+ if( $interval == 2 ) {
1490
+ $txt = __( 'Every other month', AI1EC_PLUGIN_NAME );
1491
+ } else {
1492
+ $txt = sprintf( __( 'Every %d months', AI1EC_PLUGIN_NAME ), $interval );
1493
+ }
1494
+ }
1495
+ break;
1496
+ case 'yearly':
1497
+ // check if interval is set
1498
+ if( ! $interval || $interval == 1 ) {
1499
+ $txt = __( 'Yearly', AI1EC_PLUGIN_NAME );
1500
+ } else {
1501
+ if( $interval == 2 ) {
1502
+ $txt = __( 'Every other year', AI1EC_PLUGIN_NAME );
1503
+ } else {
1504
+ $txt = sprintf( __( 'Every %d years', AI1EC_PLUGIN_NAME ), $interval );
1505
+ }
1506
+ }
1507
+ break;
1508
+ }
1509
  }
1510
+
1511
  /**
1512
  * _ending_sentence function
1513
  *
1528
  else
1529
  $txt .= ' - ' . __( 'forever', AI1EC_PLUGIN_NAME );
1530
  }
1531
+
1532
  /**
1533
  * undocumented function
1534
  *
1537
  * @return void
1538
  **/
1539
  function convert_rrule_to_text() {
1540
+ $error = false;
1541
+ // check to see if RRULE is set
1542
+ if( isset( $_REQUEST["rrule"] ) ) {
1543
+
1544
+ // check to see if rrule is empty
1545
+ if( empty( $_REQUEST["rrule"] ) ) {
1546
+ $error = true;
1547
+ $message = 'Recurrence rule cannot be empty!';
1548
+ } else {
1549
+ // convert rrule to text
1550
+ $message = $this->rrule_to_text( $_REQUEST["rrule"] );
1551
+ }
1552
+
1553
+ } else {
1554
+ $error = true;
1555
+ $message = 'Recurrence rule is not provided!';
1556
+ }
1557
+
1558
+ $output = array(
1559
  "error" => $error,
1560
  "message" => stripslashes( $message )
1561
  );
1563
  echo json_encode( $output );
1564
  exit();
1565
  }
1566
+
1567
  /**
1568
  * post_type_link function
1569
  *
1573
  **/
1574
  function post_type_link( $permalink, $post, $leavename ) {
1575
  global $ai1ec_app_helper;
1576
+
1577
  if( $post->post_type == AI1EC_POST_TYPE ) {
1578
  $delimiter = $ai1ec_app_helper->get_param_delimiter_char( $permalink );
1579
  return $permalink . $delimiter . 'instance_id=';
1580
  }
1581
+
1582
  return $permalink;
1583
  }
1584
+
1585
+ /**
1586
+ * get_week_start_day_offset function
1587
+ *
1588
+ * Returns the day offset of the first day of the week given a weekday in
1589
+ * question.
1590
+ *
1591
+ * @param int $wday The weekday to get information about
1592
+ * @return int A value between -6 and 0 indicating the week start
1593
+ * day relative to the given weekday.
1594
+ */
1595
+ function get_week_start_day_offset( $wday ) {
1596
+ global $ai1ec_settings;
1597
+
1598
+ return - ( 7 - ( $ai1ec_settings->week_start_day - $wday ) ) % 7;
1599
+ }
1600
  }
1601
  // END class
app/helper/class-ai1ec-settings-helper.php CHANGED
@@ -147,6 +147,9 @@ class Ai1ec_Settings_Helper {
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>
@@ -154,7 +157,7 @@ class Ai1ec_Settings_Helper {
154
  <?php
155
  return ob_get_clean();
156
  }
157
-
158
  /**
159
  * get_timezone_dropdown function
160
  *
@@ -186,7 +189,7 @@ class Ai1ec_Settings_Helper {
186
  <?php
187
  return ob_get_clean();
188
  }
189
-
190
  /**
191
  * get_date_format_dropdown function
192
  *
@@ -213,7 +216,7 @@ class Ai1ec_Settings_Helper {
213
  <?php
214
  return ob_get_clean();
215
  }
216
-
217
  /**
218
  * get_cron_freq_dropdown function
219
  *
147
  <option value="month" <?php echo $view == 'month' ? 'selected' : '' ?>>
148
  <?php _e( 'Month', AI1EC_PLUGIN_NAME ) ?>
149
  </option>
150
+ <option value="week" <?php echo $view == 'week' ? 'selected' : '' ?>>
151
+ <?php _e( 'Week', AI1EC_PLUGIN_NAME ) ?>
152
+ </option>
153
  <option value="agenda" <?php echo $view == 'agenda' ? 'selected' : '' ?>>
154
  <?php _e( 'Agenda', AI1EC_PLUGIN_NAME ) ?>
155
  </option>
157
  <?php
158
  return ob_get_clean();
159
  }
160
+
161
  /**
162
  * get_timezone_dropdown function
163
  *
189
  <?php
190
  return ob_get_clean();
191
  }
192
+
193
  /**
194
  * get_date_format_dropdown function
195
  *
216
  <?php
217
  return ob_get_clean();
218
  }
219
+
220
  /**
221
  * get_cron_freq_dropdown function
222
  *
app/model/class-ai1ec-event.php CHANGED
@@ -51,6 +51,26 @@ class Ai1ec_Event {
51
  **/
52
  var $end;
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  /**
55
  * allday class variable
56
  *
@@ -236,6 +256,13 @@ class Ai1ec_Event {
236
  **/
237
  private $color_style;
238
 
 
 
 
 
 
 
 
239
  /**
240
  * tags_html class variable
241
  *
@@ -283,7 +310,7 @@ class Ai1ec_Event {
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
  $left_join = "";
288
  $select_sql = "e.post_id, e.recurrence_rules, e.exception_rules, e.allday, " .
289
  "e.recurrence_dates, e.exception_dates, e.venue, e.country, e.address, e.city, e.province, e.postal_code, " .
@@ -291,10 +318,10 @@ class Ai1ec_Event {
291
  "e.ical_organizer, e.ical_contact, e.ical_uid, " .
292
  "GROUP_CONCAT( ttc.term_id ) AS categories, " .
293
  "GROUP_CONCAT( ttt.term_id ) AS tags ";
294
-
295
  if( $instance ) {
296
  $select_sql .= ", UNIX_TIMESTAMP( aei.start ) as start, UNIX_TIMESTAMP( aei.end ) as end ";
297
-
298
  $instance = (int) $instance;
299
  $left_join = "LEFT JOIN {$wpdb->prefix}ai1ec_event_instances aei ON aei.id = $instance ";
300
  } else {
@@ -560,6 +587,17 @@ class Ai1ec_Event {
560
  }
561
  return $this->color_style;
562
 
 
 
 
 
 
 
 
 
 
 
 
563
  // ===============================================
564
  // = HTML of category color boxes for this event =
565
  // ===============================================
51
  **/
52
  var $end;
53
 
54
+ /**
55
+ * start_truncated class variable
56
+ *
57
+ * Whether this copy of the event was broken up for rendering and the start
58
+ * time is not its "real" start time.
59
+ *
60
+ * @var bool
61
+ **/
62
+ var $start_truncated;
63
+
64
+ /**
65
+ * end_truncated class variable
66
+ *
67
+ * Whether this copy of the event was broken up for rendering and the end
68
+ * time is not its "real" end time.
69
+ *
70
+ * @var bool
71
+ **/
72
+ var $end_truncated;
73
+
74
  /**
75
  * allday class variable
76
  *
256
  **/
257
  private $color_style;
258
 
259
+ /**
260
+ * faded_color class variable
261
+ *
262
+ * @var string
263
+ **/
264
+ private $faded_color;
265
+
266
  /**
267
  * tags_html class variable
268
  *
310
 
311
  if( ! $post || $post->post_status == 'auto-draft' )
312
  throw new Ai1ec_Event_Not_Found( "Post with ID '$data' could not be retrieved from the database." );
313
+
314
  $left_join = "";
315
  $select_sql = "e.post_id, e.recurrence_rules, e.exception_rules, e.allday, " .
316
  "e.recurrence_dates, e.exception_dates, e.venue, e.country, e.address, e.city, e.province, e.postal_code, " .
318
  "e.ical_organizer, e.ical_contact, e.ical_uid, " .
319
  "GROUP_CONCAT( ttc.term_id ) AS categories, " .
320
  "GROUP_CONCAT( ttt.term_id ) AS tags ";
321
+
322
  if( $instance ) {
323
  $select_sql .= ", UNIX_TIMESTAMP( aei.start ) as start, UNIX_TIMESTAMP( aei.end ) as end ";
324
+
325
  $instance = (int) $instance;
326
  $left_join = "LEFT JOIN {$wpdb->prefix}ai1ec_event_instances aei ON aei.id = $instance ";
327
  } else {
587
  }
588
  return $this->color_style;
589
 
590
+ // =========================================
591
+ // = Faded version of event category color =
592
+ // =========================================
593
+ case 'faded_color':
594
+ if( $this->faded_color === null ) {
595
+ $categories = wp_get_post_terms( $this->post_id, 'events_categories' );
596
+ if( $categories && ! empty( $categories ) )
597
+ $this->faded_color = $ai1ec_events_helper->get_event_category_faded_color( $categories[0]->term_id );
598
+ }
599
+ return $this->faded_color;
600
+
601
  // ===============================================
602
  // = HTML of category color boxes for this event =
603
  // ===============================================
app/view/agenda.php CHANGED
@@ -8,15 +8,16 @@
8
  <?php _e( '− Collapse All', AI1EC_PLUGIN_NAME ) ?>
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_PLUGIN_NAME ) ?>
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>
8
  <?php _e( '− Collapse All', AI1EC_PLUGIN_NAME ) ?>
9
  </a
10
  ><?php endif ?><a
11
+ id="ai1ec-today" class="ai1ec-load-view ai1ec-button" href="#action=ai1ec_agenda&amp;ai1ec_post_ids=<?php echo $post_ids ?>">
12
  <?php _e( 'Today', AI1EC_PLUGIN_NAME ) ?>
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'] ?>"
19
+ class="ai1ec-load-view ai1ec-button"
20
+ href="<?php echo esc_attr( $link['href'] ) ?>&amp;ai1ec_post_ids=<?php echo $post_ids ?>">
21
  <?php echo esc_html( $link['text'] ) ?>
22
  </a>
23
  </li>
app/view/calendar.php CHANGED
@@ -1,3 +1,4 @@
 
1
  <table class="ai1ec-calendar-toolbar">
2
  <tbody>
3
  <tr>
@@ -6,14 +7,21 @@
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>
@@ -84,6 +92,7 @@
84
  </div>
85
  </td>
86
  <?php endif // $categories || $tags ?>
 
87
  </tr>
88
  </tbody>
89
  </table>
@@ -109,3 +118,4 @@
109
  <?php _e( 'Subscribe in Google Calendar', AI1EC_PLUGIN_NAME ) ?>
110
  </a>
111
  <?php endif ?>
 
1
+ <!-- START All-in-One Event Calendar Plugin - Version 1.2 -->
2
  <table class="ai1ec-calendar-toolbar">
3
  <tbody>
4
  <tr>
7
  <ul class="ai1ec-view-tabs">
8
  <li>
9
  <a id="ai1ec-view-month" class="ai1ec-load-view ai1ec-button"
10
+ href="#action=ai1ec_month&amp;ai1ec_post_ids=<?php echo $selected_post_ids ?>">
11
  <img src="<?php echo AI1EC_IMAGE_URL ?>/month-view.png" alt="<?php _e( 'Month', AI1EC_PLUGIN_NAME ) ?>" />
12
  <?php _e( 'Month', AI1EC_PLUGIN_NAME ) ?>
13
  </a>
14
  </li>
15
+ <li>
16
+ <a id="ai1ec-view-week" class="ai1ec-load-view ai1ec-button"
17
+ href="#action=ai1ec_week&amp;ai1ec_post_ids=<?php echo $selected_post_ids ?>">
18
+ <img src="<?php echo AI1EC_IMAGE_URL ?>/week-view.png" alt="<?php _e( 'Week', AI1EC_PLUGIN_NAME ) ?>" />
19
+ <?php _e( 'Week', AI1EC_PLUGIN_NAME ) ?>
20
+ </a>
21
+ </li>
22
  <li>
23
  <a id="ai1ec-view-agenda" class="ai1ec-load-view ai1ec-button"
24
+ href="#action=ai1ec_agenda&amp;ai1ec_post_ids=<?php echo $selected_post_ids ?>">
25
  <img src="<?php echo AI1EC_IMAGE_URL ?>/agenda-view.png" alt="<?php _e( 'Month', AI1EC_PLUGIN_NAME ) ?>" />
26
  <?php _e( 'Agenda', AI1EC_PLUGIN_NAME ) ?>
27
  </a>
92
  </div>
93
  </td>
94
  <?php endif // $categories || $tags ?>
95
+
96
  </tr>
97
  </tbody>
98
  </table>
118
  <?php _e( 'Subscribe in Google Calendar', AI1EC_PLUGIN_NAME ) ?>
119
  </a>
120
  <?php endif ?>
121
+ <!-- END All-in-One Event Calendar Plugin -->
app/view/class-ai1ec-agenda-widget.php CHANGED
@@ -136,7 +136,7 @@ class Ai1ec_Agenda_Widget extends WP_Widget
136
  $ai1ec_events_helper,
137
  $ai1ec_calendar_helper,
138
  $ai1ec_settings;
139
-
140
  $defaults = array(
141
  'hide_on_calendar_page' => true,
142
  'event_cat_ids' => array(),
@@ -160,10 +160,10 @@ class Ai1ec_Agenda_Widget extends WP_Widget
160
  $timestamp = $ai1ec_events_helper->gmt_to_local( time() );
161
 
162
  // Set $limit to the specified category/tag
163
- $limit = array(
164
- "cat_ids" => $instance['event_cat_ids'],
165
- "tag_ids" => $instance['event_tag_ids'],
166
- "post_ids" => $instance['event_post_ids'],
167
  );
168
 
169
  // Get events, then classify into date array
@@ -175,7 +175,7 @@ class Ai1ec_Agenda_Widget extends WP_Widget
175
  $args['show_subscribe_buttons'] = $instance['show_subscribe_buttons'];
176
  $args['show_calendar_button'] = $instance['show_calendar_button'];
177
  $args['dates'] = $dates;
178
- $args['calendar_url'] = $ai1ec_calendar_helper->get_calendar_url( null, $instance['event_cat_ids'], $instance['event_tag_ids'] );
179
  $args['subscribe_url'] = AI1EC_EXPORT_URL . $subscribe_filter;
180
 
181
  $ai1ec_view_helper->display( 'agenda-widget.php', $args );
136
  $ai1ec_events_helper,
137
  $ai1ec_calendar_helper,
138
  $ai1ec_settings;
139
+
140
  $defaults = array(
141
  'hide_on_calendar_page' => true,
142
  'event_cat_ids' => array(),
160
  $timestamp = $ai1ec_events_helper->gmt_to_local( time() );
161
 
162
  // Set $limit to the specified category/tag
163
+ $limit = array(
164
+ 'cat_ids' => $instance['event_cat_ids'],
165
+ 'tag_ids' => $instance['event_tag_ids'],
166
+ 'post_ids' => $instance['event_post_ids'],
167
  );
168
 
169
  // Get events, then classify into date array
175
  $args['show_subscribe_buttons'] = $instance['show_subscribe_buttons'];
176
  $args['show_calendar_button'] = $instance['show_calendar_button'];
177
  $args['dates'] = $dates;
178
+ $args['calendar_url'] = $ai1ec_calendar_helper->get_calendar_url( null, $limit );
179
  $args['subscribe_url'] = AI1EC_EXPORT_URL . $subscribe_filter;
180
 
181
  $ai1ec_view_helper->display( 'agenda-widget.php', $args );
app/view/month.php CHANGED
@@ -1,6 +1,6 @@
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_PLUGIN_NAME ) ?>
5
  </a>
6
  </span>
@@ -9,7 +9,7 @@
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>
@@ -70,7 +70,7 @@
70
  </div>
71
  </div><!-- .event-popup -->
72
 
73
- <div class="ai1ec-event <?php if( $event->post_id == $active_event ) echo 'ai1ec-active-event' ?>" <?php echo $event->color_style; ?>>
74
  <?php if( ! $event->allday ): ?>
75
  <span class="ai1ec-event-time"><?php echo esc_html( $event->short_start_time ) ?></span>
76
  <?php endif ?>
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&amp;ai1ec_post_ids=<?php echo $post_ids ?>">
4
  <?php _e( 'Today', AI1EC_PLUGIN_NAME ) ?>
5
  </a>
6
  </span>
9
  <li>
10
  <a id="<?php echo $link['id'] ?>"
11
  class="ai1ec-load-view ai1ec-button"
12
+ href="<?php echo esc_attr( $link['href'] ) ?>&amp;ai1ec_post_ids=<?php echo $post_ids ?>">
13
  <?php echo esc_html( $link['text'] ) ?>
14
  </a>
15
  </li>
70
  </div>
71
  </div><!-- .event-popup -->
72
 
73
+ <div class="ai1ec-event <?php if( $event->post_id == $active_event ) echo 'ai1ec-active-event' ?>" style="<?php echo $event->color_style ?>">
74
  <?php if( ! $event->allday ): ?>
75
  <span class="ai1ec-event-time"><?php echo esc_html( $event->short_start_time ) ?></span>
76
  <?php endif ?>
app/view/week.php ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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_week&amp;ai1ec_post_ids=<?php echo $post_ids ?>">
4
+ <?php _e( 'Today', AI1EC_PLUGIN_NAME ) ?>
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'] ) ?>&amp;ai1ec_post_ids=<?php echo $post_ids ?>">
13
+ <?php echo esc_html( $link['text'] ) ?>
14
+ </a>
15
+ </li>
16
+ <?php endforeach ?>
17
+ </ul>
18
+ <table class="ai1ec-week-view-original">
19
+ <thead>
20
+ <tr>
21
+ <?php foreach( $cell_array as $date => $day ) : ?>
22
+ <th class="ai1ec-weekday <?php if( $day['today'] ) echo 'ai1ec-today' ?>">
23
+ <span class="ai1ec-weekday-date"><?php echo date_i18n( 'j', $date, true ) ?></span>
24
+ <span class="ai1ec-weekday-day"><?php echo date_i18n( 'D', $date, true ) ?></span>
25
+ </th>
26
+ <?php endforeach // weekday ?>
27
+ </tr>
28
+ <tr>
29
+ <?php foreach( $cell_array as $day ) : ?>
30
+ <td class="ai1ec-allday-events <?php if( $day['today'] ) echo 'ai1ec-today' ?>">
31
+
32
+ <?php if( ! $done_allday_label ) : ?>
33
+ <div class="ai1ec-allday-label"><?php _e( 'All-day', AI1EC_PLUGIN_NAME ) ?></div>
34
+ <?php $done_allday_label = true ?>
35
+ <?php endif ?>
36
+
37
+ <?php foreach( $day['allday'] as $event ) : ?>
38
+ <a href="<?php echo esc_attr( get_permalink( $event->post_id ) ) . $event->instance_id ?>"
39
+ class="ai1ec-event-container
40
+ ai1ec-event-id-<?php echo $event->post_id ?>
41
+ ai1ec-event-instance-id-<?php echo $event->instance_id ?>
42
+ ai1ec-allday
43
+ <?php if( $event->start_truncated ) echo 'ai1ec-start-truncated' ?>
44
+ <?php if( $event->end_truncated ) echo 'ai1ec-end-truncated' ?>">
45
+
46
+ <?php // Insert post ID for use by JavaScript filtering later ?>
47
+ <input type="hidden" class="ai1ec-post-id" value="<?php echo $event->post_id ?>" />
48
+
49
+ <div class="ai1ec-event-popup">
50
+ <div class="ai1ec-event-summary">
51
+ <?php if( $event->category_colors ): ?>
52
+ <div class="ai1ec-category-colors"><?php echo $event->category_colors ?></div>
53
+ <?php endif ?>
54
+ <?php if( $event->post_excerpt ): ?>
55
+ <strong><?php _e( 'Summary:', AI1EC_PLUGIN_NAME ) ?></strong>
56
+ <p><?php echo esc_html( $event->post_excerpt ) ?></p>
57
+ <?php endif ?>
58
+ <div class="ai1ec-read-more"><?php esc_html_e( 'click anywhere for details', AI1EC_PLUGIN_NAME ) ?></div>
59
+ </div>
60
+ <div class="ai1ec-event-popup-bg">
61
+ <span class="ai1ec-event-title">
62
+ <?php if( function_exists( 'mb_strimwidth' ) ) : ?>
63
+ <?php echo esc_html( mb_strimwidth( apply_filters( 'the_title', $event->post->post_title ), 0, 35, '...' ) ) ?></span>
64
+ <?php else : ?>
65
+ <?php $read_more = strlen( apply_filters( 'the_title', $event->post->post_title ) ) > 35 ? '...' : '' ?>
66
+ <?php echo esc_html( substr( apply_filters( 'the_title', $event->post->post_title ), 0, 35 ) . $read_more ); ?>
67
+ <?php endif; ?>
68
+ </span>
69
+ <small><?php esc_html_e( '(all-day)', AI1EC_PLUGIN_NAME ) ?></small>
70
+ </div>
71
+ </div><!-- .event-popup -->
72
+
73
+ <div class="ai1ec-event <?php if( $event->post_id == $active_event ) echo 'ai1ec-active-event' ?>" style="<?php echo $event->color_style ?>">
74
+ <span class="ai1ec-event-title"><?php echo esc_html( apply_filters( 'the_title', $event->post->post_title ) ) ?></span>
75
+ </div>
76
+
77
+ </a>
78
+ <?php endforeach // allday ?>
79
+
80
+ </td>
81
+ <?php endforeach // weekday ?>
82
+ </tr>
83
+ </thead>
84
+ <tbody>
85
+ <tr class="ai1ec-week">
86
+ <?php foreach( $cell_array as $day ): ?>
87
+ <td <?php if( $day['today'] ) echo 'class="ai1ec-today"' ?>>
88
+
89
+ <?php if( ! $done_grid ) : ?>
90
+ <div class="ai1ec-grid-container">
91
+ <?php for( $hour = 0; $hour < 24; $hour++ ) : ?>
92
+ <div class="ai1ec-hour-marker <?php if( $hour >= 8 && $hour < 18 ) echo 'ai1ec-business-hour' ?>" style="top: <?php echo $hour * 60 ?>px;">
93
+ <div><?php echo esc_html( date_i18n( 'g a', gmmktime( $hour ) ) ) ?></div>
94
+ </div>
95
+ <?php for( $quarter = 1; $quarter < 4; $quarter++ ) : ?>
96
+ <div class="ai1ec-quarter-marker" style="top: <?php echo $hour * 60 + $quarter * 15 ?>px;"></div>
97
+ <?php endfor ?>
98
+ <?php endfor ?>
99
+ <div class="ai1ec-now-marker" style="top: <?php echo $now_top ?>px;"></div>
100
+ </div>
101
+ <?php $done_grid = true ?>
102
+ <?php endif ?>
103
+
104
+ <div class="ai1ec-day">
105
+ <?php foreach( $day['notallday'] as $notallday ): ?>
106
+ <?php extract( $notallday ) ?>
107
+ <a href="<?php echo esc_attr( get_permalink( $event->post_id ) ) . $event->instance_id ?>"
108
+ class="ai1ec-event-container
109
+ ai1ec-event-id-<?php echo $event->post_id ?>
110
+ ai1ec-event-instance-id-<?php echo $event->instance_id ?>
111
+ <?php if( $event->start_truncated ) echo 'ai1ec-start-truncated' ?>
112
+ <?php if( $event->end_truncated ) echo 'ai1ec-end-truncated' ?>"
113
+ style="top: <?php echo $top ?>px; height: <?php echo max( $height, 31 ) ?>px; left: <?php echo $indent * 8 ?>px; <?php echo $event->color_style ?> <?php if( $event->faded_color ) echo "border: 2px solid $event->faded_color !important;" ?> background: linear-gradient( top, #fff, <?php echo $event->faded_color ?> ) !important; background: -o-linear-gradient( top, #fff, <?php echo $event->faded_color ?> ) !important; background: -moz-linear-gradient( top, #fff, <?php echo $event->faded_color ?> ) !important; background: -webkit-gradient( linear, left top, left bottom, color-stop( 0, # ), color-stop( 1, <?php echo $event->faded_color ?> ) ) !important; background: -webkit-linear-gradient( top, #fff, <?php echo $event->faded_color ?> ) !important;">
114
+
115
+ <?php if( $event->start_truncated ) : ?><div class="ai1ec-start-truncator">◤</div><?php endif ?>
116
+ <?php if( $event->end_truncated ) : ?><div class="ai1ec-end-truncator">◢</div><?php endif ?>
117
+
118
+ <?php // Insert post ID for use by JavaScript filtering later ?>
119
+ <input type="hidden" class="ai1ec-post-id" value="<?php echo $event->post_id ?>" />
120
+
121
+ <div class="ai1ec-event-popup">
122
+ <div class="ai1ec-event-summary">
123
+ <?php if( $event->category_colors ): ?>
124
+ <div class="ai1ec-category-colors"><?php echo $event->category_colors ?></div>
125
+ <?php endif ?>
126
+ <?php if( $event->post_excerpt ): ?>
127
+ <strong><?php _e( 'Summary:', AI1EC_PLUGIN_NAME ) ?></strong>
128
+ <p><?php echo esc_html( $event->post_excerpt ) ?></p>
129
+ <?php endif ?>
130
+ <div class="ai1ec-read-more"><?php esc_html_e( 'click anywhere for details', AI1EC_PLUGIN_NAME ) ?></div>
131
+ </div>
132
+ <div class="ai1ec-event-popup-bg">
133
+ <span class="ai1ec-event-time"><?php echo esc_html( $event->short_start_time ) ?></span>
134
+ <span class="ai1ec-event-title"><?php echo esc_html( apply_filters( 'the_title', $event->post->post_title ) ) ?></span>
135
+ </span>
136
+ </div>
137
+ </div><!-- .event-popup -->
138
+
139
+ <div class="ai1ec-event <?php if( $event->post_id == $active_event ) echo 'ai1ec-active-event' ?>">
140
+ <span class="ai1ec-event-time"><?php echo esc_html( $event->short_start_time ) ?></span>
141
+ <span class="ai1ec-event-title"><?php echo esc_html( apply_filters( 'the_title', $event->post->post_title ) ) ?></span>
142
+ </div>
143
+
144
+ </a>
145
+ <?php endforeach // events ?>
146
+ </div>
147
+ </td>
148
+ <?php endforeach // day ?>
149
+ </tr>
150
+ </tbody>
151
+ </table>
css/calendar.css CHANGED
@@ -6,9 +6,14 @@
6
  .ai1ec-container small {
7
  margin: 0 !important;
8
  }
 
 
 
 
9
 
10
  /* Active buttons */
11
  .ai1ec-action-month #ai1ec-view-month,
 
12
  .ai1ec-action-agenda #ai1ec-view-agenda {
13
  color: #000;
14
  background: #ddd;
@@ -163,7 +168,10 @@ a.ai1ec-pagination {
163
  .ai1ec-action-month .ai1ec-pagination a {
164
  width: 7em;
165
  }
166
- .ai1ec-action-agenda a.ai1ec-pagination {
 
 
 
167
  float: right;
168
  width: 9em;
169
  }
@@ -171,8 +179,9 @@ a.ai1ec-pagination {
171
  width: 4em;
172
  }
173
 
174
- /* Month table */
175
- table.ai1ec-month-view {
 
176
  border-collapse: collapse;
177
  border: 1px solid #ddd !important;
178
  margin: 0 !important;
@@ -181,7 +190,28 @@ table.ai1ec-month-view {
181
  clear: both;
182
  width: 100% !important;
183
  }
184
- .ai1ec-month-view td {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  border: 1px solid #ddd !important;
186
  vertical-align: top;
187
  background: none !important;
@@ -189,13 +219,17 @@ table.ai1ec-month-view {
189
  .ai1ec-month-view td.ai1ec-empty {
190
  background: #f8f8f8 !important;
191
  }
192
- .ai1ec-month-view th {
 
 
 
193
  padding: 0.2em !important;
194
- border-bottom: 1px solid #ddd;
 
 
 
 
195
  font: bold 9pt Tahoma, Geneva, sans-serif !important;
196
- text-align: center !important;
197
- background: #f2f2f2 !important;
198
- border: none !important;
199
  color: #999 !important;
200
  text-shadow: 0 1px 0 #fff;
201
  -o-text-shadow: 0 1px 0 #fff;
@@ -203,15 +237,38 @@ table.ai1ec-month-view {
203
  -moz-text-shadow: 0 1px 0 #fff;
204
  -webkit-text-shadow: 0 1px 0 #fff;
205
  }
206
- .ai1ec-month-view td {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  padding: 0 !important;
208
  text-align: left;
209
  }
210
- .ai1ec-month-view .ai1ec-day {
 
 
211
  position: relative;
 
 
212
  min-height: 5em;
213
  }
214
- .ai1ec-month-view .ai1ec-today {
 
 
 
 
215
  background: #ffd !important;
216
  background: rgba(255,255,128,0.3) !important;
217
  }
@@ -230,36 +287,119 @@ table.ai1ec-month-view {
230
  -webkit-text-shadow: 0 1px 0 #fff;
231
  }
232
 
233
- /* Event summaries in month view, including popups */
234
- .ai1ec-month-view a.ai1ec-event-container {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  position: relative;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  font: 9pt Tahoma, Geneva, sans-serif !important;
237
  text-decoration: none !important;
238
  display: block;
239
  border: none !important;
 
 
 
 
 
240
  }
241
- .ai1ec-month-view .ai1ec-event {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  border-radius: 0.3em;
243
  -o-border-radius: 0.3em;
244
  -moz-border-radius: 0.3em;
245
  -webkit-border-radius: 0.3em;
246
- margin: 1px 1px 0 1px;
247
- padding: 0 0 1px 2px;
248
  white-space: nowrap;
249
  overflow: hidden;
250
- color: #568 !important;
251
  }
252
- .ai1ec-month-view .ai1ec-allday .ai1ec-event {
 
253
  background: #568;
254
  color: #fff !important;
255
  }
256
- .ai1ec-month-view .ai1ec-category-colors {
 
257
  float: right;
258
  font-size: 1.2em;
259
  margin-top: 1px;
260
  }
261
  .ai1ec-month-view .ai1ec-event-popup,
262
- .ai1ec-month-view .ai1ec-event-summary {
 
 
263
  border: 2px solid #d4c4b0;
264
  box-shadow: 1px 5px 8px rgba(0,0,0,0.08);
265
  -o-box-shadow: 1px 5px 8px rgba(0,0,0,0.08);
@@ -268,7 +408,8 @@ table.ai1ec-month-view {
268
  -webkit-box-shadow: 1px 5px 8px rgba(0,0,0,0.08);
269
  background: #fff;
270
  }
271
- .ai1ec-month-view .ai1ec-event-popup {
 
272
  position: absolute;
273
  z-index: 5;
274
  display: none;
@@ -282,32 +423,41 @@ table.ai1ec-month-view {
282
  -moz-border-radius: 0 0.3em 0.3em 0;
283
  -webkit-border-radius: 0 0.3em 0.3em 0;
284
  }
285
- .ai1ec-month-view .ai1ec-event-popup.ai1ec-shifted-right {
 
 
 
 
286
  border-radius: 0.3em 0 0 0.3em;
287
  -o-border-radius: 0.3em 0 0 0.3em;
288
- -ms-border-radius: 0.3em 0 0 0.3em;
289
  -moz-border-radius: 0.3em 0 0 0.3em;
290
  -webkit-border-radius: 0.3em 0 0 0.3em;
291
  }
292
- .ai1ec-month-view .ai1ec-event-popup-bg {
 
293
  position: relative;
294
  left: -2px;
295
  margin-right: -1px;
296
  padding: 2px 2px 2px 5px;
297
  background: #fff;
298
  }
299
- .ai1ec-month-view .ai1ec-shifted-right .ai1ec-event-popup-bg {
 
300
  left: 1px;
301
  padding: 2px 5px 2px 2px;
302
  }
303
  .ai1ec-month-view .ai1ec-event-summary,
304
- .ai1ec-month-view .ai1ec-event-summary p {
 
 
305
  font-size: 0.95em !important;
306
  }
307
- .ai1ec-month-view .ai1ec-event-summary p {
 
308
  line-height: 1.4em !important;
309
  }
310
- .ai1ec-month-view .ai1ec-event-summary {
 
311
  position: absolute;
312
  overflow: hidden;
313
  top: -2px;
@@ -328,22 +478,31 @@ table.ai1ec-month-view {
328
  background: -webkit-linear-gradient( #fff 1.5em, #fdfadc );
329
  color: #6d5e4a;
330
  }
331
- .ai1ec-month-view .ai1ec-shifted-right .ai1ec-event-summary {
 
332
  left: 100%;
333
  border-radius: 0 0.3em 0.3em 0.3em;
334
  -o-border-radius: 0 0.3em 0.3em 0.3em;
335
  -moz-border-radius: 0 0.3em 0.3em 0.3em;
336
  -webkit-border-radius: 0 0.3em 0.3em 0.3em;
337
  }
338
- .ai1ec-month-view .ai1ec-event-summary p {
 
339
  margin: 0 0 0.3em !important;
340
  padding: 0 !important;
341
  }
342
- .ai1ec-month-view .ai1ec-event-time {
 
343
  font-size: 8pt;
344
  font-weight: bold;
345
  }
346
- .ai1ec-month-view .ai1ec-read-more {
 
 
 
 
 
 
347
  text-align: center;
348
  font-size: 8pt;
349
  color: #aaa;
@@ -394,6 +553,41 @@ table.ai1ec-month-view {
394
  cursor: default;
395
  }
396
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
397
  /* Agenda view */
398
  .ai1ec-agenda-view {
399
  clear: both;
6
  .ai1ec-container small {
7
  margin: 0 !important;
8
  }
9
+ /* Some themes pollute table row backgrounds - this line undoes this: */
10
+ .ai1ec-container tr {
11
+ background: none !important;
12
+ }
13
 
14
  /* Active buttons */
15
  .ai1ec-action-month #ai1ec-view-month,
16
+ .ai1ec-action-week #ai1ec-view-week,
17
  .ai1ec-action-agenda #ai1ec-view-agenda {
18
  color: #000;
19
  background: #ddd;
168
  .ai1ec-action-month .ai1ec-pagination a {
169
  width: 7em;
170
  }
171
+ .ai1ec-action-week .ai1ec-pagination a {
172
+ width: 8em;
173
+ }
174
+ .ai1ec-action-agenda .ai1ec-pagination a {
175
  float: right;
176
  width: 9em;
177
  }
179
  width: 4em;
180
  }
181
 
182
+ /* Month, week tables */
183
+ table.ai1ec-month-view,
184
+ .ai1ec-week-view table {
185
  border-collapse: collapse;
186
  border: 1px solid #ddd !important;
187
  margin: 0 !important;
190
  clear: both;
191
  width: 100% !important;
192
  }
193
+ .ai1ec-week-view .tablescroll_wrapper {
194
+ position: relative;
195
+ border-bottom: 1px solid #ddd;
196
+ }
197
+ table.ai1ec-week-view-original {
198
+ visibility: hidden;
199
+ height: 400px;
200
+ }
201
+ table.ai1ec-week-view-original.tablescroll_body {
202
+ visibility: visible;
203
+ height: auto;
204
+ }
205
+ .ai1ec-week-view table.tablescroll_head,
206
+ .ai1ec-week-view table.tablescroll_head th {
207
+ border-bottom: none !important;
208
+ }
209
+ table.ai1ec-week-view-original.tablescroll_body,
210
+ table.ai1ec-week-view-original.tablescroll_body tr:first-child td {
211
+ border-top: none !important;
212
+ }
213
+ .ai1ec-month-view td,
214
+ .ai1ec-week-view td {
215
  border: 1px solid #ddd !important;
216
  vertical-align: top;
217
  background: none !important;
219
  .ai1ec-month-view td.ai1ec-empty {
220
  background: #f8f8f8 !important;
221
  }
222
+ .ai1ec-month-view th,
223
+ .ai1ec-week-view th {
224
+ border: none !important;
225
+ background: #f2f2f2 !important;
226
  padding: 0.2em !important;
227
+ }
228
+ .ai1ec-month-view th,
229
+ .ai1ec-week-view th,
230
+ .ai1ec-week-view .ai1ec-hour-marker div,
231
+ .ai1ec-week-view .ai1ec-allday-label {
232
  font: bold 9pt Tahoma, Geneva, sans-serif !important;
 
 
 
233
  color: #999 !important;
234
  text-shadow: 0 1px 0 #fff;
235
  -o-text-shadow: 0 1px 0 #fff;
237
  -moz-text-shadow: 0 1px 0 #fff;
238
  -webkit-text-shadow: 0 1px 0 #fff;
239
  }
240
+ .ai1ec-month-view th {
241
+ text-align: center !important;
242
+ }
243
+ .ai1ec-week-view th {
244
+ text-align: left !important;
245
+ }
246
+ .ai1ec-week-view th .ai1ec-weekday-date {
247
+ font-size: 10.5pt !important;
248
+ font-weight: normal !important;
249
+ }
250
+ .ai1ec-week-view th .ai1ec-weekday-day {
251
+ font-size: 9pt !important;
252
+ font-weight: normal !important;
253
+ }
254
+ .ai1ec-month-view td,
255
+ .ai1ec-week-view td {
256
  padding: 0 !important;
257
  text-align: left;
258
  }
259
+ .ai1ec-month-view .ai1ec-day,
260
+ .ai1ec-week-view .ai1ec-day,
261
+ .ai1ec-week-view .ai1ec-allday-events {
262
  position: relative;
263
+ }
264
+ .ai1ec-month-view .ai1ec-day {
265
  min-height: 5em;
266
  }
267
+ .ai1ec-week-view .ai1ec-day {
268
+ height: 1440px;
269
+ }
270
+ .ai1ec-month-view .ai1ec-today,
271
+ .ai1ec-week-view .ai1ec-today {
272
  background: #ffd !important;
273
  background: rgba(255,255,128,0.3) !important;
274
  }
287
  -webkit-text-shadow: 0 1px 0 #fff;
288
  }
289
 
290
+ /* Week view now marker */
291
+ .ai1ec-week-view .ai1ec-grid-container {
292
+ position: absolute;
293
+ top: auto;
294
+ left: 0;
295
+ right: 0;
296
+ }
297
+ .ai1ec-week-view .ai1ec-now-marker,
298
+ .ai1ec-week-view .ai1ec-hour-marker,
299
+ .ai1ec-week-view .ai1ec-quarter-marker {
300
+ position: absolute;
301
+ left: 0;
302
+ right: 0;
303
+ padding-right: 8px;
304
+ }
305
+ .ai1ec-week-view .ai1ec-hour-marker {
306
+ border-top: 1px solid #e8e8e8;
307
+ border-top: 1px solid rgba(0,0,0,0.08);
308
+ height: 60px;
309
+ background: #f2f2f2 !important;
310
+ background: rgba(0,0,0,0.054) !important;
311
+ }
312
+ .ai1ec-week-view .ai1ec-hour-marker div,
313
+ .ai1ec-week-view .ai1ec-allday-label {
314
  position: relative;
315
+ z-index: 2;
316
+ margin-left: 1px;
317
+ padding: 0 0.2em;
318
+ font-size: 8pt !important;
319
+ font-weight: normal !important;
320
+ background: #f2f2f2;
321
+ float: left;
322
+ border-radius: 0.3em;
323
+ -moz-border-radius: 0.3em;
324
+ -webkit-border-radius: 0.3em;
325
+ }
326
+ .ai1ec-week-view .ai1ec-allday-label {
327
+ margin-top: 1px;
328
+ }
329
+ .ai1ec-week-view .ai1ec-hour-marker.ai1ec-business-hour {
330
+ background: #fff !important;
331
+ background: transparent !important;
332
+ }
333
+ .ai1ec-week-view .ai1ec-quarter-marker {
334
+ border-top: 1px solid #f4f4f4;
335
+ border-top: 1px solid rgba(0,0,0,0.05);
336
+ }
337
+ .ai1ec-week-view .ai1ec-now-marker {
338
+ border-top: 1px solid #f2c539;
339
+ border-bottom: 1px solid rgba(0, 0, 0, 0.15);
340
+ z-index: 2;
341
+ }
342
+ /* Event summaries in month/week view, including popups */
343
+ .ai1ec-month-view a.ai1ec-event-container,
344
+ .ai1ec-week-view a.ai1ec-event-container {
345
  font: 9pt Tahoma, Geneva, sans-serif !important;
346
  text-decoration: none !important;
347
  display: block;
348
  border: none !important;
349
+ color: #568 !important;
350
+ }
351
+ .ai1ec-month-view a.ai1ec-event-container,
352
+ .ai1ec-week-view .ai1ec-allday-events a.ai1ec-event-container {
353
+ position: relative;
354
  }
355
+ .ai1ec-week-view .ai1ec-week a.ai1ec-event-container {
356
+ position: absolute;
357
+ box-sizing: border-box;
358
+ -o-box-sizing: border-box;
359
+ -moz-box-sizing: border-box;
360
+ -webkit-box-sizing: border-box;
361
+ right: 0;
362
+ border-radius: 0.3em;
363
+ -moz-border-radius: 0.3em;
364
+ -webkit-border-radius: 0.3em;
365
+ box-shadow: 0 3px 6px rgba(0,0,0,0.2);
366
+ -moz-box-shadow: 0 3px 6px rgba(0,0,0,0.2);
367
+ -webkit-box-shadow: 0 3px 6px rgba(0,0,0,0.2);
368
+ border: 2px solid #ced3dd !important;
369
+ background: #fff !important;
370
+ background: linear-gradient( top, #fff, #ced3dd ) !important;
371
+ background: -o-linear-gradient( top, #fff, #ced3dd ) !important;
372
+ background: -moz-linear-gradient( top, #fff, #ced3dd ) !important;
373
+ background: -webkit-gradient( linear, left top, left bottom, color-stop( 0, #fff ), color-stop( 1, #ced3dd ) ) !important;
374
+ background: -webkit-linear-gradient( top, #fff, #ced3dd ) !important;
375
+ }
376
+ .ai1ec-month-view .ai1ec-event,
377
+ .ai1ec-week-view .ai1ec-event {
378
  border-radius: 0.3em;
379
  -o-border-radius: 0.3em;
380
  -moz-border-radius: 0.3em;
381
  -webkit-border-radius: 0.3em;
382
+ margin: 1px 0 0;
383
+ padding: 0 3px 1px;
384
  white-space: nowrap;
385
  overflow: hidden;
386
+ max-height: 100%;
387
  }
388
+ .ai1ec-month-view .ai1ec-allday .ai1ec-event,
389
+ .ai1ec-week-view .ai1ec-allday .ai1ec-event {
390
  background: #568;
391
  color: #fff !important;
392
  }
393
+ .ai1ec-month-view .ai1ec-category-colors,
394
+ .ai1ec-week-view .ai1ec-category-colors {
395
  float: right;
396
  font-size: 1.2em;
397
  margin-top: 1px;
398
  }
399
  .ai1ec-month-view .ai1ec-event-popup,
400
+ .ai1ec-month-view .ai1ec-event-summary,
401
+ .ai1ec-week-view .ai1ec-event-popup,
402
+ .ai1ec-week-view .ai1ec-event-summary {
403
  border: 2px solid #d4c4b0;
404
  box-shadow: 1px 5px 8px rgba(0,0,0,0.08);
405
  -o-box-shadow: 1px 5px 8px rgba(0,0,0,0.08);
408
  -webkit-box-shadow: 1px 5px 8px rgba(0,0,0,0.08);
409
  background: #fff;
410
  }
411
+ .ai1ec-month-view .ai1ec-event-popup,
412
+ .ai1ec-week-view .ai1ec-event-popup {
413
  position: absolute;
414
  z-index: 5;
415
  display: none;
423
  -moz-border-radius: 0 0.3em 0.3em 0;
424
  -webkit-border-radius: 0 0.3em 0.3em 0;
425
  }
426
+ .ai1ec-week-view .ai1ec-week .ai1ec-event-popup {
427
+ top: -3px;
428
+ }
429
+ .ai1ec-month-view .ai1ec-event-popup.ai1ec-shifted-right,
430
+ .ai1ec-week-view .ai1ec-event-popup.ai1ec-shifted-right {
431
  border-radius: 0.3em 0 0 0.3em;
432
  -o-border-radius: 0.3em 0 0 0.3em;
 
433
  -moz-border-radius: 0.3em 0 0 0.3em;
434
  -webkit-border-radius: 0.3em 0 0 0.3em;
435
  }
436
+ .ai1ec-month-view .ai1ec-event-popup-bg,
437
+ .ai1ec-week-view .ai1ec-event-popup-bg {
438
  position: relative;
439
  left: -2px;
440
  margin-right: -1px;
441
  padding: 2px 2px 2px 5px;
442
  background: #fff;
443
  }
444
+ .ai1ec-month-view .ai1ec-shifted-right .ai1ec-event-popup-bg,
445
+ .ai1ec-week-view .ai1ec-shifted-right .ai1ec-event-popup-bg {
446
  left: 1px;
447
  padding: 2px 5px 2px 2px;
448
  }
449
  .ai1ec-month-view .ai1ec-event-summary,
450
+ .ai1ec-month-view .ai1ec-event-summary p,
451
+ .ai1ec-week-view .ai1ec-event-summary,
452
+ .ai1ec-week-view .ai1ec-event-summary p {
453
  font-size: 0.95em !important;
454
  }
455
+ .ai1ec-month-view .ai1ec-event-summary p,
456
+ .ai1ec-week-view .ai1ec-event-summary p {
457
  line-height: 1.4em !important;
458
  }
459
+ .ai1ec-month-view .ai1ec-event-summary,
460
+ .ai1ec-week-view .ai1ec-event-summary {
461
  position: absolute;
462
  overflow: hidden;
463
  top: -2px;
478
  background: -webkit-linear-gradient( #fff 1.5em, #fdfadc );
479
  color: #6d5e4a;
480
  }
481
+ .ai1ec-month-view .ai1ec-shifted-right .ai1ec-event-summary,
482
+ .ai1ec-week-view .ai1ec-shifted-right .ai1ec-event-summary {
483
  left: 100%;
484
  border-radius: 0 0.3em 0.3em 0.3em;
485
  -o-border-radius: 0 0.3em 0.3em 0.3em;
486
  -moz-border-radius: 0 0.3em 0.3em 0.3em;
487
  -webkit-border-radius: 0 0.3em 0.3em 0.3em;
488
  }
489
+ .ai1ec-month-view .ai1ec-event-summary p,
490
+ .ai1ec-week-view .ai1ec-event-summary p {
491
  margin: 0 0 0.3em !important;
492
  padding: 0 !important;
493
  }
494
+ .ai1ec-month-view .ai1ec-event-time,
495
+ .ai1ec-week-view .ai1ec-event-time {
496
  font-size: 8pt;
497
  font-weight: bold;
498
  }
499
+ .ai1ec-week-view .ai1ec-week .ai1ec-event-title {
500
+ display: block;
501
+ white-space: normal;
502
+ font-size: 8pt !important;
503
+ }
504
+ .ai1ec-month-view .ai1ec-read-more,
505
+ .ai1ec-week-view .ai1ec-read-more {
506
  text-align: center;
507
  font-size: 8pt;
508
  color: #aaa;
553
  cursor: default;
554
  }
555
 
556
+ /* Truncated events */
557
+ .ai1ec-week-view .ai1ec-start-truncated .ai1ec-event-time {
558
+ display: none;
559
+ }
560
+ .ai1ec-week-view .ai1ec-week a.ai1ec-event-container.ai1ec-start-truncated {
561
+ border-top-left-radius: 0;
562
+ border-top-right-radius: 0;
563
+ -moz-border-radius-topleft: 0;
564
+ -moz-border-radius-topright: 0;
565
+ -webkit-border-top-left-radius: 0;
566
+ -webkit-border-top-right-radius: 0;
567
+ }
568
+ .ai1ec-week-view .ai1ec-week .ai1ec-start-truncator {
569
+ position: absolute;
570
+ top: -2px;
571
+ left: -2px;
572
+ line-height: 1em;
573
+ font-size: 6pt;
574
+ }
575
+ .ai1ec-week-view .ai1ec-week a.ai1ec-event-container.ai1ec-end-truncated {
576
+ border-bottom-left-radius: 0;
577
+ border-bottom-right-radius: 0;
578
+ -moz-border-radius-bottomleft: 0;
579
+ -moz-border-radius-bottomright: 0;
580
+ -webkit-border-bottom-left-radius: 0;
581
+ -webkit-border-bottom-right-radius: 0;
582
+ }
583
+ .ai1ec-week-view .ai1ec-week .ai1ec-end-truncator {
584
+ position: absolute;
585
+ bottom: -2px;
586
+ right: -2px;
587
+ line-height: 1em;
588
+ font-size: 6pt;
589
+ }
590
+
591
  /* Agenda view */
592
  .ai1ec-agenda-view {
593
  clear: both;
img/week-view.png ADDED
Binary file
js/calendar.js CHANGED
@@ -1,21 +1,22 @@
1
- //Used to ensure that Entities used in L10N strings are correct
2
- function ai1ec_convert_entities(o) {
3
  var c, v;
4
- c = function(s) {
5
- if (/&[^;]+;/.test(s)) {
6
- var e = document.createElement("div");
 
7
  e.innerHTML = s;
8
- return !e.firstChild ? s : e.firstChild.nodeValue;
9
  }
10
  return s;
11
  }
12
 
13
- if ( typeof o === 'string' ) {
14
- return c(o);
15
- } else if ( typeof o === 'object' ) {
16
- for (v in o) {
17
- if ( typeof o[v] === 'string' ) {
18
- o[v] = c(o[v]);
19
  }
20
  }
21
  }
@@ -149,7 +150,7 @@ jQuery( document ).ready( function( $ ) {
149
  load_view( $(this).attr( 'href' ) );
150
  } );
151
 
152
- // *** Month view ***
153
 
154
  /**
155
  * Callback for mouseenter event on .ai1ec-event element
@@ -196,22 +197,35 @@ jQuery( document ).ready( function( $ ) {
196
  .data( 'ai1ec_mouseinside', false );
197
  }
198
 
199
- // Register popup hover handlers for month view
200
- $('.ai1ec-month-view .ai1ec-event')
201
  .live( 'mouseenter', show_popup );
202
- $('.ai1ec-month-view .ai1ec-event-popup')
203
  .live( 'mouseleave', hide_popup )
204
  .live( 'mousemove', function() {
205
  // Track whether popup contains mouse cursor
206
  $(this).data( 'ai1ec_mouseinside', true );
207
  } );
208
  // Hide any popups that were visible when the window lost focus
209
- if( $('.ai1ec-month-view').length ) {
210
  $(window).blur( function() {
211
  $('.ai1ec-event-popup:visible').each( hide_popup );
212
  } );
213
  }
214
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  /**
216
  * Trims date boxes for which there are too many listed events.
217
  */
@@ -539,12 +553,16 @@ jQuery( document ).ready( function( $ ) {
539
  } );
540
  post_ids = post_ids.join(); // Store IDs as comma-separated values
541
 
 
 
 
542
  // ===========================
543
  // = Pop up the active event =
544
  // ===========================
545
  if( $( '.ai1ec-active-event:first' ).length ) {
546
- // Pop up any active event in month view
547
- $( '.ai1ec-month-view .ai1ec-active-event:first' ).each( function() {
 
548
  $(this)
549
  .each( show_popup )
550
  .prev() // .ai1ec-popup
@@ -562,11 +580,15 @@ jQuery( document ).ready( function( $ ) {
562
  }
563
  );
564
  }
 
 
 
 
565
 
566
  // Apply category/tag filters if any; hide all events by default, then fade
567
  // in filtered ones.
568
  if( $('.ai1ec-dropdown.ai1ec-selected').length ) {
569
- $('.ai1ec-month-view .ai1ec-event-container, .ai1ec-agenda-view .ai1ec-event').hide();
570
  apply_filters();
571
  } else {
572
  // Else do month view trimming
1
+ // Used to ensure that Entities used in L10N strings are correct
2
+ function ai1ec_convert_entities( o ) {
3
  var c, v;
4
+
5
+ c = function( s ) {
6
+ if( /&[^;]+;/.test( s ) ) {
7
+ var e = document.createElement( 'div' );
8
  e.innerHTML = s;
9
+ return ! e.firstChild ? s : e.firstChild.nodeValue;
10
  }
11
  return s;
12
  }
13
 
14
+ if( typeof o === 'string' ) {
15
+ return c( o );
16
+ } else if( typeof o === 'object' ) {
17
+ for( v in o ) {
18
+ if( typeof o[v] === 'string' ) {
19
+ o[v] = c( o[v] );
20
  }
21
  }
22
  }
150
  load_view( $(this).attr( 'href' ) );
151
  } );
152
 
153
+ // *** Month/week views ***
154
 
155
  /**
156
  * Callback for mouseenter event on .ai1ec-event element
197
  .data( 'ai1ec_mouseinside', false );
198
  }
199
 
200
+ // Register popup hover handlers for month/week views
201
+ $('.ai1ec-month-view .ai1ec-event, .ai1ec-week-view .ai1ec-event')
202
  .live( 'mouseenter', show_popup );
203
+ $('.ai1ec-month-view .ai1ec-event-popup, .ai1ec-week-view .ai1ec-event-popup')
204
  .live( 'mouseleave', hide_popup )
205
  .live( 'mousemove', function() {
206
  // Track whether popup contains mouse cursor
207
  $(this).data( 'ai1ec_mouseinside', true );
208
  } );
209
  // Hide any popups that were visible when the window lost focus
210
+ if( $('.ai1ec-month-view, .ai1ec-week-view').length ) {
211
  $(window).blur( function() {
212
  $('.ai1ec-event-popup:visible').each( hide_popup );
213
  } );
214
  }
215
 
216
+ // ================================
217
+ // = Week view hover-raise effect =
218
+ // ================================
219
+ $( '.ai1ec-week-view .ai1ec-week a.ai1ec-event-container' )
220
+ .live( 'mouseenter',
221
+ function() {
222
+ $(this).delay( 500 ).queue( function() { $(this).css( 'z-index', 5 ) } );
223
+ } )
224
+ .live( 'mouseleave',
225
+ function() {
226
+ $(this).clearQueue().css( 'z-index', 'auto' );
227
+ } );
228
+
229
  /**
230
  * Trims date boxes for which there are too many listed events.
231
  */
553
  } );
554
  post_ids = post_ids.join(); // Store IDs as comma-separated values
555
 
556
+ // Make week view table scrollable
557
+ $( 'table.ai1ec-week-view-original' ).tableScroll( { height: 400, containerClass: 'ai1ec-week-view' } );
558
+
559
  // ===========================
560
  // = Pop up the active event =
561
  // ===========================
562
  if( $( '.ai1ec-active-event:first' ).length ) {
563
+ // Pop up any active event in month/week view views
564
+ $( '.ai1ec-month-view .ai1ec-active-event:first, .ai1ec-week-view .ai1ec-active-event:first' )
565
+ .each( function() {
566
  $(this)
567
  .each( show_popup )
568
  .prev() // .ai1ec-popup
580
  }
581
  );
582
  }
583
+ else if( $( '.ai1ec-week-view' ).length ) {
584
+ // If no active event, then in week view, scroll down to 6am.
585
+ $( '.ai1ec-week-view .tablescroll_wrapper' ).scrollTo( '.ai1ec-hour-marker:eq(6)' );
586
+ }
587
 
588
  // Apply category/tag filters if any; hide all events by default, then fade
589
  // in filtered ones.
590
  if( $('.ai1ec-dropdown.ai1ec-selected').length ) {
591
+ $('.ai1ec-month-view .ai1ec-event-container, .ai1ec-week-view .ai1ec-event-container, .ai1ec-agenda-view .ai1ec-event').hide();
592
  apply_filters();
593
  } else {
594
  // Else do month view trimming
js/jquery.tablescroll.js ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+
3
+ Copyright (c) 2009 Dimas Begunoff, http://www.farinspace.com
4
+
5
+ Licensed under the MIT license
6
+ http://en.wikipedia.org/wiki/MIT_License
7
+
8
+ Permission is hereby granted, free of charge, to any person
9
+ obtaining a copy of this software and associated documentation
10
+ files (the "Software"), to deal in the Software without
11
+ restriction, including without limitation the rights to use,
12
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the
14
+ Software is furnished to do so, subject to the following
15
+ conditions:
16
+
17
+ The above copyright notice and this permission notice shall be
18
+ included in all copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27
+ OTHER DEALINGS IN THE SOFTWARE.
28
+
29
+ */
30
+
31
+ ;(function($){
32
+
33
+ var scrollbarWidth = 0;
34
+
35
+ // http://jdsharp.us/jQuery/minute/calculate-scrollbar-width.php
36
+ function getScrollbarWidth()
37
+ {
38
+ if (scrollbarWidth) return scrollbarWidth;
39
+ var div = $('<div style="width:50px;height:50px;overflow:hidden;position:absolute;top:-200px;left:-200px;"><div style="height:100px;"></div></div>');
40
+ $('body').append(div);
41
+ var w1 = $('div', div).innerWidth();
42
+ div.css('overflow-y', 'auto');
43
+ var w2 = $('div', div).innerWidth();
44
+ $(div).remove();
45
+ scrollbarWidth = (w1 - w2);
46
+ return scrollbarWidth;
47
+ }
48
+
49
+ $.fn.tableScroll = function(options)
50
+ {
51
+ if (options == 'undo')
52
+ {
53
+ var container = $(this).parent().parent();
54
+ if (container.hasClass('tablescroll_wrapper'))
55
+ {
56
+ container.find('.tablescroll_head thead').prependTo(this);
57
+ container.find('.tablescroll_foot tfoot').appendTo(this);
58
+ container.before(this);
59
+ container.empty();
60
+ }
61
+ return;
62
+ }
63
+
64
+ var settings = $.extend({},$.fn.tableScroll.defaults,options);
65
+
66
+ // Bail out if there's no vertical overflow
67
+ //if ($(this).height() <= settings.height)
68
+ //{
69
+ // return this;
70
+ //}
71
+
72
+ settings.scrollbarWidth = getScrollbarWidth();
73
+
74
+ this.each(function()
75
+ {
76
+ var flush = settings.flush;
77
+
78
+ var tb = $(this).addClass('tablescroll_body');
79
+
80
+ var wrapper = $('<div class="tablescroll_wrapper"></div>').insertBefore(tb).append(tb);
81
+
82
+ // check for a predefined container
83
+ if (!wrapper.parent('div').hasClass(settings.containerClass))
84
+ {
85
+ $('<div></div>').addClass(settings.containerClass).insertBefore(wrapper).append(wrapper);
86
+ }
87
+
88
+ var width = settings.width ? settings.width : tb.outerWidth();
89
+
90
+ wrapper.css
91
+ ({
92
+ 'width': width+'px',
93
+ 'height': settings.height+'px',
94
+ 'overflow': 'auto'
95
+ });
96
+
97
+ tb.css('width',width+'px');
98
+
99
+ // with border difference
100
+ var wrapper_width = wrapper.outerWidth();
101
+ var diff = wrapper_width-width;
102
+
103
+ // assume table will scroll
104
+ wrapper.css({width:((width-diff)+settings.scrollbarWidth)+'px'});
105
+ tb.css('width',(width-diff)+'px');
106
+
107
+ if (tb.outerHeight() <= settings.height)
108
+ {
109
+ wrapper.css({height:'auto',width:(width-diff)+'px'});
110
+ flush = false;
111
+ }
112
+
113
+ // using wrap does not put wrapper in the DOM right
114
+ // away making it unavailable for use during runtime
115
+ // tb.wrap(wrapper);
116
+
117
+ // possible speed enhancements
118
+ var has_thead = $('thead',tb).length ? true : false ;
119
+ var has_tfoot = $('tfoot',tb).length ? true : false ;
120
+ var thead_tr_first = $('thead tr:first',tb);
121
+ var tbody_tr_first = $('tbody tr:first',tb);
122
+ var tfoot_tr_first = $('tfoot tr:first',tb);
123
+
124
+ // remember width of last cell
125
+ var w = 0;
126
+
127
+ $('th, td',thead_tr_first).each(function(i)
128
+ {
129
+ w = $(this).width();
130
+
131
+ $('th:eq('+i+'), td:eq('+i+')',thead_tr_first).css('width',w+'px');
132
+ $('th:eq('+i+'), td:eq('+i+')',tbody_tr_first).css('width',w+'px');
133
+ if (has_tfoot) $('th:eq('+i+'), td:eq('+i+')',tfoot_tr_first).css('width',w+'px');
134
+ });
135
+
136
+ if (has_thead)
137
+ {
138
+ var tbh = $('<table class="tablescroll_head" cellspacing="0"></table>').insertBefore(wrapper).prepend($('thead',tb));
139
+ }
140
+
141
+ if (has_tfoot)
142
+ {
143
+ var tbf = $('<table class="tablescroll_foot" cellspacing="0"></table>').insertAfter(wrapper).prepend($('tfoot',tb));
144
+ }
145
+
146
+ if (tbh != undefined)
147
+ {
148
+ tbh.css('width',width+'px');
149
+
150
+ if (flush)
151
+ {
152
+ $('tr:first th:last, tr:first td:last',tbh).css('width',(w+settings.scrollbarWidth)+'px');
153
+ tbh.css('width',wrapper.outerWidth() + 'px');
154
+ }
155
+ }
156
+
157
+ if (tbf != undefined)
158
+ {
159
+ tbf.css('width',width+'px');
160
+
161
+ if (flush)
162
+ {
163
+ $('tr:first th:last, tr:first td:last',tbf).css('width',(w+settings.scrollbarWidth)+'px');
164
+ tbf.css('width',wrapper.outerWidth() + 'px');
165
+ }
166
+ }
167
+ });
168
+
169
+ return this;
170
+ };
171
+
172
+ // public
173
+ $.fn.tableScroll.defaults =
174
+ {
175
+ flush: true, // makes the last thead and tbody column flush with the scrollbar
176
+ width: null, // width of the table (head, body and foot), null defaults to the tables natural width
177
+ height: 100, // height of the scrollable area
178
+ containerClass: 'tablescroll' // the plugin wraps the table in a div with this css class
179
+ };
180
+
181
+ })(jQuery);
readme.txt CHANGED
@@ -4,31 +4,31 @@ Donate link: http://theseednetwork.com/software/all-in-one-event-calendar-wordpr
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, widget, events widget, upcoming events widget, calendar widget, agenda widget
5
  Requires at least: 3.1.3
6
  Tested up to: 3.2.1
7
- Stable tag: 1.1.3
8
 
9
- An event calendar system with month and agenda views, upcoming events widget, color-coded categories, recurrence, and import/export of .ics feeds.
10
 
11
  == Description ==
12
 
13
- Welcome to the [All-in-One Event Calendar Plugin](http://theseednetwork.com/software/all-in-one-event-calendar-wordpress/), from [The Seed Studio](http://theseednetwork.com), a [web development](http://theseednetwork.com) company. The All-in-One Event Calendar is 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
- * Upcoming Events widget
29
- * Links to filtered calendar views
30
-
31
- (* Limited recurrence patterns available in first release. Full support to come in the next release.)
32
 
33
  = Features for Website and Blog Owners =
34
 
@@ -48,63 +48,67 @@ The All-in-One Event Calendar Plugin also has a few features that will prove use
48
  * Each event links to the original calendar
49
  * Your calendar can be embedded into a WordPress page without needing to create template files or modify the theme
50
 
51
- [Check out the DEMO »](http://demo.theseedstudio.com/)
52
 
53
- [Track the development process »](http://trac.the-seed.ca/roadmap)
54
 
55
- [Bug reports (only for registered users) »](http://trac.the-seed.ca/newticket)
56
 
57
- [Get Premium Support »](http://theseednetwork.com/get-supported) from [The Seed Studio »](http://theseednetwork.com/)
58
 
59
  == Changelog ==
 
 
 
 
60
  = Version 1.1.3 =
61
- * Fixed: last date issue for recurring events "until" end date [#147](http://trac.the-seed.ca/ticket/147)
62
  * Fixed an issue with settings page not saving changes.
63
  * Fixed issues when subscribing to calendars.
64
- * Export only published events [#95](http://trac.the-seed.ca/ticket/95)
65
- * Added translation patch. Thank you josjo! [#150](http://trac.the-seed.ca/ticket/150)
66
- * Add language and region awareness in functions for Google Map. Thank you josjo! [#102](http://trac.the-seed.ca/ticket/102)
67
- * Small translation error in class-ai1ec-app-helper.php. Thank you josjo! [#94](http://trac.the-seed.ca/ticket/94)
68
- * Added Dutch, Spanish, and Swedish translations. For up to date language files, visit: [http://trac.the-seed.ca/ticket/78](http://trac.the-seed.ca/ticket/78)
69
 
70
  = Version 1.1.2 =
71
- * Fixed: Problem in repeat UI when selecting months before October [#136](http://trac.the-seed.ca/ticket/136)
72
- * Fixed: Append instance_id only to events permalink [#140](http://trac.the-seed.ca/ticket/140)
73
- * Fixed: Events ending on date problem [#141](http://trac.the-seed.ca/ticket/141)
74
  * Feature: Added French translations
75
 
76
  = Version 1.1.1 =
77
  * Fixes a problem when plugin is enabled for first time
78
 
79
  = Version 1.1 =
80
- * Feature: New recurrence UI when adding events [#40](http://trac.the-seed.ca/ticket/40)
81
- * Feature: Translate recurrence rule to Human readable format that allows localization [#40](http://trac.the-seed.ca/ticket/40)
82
- * Feature: Add Filter by Categories, Tags to Widget [#44](http://trac.the-seed.ca/ticket/44)
83
- * Feature: Add option to keep all events expanded in the agenda view [#33](http://trac.the-seed.ca/ticket/33)
84
- * Feature: Make it possible to globalize the date picker. Thank you josjo! [#52](http://trac.the-seed.ca/ticket/52)
85
- * Fixed: On recurring events show the date time of the current event and NOT the original event [#39](http://trac.the-seed.ca/ticket/39)
86
- * Fixed: Events posted in Standard time from Daylight Savings Time are wrong [#42](http://trac.the-seed.ca/ticket/42)
87
- * Fixed: Multi-day Events listing twice [#56](http://trac.the-seed.ca/ticket/56)
88
- * Fixed: %e is not supported in gmstrftime on Windows [#53](http://trac.the-seed.ca/ticket/53)
89
- * Improved: IE9 Support [#11](http://trac.the-seed.ca/ticket/11)
90
- * Improved: Corrected as many as possible HTML validation errors [#9](http://trac.the-seed.ca/ticket/9)
91
  * Improved: Optimization changes for better performance.
92
 
93
  = Version 1.0.9 =
94
  * Fixed a problem with timezone dropdown list
95
 
96
  = Version 1.0.8 =
97
- * Added better if not full localization support [#25](http://trac.the-seed.ca/ticket/25) [#23](http://trac.the-seed.ca/ticket/23) [#10](http://trac.the-seed.ca/ticket/10) - thank you josjo
98
- * Added qTranslate support and output to post data using WordPress filters [#1](http://trac.the-seed.ca/ticket/1)
99
- * Added uninstall support [#7](http://trac.the-seed.ca/ticket/7)
100
- * Added 24h time in time pickers [#26](http://trac.the-seed.ca/ticket/26) - thank you josjo
101
- * Fixed an issue when event duration time is decremented in single (detailed) view [#2](http://trac.the-seed.ca/ticket/2)
102
- * Fixed an issue with times for ics imported events [#6](http://trac.the-seed.ca/ticket/6)
103
- * Better timezone control [#27](http://trac.the-seed.ca/ticket/27)
104
- * Fixed the category filter in agenda view [#12](http://trac.the-seed.ca/ticket/12)
105
- * Fixed event date being set to null when using quick edit [#16](http://trac.the-seed.ca/ticket/16)
106
- * Fixed a bug in time pickers [#17](http://trac.the-seed.ca/ticket/17) - thank you josjo
107
- * Deprecated function split() is removed [#8](http://trac.the-seed.ca/ticket/8)
108
 
109
  = Version 1.0.7 =
110
  * Fixed issue with some MySQL version
@@ -176,10 +180,11 @@ To place the calendar in a DOM/HTML element besides the default page content con
176
  6. Front-end: Month view of calendar with mouse cursor hovering over event
177
  7. Front-end: Month view of calendar with active category filter
178
  8. Front-end: Month view of calendar with active tag filter
179
- 9. Front-end: Agenda view of calendar
180
- 10. Settings page
181
- 11. Upcoming Events widget
182
- 12. Upcoming Events widget - configuration options
 
183
 
184
  == Upgrade Notice ==
185
 
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, widget, events widget, upcoming events widget, calendar widget, agenda widget
5
  Requires at least: 3.1.3
6
  Tested up to: 3.2.1
7
+ Stable tag: 1.2
8
 
9
+ An event calendar system with month, week & agenda views, upcoming events widget, color-coded categories, recurrence, and import/export of .ics feeds.
10
 
11
  == Description ==
12
 
13
+ Welcome to the [All-in-One Event Calendar Plugin](http://theseednetwork.com/software/all-in-one-event-calendar-wordpress/), from [The Seed Network](http://theseednetwork.com/), a web development company. The All-in-One Event Calendar is 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 a powerful set of features to create the most advanced calendar system available for WordPress. Best of all: it’s completely free.
16
+
17
+ **New in version 1.2:** A beautifully rendered, scrolling **Week view**!
18
 
19
  = Calendar Features For Users =
20
 
21
  This plugin has many features we hope will prove useful to users, including:
22
 
23
+ * **Recurring** events
24
+ * **Filtering** by event category or tag
25
+ * Easy **sharing** with Google Calendar, Apple iCal, MS Outlook and any other system that accepts iCalendar (.ics) feeds
26
+ * Embedded **Google Maps**
27
+ * **Color-coded** events based on category
28
+ * **Event-registration** ready
29
+ * **Month**, **week** and **agenda** views
30
+ * **Upcoming Events** widget
31
+ * Direct links to **filtered calendar views**
 
 
32
 
33
  = Features for Website and Blog Owners =
34
 
48
  * Each event links to the original calendar
49
  * Your calendar can be embedded into a WordPress page without needing to create template files or modify the theme
50
 
51
+ **[Check out the DEMO »](http://demo.theseednetwork.com/)**
52
 
53
+ **[Track the development process »](http://trac.theseednetwork.com/roadmap)**
54
 
55
+ **[Bug reports »](http://trac.theseednetwork.com/newticket)** ([registration](http://trac.theseednetwork.com/register) required)
56
 
57
+ **[Get Premium Support »](http://theseednetwork.com/get-supported)** from [The Seed Studio](http://theseednetwork.com/)
58
 
59
  == Changelog ==
60
+ = Version 1.2 =
61
+ * Added scrollable Week view [#117](http://trac.the-seed.ca/ticket/117)
62
+ * Fixed some notice-level errors
63
+
64
  = Version 1.1.3 =
65
+ * Fixed: last date issue for recurring events "until" end date [#147](http://trac.theseednetwork.com/ticket/147)
66
  * Fixed an issue with settings page not saving changes.
67
  * Fixed issues when subscribing to calendars.
68
+ * Export only published events [#95](http://trac.theseednetwork.com/ticket/95)
69
+ * Added translation patch. Thank you josjo! [#150](http://trac.theseednetwork.com/ticket/150)
70
+ * Add language and region awareness in functions for Google Map. Thank you josjo! [#102](http://trac.theseednetwork.com/ticket/102)
71
+ * Small translation error in class-ai1ec-app-helper.php. Thank you josjo! [#94](http://trac.theseednetwork.com/ticket/94)
72
+ * Added Dutch, Spanish, and Swedish translations. For up to date language files, visit [ticket #78](http://trac.theseednetwork.com/ticket/78).
73
 
74
  = Version 1.1.2 =
75
+ * Fixed: Problem in repeat UI when selecting months before October [#136](http://trac.theseednetwork.com/ticket/136)
76
+ * Fixed: Append instance_id only to events permalink [#140](http://trac.theseednetwork.com/ticket/140)
77
+ * Fixed: Events ending on date problem [#141](http://trac.theseednetwork.com/ticket/141)
78
  * Feature: Added French translations
79
 
80
  = Version 1.1.1 =
81
  * Fixes a problem when plugin is enabled for first time
82
 
83
  = Version 1.1 =
84
+ * Feature: New recurrence UI when adding events [#40](http://trac.theseednetwork.com/ticket/40)
85
+ * Feature: Translate recurrence rule to Human readable format that allows localization [#40](http://trac.theseednetwork.com/ticket/40)
86
+ * Feature: Add Filter by Categories, Tags to Widget [#44](http://trac.theseednetwork.com/ticket/44)
87
+ * Feature: Add option to keep all events expanded in the agenda view [#33](http://trac.theseednetwork.com/ticket/33)
88
+ * Feature: Make it possible to globalize the date picker. Thank you josjo! [#52](http://trac.theseednetwork.com/ticket/52)
89
+ * Fixed: On recurring events show the date time of the current event and NOT the original event [#39](http://trac.theseednetwork.com/ticket/39)
90
+ * Fixed: Events posted in Standard time from Daylight Savings Time are wrong [#42](http://trac.theseednetwork.com/ticket/42)
91
+ * Fixed: Multi-day Events listing twice [#56](http://trac.theseednetwork.com/ticket/56)
92
+ * Fixed: %e is not supported in gmstrftime on Windows [#53](http://trac.theseednetwork.com/ticket/53)
93
+ * Improved: IE9 Support [#11](http://trac.theseednetwork.com/ticket/11)
94
+ * Improved: Corrected as many as possible HTML validation errors [#9](http://trac.theseednetwork.com/ticket/9)
95
  * Improved: Optimization changes for better performance.
96
 
97
  = Version 1.0.9 =
98
  * Fixed a problem with timezone dropdown list
99
 
100
  = Version 1.0.8 =
101
+ * Added better if not full localization support [#25](http://trac.theseednetwork.com/ticket/25) [#23](http://trac.theseednetwork.com/ticket/23) [#10](http://trac.theseednetwork.com/ticket/10) - thank you josjo
102
+ * Added qTranslate support and output to post data using WordPress filters [#1](http://trac.theseednetwork.com/ticket/1)
103
+ * Added uninstall support [#7](http://trac.theseednetwork.com/ticket/7)
104
+ * Added 24h time in time pickers [#26](http://trac.theseednetwork.com/ticket/26) - thank you josjo
105
+ * Fixed an issue when event duration time is decremented in single (detailed) view [#2](http://trac.theseednetwork.com/ticket/2)
106
+ * Fixed an issue with times for ics imported events [#6](http://trac.theseednetwork.com/ticket/6)
107
+ * Better timezone control [#27](http://trac.theseednetwork.com/ticket/27)
108
+ * Fixed the category filter in agenda view [#12](http://trac.theseednetwork.com/ticket/12)
109
+ * Fixed event date being set to null when using quick edit [#16](http://trac.theseednetwork.com/ticket/16)
110
+ * Fixed a bug in time pickers [#17](http://trac.theseednetwork.com/ticket/17) - thank you josjo
111
+ * Deprecated function split() is removed [#8](http://trac.theseednetwork.com/ticket/8)
112
 
113
  = Version 1.0.7 =
114
  * Fixed issue with some MySQL version
180
  6. Front-end: Month view of calendar with mouse cursor hovering over event
181
  7. Front-end: Month view of calendar with active category filter
182
  8. Front-end: Month view of calendar with active tag filter
183
+ 9. Front-end: Week view of calendar
184
+ 10. Front-end: Agenda view of calendar
185
+ 11. Settings page
186
+ 12. Upcoming Events widget
187
+ 13. Upcoming Events widget - configuration options
188
 
189
  == Upgrade Notice ==
190
 
screenshot-10.png CHANGED
Binary file
screenshot-11.png CHANGED
Binary file
screenshot-12.png CHANGED
Binary file
screenshot-13.png ADDED
Binary file
screenshot-9.png CHANGED
Binary file