Events Manager - Version 5.9.4

Version Description

  • added em_rewrite_rules_array filter for final permalink rule manipulation
  • fixed privacy consent blocking certain actions such as single booking button and admin-side submissions
  • fixed fatal errors when showing the consent checkbox in WordPress 5.9.5 and earlier
  • fixed the quick booking button not working in ajax search results
  • fixed privacy policy consent form label not being translatable for multilingual sites
  • fixed inconsistent date headers in certain situations with UTC manual offset dates
  • fixed incorrect link to .eot dashicon file for IE11
  • added anonymous submitter data to locations for new event submissions and integrated this with GDPR export/erase tools
  • fixed location slug blanks when directly published from front-end via submitting an event
  • added default ical and rss feed limits to avoid overloading as number of events grow
  • corrected docs to include 'recurrences' search attribute
  • added timezone pre-formatting to functions that produced incorrect output for timezone date placeholders
  • fixed default categories not being applied for events with no categories
  • fixed locations being selectable for events in other blogs within a multisite global mode when locations don't all reside on main blog
Download this release

Release Info

Developer netweblogic
Plugin Icon 128x128 Events Manager
Version 5.9.4
Comparing to
See all releases

Code changes from version 5.9.3 to 5.9.4

admin/em-admin.php CHANGED
@@ -91,7 +91,7 @@ function em_admin_dashicon(){
91
  <style type="text/css">
92
  @font-face {
93
  font-family: 'em_dashicons';
94
- src: url('../fonts/em-dashicons.eot'); // this is for IE
95
  }
96
  @font-face {
97
  font-family: 'em_dashicons';
91
  <style type="text/css">
92
  @font-face {
93
  font-family: 'em_dashicons';
94
+ src: url('<?php echo EM_DIR_URI; ?>includes/fonts/em-dashicons.eot'); // this is for IE
95
  }
96
  @font-face {
97
  font-family: 'em_dashicons';
admin/em-data-privacy.php CHANGED
@@ -112,14 +112,16 @@ class EM_Data_Privacy {
112
  $done = true;
113
  }else{
114
  //get event IDs submitted by user or "anonymously" by email
115
- $events = self::get_events($email_address, $page, $post_type);
116
 
117
  foreach( $events as $post_id ){
118
  $EM_Event = em_get_event($post_id, 'post_id');
119
  //erase the location first
120
  $EM_Location = $EM_Event->get_location();
121
  if( $EM_Location->location_id && get_option('dbem_data_privacy_erase_locations', 2) ){
122
- if( ($user !== false && $EM_Location->location_owner == $user->ID) || ($user === false && $EM_Location->location_owner == get_option('dbem_events_anonymous_user')) ){
 
 
123
  //depending on settings delete location only if it has no other events (eventually if we're deleting the last event of this user with the same location, it'll only have one event)
124
  if( get_option('dbem_data_privacy_erase_locations', 2) == 1 || EM_Events::count(array('location_id' => $EM_Location->location_id, 'status' => 'all')) <= 1 ){
125
  if( $EM_Location->delete(true) ){
@@ -150,30 +152,25 @@ class EM_Data_Privacy {
150
  }
151
 
152
  public static function erase_locations( $email_address, $page = 1 ) {
153
- $user = get_user_by('email', $email_address); //is user or no-user?
154
  $page = (int) $page;
155
  $limit = apply_filters('em_data_privacy_erase_limit', 100);
156
- $offset = ($page -1) * $limit;
157
  $items_removed = $items_retained = false;
158
  $messages = array();
159
-
160
- if( get_option('dbem_data_privacy_erase_locations') == 2 ){ //for locations, we currently don't store anon info about locations
161
- //we're only deleting anonymous events, and letting WP handle CPT deletion the traditional way for registered accounts
162
- $done = true;
163
- }elseif( $user !== false ){
164
- $locations_count = 0;
165
- foreach( EM_Locations::get(array('owner' => $user->ID, 'limit' => $limit, 'offset' => $offset, 'status'=>'everything')) as $EM_Location ){ /* @var EM_Location $EM_Location */
166
- if( $EM_Location->delete(true) ){
167
- $items_removed = true;
168
- }else{
169
- $items_retained = true;
170
- $messages = array_merge($messages, $EM_Location->get_errors());
171
- }
172
- $locations_count++;
173
  }
174
- // Tell core if we have more comments to work on still
175
- $done = $locations_count < $limit;
176
  }
 
 
177
  return array(
178
  'items_removed' => $items_removed,
179
  'items_retained' => $items_retained, // always false in this example
@@ -360,7 +357,7 @@ class EM_Data_Privacy {
360
  $event_export_default['group_label'] = $post_type == 'event-recurring' ? __('Recurring Events', 'events-manager'):__('Events', 'events-manager');
361
 
362
  //get event IDs submitted by user or "anonymously" by email
363
- $events = self::get_events($email_address, $page, $post_type);
364
 
365
  foreach( $events as $post_id ){
366
  $EM_Event = em_get_event($post_id, 'post_id');
@@ -383,8 +380,10 @@ class EM_Data_Privacy {
383
  $export_item['data'][] = array('name' => __('Location','events-manager'), 'value' => $EM_Location->location_name . ', '. $EM_Location->get_full_address() .', '. $EM_Location->location_country);
384
  //put this location as a new export item
385
  $already_exported = in_array($EM_Location->location_id, $locations_exported);
386
- $user_owns_location = ($user !== false && $EM_Location->location_owner == $user->ID) || ($user === false && $EM_Location->location_owner == get_option('dbem_events_anonymous_user'));
387
- if( !$already_exported && $user_owns_location ){
 
 
388
  $location_export_item = $locations_export_default;
389
  $location_export_item['item_id'] = 'location-post-'.$EM_Location->post_id;
390
  $location_export_item['data'][] = array('name' => __('Name','events-manager'), 'value' => $EM_Location->location_name );
@@ -439,9 +438,6 @@ class EM_Data_Privacy {
439
  $export_items = array();
440
  $items_count = 0;
441
 
442
- //check if we're only exporting locations to those who submitted anonymously - eventual since currently it's not possible to submit anonymously
443
- if( $user !== false && get_option('dbem_data_privacy_export_locations') == 2 ) return array( 'data' => $export_items, 'done' => true ); //return if user is registered and we're only exporting anon events
444
-
445
  $locations_export_default = array(
446
  'group_id' => 'events-manager-'.EM_POST_TYPE_LOCATION,
447
  'group_label' => __('Locations', 'events-manager'),
@@ -449,9 +445,14 @@ class EM_Data_Privacy {
449
  'data' => array() // replace this with assoc array of name/value key arrays
450
  );
451
 
452
- //Locations - here we only get event-less events submitted by registered users. Anonymous submissions would have been handled above as event/locations come in pairs.
453
- if( $user !== false ){
454
- foreach( EM_Locations::get(array('eventless' => get_option('dbem_data_privacy_export_events') != 0, 'owner' => $user->ID, 'limit' => $limit, 'offset' => $offset, 'status'=>'everything')) as $EM_Location ){ /* @var EM_Location $EM_Location */
 
 
 
 
 
455
  $location_export_item = $locations_export_default;
456
  $location_export_item['item_id'] = 'event-post-'.$EM_Location->post_id;
457
  $location_export_item['data'][] = array('name' => __('Name','events-manager'), 'value' => $EM_Location->location_name );
@@ -467,11 +468,12 @@ class EM_Data_Privacy {
467
  $location_export_item = apply_filters('em_data_privacy_export_locations_item', $location_export_item, $EM_Location);
468
  $export_items[] = $location_export_item;
469
  $export_items = apply_filters('em_data_privacy_export_locations_items_after_item', $export_items, $location_export_item, $EM_Location); //could be used for cross-referencing and add-ing other groups e.g. Multiple Bookings in Pro
 
470
  $items_count++;
471
  if( $items_count == $limit ) break;
472
  }
473
  }
474
-
475
  $done = $items_count < $limit; //if we didn't reach limit of bookings then we must be done
476
  return array(
477
  'data' => $export_items,
@@ -479,12 +481,13 @@ class EM_Data_Privacy {
479
  );
480
  }
481
 
482
- public static function get_events( $email_address, $page, $post_type ){
483
  global $wpdb;
484
  $page = (int) $page;
485
  $limit = apply_filters('em_data_privacy_export_limit', 100);
486
  $offset = ($page -1) * $limit;
487
  $user = get_user_by('email', $email_address); //is user or no-user?
 
488
  //get event IDs submitted by user or "anonymously" by email
489
  $events = array();
490
  if( $user !== false ){
@@ -492,7 +495,7 @@ class EM_Data_Privacy {
492
  $events = $wpdb->get_col($sql);
493
  }
494
  //if user ever submitted anonymous events with same email, we also process these
495
- $sql = $wpdb->prepare("SELECT ID FROM {$wpdb->posts} WHERE ID IN (SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='_event_owner_email' AND meta_value=%s) AND post_type = %s LIMIT %d OFFSET %d", $email_address, $post_type, $limit, $offset);
496
  $events = array_merge($events, $wpdb->get_col($sql));
497
  return $events;
498
  }
112
  $done = true;
113
  }else{
114
  //get event IDs submitted by user or "anonymously" by email
115
+ $events = self::get_cpts($email_address, $page, $post_type);
116
 
117
  foreach( $events as $post_id ){
118
  $EM_Event = em_get_event($post_id, 'post_id');
119
  //erase the location first
120
  $EM_Location = $EM_Event->get_location();
121
  if( $EM_Location->location_id && get_option('dbem_data_privacy_erase_locations', 2) ){
122
+ //prior to 5.9.3 locations submitted alongside anonymous events didn't store email info, so for older locations we can only assume if the location is guest submitted and only linked to this one event
123
+ $user_probably_owns_location = $user === false && empty($EM_Location->owner_email) && $EM_Location->location_owner == get_option('dbem_events_anonymous_user');
124
+ if( $user_probably_owns_location ){
125
  //depending on settings delete location only if it has no other events (eventually if we're deleting the last event of this user with the same location, it'll only have one event)
126
  if( get_option('dbem_data_privacy_erase_locations', 2) == 1 || EM_Events::count(array('location_id' => $EM_Location->location_id, 'status' => 'all')) <= 1 ){
127
  if( $EM_Location->delete(true) ){
152
  }
153
 
154
  public static function erase_locations( $email_address, $page = 1 ) {
 
155
  $page = (int) $page;
156
  $limit = apply_filters('em_data_privacy_erase_limit', 100);
 
157
  $items_removed = $items_retained = false;
158
  $messages = array();
159
+
160
+ $locations_count = 0;
161
+ $locations = self::get_cpts($email_address, $page, EM_POST_TYPE_LOCATION);
162
+ foreach( $locations as $post_id ){
163
+ $EM_Location = em_get_location( $post_id, 'post_id' ); /* @var EM_Location $EM_Location */
164
+ if( $EM_Location->delete(true) ){
165
+ $items_removed = true;
166
+ }else{
167
+ $items_retained = true;
168
+ $messages = array_merge($messages, $EM_Location->get_errors());
 
 
 
 
169
  }
170
+ $locations_count++;
 
171
  }
172
+ // Tell core if we have more comments to work on still
173
+ $done = $locations_count < $limit;
174
  return array(
175
  'items_removed' => $items_removed,
176
  'items_retained' => $items_retained, // always false in this example
357
  $event_export_default['group_label'] = $post_type == 'event-recurring' ? __('Recurring Events', 'events-manager'):__('Events', 'events-manager');
358
 
359
  //get event IDs submitted by user or "anonymously" by email
360
+ $events = self::get_cpts($email_address, $page, $post_type);
361
 
362
  foreach( $events as $post_id ){
363
  $EM_Event = em_get_event($post_id, 'post_id');
380
  $export_item['data'][] = array('name' => __('Location','events-manager'), 'value' => $EM_Location->location_name . ', '. $EM_Location->get_full_address() .', '. $EM_Location->location_country);
381
  //put this location as a new export item
382
  $already_exported = in_array($EM_Location->location_id, $locations_exported);
383
+ $user_probably_owns_location = $user === false && empty($EM_Location->owner_email) && $EM_Location->location_owner == get_option('dbem_events_anonymous_user');
384
+ $user_submitted_location = $user === false && $EM_Location->owner_email == $email_address;
385
+ $user_owns_location = $user !== false && $EM_Location->location_owner == $user->ID;
386
+ if( !$already_exported && ($user_owns_location || $user_submitted_location || $user_probably_owns_location) ){
387
  $location_export_item = $locations_export_default;
388
  $location_export_item['item_id'] = 'location-post-'.$EM_Location->post_id;
389
  $location_export_item['data'][] = array('name' => __('Name','events-manager'), 'value' => $EM_Location->location_name );
438
  $export_items = array();
439
  $items_count = 0;
440
 
 
 
 
441
  $locations_export_default = array(
442
  'group_id' => 'events-manager-'.EM_POST_TYPE_LOCATION,
443
  'group_label' => __('Locations', 'events-manager'),
445
  'data' => array() // replace this with assoc array of name/value key arrays
446
  );
447
 
448
+ //Locations - previous to 5.9.4 locations submitted anonymously did nint include
449
+ $locations_exported = get_post_meta( $_REQUEST['id'], '_em_locations_exported', true);
450
+ if( empty($locations_exported) ) $locations_exported = array();
451
+
452
+ $locations = self::get_cpts($email_address, $page, EM_POST_TYPE_LOCATION);
453
+ foreach( $locations as $post_id ){
454
+ $EM_Location = em_get_location( $post_id, 'post_id' ); /* @var EM_Location $EM_Location */
455
+ if( !in_array($EM_Location->location_id, $locations_exported) ){
456
  $location_export_item = $locations_export_default;
457
  $location_export_item['item_id'] = 'event-post-'.$EM_Location->post_id;
458
  $location_export_item['data'][] = array('name' => __('Name','events-manager'), 'value' => $EM_Location->location_name );
468
  $location_export_item = apply_filters('em_data_privacy_export_locations_item', $location_export_item, $EM_Location);
469
  $export_items[] = $location_export_item;
470
  $export_items = apply_filters('em_data_privacy_export_locations_items_after_item', $export_items, $location_export_item, $EM_Location); //could be used for cross-referencing and add-ing other groups e.g. Multiple Bookings in Pro
471
+ $locations_exported[] = $EM_Location->location_id;
472
  $items_count++;
473
  if( $items_count == $limit ) break;
474
  }
475
  }
476
+ update_post_meta( $_REQUEST['id'], '_em_locations_exported', $locations_exported);
477
  $done = $items_count < $limit; //if we didn't reach limit of bookings then we must be done
478
  return array(
479
  'data' => $export_items,
481
  );
482
  }
483
 
484
+ public static function get_cpts($email_address, $page, $post_type ){
485
  global $wpdb;
486
  $page = (int) $page;
487
  $limit = apply_filters('em_data_privacy_export_limit', 100);
488
  $offset = ($page -1) * $limit;
489
  $user = get_user_by('email', $email_address); //is user or no-user?
490
+ $anon_email_key = $post_type == EM_POST_TYPE_LOCATION ? '_owner_email':'_event_owner_email';
491
  //get event IDs submitted by user or "anonymously" by email
492
  $events = array();
493
  if( $user !== false ){
495
  $events = $wpdb->get_col($sql);
496
  }
497
  //if user ever submitted anonymous events with same email, we also process these
498
+ $sql = $wpdb->prepare("SELECT ID FROM {$wpdb->posts} WHERE ID IN (SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key=%s AND meta_value=%s) AND post_type = %s LIMIT %d OFFSET %d", $anon_email_key, $email_address, $post_type, $limit, $offset);
499
  $events = array_merge($events, $wpdb->get_col($sql));
500
  return $events;
501
  }
admin/em-docs.php CHANGED
@@ -25,8 +25,9 @@ function em_docs_init($force_init = false){
25
  'post_id' => array( 'desc' => sprintf('Supply a single id or comma-separated ids (e.g. "1,2,3") to limit the search to %s with the %s.','events', 'post_id(s)')),
26
  'private' => array( 'desc' => sprintf('Display private %s within your list?','events'), 'args' => '1 = yes, 0 = no', 'default' => 'If user can view private events, 1, otherwise 0.'),
27
  'private_only' => array( 'desc' =>sprintf('Display only private %s ?','events'), 'args' => '1 = yes, 0 = no', 'default' => '0'),
 
28
  'recurrence' => array( 'desc'=> 'If set to the event id of the recurring event, this will show only events this event recurrences.', 'default'=>0),
29
- 'recurring' => array( 'desc'=> 'If set to 1, will only show recurring event templates. Only useful if you know what you\'re doing, use recurrence if you want events that are recurrences.', 'default'=>0),
30
  'scope' => array( 'desc'=> 'Choose the time frame of events to show. Additionally you can supply dates (in format of YYYY-MM-DD), either single for events on a specific date or two dates separated by a comma (e.g. 2010-12-25,2010-12-31) for events ocurring between these dates.', 'default'=>'future', 'args'=>array("future", "past", "today", "tomorrow", "month", "next-month", "1-months", "2-months", "3-months", "6-months", "12-months","all")),
31
  'search' => array( 'desc'=> 'Do a search for this string within event name, details and location address.' ),
32
  'status' => array( 'desc' => sprintf('Limit search to %s with a spefic status (1 is active, 0 is pending approval)','events'), 'default'=>1),
25
  'post_id' => array( 'desc' => sprintf('Supply a single id or comma-separated ids (e.g. "1,2,3") to limit the search to %s with the %s.','events', 'post_id(s)')),
26
  'private' => array( 'desc' => sprintf('Display private %s within your list?','events'), 'args' => '1 = yes, 0 = no', 'default' => 'If user can view private events, 1, otherwise 0.'),
27
  'private_only' => array( 'desc' =>sprintf('Display only private %s ?','events'), 'args' => '1 = yes, 0 = no', 'default' => '0'),
28
+ 'recurrences' => array( 'desc'=> 'Show only recurrences if set to 1 or non-recurrences if set to 0, shows all events if not used.'),
29
  'recurrence' => array( 'desc'=> 'If set to the event id of the recurring event, this will show only events this event recurrences.', 'default'=>0),
30
+ 'recurring' => array( 'desc'=> 'If set to 1, will only show recurring event templates. Only useful if you know what you\'re doing, use recurrence or recurrences if you want to filter event recurrences.', 'default'=>0),
31
  'scope' => array( 'desc'=> 'Choose the time frame of events to show. Additionally you can supply dates (in format of YYYY-MM-DD), either single for events on a specific date or two dates separated by a comma (e.g. 2010-12-25,2010-12-31) for events ocurring between these dates.', 'default'=>'future', 'args'=>array("future", "past", "today", "tomorrow", "month", "next-month", "1-months", "2-months", "3-months", "6-months", "12-months","all")),
32
  'search' => array( 'desc'=> 'Do a search for this string within event name, details and location address.' ),
33
  'status' => array( 'desc' => sprintf('Limit search to %s with a spefic status (1 is active, 0 is pending approval)','events'), 'default'=>1),
classes/em-datetime.php CHANGED
@@ -81,6 +81,7 @@ class EM_DateTime extends DateTime {
81
  if( !$this->valid && ($format == 'Y-m-d' || $format == em_get_date_format())) return '';
82
  //if we deal with offsets, then we offset UTC time by that much
83
  if( $this->timezone_manual_offset !== false ){
 
84
  if( function_exists('date_timestamp_get') ){
85
  return date($format, $this->getTimestampWithOffset(true) );
86
  }else{
@@ -93,6 +94,60 @@ class EM_DateTime extends DateTime {
93
  return parent::format($format);
94
  }
95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  /**
97
  * Returns a date and time representation in the format stored in Events Manager settings.
98
  * @param string $include_hour
@@ -112,6 +167,8 @@ class EM_DateTime extends DateTime {
112
  */
113
  public function i18n( $format = 'Y-m-d H:i:s' ){
114
  if( !$this->valid && $format == em_get_date_format()) return '';
 
 
115
  //if we deal with offsets, then we offset UTC time by that much
116
  if( !function_exists('date_timestamp_get') && $this->timezone_manual_offset !== false ){
117
  //PHP < 5.3 fallback :/ Messed up, but it works...
@@ -196,17 +253,17 @@ class EM_DateTime extends DateTime {
196
  * action: set the time to 12:00
197
  * result: 2018-01-01 02:00 UTC => 2018-01-01 12:00 UTC+10 -> after offset handling
198
  * expected: 2018-01-02 02:00 UTC => 2018-01-02 12:00 UTC+10 -> after offset handling
199
- * solution : change date AFTER setting the time and BEFORE offset handling
200
  */
201
  if( $this->timezone_manual_offset !== false ){
202
  $date_array = explode('-', $this->format('Y-m-d'));
203
  }
204
  $return = parent::setTime( $hour, $minute, $second );
205
- //pre-handle offsets for time changes where dates change as stated above
 
206
  if( $this->timezone_manual_offset !== false ){
207
  $this->setDate($date_array[0], $date_array[1], $date_array[2]);
208
  }
209
- $this->handleOffsets();
210
  $this->valid = $return !== false;
211
  return $this;
212
  }
81
  if( !$this->valid && ($format == 'Y-m-d' || $format == em_get_date_format())) return '';
82
  //if we deal with offsets, then we offset UTC time by that much
83
  if( $this->timezone_manual_offset !== false ){
84
+ $format = $this->formatTimezones( $format );
85
  if( function_exists('date_timestamp_get') ){
86
  return date($format, $this->getTimestampWithOffset(true) );
87
  }else{
94
  return parent::format($format);
95
  }
96
 
97
+ /**
98
+ * Formats timezone placeholders when there is a manual offset, which would be passed onto date formatting functions and usually output UTC timezone information.
99
+ * The $force_format flag is also useful if passing a format to any date() type of function, such as date_i18n, which will not inherit this function's timezone settings.
100
+ * @param string $format The format to be parsed.
101
+ * @param bool $force_format If set to true timezone placeholders will be formatted regardless.
102
+ * @return string
103
+ */
104
+ public function formatTimezones($format, $force_format = false ){
105
+ $timezone_formats = array( 'P', 'I', 'O', 'T', 'Z', 'e' );
106
+ $timezone_formats_regex = "/P|I|O|T|Z|e/";
107
+ if( $this->timezone_manual_offset !== false ){
108
+ if ( preg_match( $timezone_formats_regex, $format ) ) {
109
+ foreach ( $timezone_formats as $timezone_format ) {
110
+ if ( false !== strpos( $format, $timezone_format ) ) {
111
+ switch( $timezone_format ){
112
+ case 'P':
113
+ case 'O':
114
+ $offset = $this->getOffset();
115
+ $formatted_format = $timezone_format == 'P' ? 'H:i':'Hi';
116
+ $plus_minus = $offset < 0 ? '-':'+';
117
+ $formatted = $plus_minus . gmdate($formatted_format, absint($offset));
118
+ break;
119
+ case 'I':
120
+ $formatted = '0';
121
+ break;
122
+ case 'T':
123
+ case 'e':
124
+ $formatted = $this->getTimezone()->getName();
125
+ break;
126
+ case 'Z':
127
+ $formatted = $this->getOffset();
128
+ break;
129
+ }
130
+ $format = ' '.$format;
131
+ $format = preg_replace( "/([^\\\])$timezone_format/", "\\1" . backslashit( $formatted ), $format );
132
+ $format = substr( $format, 1, strlen( $format ) -1 );
133
+ }
134
+ }
135
+ }
136
+ }elseif( $force_format ){
137
+ //useful in cases where we may pass a format onto a date() function e.g. date_i18n, where the timezone is not relative to this object
138
+ if ( preg_match( $timezone_formats_regex, $format ) ) {
139
+ foreach ( $timezone_formats as $timezone_format ) {
140
+ if ( false !== strpos( $format, $timezone_format ) ) {
141
+ $format = ' '.$format;
142
+ $format = preg_replace( "/([^\\\])$timezone_format/", "\\1" . backslashit( $this->format( $timezone_format ) ), $format );
143
+ $format = substr( $format, 1, strlen( $format ) -1 );
144
+ }
145
+ }
146
+ }
147
+ }
148
+ return $format;
149
+ }
150
+
151
  /**
152
  * Returns a date and time representation in the format stored in Events Manager settings.
153
  * @param string $include_hour
167
  */
168
  public function i18n( $format = 'Y-m-d H:i:s' ){
169
  if( !$this->valid && $format == em_get_date_format()) return '';
170
+ //since we use WP's date functions which don't use DateTime (and if so, don't inherit our timezones), we need to preformat timezone related formats, adapted from date_i18n
171
+ $format = $this->formatTimezones( $format, true );
172
  //if we deal with offsets, then we offset UTC time by that much
173
  if( !function_exists('date_timestamp_get') && $this->timezone_manual_offset !== false ){
174
  //PHP < 5.3 fallback :/ Messed up, but it works...
253
  * action: set the time to 12:00
254
  * result: 2018-01-01 02:00 UTC => 2018-01-01 12:00 UTC+10 -> after offset handling
255
  * expected: 2018-01-02 02:00 UTC => 2018-01-02 12:00 UTC+10 -> after offset handling
256
+ * solution : change date AFTER setting the time and offset handling
257
  */
258
  if( $this->timezone_manual_offset !== false ){
259
  $date_array = explode('-', $this->format('Y-m-d'));
260
  }
261
  $return = parent::setTime( $hour, $minute, $second );
262
+ $this->handleOffsets();
263
+ //post-handle offsets for time changes where dates change as stated above
264
  if( $this->timezone_manual_offset !== false ){
265
  $this->setDate($date_array[0], $date_array[1], $date_array[2]);
266
  }
 
267
  $this->valid = $return !== false;
268
  return $this;
269
  }
classes/em-event-post-admin.php CHANGED
@@ -56,7 +56,7 @@ class EM_Event_Post_Admin{
56
 
57
  public static function admin_notices_filter($messages){
58
  //When editing
59
- global $post, $EM_Notices;
60
  if( $post->post_type == EM_POST_TYPE_EVENT || $post->post_type == 'event-recurring' ){
61
  if ( $EM_Notices->count_errors() > 0 ) {
62
  unset($_GET['message']);
@@ -96,7 +96,7 @@ class EM_Event_Post_Admin{
96
  }
97
 
98
  public static function save_post($post_id, $post = false){
99
- global $wpdb, $EM_Event, $EM_Location, $EM_Notices, $EM_SAVING_EVENT, $EM_EVENT_SAVE_POST;
100
  if( !empty($EM_SAVING_EVENT) ) return; //never proceed with this if using EM_Event::save();
101
  if ( isset($_GET['preview_id']) && isset($_GET['preview_nonce']) && wp_verify_nonce( $_GET['preview_nonce'], 'post_preview_' . $post_id ) ) return; //don't proceed with saving when previewing, may cause issues
102
  $post_type = get_post_type($post_id);
@@ -306,7 +306,7 @@ class EM_Event_Post_Admin{
306
  public static function meta_box_anonymous(){
307
  global $EM_Event;
308
  ?>
309
- <div class='updated'><p><?php _e('This event was submitted by a guest. You will find their details in the <em>Anonymous Submitter Info</em> box','events-manager')?></p></div>
310
  <p><strong><?php _e('Name','events-manager'); ?> :</strong> <?php echo $EM_Event->event_owner_name; ?></p>
311
  <p><strong><?php _e('Email','events-manager'); ?> :</strong> <?php echo $EM_Event->event_owner_email; ?></p>
312
  <?php
56
 
57
  public static function admin_notices_filter($messages){
58
  //When editing
59
+ global $post, $EM_Notices; /* @var EM_Notices $EM_Notices */
60
  if( $post->post_type == EM_POST_TYPE_EVENT || $post->post_type == 'event-recurring' ){
61
  if ( $EM_Notices->count_errors() > 0 ) {
62
  unset($_GET['message']);
96
  }
97
 
98
  public static function save_post($post_id, $post = false){
99
+ global $wpdb, $EM_Event, $EM_Notices, $EM_SAVING_EVENT, $EM_EVENT_SAVE_POST; /* @var EM_Notices $EM_Notices */
100
  if( !empty($EM_SAVING_EVENT) ) return; //never proceed with this if using EM_Event::save();
101
  if ( isset($_GET['preview_id']) && isset($_GET['preview_nonce']) && wp_verify_nonce( $_GET['preview_nonce'], 'post_preview_' . $post_id ) ) return; //don't proceed with saving when previewing, may cause issues
102
  $post_type = get_post_type($post_id);
306
  public static function meta_box_anonymous(){
307
  global $EM_Event;
308
  ?>
309
+ <div class='updated'><p><?php echo sprintf(__('This %s was submitted by a guest. You will find their details in the <em>Anonymous Submitter Info</em> box','events-manager'), __('event', 'events-manager')); ?></p></div>
310
  <p><strong><?php _e('Name','events-manager'); ?> :</strong> <?php echo $EM_Event->event_owner_name; ?></p>
311
  <p><strong><?php _e('Email','events-manager'); ?> :</strong> <?php echo $EM_Event->event_owner_email; ?></p>
312
  <?php
classes/em-event-post.php CHANGED
@@ -7,7 +7,6 @@
7
  class EM_Event_Post {
8
 
9
  public static function init(){
10
- global $wp_query;
11
  //Front Side Modifiers
12
  if( !is_admin() ){
13
  //override single page with formats?
@@ -41,7 +40,7 @@ class EM_Event_Post {
41
  }
42
 
43
  public static function publish_future_post($post_id){
44
- global $wpdb, $EM_Event, $EM_Location, $EM_Notices;
45
  $post_type = get_post_type($post_id);
46
  $is_post_type = $post_type == EM_POST_TYPE_EVENT || $post_type == 'event-recurring';
47
  $saving_status = !in_array(get_post_status($post_id), array('trash','auto-draft')) && !defined('DOING_AUTOSAVE');
7
  class EM_Event_Post {
8
 
9
  public static function init(){
 
10
  //Front Side Modifiers
11
  if( !is_admin() ){
12
  //override single page with formats?
40
  }
41
 
42
  public static function publish_future_post($post_id){
43
+ global $EM_Event;
44
  $post_type = get_post_type($post_id);
45
  $is_post_type = $post_type == EM_POST_TYPE_EVENT || $post_type == 'event-recurring';
46
  $saving_status = !in_array(get_post_status($post_id), array('trash','auto-draft')) && !defined('DOING_AUTOSAVE');
classes/em-event.php CHANGED
@@ -553,14 +553,20 @@ class EM_Event extends EM_Object{
553
  $this->post_type = ($this->is_recurring() || !empty($_POST['recurring'])) ? 'event-recurring':EM_POST_TYPE_EVENT;
554
  //don't forget categories!
555
  if( get_option('dbem_categories_enabled') ) $this->get_categories()->get_post();
 
 
556
  //anonymous submissions and guest basic info
557
  if( !is_user_logged_in() && get_option('dbem_events_anonymous_submissions') && empty($this->event_id) ){
558
  $this->event_owner_anonymous = 1;
559
  $this->event_owner_name = !empty($_POST['event_owner_name']) ? wp_kses_data(wp_unslash($_POST['event_owner_name'])):'';
560
  $this->event_owner_email = !empty($_POST['event_owner_email']) ? wp_kses_data($_POST['event_owner_email']):'';
 
 
 
 
 
561
  }
562
- //get the rest and validate (optional)
563
- $this->get_post_meta(false);
564
  $result = $validate ? $this->validate():true; //validate both post and meta, otherwise return true
565
  return apply_filters('em_event_get_post', $result, $this);
566
  }
@@ -1120,13 +1126,21 @@ class EM_Event extends EM_Object{
1120
  }
1121
  }
1122
  $result = count($this->errors) == 0;
1123
- //If we're saving event categories in MS Global mode, we'll add them here, saving by term id (cat ids are gone now)
1124
- if( EM_MS_GLOBAL && get_option('dbem_categories_enabled') ){ //EM_MS_Globals should look up original blog
1125
- if( !is_main_site() ){
1126
- $this->get_categories()->save(); //it'll know what to do
1127
- }else{
1128
- $this->get_categories()->save_index(); //just save to index, we assume cats are saved in $this->save();
1129
- }
 
 
 
 
 
 
 
 
1130
  }
1131
  $this->compat_keys(); //compatability keys, loaded before saving recurrences
1132
  //build recurrences if needed
553
  $this->post_type = ($this->is_recurring() || !empty($_POST['recurring'])) ? 'event-recurring':EM_POST_TYPE_EVENT;
554
  //don't forget categories!
555
  if( get_option('dbem_categories_enabled') ) $this->get_categories()->get_post();
556
+ //get the rest and validate (optional)
557
+ $this->get_post_meta(false);
558
  //anonymous submissions and guest basic info
559
  if( !is_user_logged_in() && get_option('dbem_events_anonymous_submissions') && empty($this->event_id) ){
560
  $this->event_owner_anonymous = 1;
561
  $this->event_owner_name = !empty($_POST['event_owner_name']) ? wp_kses_data(wp_unslash($_POST['event_owner_name'])):'';
562
  $this->event_owner_email = !empty($_POST['event_owner_email']) ? wp_kses_data($_POST['event_owner_email']):'';
563
+ if( empty($this->location_id) && !($this->location_id === 0 && !get_option('dbem_require_location',true)) ){
564
+ $this->get_location()->owner_anonymous = 1;
565
+ $this->location->owner_email = $this->event_owner_email;
566
+ $this->location->owner_name = $this->event_owner_name;
567
+ }
568
  }
569
+ //validate and return results
 
570
  $result = $validate ? $this->validate():true; //validate both post and meta, otherwise return true
571
  return apply_filters('em_event_get_post', $result, $this);
572
  }
1126
  }
1127
  }
1128
  $result = count($this->errors) == 0;
1129
+ //deal with categories
1130
+ if( get_option('dbem_categories_enabled') ){
1131
+ if( EM_MS_GLOBAL ){ //EM_MS_Globals should look up original blog
1132
+ //If we're saving event categories in MS Global mode, we'll add them here, saving by term id (cat ids are gone now)
1133
+ if( !is_main_site() ){
1134
+ $this->get_categories()->save(); //it'll know what to do
1135
+ }else{
1136
+ $this->get_categories()->save_index(); //just save to index, we assume cats are saved in $this->save();
1137
+ }
1138
+ }elseif( get_option('dbem_default_category') > 0 ){
1139
+ //double-check for default category in other instances
1140
+ if( count($this->get_categories()) == 0 ){
1141
+ $this->get_categories()->save(); //let the object deal with this...
1142
+ }
1143
+ }
1144
  }
1145
  $this->compat_keys(); //compatability keys, loaded before saving recurrences
1146
  //build recurrences if needed
classes/em-location-post-admin.php CHANGED
@@ -180,6 +180,10 @@ class EM_Location_Post_Admin{
180
  if( get_option('dbem_location_attributes_enabled') ){
181
  add_meta_box('em-location-attributes', __('Attributes','events-manager'), array('EM_Location_Post_Admin','meta_box_attributes'),EM_POST_TYPE_LOCATION, 'normal','default');
182
  }
 
 
 
 
183
  }
184
 
185
  public static function meta_box_metadump(){
@@ -187,6 +191,7 @@ class EM_Location_Post_Admin{
187
  echo "<pre>"; print_r(get_post_custom($post->ID)); echo "</pre>";
188
  echo "<pre>"; print_r($EM_Location); echo "</pre>";
189
  }
 
190
  public static function meta_box_where(){
191
  ?><input type="hidden" name="_emnonce" value="<?php echo wp_create_nonce('edit_location'); ?>" /><?php
192
  em_locate_template('forms/location/where.php',true);
@@ -195,5 +200,14 @@ class EM_Location_Post_Admin{
195
  public static function meta_box_attributes(){
196
  em_locate_template('forms/location/attributes.php',true);
197
  }
 
 
 
 
 
 
 
 
 
198
  }
199
  add_action('admin_init',array('EM_Location_Post_Admin','init'));
180
  if( get_option('dbem_location_attributes_enabled') ){
181
  add_meta_box('em-location-attributes', __('Attributes','events-manager'), array('EM_Location_Post_Admin','meta_box_attributes'),EM_POST_TYPE_LOCATION, 'normal','default');
182
  }
183
+ //anonymous submission meta
184
+ if( !empty($EM_Location->owner_anonymous) ){
185
+ add_meta_box('em-location-anonymous', __('Anonymous Submitter Info','events-manager'), array('EM_Location_Post_Admin','meta_box_anonymous'),EM_POST_TYPE_LOCATION, 'side','high');
186
+ }
187
  }
188
 
189
  public static function meta_box_metadump(){
191
  echo "<pre>"; print_r(get_post_custom($post->ID)); echo "</pre>";
192
  echo "<pre>"; print_r($EM_Location); echo "</pre>";
193
  }
194
+
195
  public static function meta_box_where(){
196
  ?><input type="hidden" name="_emnonce" value="<?php echo wp_create_nonce('edit_location'); ?>" /><?php
197
  em_locate_template('forms/location/where.php',true);
200
  public static function meta_box_attributes(){
201
  em_locate_template('forms/location/attributes.php',true);
202
  }
203
+
204
+ public static function meta_box_anonymous(){
205
+ global $EM_Location; /* @var EM_Location $EM_Location */
206
+ ?>
207
+ <div class='updated'><p><?php echo sprintf(__('This %s was submitted by a guest. You will find their details in the <em>Anonymous Submitter Info</em> box','events-manager'), __('location', 'events-manager')); ?></p></div>
208
+ <p><strong><?php _e('Name','events-manager'); ?> :</strong> <?php echo $EM_Location->owner_name; ?></p>
209
+ <p><strong><?php _e('Email','events-manager'); ?> :</strong> <?php echo $EM_Location->owner_email; ?></p>
210
+ <?php
211
+ }
212
  }
213
  add_action('admin_init',array('EM_Location_Post_Admin','init'));
classes/em-location.php CHANGED
@@ -64,6 +64,10 @@ class EM_Location extends EM_Object {
64
  var $post_content = '';
65
  var $location_owner = '';
66
  var $location_status = 0;
 
 
 
 
67
  //Other Vars
68
  var $fields = array(
69
  'location_id' => array('name'=>'id','type'=>'%d'),
@@ -199,16 +203,16 @@ class EM_Location extends EM_Object {
199
  }
200
  //Get custom fields
201
  foreach($location_meta as $location_meta_key => $location_meta_val){
202
- $found = false;
203
- foreach($this->fields as $field_name => $field_info){
204
- if( $location_meta_key == '_'.$field_name){
 
 
 
 
205
  $this->$field_name = $location_meta_val[0];
206
- $found = true;
207
  }
208
  }
209
- if(!$found && $location_meta_key[0] != '_'){
210
- $this->location_attributes[$location_meta_key] = ( is_array($location_meta_val) ) ? $location_meta_val[0]:$location_meta_val;
211
- }
212
  }
213
  }
214
  //load post data - regardless
@@ -244,6 +248,12 @@ class EM_Location extends EM_Object {
244
  $this->location_name = ( !empty($_POST['location_name']) ) ? sanitize_post_field('post_title', $_POST['location_name'], $this->post_id, 'db'):'';
245
  $this->post_content = ( !empty($_POST['content']) ) ? wp_kses( wp_unslash($_POST['content']), $allowedtags):'';
246
  $this->get_post_meta(false);
 
 
 
 
 
 
247
  $result = $validate ? $this->validate():true; //validate both post and meta, otherwise return true
248
  $this->compat_keys();
249
  return apply_filters('em_location_get_post', $result, $this);
@@ -294,6 +304,15 @@ class EM_Location extends EM_Object {
294
  $validate_post = false;
295
  $this->add_error( __('Location name','events-manager').__(" is required.", 'events-manager') );
296
  }
 
 
 
 
 
 
 
 
 
297
  $validate_image = $this->image_validate();
298
  $validate_meta = $this->validate_meta();
299
  return apply_filters('em_location_validate', $validate_post && $validate_image && $validate_meta, $this );
@@ -377,6 +396,12 @@ class EM_Location extends EM_Object {
377
  $this->location_owner = $post_data->post_author;
378
  $this->post_status = $post_data->post_status;
379
  $this->get_status();
 
 
 
 
 
 
380
  //save the image, errors here will surface during $this->save_meta()
381
  $this->image_upload();
382
  //now save the meta
@@ -390,18 +415,20 @@ class EM_Location extends EM_Object {
390
  $EM_SAVING_LOCATION = false;
391
  //reload post data and add this location to the cache, after any other hooks have done their thing
392
  //cache refresh when saving via admin area is handled in EM_Event_Post_Admin::save_post/refresh_cache
393
- if( $post_save && $meta_save && $this->is_published() ){
394
- //we won't depend on hooks, if we saved the event and it's still published in its saved state, refresh the cache regardless
395
  $this->load_postdata($post_data);
396
- wp_cache_set($this->location_id, $this, 'em_locations');
397
- wp_cache_set($this->post_id, $this->location_id, 'em_locations_ids');
 
 
 
398
  }
399
  return $return;
400
  }
401
 
402
  function save_meta(){
403
  //echo "<pre>"; print_r($this); echo "</pre>"; die();
404
- global $wpdb, $current_user;
405
  if( $this->can_manage('edit_locations','edit_others_locations') || ( get_option('dbem_events_anonymous_submissions') && empty($this->location_id)) ){
406
  do_action('em_location_save_meta_pre', $this);
407
  //Set Blog ID if in multisite mode
@@ -470,6 +497,14 @@ class EM_Location extends EM_Object {
470
  //Also set the status here if status != previous status
471
  if( $this->previous_status != $this->get_status() ) $this->set_status($this->get_status());
472
  }
 
 
 
 
 
 
 
 
473
  }
474
  }else{
475
  $this->add_error( sprintf(__('You do not have permission to create/edit %s.','events-manager'), __('locations','events-manager')) );
64
  var $post_content = '';
65
  var $location_owner = '';
66
  var $location_status = 0;
67
+ /* anonymous submission information */
68
+ var $owner_anonymous;
69
+ var $owner_name;
70
+ var $owner_email;
71
  //Other Vars
72
  var $fields = array(
73
  'location_id' => array('name'=>'id','type'=>'%d'),
203
  }
204
  //Get custom fields
205
  foreach($location_meta as $location_meta_key => $location_meta_val){
206
+ $field_name = substr($location_meta_key, 1);
207
+ if($location_meta_key[0] != '_'){
208
+ $this->event_attributes[$location_meta_key] = ( is_array($location_meta_val) ) ? $location_meta_val[0]:$location_meta_val;
209
+ }elseif( is_string($field_name) && !in_array($field_name, $this->post_fields) ){
210
+ if( array_key_exists($field_name, $this->fields) ){
211
+ $this->$field_name = $location_meta_val[0];
212
+ }elseif( in_array($field_name, array('owner_name','owner_anonymous','owner_email')) ){
213
  $this->$field_name = $location_meta_val[0];
 
214
  }
215
  }
 
 
 
216
  }
217
  }
218
  //load post data - regardless
248
  $this->location_name = ( !empty($_POST['location_name']) ) ? sanitize_post_field('post_title', $_POST['location_name'], $this->post_id, 'db'):'';
249
  $this->post_content = ( !empty($_POST['content']) ) ? wp_kses( wp_unslash($_POST['content']), $allowedtags):'';
250
  $this->get_post_meta(false);
251
+ //anonymous submissions and guest basic info
252
+ if( !is_user_logged_in() && get_option('dbem_events_anonymous_submissions') && empty($this->location_id) ){
253
+ $this->owner_anonymous = 1;
254
+ $this->owner_name = !empty($_POST['owner_name']) ? wp_kses_data(wp_unslash($_POST['owner_name'])):'';
255
+ $this->owner_email = !empty($_POST['owner_email']) ? wp_kses_data($_POST['owner_email']):'';
256
+ }
257
  $result = $validate ? $this->validate():true; //validate both post and meta, otherwise return true
258
  $this->compat_keys();
259
  return apply_filters('em_location_get_post', $result, $this);
304
  $validate_post = false;
305
  $this->add_error( __('Location name','events-manager').__(" is required.", 'events-manager') );
306
  }
307
+ //anonymous submissions and guest basic info
308
+ if( !empty($this->owner_anonymous) ){
309
+ if( !is_email($this->owner_email) ){
310
+ $this->add_error( sprintf(__("%s is required.", 'events-manager'), __('A valid email','events-manager')) );
311
+ }
312
+ if( empty($this->owner_name) ){
313
+ $this->add_error( sprintf(__("%s is required.", 'events-manager'), __('Your name','events-manager')) );
314
+ }
315
+ }
316
  $validate_image = $this->image_validate();
317
  $validate_meta = $this->validate_meta();
318
  return apply_filters('em_location_validate', $validate_post && $validate_image && $validate_meta, $this );
396
  $this->location_owner = $post_data->post_author;
397
  $this->post_status = $post_data->post_status;
398
  $this->get_status();
399
+ //anonymous submissions should save this information
400
+ if( !empty($this->owner_anonymous) ){
401
+ update_post_meta($this->post_id, '_owner_anonymous', 1);
402
+ update_post_meta($this->post_id, '_owner_name', $this->owner_name);
403
+ update_post_meta($this->post_id, '_owner_email', $this->owner_email);
404
+ }
405
  //save the image, errors here will surface during $this->save_meta()
406
  $this->image_upload();
407
  //now save the meta
415
  $EM_SAVING_LOCATION = false;
416
  //reload post data and add this location to the cache, after any other hooks have done their thing
417
  //cache refresh when saving via admin area is handled in EM_Event_Post_Admin::save_post/refresh_cache
418
+ if( $post_save && $meta_save ){
 
419
  $this->load_postdata($post_data);
420
+ if( $this->is_published() ){
421
+ //we won't depend on hooks, if we saved the event and it's still published in its saved state, refresh the cache regardless
422
+ wp_cache_set($this->location_id, $this, 'em_locations');
423
+ wp_cache_set($this->post_id, $this->location_id, 'em_locations_ids');
424
+ }
425
  }
426
  return $return;
427
  }
428
 
429
  function save_meta(){
430
  //echo "<pre>"; print_r($this); echo "</pre>"; die();
431
+ global $wpdb;
432
  if( $this->can_manage('edit_locations','edit_others_locations') || ( get_option('dbem_events_anonymous_submissions') && empty($this->location_id)) ){
433
  do_action('em_location_save_meta_pre', $this);
434
  //Set Blog ID if in multisite mode
497
  //Also set the status here if status != previous status
498
  if( $this->previous_status != $this->get_status() ) $this->set_status($this->get_status());
499
  }
500
+ //check anonymous submission information
501
+ if( !empty($this->owner_anonymous) && get_option('dbem_events_anonymous_user') != $this->location_owner ){
502
+ //anonymous user owner has been replaced with a valid wp user account, so we remove anonymous status flag but leave email and name for future reference
503
+ update_post_meta($this->post_id, '_owner_anonymous', 0);
504
+ }elseif( get_option('dbem_events_anonymous_submissions') && get_option('dbem_events_anonymous_user') == $this->location_owner && is_email($this->owner_email) && !empty($this->owner_name) ){
505
+ //anonymous user account has been reinstated as the owner, so we can restore anonymous submission status
506
+ update_post_meta($this->post_id, '_owner_anonymous', 1);
507
+ }
508
  }
509
  }else{
510
  $this->add_error( sprintf(__('You do not have permission to create/edit %s.','events-manager'), __('locations','events-manager')) );
classes/em-permalinks.php CHANGED
@@ -214,6 +214,7 @@ if( !class_exists('EM_Permalinks') ){
214
  if( get_option('dbem_locations_enabled') ){
215
  $em_rules[EM_POST_TYPE_LOCATION_SLUG."/([^/]+)/rss/?$"] = 'index.php?'.EM_POST_TYPE_LOCATION.'=$matches[1]&rss=1';
216
  }
 
217
  return $em_rules + $rules;
218
  }
219
 
214
  if( get_option('dbem_locations_enabled') ){
215
  $em_rules[EM_POST_TYPE_LOCATION_SLUG."/([^/]+)/rss/?$"] = 'index.php?'.EM_POST_TYPE_LOCATION.'=$matches[1]&rss=1';
216
  }
217
+ $em_rules = apply_filters('em_rewrite_rules_array', $em_rules);
218
  return $em_rules + $rules;
219
  }
220
 
classes/em-taxonomy-terms.php CHANGED
@@ -93,14 +93,16 @@ class EM_Taxonomy_Terms extends EM_Object implements Iterator, Countable{
93
  }
94
 
95
  public function save(){
96
- $term_slugs = array();
97
- foreach($this->terms as $EM_Taxonomy_Term){
98
- /* @var $EM_Taxonomy_Term EM_Taxonomy_Term */
99
- if( !empty($EM_Taxonomy_Term->slug) ) $term_slugs[] = $EM_Taxonomy_Term->slug; //save of taxonomy will soft-fail if slug is empty
100
- }
101
- if( count($term_slugs) == 0 && get_option('dbem_default_'.$this->singular) > 0 ){
102
- $default_term = get_term_by('id',get_option('dbem_default_'.$this->singular), $this->taxonomy);
103
- if($default_term) $term_slugs[] = $default_term->slug;
 
 
104
  }
105
  if( is_multisite() && $this->is_ms_global ){
106
  //In MS Global mode, we also save taxonomy meta information for global lookups
@@ -109,10 +111,10 @@ class EM_Taxonomy_Terms extends EM_Object implements Iterator, Countable{
109
  $this->save_index();
110
  }
111
  if( !EM_MS_GLOBAL || is_main_site() ){
112
- wp_set_object_terms($this->post_id, $term_slugs, $this->taxonomy);
113
  }
114
  }else{
115
- wp_set_object_terms($this->post_id, $term_slugs, $this->taxonomy);
116
  }
117
  do_action('em_'. $this->terms_name .'_save', $this);
118
  }
@@ -155,6 +157,14 @@ class EM_Taxonomy_Terms extends EM_Object implements Iterator, Countable{
155
  return $ids;
156
  }
157
 
 
 
 
 
 
 
 
 
158
  /**
159
  * Gets the event for this object, or a blank event if none exists
160
  * @return EM_Event
93
  }
94
 
95
  public function save(){
96
+ if( empty($this->terms) ){
97
+ $EM_Taxonomy_Term = new $this->term_class();
98
+ $opt = 'dbem_default_'.$EM_Taxonomy_Term->option_name;
99
+ $default_category = EM_MS_GLOBAL && $this->is_ms_global ? get_blog_option( get_current_site()->blog_id, $opt ) : get_option($opt);
100
+ if( $default_category > 0 ){
101
+ $EM_Taxonomy_Term = new $this->term_class($default_category);
102
+ if( !empty($EM_Taxonomy_Term->slug) ){
103
+ $this->terms[] = $EM_Taxonomy_Term;
104
+ }
105
+ }
106
  }
107
  if( is_multisite() && $this->is_ms_global ){
108
  //In MS Global mode, we also save taxonomy meta information for global lookups
111
  $this->save_index();
112
  }
113
  if( !EM_MS_GLOBAL || is_main_site() ){
114
+ wp_set_object_terms($this->post_id, $this->get_slugs(), $this->taxonomy);
115
  }
116
  }else{
117
+ wp_set_object_terms($this->post_id, $this->get_slugs(), $this->taxonomy);
118
  }
119
  do_action('em_'. $this->terms_name .'_save', $this);
120
  }
157
  return $ids;
158
  }
159
 
160
+ public function get_slugs(){
161
+ $term_slugs = array();
162
+ foreach( $this->terms as $EM_Taxonomy_Term ){ /* @var EM_Taxonomy_Term $EM_Taxonomy_Term */
163
+ $term_slugs[] = $EM_Taxonomy_Term->slug;
164
+ }
165
+ return $term_slugs;
166
+ }
167
+
168
  /**
169
  * Gets the event for this object, or a blank event if none exists
170
  * @return EM_Event
em-actions.php CHANGED
@@ -221,6 +221,9 @@ function em_init_actions() {
221
  }elseif( !is_user_logged_in() ){
222
  $location_cond = " AND location_private=0";
223
  }
 
 
 
224
  $location_cond = apply_filters('em_actions_locations_search_cond', $location_cond);
225
  $term = (isset($_REQUEST['term'])) ? '%'.$wpdb->esc_like(wp_unslash($_REQUEST['term'])).'%' : '%'.$wpdb->esc_like(wp_unslash($_REQUEST['q'])).'%';
226
  $sql = $wpdb->prepare("
221
  }elseif( !is_user_logged_in() ){
222
  $location_cond = " AND location_private=0";
223
  }
224
+ if( EM_MS_GLOBAL && !get_site_option('dbem_ms_mainblog_locations') ){
225
+ $location_cond .= " AND blog_id=". absint(get_current_blog_id());
226
+ }
227
  $location_cond = apply_filters('em_actions_locations_search_cond', $location_cond);
228
  $term = (isset($_REQUEST['term'])) ? '%'.$wpdb->esc_like(wp_unslash($_REQUEST['term'])).'%' : '%'.$wpdb->esc_like(wp_unslash($_REQUEST['q'])).'%';
229
  $sql = $wpdb->prepare("
em-data-privacy.php CHANGED
@@ -5,7 +5,8 @@
5
  */
6
  function em_data_privacy_consent_checkbox( $EM_Object = false ){
7
  if( !empty($EM_Object) && (!empty($EM_Object->booking_id) || !empty($EM_Object->post_id)) ) return; //already saved so consent was given at one point
8
- $label = sprintf(get_option('dbem_data_privacy_consent_text'), get_the_privacy_policy_link());
 
9
  if( is_user_logged_in() ){
10
  //check if consent was previously given and check box if true
11
  $consent_given_already = get_user_meta( get_current_user_id(), 'em_data_privacy_consent', true );
@@ -65,7 +66,9 @@ function em_data_privacy_consent_hooks(){
65
  add_action('em_location_save', 'em_data_privacy_cpt_save', 10, 2);
66
  }
67
  }
68
- add_action('init', 'em_data_privacy_consent_hooks');
 
 
69
 
70
  /**
71
  * Gets consent information for the submitted booking and and add it to the booking object for saving later.
5
  */
6
  function em_data_privacy_consent_checkbox( $EM_Object = false ){
7
  if( !empty($EM_Object) && (!empty($EM_Object->booking_id) || !empty($EM_Object->post_id)) ) return; //already saved so consent was given at one point
8
+ $label = get_option('dbem_data_privacy_consent_text');
9
+ if( function_exists('get_the_privacy_policy_link') ) $label = sprintf($label, get_the_privacy_policy_link());
10
  if( is_user_logged_in() ){
11
  //check if consent was previously given and check box if true
12
  $consent_given_already = get_user_meta( get_current_user_id(), 'em_data_privacy_consent', true );
66
  add_action('em_location_save', 'em_data_privacy_cpt_save', 10, 2);
67
  }
68
  }
69
+ if( !is_admin() || ( defined('DOING_AJAX') && DOING_AJAX && !empty($_REQUEST['action']) && !in_array($_REQUEST['action'], array('booking_add_one')) ) ){
70
+ add_action('init', 'em_data_privacy_consent_hooks');
71
+ }
72
 
73
  /**
74
  * Gets consent information for the submitted booking and and add it to the booking object for saving later.
em-install.php CHANGED
@@ -603,18 +603,17 @@ function em_add_options() {
603
  'dbem_tag_event_list_order' => 'ASC',
604
  'dbem_tag_default_color' => '#a8d145',
605
  //RSS Stuff
606
- 'dbem_rss_limit' => 0,
607
  'dbem_rss_scope' => 'future',
608
  'dbem_rss_main_title' => get_bloginfo('title')." - ".__('Events', 'events-manager'),
609
  'dbem_rss_main_description' => get_bloginfo('description')." - ".__('Events', 'events-manager'),
610
  'dbem_rss_description_format' => "#_EVENTDATES - #_EVENTTIMES <br/>#_LOCATIONNAME <br/>#_LOCATIONADDRESS <br/>#_LOCATIONTOWN",
611
  'dbem_rss_title_format' => "#_EVENTNAME",
612
- 'dbem_rss_scope' =>'future',
613
  'dbem_rss_order' => get_option('dbem_events_default_order', 'ASC'), //get event order and orderby or use same new installation defaults
614
  'dbem_rss_orderby' => get_option('dbem_events_default_orderby', 'event_start_date,event_start_time,event_name'),
615
  'em_rss_pubdate' => date('D, d M Y H:i:s +0000'),
616
  //iCal Stuff
617
- 'dbem_ical_limit' => 0,
618
  'dbem_ical_scope' => "future",
619
  'dbem_ical_description_format' => "#_EVENTNAME",
620
  'dbem_ical_real_description_format' => "#_EVENTEXCERPT",
@@ -1081,7 +1080,7 @@ function em_upgrade_current_installation(){
1081
  }
1082
  if( get_option('dbem_version') != '' && get_option('dbem_version') < 5.93 ){
1083
  $message = __('Events Manager has introduced new privacy tools to help you comply with international laws such as the GDPR, <a href="%s">see our documentation</a> for more information.','events-manager');
1084
- $message = sprintf( $message, 'https://wp-events-plugin.com/documentation/data-privacy-protection/?utm_source=plugin&utm_campaign=gdpr_update');
1085
  $EM_Admin_Notice = new EM_Admin_Notice(array( 'name' => 'gdpr_update', 'who' => 'admin', 'where' => 'all', 'message' => $message ));
1086
  EM_Admin_Notices::add($EM_Admin_Notice, is_multisite());
1087
  }
603
  'dbem_tag_event_list_order' => 'ASC',
604
  'dbem_tag_default_color' => '#a8d145',
605
  //RSS Stuff
606
+ 'dbem_rss_limit' => 50,
607
  'dbem_rss_scope' => 'future',
608
  'dbem_rss_main_title' => get_bloginfo('title')." - ".__('Events', 'events-manager'),
609
  'dbem_rss_main_description' => get_bloginfo('description')." - ".__('Events', 'events-manager'),
610
  'dbem_rss_description_format' => "#_EVENTDATES - #_EVENTTIMES <br/>#_LOCATIONNAME <br/>#_LOCATIONADDRESS <br/>#_LOCATIONTOWN",
611
  'dbem_rss_title_format' => "#_EVENTNAME",
 
612
  'dbem_rss_order' => get_option('dbem_events_default_order', 'ASC'), //get event order and orderby or use same new installation defaults
613
  'dbem_rss_orderby' => get_option('dbem_events_default_orderby', 'event_start_date,event_start_time,event_name'),
614
  'em_rss_pubdate' => date('D, d M Y H:i:s +0000'),
615
  //iCal Stuff
616
+ 'dbem_ical_limit' => 50,
617
  'dbem_ical_scope' => "future",
618
  'dbem_ical_description_format' => "#_EVENTNAME",
619
  'dbem_ical_real_description_format' => "#_EVENTEXCERPT",
1080
  }
1081
  if( get_option('dbem_version') != '' && get_option('dbem_version') < 5.93 ){
1082
  $message = __('Events Manager has introduced new privacy tools to help you comply with international laws such as the GDPR, <a href="%s">see our documentation</a> for more information.','events-manager');
1083
+ $message = sprintf( $message, 'https://wp-events-plugin.com/documentation/data-privacy-gdpr-compliance/?utm_source=plugin&utm_campaign=gdpr_update');
1084
  $EM_Admin_Notice = new EM_Admin_Notice(array( 'name' => 'gdpr_update', 'who' => 'admin', 'where' => 'all', 'message' => $message ));
1085
  EM_Admin_Notices::add($EM_Admin_Notice, is_multisite());
1086
  }
events-manager.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  /*
3
  Plugin Name: Events Manager
4
- Version: 5.9.3
5
  Plugin URI: http://wp-events-plugin.com
6
  Description: Event registration and booking management for WordPress. Recurring events, locations, google maps, rss, ical, booking registration and more!
7
  Author: Marcus Sykes
@@ -28,7 +28,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28
  */
29
 
30
  // Setting constants
31
- define('EM_VERSION', 5.93); //self expanatory
32
  define('EM_PRO_MIN_VERSION', 2.392); //self expanatory
33
  define('EM_PRO_MIN_VERSION_CRITICAL', 2.377); //self expanatory
34
  define('EM_DIR', dirname( __FILE__ )); //an absolute path to this directory
1
  <?php
2
  /*
3
  Plugin Name: Events Manager
4
+ Version: 5.9.4
5
  Plugin URI: http://wp-events-plugin.com
6
  Description: Event registration and booking management for WordPress. Recurring events, locations, google maps, rss, ical, booking registration and more!
7
  Author: Marcus Sykes
28
  */
29
 
30
  // Setting constants
31
+ define('EM_VERSION', 5.94); //self expanatory
32
  define('EM_PRO_MIN_VERSION', 2.392); //self expanatory
33
  define('EM_PRO_MIN_VERSION_CRITICAL', 2.377); //self expanatory
34
  define('EM_DIR', dirname( __FILE__ )); //an absolute path to this directory
includes/js/events-manager.js CHANGED
@@ -532,7 +532,7 @@ jQuery(document).ready( function($){
532
  }
533
 
534
  //Manual Booking
535
- $('a.em-booking-button').click(function(e){
536
  e.preventDefault();
537
  var button = $(this);
538
  if( button.text() != EM.bb_booked && $(this).text() != EM.bb_booking){
@@ -559,7 +559,7 @@ jQuery(document).ready( function($){
559
  }
560
  return false;
561
  });
562
- $('a.em-cancel-button').click(function(e){
563
  e.preventDefault();
564
  var button = $(this);
565
  if( button.text() != EM.bb_cancelled && button.text() != EM.bb_canceling){
532
  }
533
 
534
  //Manual Booking
535
+ $(document).on('click', 'a.em-booking-button', function(e){
536
  e.preventDefault();
537
  var button = $(this);
538
  if( button.text() != EM.bb_booked && $(this).text() != EM.bb_booking){
559
  }
560
  return false;
561
  });
562
+ $(document).on('click', 'a.em-cancel-button', function(e){
563
  e.preventDefault();
564
  var button = $(this);
565
  if( button.text() != EM.bb_cancelled && button.text() != EM.bb_canceling){
multilingual/em-ml-options.php CHANGED
@@ -13,6 +13,8 @@ class EM_ML_Options {
13
  'dbem_events_anonymous_result_success',
14
  'dbem_events_form_result_success',
15
  'dbem_events_form_result_success_updated',
 
 
16
  //FORMATTING TAB
17
  //events
18
  'dbem_event_list_groupby_format',
13
  'dbem_events_anonymous_result_success',
14
  'dbem_events_form_result_success',
15
  'dbem_events_form_result_success_updated',
16
+ //privacy policy consent
17
+ 'dbem_data_privacy_consent_text',
18
  //FORMATTING TAB
19
  //events
20
  'dbem_event_list_groupby_format',
readme.txt CHANGED
@@ -5,7 +5,7 @@ Tags: bookings, calendar, tickets, events, buddypress, event management, google
5
  Text Domain: events-manager
6
  Requires at least: 3.5
7
  Tested up to: 4.9.6
8
- Stable tag: 5.9.3
9
 
10
  Fully featured event registration management including recurring events, locations management, calendar, Google map integration, booking management
11
 
@@ -56,7 +56,7 @@ We provide the tools to [help you be GDPR compliant](http://wp-events-plugin.com
56
  * export/erasure of data via the WordPress Privacy Tools, including booking, event and location data
57
  * consent checkboxes on our booking, event and location forms on the frontend
58
  * settings to control what can be exported/erased as well as where/when to place consent requests
59
- * privacy policy sample describing what Events Manager does with personal data
60
 
61
  = Go Pro =
62
  We have a premium "Pro" add-on for Events Manager which not only demonstrates the flexibility of Events Manager, but also adds some important features including but not limited to:
@@ -110,6 +110,22 @@ See our [FAQ](http://wp-events-plugin.com/documentation/faq/) page, which is upd
110
  6. Manage attendees with various booking reports
111
 
112
  == Changelog ==
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  = 5.9.3 =
114
  * added Data Privacy and GDPR features
115
  * fixed user deletion not properly deleting events and not deleting locations if content is set to be deleted not reassigned
5
  Text Domain: events-manager
6
  Requires at least: 3.5
7
  Tested up to: 4.9.6
8
+ Stable tag: 5.9.4
9
 
10
  Fully featured event registration management including recurring events, locations management, calendar, Google map integration, booking management
11
 
56
  * export/erasure of data via the WordPress Privacy Tools, including booking, event and location data
57
  * consent checkboxes on our booking, event and location forms on the frontend
58
  * settings to control what can be exported/erased as well as where/when to place consent requests
59
+ * sample text for your site privacy policy describing what Events Manager does with personal data
60
 
61
  = Go Pro =
62
  We have a premium "Pro" add-on for Events Manager which not only demonstrates the flexibility of Events Manager, but also adds some important features including but not limited to:
110
  6. Manage attendees with various booking reports
111
 
112
  == Changelog ==
113
+ = 5.9.4 =
114
+ * added em_rewrite_rules_array filter for final permalink rule manipulation
115
+ * fixed privacy consent blocking certain actions such as single booking button and admin-side submissions
116
+ * fixed fatal errors when showing the consent checkbox in WordPress 5.9.5 and earlier
117
+ * fixed the quick booking button not working in ajax search results
118
+ * fixed privacy policy consent form label not being translatable for multilingual sites
119
+ * fixed inconsistent date headers in certain situations with UTC manual offset dates
120
+ * fixed incorrect link to .eot dashicon file for IE11
121
+ * added anonymous submitter data to locations for new event submissions and integrated this with GDPR export/erase tools
122
+ * fixed location slug blanks when directly published from front-end via submitting an event
123
+ * added default ical and rss feed limits to avoid overloading as number of events grow
124
+ * corrected docs to include 'recurrences' search attribute
125
+ * added timezone pre-formatting to functions that produced incorrect output for timezone date placeholders
126
+ * fixed default categories not being applied for events with no categories
127
+ * fixed locations being selectable for events in other blogs within a multisite global mode when locations don't all reside on main blog
128
+
129
  = 5.9.3 =
130
  * added Data Privacy and GDPR features
131
  * fixed user deletion not properly deleting events and not deleting locations if content is set to be deleted not reassigned
templates/forms/event/location.php CHANGED
@@ -33,8 +33,8 @@ $required = apply_filters('em_required_html','<i>*</i>');
33
  <td>
34
  <select name="location_id" id='location-select-id' size="1">
35
  <?php if(!get_option('dbem_require_location',true)): ?><option value="0"><?php esc_html_e('No Location','events-manager'); ?></option><?php endif; ?>
36
- <?php
37
- $ddm_args = array('blog'=>false, 'private'=>$EM_Event->can_manage('read_private_locations'));
38
  $ddm_args['owner'] = (is_user_logged_in() && !current_user_can('read_others_locations')) ? get_current_user_id() : false;
39
  $locations = EM_Locations::get($ddm_args);
40
  $selected_location = !empty($EM_Event->location_id) || !empty($EM_Event->event_id) ? $EM_Event->location_id:get_option('dbem_default_location');
33
  <td>
34
  <select name="location_id" id='location-select-id' size="1">
35
  <?php if(!get_option('dbem_require_location',true)): ?><option value="0"><?php esc_html_e('No Location','events-manager'); ?></option><?php endif; ?>
36
+ <?php
37
+ $ddm_args = array('private'=>$EM_Event->can_manage('read_private_locations'));
38
  $ddm_args['owner'] = (is_user_logged_in() && !current_user_can('read_others_locations')) ? get_current_user_id() : false;
39
  $locations = EM_Locations::get($ddm_args);
40
  $selected_location = !empty($EM_Event->location_id) || !empty($EM_Event->event_id) ? $EM_Event->location_id:get_option('dbem_default_location');