Events Manager - Version 5.9.3

Version Description

  • added Data Privacy and GDPR features
  • fixed user deletion not properly deleting events and not deleting locations if content is set to be deleted not reassigned
  • added location attributes array to em_get_attributes filter
  • fixed EM_MB_ICAL_WORDWRAP incorrectly not applying multibyte wordwraps if set to true
  • added 'not_all_day' conditional placeholder
  • made EM_Taxonomy_Terms objects countable
  • fixed tag placeholders not getting parsed in event format such as #_TAGIMAGE
Download this release

Release Info

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

Code changes from version 5.9.2 to 5.9.3

admin/em-data-privacy.php ADDED
@@ -0,0 +1,528 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * This file deals with new privacy tools included in WP 4.9.6 and in line with aiding users with conforming to the GPDR rules.
4
+ * Note that consent mechanisms are not included here and will be baked directly into the templates or Pro booking forms.
5
+ */
6
+ class EM_Data_Privacy {
7
+
8
+ public static function init(){
9
+ add_action( 'admin_init', 'EM_Data_Privacy::privacy_policy_content' );
10
+ add_filter( 'wp_privacy_personal_data_erasers', 'EM_Data_Privacy::register_eraser', 10 );
11
+ add_filter( 'wp_privacy_personal_data_exporters', 'EM_Data_Privacy::register_exporter', 10 );
12
+ add_action( 'wp_privacy_personal_data_export_file_created', 'EM_Data_Privacy::export_cleanup');
13
+ }
14
+
15
+ public static function privacy_policy_content() {
16
+ if ( ! function_exists( 'wp_add_privacy_policy_content' ) ) {
17
+ return;
18
+ }
19
+
20
+ $content = array();
21
+ $content[] = sprintf(
22
+ __('We use Google services to generate maps and provide autocompletion when searching for events by location, which may collect data via your browser in accordance to Google\'s <a href="%s">privacy policy</a>.', 'events-manager' ),
23
+ 'https://policies.google.com/privacy'
24
+ );
25
+ $content[] = __('We collect and store information you submit to us when making a booking, for the purpose of reserving your requested spaces at our event and maintaining a record of attendance.', 'events-manager' );
26
+ $content[] = __('We collect and store information you submit to us about events (and corresponding locations) you would like to publish on our site.', 'events-manager' );
27
+ $content[] = __('We may use cookies to temporarily store information about a booking in progress as well as any error/confirmation messages whilst submitting or managing your events and locations.', 'events-manager' );
28
+
29
+ wp_add_privacy_policy_content(
30
+ __('Events Manager', 'events-manager'),
31
+ wp_kses_post( '<p>'. implode('</p><p>', $content) .'</p>' )
32
+ );
33
+ }
34
+
35
+ public static function register_eraser( $erasers ) {
36
+ if( get_option('dbem_data_privacy_erase_bookings') ){
37
+ $erasers['events-manager-bookings'] = array(
38
+ 'eraser_friendly_name' => __( 'Events Manager', 'events-manager' ) . ' - ' . __('Bookings', 'events-manager'),
39
+ 'callback' => 'EM_Data_Privacy::erase_bookings',
40
+ );
41
+ }
42
+ if( get_option('dbem_data_privacy_erase_events') ){
43
+ $erasers['events-manager-recurring-events'] = array(
44
+ 'eraser_friendly_name' => __( 'Events Manager', 'events-manager' ) . ' - ' . __('Recurring Events', 'events-manager'),
45
+ 'callback' => 'EM_Data_Privacy::erase_recurring_events',
46
+ );
47
+ $erasers['events-manager-events'] = array(
48
+ 'eraser_friendly_name' => __( 'Events Manager', 'events-manager' ) . ' - ' . __('Events', 'events-manager'),
49
+ 'callback' => 'EM_Data_Privacy::erase_events',
50
+ );
51
+ }
52
+ if( get_option('dbem_data_privacy_erase_locations') ){
53
+ $erasers['events-manager-locations'] = array(
54
+ 'eraser_friendly_name' => __( 'Events Manager', 'events-manager' ) . ' - ' . __('Locations', 'events-manager'),
55
+ 'callback' => 'EM_Data_Privacy::erase_locations',
56
+ );
57
+ }
58
+ //in this case we don't register location deletion because we only need to handle anonymous events, locations submitted anonymously shouldn't have any personal data associated with it (unless a user submitted their home address anonymously!)
59
+ return $erasers;
60
+ }
61
+
62
+ public static function erase_bookings( $email_address, $page = 1 ) {
63
+ $page = (int) $page;
64
+ $limit = apply_filters('em_data_privacy_export_limit', 100);
65
+ $messages = array();
66
+ $user = get_user_by('email', $email_address); //is user or no-user?
67
+
68
+ if( $user !== false && get_option('dbem_data_privacy_erase_bookings') == 2 ){
69
+ //we're only deleting anonymous bookings, and letting WP handle user/booking deletion the traditional way for registered accounts
70
+ $done = true;
71
+ }else{
72
+ //get items to erase
73
+ $booking_ids = self::get_bookings($email_address, $page);
74
+
75
+ $items_removed = $items_retained = false;
76
+ foreach ( $booking_ids as $booking_id ) {
77
+ $EM_Booking = em_get_booking($booking_id);
78
+ if( $EM_Booking->delete() ){
79
+ $items_removed = true;
80
+ }else{
81
+ $items_retained = true;
82
+ $messages = array_merge($messages, $EM_Booking->get_errors());
83
+ }
84
+ }
85
+ if( $items_removed ) add_action('em_data_privacy_bookings_deleted', $booking_ids);
86
+
87
+ // Tell core if we have more comments to work on still
88
+ $done = count( $booking_ids ) < $limit;
89
+ }
90
+ return array(
91
+ 'items_removed' => $items_removed,
92
+ 'items_retained' => $items_retained, // always false in this example
93
+ 'messages' => $messages, // no messages in this example
94
+ 'done' => $done,
95
+ );
96
+ }
97
+
98
+ public static function erase_recurring_events( $email_address, $page = 1 ) {
99
+ return self::erase_events( $email_address, $page, 'event-recurring');
100
+ }
101
+
102
+ public static function erase_events( $email_address, $page = 1, $post_type = false ) {
103
+ $user = get_user_by('email', $email_address); //is user or no-user?
104
+ if( !$post_type ) $post_type = EM_POST_TYPE_EVENT; //default to event
105
+ $page = (int) $page;
106
+ $limit = apply_filters('em_data_privacy_erase_limit', 100);
107
+ $items_removed = $items_retained = false;
108
+ $messages = array();
109
+
110
+ if( $user !== false && get_option('dbem_data_privacy_erase_events') == 2 ){
111
+ //we're only deleting anonymous events, and letting WP handle CPT deletion the traditional way for registered accounts
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) ){
126
+ $items_removed = true;
127
+ }else{
128
+ $items_retained = true;
129
+ }
130
+ }
131
+ }
132
+ }
133
+ //now erase the event
134
+ if( $EM_Event->delete(true) ){
135
+ $items_removed = true;
136
+ }else{
137
+ $items_retained = true;
138
+ $messages = array_merge($messages, $EM_Event->get_errors());
139
+ }
140
+ }
141
+ // Tell core if we have more comments to work on still
142
+ $done = count( $events ) < $limit;
143
+ }
144
+ return array(
145
+ 'items_removed' => $items_removed,
146
+ 'items_retained' => $items_retained, // always false in this example
147
+ 'messages' => $messages, // no messages in this example
148
+ 'done' => $done,
149
+ );
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
180
+ 'messages' => $messages, // no messages in this example
181
+ 'done' => $done,
182
+ );
183
+ }
184
+
185
+ public static function register_exporter( $exporters ) {
186
+ $exporters['events-manager-user'] = array(
187
+ 'exporter_friendly_name' => __( 'Events Manager', 'events-manager' ) . ' - ' .__( 'Further Information', 'events-manager' ),
188
+ 'callback' => 'EM_Data_Privacy::export_user',
189
+ );
190
+ if( get_option('dbem_data_privacy_export_bookings') ){
191
+ $exporters['events-manager-bookings'] = array(
192
+ 'exporter_friendly_name' => __( 'Events Manager', 'events-manager' ) . ' - ' . __('Bookings', 'events-manager'),
193
+ 'callback' => 'EM_Data_Privacy::export_bookings',
194
+ );
195
+ }
196
+ if( get_option('dbem_data_privacy_export_events') ){
197
+ $exporters['events-manager-recurring-events'] = array(
198
+ 'exporter_friendly_name' => __( 'Events Manager', 'events-manager' ) . ' - ' . __('Recurring Events', 'events-manager'),
199
+ 'callback' => 'EM_Data_Privacy::export_recurring_events',
200
+ );
201
+ $exporters['events-manager-events'] = array(
202
+ 'exporter_friendly_name' => __( 'Events Manager', 'events-manager' ) . ' - ' . __('Events', 'events-manager'),
203
+ 'callback' => 'EM_Data_Privacy::export_events',
204
+ );
205
+ }
206
+ if( get_option('dbem_data_privacy_export_locations') ){
207
+ $exporters['events-manager-locations'] = array(
208
+ 'exporter_friendly_name' => __( 'Events Manager', 'events-manager' ) . ' - ' . __('Locations', 'events-manager'),
209
+ 'callback' => 'EM_Data_Privacy::export_locations',
210
+ );
211
+ }
212
+ return $exporters;
213
+ }
214
+
215
+ public static function export_cleanup(){
216
+ delete_post_meta($_REQUEST['id'], '_em_locations_exported');
217
+ delete_post_meta($_REQUEST['id'], '_em_bookings_exported');
218
+ }
219
+
220
+ public static function export_user( $email_address ){
221
+ $user = get_user_by('email', $email_address); //is user or no-user?
222
+ $export_items = array();
223
+ if( $user !== false ){
224
+ //we add to the WP User section
225
+ $data_to_export[] = array(
226
+ 'group_id' => 'user',
227
+ 'group_label' => __( 'User' ),
228
+ 'item_id' => "user-{$user->ID}",
229
+ 'data' => array(),
230
+ );
231
+ $dbem_phone = get_user_meta($user->ID, 'dbem_phone', true);
232
+ if( !empty($dbem_phone) ){
233
+ $export_item['data'][] = array( 'name' => __('Phone', 'events-manager'), 'value' => $dbem_phone );
234
+ }
235
+ $export_item = apply_filters('em_data_privacy_export_user', $export_item, $user);
236
+ if( !empty($export_item['data']) ){
237
+ $export_items[] = $export_item;
238
+ }
239
+ }
240
+ return array(
241
+ 'data' => $export_items,
242
+ 'done' => true,
243
+ );
244
+ }
245
+
246
+ public static function export_bookings( $email_address, $page = 1 ) {
247
+ $page = (int) $page;
248
+ $limit = apply_filters('em_data_privacy_export_limit', 100);
249
+
250
+ $export_items = array();
251
+ $items_count = 0;
252
+
253
+ //check if we're only exporting bookings to those who made anonymous bookings
254
+ $user = get_user_by('email', $email_address); //is user or no-user?
255
+ if( $user !== false && get_option('dbem_data_privacy_export_bookings') == 2 ) return array( 'data' => $export_items, 'done' => true ); //return if user is registered and we're only exporting anon bookings
256
+
257
+ $bookings = self::get_bookings($email_address, $page);
258
+
259
+ $booking_export_default = array(
260
+ 'group_id' => 'events-manager-bookings',
261
+ 'group_label' => __('Bookings', 'events-manager'),
262
+ 'item_id' => 'booking-ID', //replace ID with booking ID
263
+ 'data' => array() // replace this with assoc array of name/value key arrays
264
+ );
265
+ $booking_price_adjustments = array(
266
+ 'discounts_pre_tax' => __('Discounts Before Taxes','events-manager'),
267
+ 'surcharges_pre_tax' => __('Surcharges Before Taxes','events-manager'),
268
+ 'discounts_post_tax' => __('Discounts (After Taxes)','events-manager'),
269
+ 'surcharges_post_tax' => __('Surcharges (After Taxes)','events-manager')
270
+ );
271
+
272
+ foreach ( $bookings as $booking_id ) {
273
+ $EM_Booking = em_get_booking($booking_id);
274
+ $export_item = $booking_export_default;
275
+ $export_item['item_id'] = 'booking-'.$EM_Booking->booking_id;
276
+ $export_item['data']['status'] = array('name' => __('Status','events-manager'), 'value' => $EM_Booking->get_status() );
277
+ $export_item['data']['date'] = array('name' => __('Date','events-manager'), 'value' => $EM_Booking->date()->getDateTime() . ' ' . $EM_Booking->date()->getTimezone()->getName() );
278
+ $export_item['data']['event'] = array('name' => __('Event','events-manager'), 'value' => $EM_Booking->get_event()->output('#_EVENTLINK - #_EVENTDATES @ #_EVENTTIMES') );
279
+ if( $EM_Booking->person_id == 0 ){
280
+ foreach( $EM_Booking->get_person()->get_summary() as $key => $info ){
281
+ $export_item['data']['field-'.$key] = $info;
282
+ }
283
+ }
284
+ $booking_tickets = array();
285
+ foreach($EM_Booking->get_tickets_bookings()->tickets_bookings as $EM_Ticket_Booking){ /* @var EM_Ticket_Booking $EM_Ticket_Booking */
286
+ $booking_tickets[] = $EM_Ticket_Booking->get_ticket()->ticket_name . ' x '. $EM_Ticket_Booking->get_spaces() . ' @ ' . $EM_Ticket_Booking->get_price(true);
287
+ }
288
+ $export_item['data']['tickets'] = array('name' => __('Tickets', 'events-manager-pro'), 'value' => implode('<br>', $booking_tickets));
289
+ $export_item['data']['sub-total'] = array('name' => __('Sub Total','events-manager'), 'value' => $EM_Booking->get_price_base(true));
290
+ $price_summary = $EM_Booking->get_price_summary_array();
291
+ foreach( $booking_price_adjustments as $adjustment_key => $adjustment_title ){
292
+ if( count($price_summary[$adjustment_key]) > 0 ){
293
+ $adjustments = array();
294
+ foreach( $price_summary[$adjustment_key] as $adjustment ){
295
+ $adjustments[] = "{$adjustment['name']} @ {$adjustment['amount']}";
296
+ }
297
+ $export_item['data'][$adjustment_key] = array('name' => $adjustment_title, 'value' => implode('<br>', $adjustments));
298
+ }
299
+ }
300
+ if( !empty($price_summary['taxes']['amount']) ){
301
+ $export_item['data']['taxes'] = array('name' => __('Taxes','events-manager'), 'value' => "({$price_summary['taxes']['rate']}) {$price_summary['taxes']['amount']}");
302
+ }
303
+ $export_item['data']['total'] = array('name' => __('Total Price','events-manager'), 'value' => $price_summary['total']);
304
+ //booking notes - can be exempt with a filter since maybe notes have private info
305
+ if( apply_filters('em_data_privacy_export_bookings_include_notes', true, $EM_Booking) ){
306
+ $booking_notes = $EM_Booking->get_notes();
307
+ if( !empty($booking_notes) ){
308
+ $booking_notes_data = array();
309
+ foreach( $booking_notes as $booking_note ){
310
+ $booking_notes_data[] = date(get_option('date_format'), $booking_note['timestamp']) .' - '. $booking_note['note'];
311
+ }
312
+ $export_item['data']['notes'] = array('name' => __( 'Booking Notes', 'events-manager'), 'value' => implode('<br><br>', $booking_notes_data));
313
+ }
314
+ }
315
+ $export_item = apply_filters('em_data_privacy_export_bookings_item', $export_item, $EM_Booking);
316
+ $export_items[] = $export_item;
317
+ $export_items = apply_filters('em_data_privacy_export_bookings_items_after_item', $export_items, $export_item, $EM_Booking); //could be used for cross-referencing and add-ing other groups e.g. Multiple Bookings in Pro
318
+ $items_count++;
319
+ if( $items_count == $limit ) break;
320
+ }
321
+
322
+ $done = $items_count < $limit; //if we didn't reach limit of bookings then we must be done
323
+ return array(
324
+ 'data' => $export_items,
325
+ 'done' => $done,
326
+ );
327
+ }
328
+
329
+ public static function export_recurring_events( $email_address, $page = 1){
330
+ return self::export_events( $email_address, $page, 'event-recurring' );
331
+ }
332
+
333
+ public static function export_events( $email_address, $page = 1, $post_type = false ) {
334
+ if( !$post_type ) $post_type = EM_POST_TYPE_EVENT; //default to event
335
+ $page = (int) $page;
336
+ $limit = apply_filters('em_data_privacy_export_limit', 100);
337
+ $user = get_user_by('email', $email_address); //is user or no-user?
338
+ $export_items = array();
339
+ $items_count = 0;
340
+
341
+ //check if we're only exporting events to those who submitted anonymously
342
+ if( $user !== false && get_option('dbem_data_privacy_export_events') == 2 ) return array( 'data' => $export_items, 'done' => true ); //return if user is registered and we're only exporting anon events
343
+
344
+ //prepare some location stuff for use within events
345
+ $locations_export_default = array(
346
+ 'group_id' => 'events-manager-'.EM_POST_TYPE_LOCATION,
347
+ 'group_label' => __('Locations', 'events-manager'),
348
+ 'item_id' => 'location-post-ID', //replace ID with booking ID
349
+ 'data' => array() // replace this with assoc array of name/value key arrays
350
+ );
351
+ $locations_exported = get_post_meta( $_REQUEST['id'], '_em_locations_exported', true);
352
+ if( empty($locations_exported) ) $locations_exported = array();
353
+
354
+ //EVENTS
355
+ $event_export_default = array(
356
+ 'group_id' => 'events-manager-'.$post_type,
357
+ 'item_id' => 'event-post-ID', //replace ID with booking ID
358
+ 'data' => array() // replace this with assoc array of name/value key arrays
359
+ );
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');
367
+ $export_item = $event_export_default;
368
+ $export_item['item_id'] = 'event-post-'.$EM_Event->post_id;
369
+ $export_item['data'][] = array('name' => __('Event Name','events-manager'), 'value' => $EM_Event->event_name );
370
+ $export_item['data'][] = array('name' => sprintf(__('%s Status','events-manager'), __('Event','events-manager')), 'value' => $EM_Event->post_status );
371
+ if( $post_type == EM_POST_TYPE_EVENT && $EM_Event->event_status == 1 ){
372
+ $export_item['data'][] = array('name' => sprintf(__('%s URL','events-manager'), __('Event','events-manager')), 'value' => $EM_Event->get_permalink() );
373
+ }
374
+ if( $post_type == 'event-recurring' ){
375
+ $export_item['data'][] = array('name' => __('When','events-manager'), 'value' => $EM_Event->output('#_EVENTDATES @ #_EVENTTIMES').'<br>'.$EM_Event->get_recurrence_description() );
376
+ }else{
377
+ $export_item['data'][] = array('name' => __('When','events-manager'), 'value' => $EM_Event->output('#_EVENTDATES @ #_EVENTTIMES') );
378
+ }
379
+ $export_item['data'][] = array('name' => __('Timezone','events-manager'), 'value' => $EM_Event->start()->getTimezone()->getName() );
380
+ if( !empty($EM_Event->event_owner_name) ) $export_item['data'][] = array('name' => __('Name','events-manager'), 'value' => $EM_Event->event_owner_name );
381
+ if( $EM_Event->get_location()->location_id ){
382
+ $EM_Location = $EM_Event->get_location();
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 );
391
+ $location_export_item['data'][] = array('name' => __('Address','events-manager'), 'value' => $EM_Location->get_full_address() .', '. $EM_Location->location_country );
392
+ $location_export_item['data'][] = array('name' => __('Coordinates','events-manager'), 'value' => $EM_Location->location_latitude .', '. $EM_Location->location_longitude );
393
+ $location_export_item['data'][] = array('name' => sprintf(__('%s Status','events-manager'), __('Location','events-manager')), 'value' => $EM_Location->post_status );
394
+ if( $EM_Location->post_status == 'publish' ){
395
+ $location_export_item['data'][] = array('name' => sprintf(__('%s URL','events-manager'), __('Location','events-manager')), 'value' => $EM_Location->get_permalink() );
396
+ }
397
+ foreach( $EM_Location->location_attributes as $k => $v ){
398
+ $location_export_item['data'][] = array('name' => $k, 'value' => $v);
399
+ }
400
+ $location_export_item = apply_filters('em_data_privacy_export_locations_item', $location_export_item, $EM_Location);
401
+ $export_items[] = $location_export_item;
402
+ $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
403
+ $items_count++;
404
+ $locations_exported[] = $EM_Location->location_id;
405
+ }
406
+ }
407
+ if( $EM_Event->event_rsvp ){
408
+ $tickets = array();
409
+ foreach( $EM_Event->get_tickets() as $EM_Ticket ){ /* @var EM_Ticket $EM_Ticket */
410
+ $ticket = array($EM_Ticket->ticket_name, $EM_Ticket->ticket_description, $EM_Ticket->get_price(true));
411
+ if( empty($EM_Ticket->ticket_description) ) unset($ticket[1]);
412
+ $tickets[] = implode( ' - ', $ticket);
413
+ }
414
+ $export_item['data'][] = array('name' => __('Tickets','events-manager'), 'value' => implode('<br>', $tickets) );
415
+ }
416
+ foreach( $EM_Event->event_attributes as $k => $v ){
417
+ $export_item['data'][] = array('name' => $k, 'value' => $v);
418
+ }
419
+ $export_item = apply_filters('em_data_privacy_export_events_item', $export_item, $EM_Event);
420
+ $export_items[] = $export_item;
421
+ $export_items = apply_filters('em_data_privacy_export_events_items_after_item', $export_items, $export_item, $EM_Event); //could be used for cross-referencing and add-ing other groups e.g. Multiple Bookings in Pro
422
+ $items_count++;
423
+ if( $items_count >= $limit ) break;
424
+ }
425
+
426
+ $done = $items_count < $limit; //if we didn't reach limit of bookings then we must be done
427
+ update_post_meta( $_REQUEST['id'], '_em_locations_exported', $locations_exported);
428
+ return array(
429
+ 'data' => $export_items,
430
+ 'done' => $done,
431
+ );
432
+ }
433
+
434
+ public static function export_locations( $email_address, $page = 1 ) {
435
+ $page = (int) $page;
436
+ $limit = apply_filters('em_data_privacy_export_limit', 100);
437
+ $offset = ($page -1) * $limit;
438
+ $user = get_user_by('email', $email_address); //is user or no-user?
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'),
448
+ 'item_id' => 'location-post-ID', //replace ID with booking ID
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 );
458
+ $location_export_item['data'][] = array('name' => sprintf(__('%s Status','events-manager'), __('Location','events-manager')), 'value' => $EM_Location->post_status );
459
+ if( $EM_Location->post_status == 'publish' ){
460
+ $location_export_item['data'][] = array('name' => sprintf(__('%s URL','events-manager'), __('Location','events-manager')), 'value' => $EM_Location->get_permalink() );
461
+ }
462
+ $location_export_item['data'][] = array('name' => __('Address','events-manager'), 'value' => $EM_Location->get_full_address() .', '. $EM_Location->location_country );
463
+ $location_export_item['data'][] = array('name' => __('Coordinates','events-manager'), 'value' => $EM_Location->location_latitude .', '. $EM_Location->location_longitude );
464
+ foreach( $EM_Location->location_attributes as $k => $v ){
465
+ $location_export_item['data'][] = array('name' => $k, 'value' => $v);
466
+ }
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,
478
+ 'done' => $done,
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 ){
491
+ $sql = $wpdb->prepare("SELECT ID FROM {$wpdb->posts} WHERE post_author = %d AND post_type = %s LIMIT %d OFFSET %d", $user->ID, $post_type, $limit, $offset);
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
+ }
499
+
500
+ public static function get_bookings( $email_address, $page ){
501
+ global $wpdb;
502
+ $page = (int) $page;
503
+ $limit = apply_filters('em_data_privacy_export_limit', 100);
504
+ $offset = ($page -1) * $limit;
505
+ $user = get_user_by('email', $email_address); //is user or no-user?
506
+
507
+ $conditions = array();
508
+ if( $user !== false ){
509
+ $conditions[] = $wpdb->prepare('person_id = %d', $user->ID);
510
+ }
511
+ $conditions[] = $wpdb->prepare('person_id=0 AND booking_meta LIKE %s', "%\"user_email\";s:".strlen($email_address).":\"$email_address\"%"); //find any booking that may have their email, anonymous or previous email address.
512
+ $bookings = $wpdb->get_col('SELECT booking_id FROM '.EM_BOOKINGS_TABLE.' WHERE '. implode(' OR ', $conditions) .' LIMIT '.$limit . ' OFFSET '.$offset);
513
+
514
+ return $bookings;
515
+
516
+ }
517
+ }
518
+ EM_Data_Privacy::init();
519
+ /*
520
+ add_action('admin_init', function(){
521
+ $data = EM_Data_Privacy::exporter('subscriber@netweblogic.com');
522
+ echo "<table>";
523
+ foreach( $data['data'] as $items ){
524
+ foreach($items['data'] as $item) echo "<tr><th>{$item['name']}</th><td>{$item['value']}</td>";
525
+ }
526
+ echo "</table>";
527
+ die();
528
+ }); //*/
admin/em-options.php CHANGED
@@ -316,12 +316,15 @@ function em_options_save(){
316
 
317
  //update scripts that may need to run
318
  $blog_updates = is_multisite() ? array_merge(EM_Options::get('updates'), EM_Options::site_get('updates')) : EM_Options::get('updates');
319
- foreach( $blog_updates as $update => $update_data ){
320
- $filename = EM_DIR.'/admin/settings/updates/'.$update.'.php';
321
- if( file_exists($filename) ) include_once($filename);
322
- do_action('em_admin_update_'.$update, $update_data);
 
 
 
 
323
  }
324
-
325
  }
326
  add_action('admin_init', 'em_options_save');
327
 
@@ -921,4 +924,75 @@ function em_admin_option_box_uninstall(){
921
  </div>
922
  <?php
923
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
924
  ?>
316
 
317
  //update scripts that may need to run
318
  $blog_updates = is_multisite() ? array_merge(EM_Options::get('updates'), EM_Options::site_get('updates')) : EM_Options::get('updates');
319
+ if( is_array($blog_updates) ) {
320
+ foreach ( $blog_updates as $update => $update_data ) {
321
+ $filename = EM_DIR . '/admin/settings/updates/' . $update . '.php';
322
+ if ( file_exists( $filename ) ) {
323
+ include_once( $filename );
324
+ }
325
+ do_action( 'em_admin_update_' . $update, $update_data );
326
+ }
327
  }
 
328
  }
329
  add_action('admin_init', 'em_options_save');
330
 
924
  </div>
925
  <?php
926
  }
927
+
928
+ /**
929
+ * Meta options box for privacy and data protection rules for GDPR (and other dp laws) compliancy
930
+ */
931
+ function em_admin_option_box_data_privacy(){
932
+ global $save_button;
933
+ $privacy_options = array(
934
+ 0 => __('Do not include', 'events-manager'),
935
+ 1 => __('Include all', 'events-manager'),
936
+ 2 => __('Include only guest submissions', 'events-manager')
937
+ );
938
+ ?>
939
+ <div class="postbox " id="em-opt-data-privacy" >
940
+ <div class="handlediv" title="<?php __('Click to toggle', 'events-manager'); ?>"><br /></div><h3><span><?php _e ( 'Privacy', 'events-manager'); ?> </span></h3>
941
+ <div class="inside">
942
+ <p class="em-boxheader"><?php echo sprintf(__('Depending on the nature of your site, you will be subject to one or more national and international privacy/data protection laws such as the %s. Below are some options that you can use to tailor how Events Manager interacts with WordPress privacy tools.','events-manager'), '<a href=http://ec.europa.eu/justice/smedataprotect/index_en.htm">GDPR</a>'); ?></p>
943
+ <p class="em-boxheader"><?php echo sprintf(__('For more information see our <a href="%s">data privacy documentation</a>.','events-manager'), 'http://wp-events-plugin.com/documentation/data-privacy-gdpr-compliance/'); ?></p>
944
+ <p class="em-boxheader"><?php echo __('All options below relate to data that may have been submitted by or collected from the user requesting their personal data, which would also include events and locations where they are the author.', 'events-manager'); ?></p>
945
+ <table class='form-table'>
946
+ <thead>
947
+ <tr class="em-header">
948
+ <th colspan="2"><h4><?php esc_html_e('Export Personal Data'); ?></h4></th>
949
+ </tr>
950
+ </thead>
951
+ <?php
952
+ em_options_select ( __( 'Events', 'events-manager'), 'dbem_data_privacy_export_events', $privacy_options );
953
+ em_options_select ( __( 'Locations', 'events-manager'), 'dbem_data_privacy_export_locations', $privacy_options, __('Locations submitted by guest users are not included, unless they are linked to events also submitted by them.', 'events-manager') );
954
+ em_options_select ( __( 'Bookings', 'events-manager'), 'dbem_data_privacy_export_bookings', $privacy_options, __('This is specific to bookings made by the user, not bookings that may have been made to events they own.', 'events-manager'), $privacy_options );
955
+ ?>
956
+ <thead>
957
+ <tr class="em-header">
958
+ <th colspan="2"><h4><?php esc_html_e('Erase Personal Data'); ?></h4></th>
959
+ </tr>
960
+ </thead>
961
+ <?php
962
+ em_options_select ( __( 'Events', 'events-manager'), 'dbem_data_privacy_erase_events', $privacy_options );
963
+ em_options_select ( __( 'Locations', 'events-manager'), 'dbem_data_privacy_erase_locations', $privacy_options, __('Locations submitted by guest users are not included, unless they are linked to events also submitted by them.', 'events-manager') );
964
+ em_options_select ( __( 'Bookings', 'events-manager'), 'dbem_data_privacy_erase_bookings', $privacy_options, __('This is specific to bookings made by the user, not bookings that may have been made to events they own.', 'events-manager'), $privacy_options );
965
+ ?>
966
+ <thead>
967
+ <tr class="em-header">
968
+ <th colspan="2">
969
+ <h4><?php esc_html_e('Consent', 'events-manager'); ?></h4>
970
+ <p><?php esc_html_e('If you collect personal data, you may want to request their consent. The options below will automatically add checkboxes requesting this consent.', 'events-manager'); ?></p>
971
+ </th>
972
+ </tr>
973
+ </thead>
974
+ <?php
975
+ $consent_options = array(
976
+ 0 => __('Do not show', 'events-manager'),
977
+ 1 => __('Show to all', 'events-manager'),
978
+ 2 => __('Only show to guests', 'events-manager')
979
+ );
980
+ $consent_remember = array(
981
+ 0 => __('Always show and ask for consent', 'events-manager'),
982
+ 1 => __('Remember and hide checkbox', 'events-manager'),
983
+ 2 => __('Remember and show checkbox', 'events-manager')
984
+ );
985
+ em_options_input_text( __('Consent Text', 'events-manager'), 'dbem_data_privacy_consent_text', __('%s will be replaced by a link to your site privacy policy page.', 'events-manager') );
986
+ em_options_select( __('Remembering Consent', 'events-manager'), 'dbem_data_privacy_consent_remember', $consent_remember, __('You can hide or leave the consent box checked for registered users who have provided consent previously.', 'events-manager') );
987
+ em_options_select( __( 'Event Submission Forms', 'events-manager'), 'dbem_data_privacy_consent_events', $privacy_options );
988
+ em_options_select( __( 'Location Submission Forms', 'events-manager'), 'dbem_data_privacy_consent_locations', $privacy_options );
989
+ em_options_select( __( 'Bookings Forms', 'events-manager'), 'dbem_data_privacy_consent_bookings', $privacy_options );
990
+
991
+ echo $save_button;
992
+ ?>
993
+ </table>
994
+ </div> <!-- . inside -->
995
+ </div> <!-- .postbox -->
996
+ <?php
997
+ }
998
  ?>
admin/settings/tabs/general.php CHANGED
@@ -262,7 +262,8 @@
262
  </table>
263
  </div> <!-- . inside -->
264
  </div> <!-- .postbox -->
265
-
 
266
  <?php if ( !is_multisite() ) { em_admin_option_box_uninstall(); } ?>
267
 
268
  <?php if( get_option('dbem_migrate_images') ): ?>
262
  </table>
263
  </div> <!-- . inside -->
264
  </div> <!-- .postbox -->
265
+
266
+ <?php em_admin_option_box_data_privacy(); ?>
267
  <?php if ( !is_multisite() ) { em_admin_option_box_uninstall(); } ?>
268
 
269
  <?php if( get_option('dbem_migrate_images') ): ?>
classes/em-admin-notices.php CHANGED
@@ -37,9 +37,15 @@ class EM_Admin_Notices {
37
  $network = $network && is_multisite(); //make sure we are actually in multisite!
38
  if( is_string($EM_Admin_Notice) ) $EM_Admin_Notice = new EM_Admin_Notice( $EM_Admin_Notice );
39
  if( !$EM_Admin_Notice->name ) return false;
 
40
  $data = $network ? get_site_option('dbem_data') : get_option('dbem_data');
41
- $notices = !empty($data['admin_notices']) ? $data['admin_notices'] : array();
 
42
  $notices_data = $network ? get_site_option('dbem_admin_notices') : get_option('dbem_admin_notices');
 
 
 
 
43
  $notices[$EM_Admin_Notice->name] = !empty($EM_Admin_Notice->when) ? $EM_Admin_Notice->when : 0;
44
  //if no message supplied, we assume it's a hook, possibly with a time-to-show assigned above
45
  if( !empty($EM_Admin_Notice->message) ){
@@ -103,7 +109,7 @@ class EM_Admin_Notices {
103
  public static function admin_notices( $network = false ){
104
  $notices = array();
105
  $data = $network ? get_site_option('dbem_data') : get_option('dbem_data');
106
- $possible_notices = !empty($data['admin_notices']) ? $data['admin_notices'] : array();
107
  //we may have something to show, so we make sure that there's something to show right now
108
  foreach( $possible_notices as $key => $val ){
109
  //to avoid extra loading etc. we weed out time-based notices that aren't triggered right now
37
  $network = $network && is_multisite(); //make sure we are actually in multisite!
38
  if( is_string($EM_Admin_Notice) ) $EM_Admin_Notice = new EM_Admin_Notice( $EM_Admin_Notice );
39
  if( !$EM_Admin_Notice->name ) return false;
40
+ //get options data
41
  $data = $network ? get_site_option('dbem_data') : get_option('dbem_data');
42
+ $data = empty($data) ? array() : maybe_unserialize($data);
43
+ if( !is_array($data)) $data = array();
44
  $notices_data = $network ? get_site_option('dbem_admin_notices') : get_option('dbem_admin_notices');
45
+ $notices_data = empty($notices_data) ? array() : maybe_unserialize($notices_data);
46
+ if( !is_array($notices_data)) $notices_data = array();
47
+ //start building data
48
+ $notices = !empty($data['admin_notices']) ? $data['admin_notices'] : array();
49
  $notices[$EM_Admin_Notice->name] = !empty($EM_Admin_Notice->when) ? $EM_Admin_Notice->when : 0;
50
  //if no message supplied, we assume it's a hook, possibly with a time-to-show assigned above
51
  if( !empty($EM_Admin_Notice->message) ){
109
  public static function admin_notices( $network = false ){
110
  $notices = array();
111
  $data = $network ? get_site_option('dbem_data') : get_option('dbem_data');
112
+ $possible_notices = is_array($data) && !empty($data['admin_notices']) ? $data['admin_notices'] : array();
113
  //we may have something to show, so we make sure that there's something to show right now
114
  foreach( $possible_notices as $key => $val ){
115
  //to avoid extra loading etc. we weed out time-based notices that aren't triggered right now
classes/em-booking.php CHANGED
@@ -73,6 +73,9 @@ class EM_Booking extends EM_Object{
73
  * @var EM_DateTime
74
  */
75
  protected $date;
 
 
 
76
  var $person;
77
  var $required_fields = array('booking_id', 'event_id', 'person_id', 'booking_spaces');
78
  var $feedback_message = "";
73
  * @var EM_DateTime
74
  */
75
  protected $date;
76
+ /**
77
+ * @var EM_Person
78
+ */
79
  var $person;
80
  var $required_fields = array('booking_id', 'event_id', 'person_id', 'booking_spaces');
81
  var $feedback_message = "";
classes/em-datetime.php CHANGED
@@ -34,11 +34,15 @@ class EM_DateTime extends DateTime {
34
  public function __construct( $time = null, $timezone = null ){
35
  //get our EM_DateTimeZone
36
  $timezone = EM_DateTimeZone::create($timezone);
 
 
 
37
  //fix DateTime error if a regular timestamp is supplied without prepended @ symbol
38
  if( is_numeric($time) ) $time = '@'.$time;
39
  //finally, run parent function with our custom timezone
40
  try{
41
  @parent::__construct($time, $timezone);
 
42
  $this->valid = true; //if we get this far, supplied time is valid
43
  }catch( Exception $e ){
44
  //get current date/time in relevant timezone and set valid flag to false
@@ -48,11 +52,8 @@ class EM_DateTime extends DateTime {
48
  $this->setTime(0,0,0);
49
  $this->valid = false;
50
  }
51
- //save timezone name for use in getTimezone()
52
- $this->timezone_name = $timezone->getName();
53
- $this->timezone_manual_offset = $timezone->manual_offset;
54
  //deal with manual UTC offsets, but only if we haven't defaulted to the current timestamp since that would already be a correct relative value
55
- if( $time !== null && $time != 'now' ) $this->handleOffsets($timezone);
56
  }
57
 
58
  /**
34
  public function __construct( $time = null, $timezone = null ){
35
  //get our EM_DateTimeZone
36
  $timezone = EM_DateTimeZone::create($timezone);
37
+ //save timezone name for use in getTimezone()
38
+ $this->timezone_name = $timezone->getName();
39
+ $this->timezone_manual_offset = $timezone->manual_offset;
40
  //fix DateTime error if a regular timestamp is supplied without prepended @ symbol
41
  if( is_numeric($time) ) $time = '@'.$time;
42
  //finally, run parent function with our custom timezone
43
  try{
44
  @parent::__construct($time, $timezone);
45
+ if( substr($time,0,1) == '@' || $time == 'now' ) $this->setTimezone($timezone);
46
  $this->valid = true; //if we get this far, supplied time is valid
47
  }catch( Exception $e ){
48
  //get current date/time in relevant timezone and set valid flag to false
52
  $this->setTime(0,0,0);
53
  $this->valid = false;
54
  }
 
 
 
55
  //deal with manual UTC offsets, but only if we haven't defaulted to the current timestamp since that would already be a correct relative value
56
+ if( $time !== null && $time != 'now' && substr($time,0,1) != '@' ) $this->handleOffsets($timezone);
57
  }
58
 
59
  /**
classes/em-event.php CHANGED
@@ -1795,6 +1795,9 @@ class EM_Event extends EM_Object{
1795
  }elseif ($condition == 'all_day'){
1796
  //is it an all day event
1797
  $show_condition = !empty($this->event_all_day);
 
 
 
1798
  }elseif ($condition == 'logged_in'){
1799
  //user is logged in
1800
  $show_condition = is_user_logged_in();
@@ -2335,14 +2338,22 @@ class EM_Event extends EM_Object{
2335
 
2336
  if( get_option('dbem_categories_enabled') ){
2337
  //for backwards compat and easy use, take over the individual category placeholders with the frirst cat in th elist.
2338
- $EM_Categories = $this->get_categories();
2339
- if( count($EM_Categories->categories) > 0 ){
2340
- $EM_Category = $EM_Categories->get_first();
2341
  }
2342
  if( empty($EM_Category) ) $EM_Category = new EM_Category();
2343
  $event_string = $EM_Category->output($event_string, $target);
2344
  }
2345
 
 
 
 
 
 
 
 
 
 
2346
  //Finally, do the event notes, so that previous placeholders don't get replaced within the content, which may use shortcodes
2347
  if( !empty($desc_replace) ){
2348
  foreach($desc_replace as $full_result => $replacement){
1795
  }elseif ($condition == 'all_day'){
1796
  //is it an all day event
1797
  $show_condition = !empty($this->event_all_day);
1798
+ }elseif ($condition == 'not_all_day'){
1799
+ //is not an all day event
1800
+ $show_condition = !empty($this->event_all_day);
1801
  }elseif ($condition == 'logged_in'){
1802
  //user is logged in
1803
  $show_condition = is_user_logged_in();
2338
 
2339
  if( get_option('dbem_categories_enabled') ){
2340
  //for backwards compat and easy use, take over the individual category placeholders with the frirst cat in th elist.
2341
+ if( count($this->get_categories()) > 0 ){
2342
+ $EM_Category = $this->get_categories()->get_first();
 
2343
  }
2344
  if( empty($EM_Category) ) $EM_Category = new EM_Category();
2345
  $event_string = $EM_Category->output($event_string, $target);
2346
  }
2347
 
2348
+ if( get_option('dbem_tags_enabled') ){
2349
+ $EM_Tags = new EM_Tags($this);
2350
+ if( count($EM_Tags) > 0 ){
2351
+ $EM_Tag = $EM_Tags->get_first();
2352
+ }
2353
+ if( empty($EM_Tag) ) $EM_Tag = new EM_Tag();
2354
+ $event_string = $EM_Tag->output($event_string, $target);
2355
+ }
2356
+
2357
  //Finally, do the event notes, so that previous placeholders don't get replaced within the content, which may use shortcodes
2358
  if( !empty($desc_replace) ){
2359
  foreach($desc_replace as $full_result => $replacement){
classes/em-notices.php CHANGED
@@ -141,7 +141,11 @@
141
  $string = '';
142
  foreach ($this->notices[$type] as $message){
143
  if( !is_array($message['string']) ){
144
- $string .= "<p>{$message['string']}</p>";
 
 
 
 
145
  }else{
146
  $string .= "<p><strong>".$message['title']."</strong><ul>";
147
  foreach($message['string'] as $msg){
141
  $string = '';
142
  foreach ($this->notices[$type] as $message){
143
  if( !is_array($message['string']) ){
144
+ if( preg_match('/<p>/', $message['string']) ){
145
+ $string .= $message['string'];
146
+ }else{
147
+ $string .= "<p>{$message['string']}</p>";
148
+ }
149
  }else{
150
  $string .= "<p><strong>".$message['title']."</strong><ul>";
151
  foreach($message['string'] as $msg){
classes/em-people.php CHANGED
@@ -13,13 +13,17 @@ class EM_People extends EM_Object {
13
  */
14
  public static function delete_user( $id ){
15
  global $wpdb;
 
16
  if( $_REQUEST['delete_option'] == 'reassign' && is_numeric($_REQUEST['reassign_user']) ){
17
  $wpdb->update(EM_EVENTS_TABLE, array('event_owner'=>$_REQUEST['reassign_user']), array('event_owner'=>$id));
 
18
  }else{
19
- //User is being deleted, so we delete their events and cancel their bookings.
20
- $wpdb->query("DELETE FROM ".EM_EVENTS_TABLE." WHERE event_owner=$id");
 
21
  }
22
  //delete their bookings completely
 
23
  $EM_Person = new EM_Person();
24
  $EM_Person->ID = $EM_Person->person_id = $id;
25
  foreach( $EM_Person->get_bookings() as $EM_Booking){
13
  */
14
  public static function delete_user( $id ){
15
  global $wpdb;
16
+ //if events are set to be deleted, we hook in correctly already, if they're meant to be reassigned, we only need to update our tables as WP updated theirs
17
  if( $_REQUEST['delete_option'] == 'reassign' && is_numeric($_REQUEST['reassign_user']) ){
18
  $wpdb->update(EM_EVENTS_TABLE, array('event_owner'=>$_REQUEST['reassign_user']), array('event_owner'=>$id));
19
+ $wpdb->update(EM_LOCATIONS_TABLE, array('location_owner'=>$_REQUEST['reassign_user']), array('location_owner'=>$id));
20
  }else{
21
+ //We delete all the events and locations owned by this user
22
+ foreach( EM_Events::get( array('owner'=>$id, 'status'=>'everything') ) as $EM_Event ) $EM_Event->delete();
23
+ foreach( EM_Locations::get( array('owner'=>$id, 'status'=>'everything', 'ids_only'=>true) ) as $EM_Location ) $EM_Location->delete();
24
  }
25
  //delete their bookings completely
26
+ //@TODO allow option to reassign bookings in a sensible way (i.e. handle personal data being transferred)
27
  $EM_Person = new EM_Person();
28
  $EM_Person->ID = $EM_Person->person_id = $id;
29
  foreach( $EM_Person->get_bookings() as $EM_Booking){
classes/em-person.php CHANGED
@@ -1,7 +1,9 @@
1
  <?php
2
  // TODO make person details more secure and integrate with WP user data
3
  class EM_Person extends WP_User{
4
-
 
 
5
  function __construct( $person_id = false, $username = '', $blog_id='' ){
6
  if( is_array($person_id) ){
7
  if( array_key_exists('person_id',$person_id) ){
@@ -104,7 +106,17 @@ class EM_Person extends WP_User{
104
  <?php
105
  return apply_filters('em_person_display_summary', ob_get_clean(), $this);
106
  }
107
-
 
 
 
 
 
 
 
 
 
 
108
  function get_name(){
109
  $full_name = $this->first_name . " " . $this->last_name ;
110
  $full_name = wp_kses_data(trim($full_name));
1
  <?php
2
  // TODO make person details more secure and integrate with WP user data
3
  class EM_Person extends WP_User{
4
+
5
+ public $custom_user_fields = array();
6
+
7
  function __construct( $person_id = false, $username = '', $blog_id='' ){
8
  if( is_array($person_id) ){
9
  if( array_key_exists('person_id',$person_id) ){
106
  <?php
107
  return apply_filters('em_person_display_summary', ob_get_clean(), $this);
108
  }
109
+
110
+ function get_summary(){
111
+ $summary = array(
112
+ 'dbem_phone' => array('name' => __('Name','events-manager'), 'value' => $this->get_name()),
113
+ 'user_name' => array('name' => __('Email','events-manager'), 'value' => $this->user_email),
114
+ 'user_email' => array('name' => __('Phone','events-manager'), 'value' => $this->phone),
115
+ );
116
+ $summary = array_merge( $summary, $this->custom_user_fields );
117
+ return apply_filters('em_person_get_summary', $summary, $this);
118
+ }
119
+
120
  function get_name(){
121
  $full_name = $this->first_name . " " . $this->last_name ;
122
  $full_name = wp_kses_data(trim($full_name));
classes/em-taxonomy-terms.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- class EM_Taxonomy_Terms extends EM_Object implements Iterator{
3
 
4
  protected $is_ms_global = false;
5
  protected $meta_key = 'event-taxonomy';
@@ -326,4 +326,7 @@ class EM_Taxonomy_Terms extends EM_Object implements Iterator{
326
  $var = ($key !== NULL && $key !== FALSE);
327
  return $var;
328
  }
 
 
 
329
  }
1
  <?php
2
+ class EM_Taxonomy_Terms extends EM_Object implements Iterator, Countable{
3
 
4
  protected $is_ms_global = false;
5
  protected $meta_key = 'event-taxonomy';
326
  $var = ($key !== NULL && $key !== FALSE);
327
  return $var;
328
  }
329
+ public function count(){
330
+ return count($this->terms);
331
+ }
332
  }
em-data-privacy.php ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Outputs a checkbox that can be used to obtain consent.
4
+ * @param EM_Event|EM_Location|EM_Booking|bool $EM_Object
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 );
12
+ if( !empty($consent_given_already) && get_option('dbem_data_privacy_consent_remember') == 1 ) return; //ignore if consent given as per settings
13
+ if( !empty($consent_given_already) && get_option('dbem_data_privacy_consent_remember') == 2 ) $checked = true;
14
+ }
15
+ if( empty($checked) && !empty($_REQUEST['data_privacy_consent']) ) $checked = true;
16
+ ?>
17
+ <p class="input-group input-checkbox input-field-data_privacy_consent">
18
+ <label>
19
+ <input type="checkbox" name="data_privacy_consent" value="1" <?php if( !empty($checked) ) echo 'checked="checked"'; ?>>
20
+ <?php echo $label; ?>
21
+ </label>
22
+ <br style="clear:both;">
23
+ </p>
24
+ <?php
25
+ }
26
+
27
+ function em_data_privacy_consent_hooks(){
28
+ //BOOKINGS
29
+ if( get_option('dbem_data_privacy_consent_bookings') == 1 || ( get_option('dbem_data_privacy_consent_bookings') == 2 && !is_user_logged_in() ) ){
30
+ add_action('em_booking_form_footer', 'em_data_privacy_bookings_consent_checkbox', 9, 1);
31
+ function em_data_privacy_bookings_consent_checkbox(){ em_data_privacy_consent_checkbox(); } //remove passed argument since it's an EM_Event and we'll confuse with submission form
32
+ add_filter('em_booking_get_post', 'em_data_privacy_consent_booking_get_post', 10, 2);
33
+ add_filter('em_booking_validate', 'em_data_privacy_consent_booking_validate', 10, 2);
34
+ add_filter('em_booking_save', 'em_data_privacy_consent_booking_save', 10, 2);
35
+ }
36
+ //EVENTS
37
+ if( get_option('dbem_data_privacy_consent_events') == 1 || ( get_option('dbem_data_privacy_consent_events') == 2 && !is_user_logged_in() ) ){
38
+ add_action('em_front_event_form_footer', 'em_data_privacy_consent_event_checkbox', 9, 1);
39
+ /**
40
+ * Wrapper function in case old overriden templates didn't pass the EM_Event object and depended on global value
41
+ * @param EM_Event $event
42
+ */
43
+ function em_data_privacy_consent_event_checkbox( $event ){
44
+ if( empty($event) ){ global $EM_Event; }
45
+ else{ $EM_Event = $event ; }
46
+ em_data_privacy_consent_checkbox($EM_Event);
47
+ }
48
+ add_action('em_event_get_post_meta', 'em_data_privacy_cpt_get_post', 10, 2);
49
+ add_action('em_event_validate', 'em_data_privacy_cpt_validate', 10, 2);
50
+ add_action('em_event_save', 'em_data_privacy_cpt_save', 10, 2);
51
+ }
52
+ //LOCATIONS
53
+ if( get_option('dbem_data_privacy_consent_locations') == 1 || ( get_option('dbem_data_privacy_consent_events') == 2 && !is_user_logged_in() ) ){
54
+ add_action('em_front_location_form_footer', 'em_data_privacy_consent_location_checkbox', 9, 1); /**
55
+ * Wrapper function in case old overriden templates didn't pass the EM_Location object and depended on global value
56
+ * @param EM_Location $location
57
+ */
58
+ function em_data_privacy_consent_location_checkbox( $location ){
59
+ if( empty($location) ){ global $EM_Location; }
60
+ else{ $EM_Location = $location ; }
61
+ em_data_privacy_consent_checkbox($EM_Location);
62
+ }
63
+ add_action('em_location_get_post_meta', 'em_data_privacy_cpt_get_post', 10, 2);
64
+ add_action('em_location_validate', 'em_data_privacy_cpt_validate', 10, 2);
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.
72
+ * @param bool $result
73
+ * @param EM_Booking $EM_Booking
74
+ * @return bool
75
+ */
76
+ function em_data_privacy_consent_booking_get_post( $result, $EM_Booking ){
77
+ if( !empty($_REQUEST['data_privacy_consent']) ){
78
+ $EM_Booking->booking_meta['consent'] = true;
79
+ }
80
+ return $result;
81
+ }
82
+
83
+ /**
84
+ * Validates a bookng to ensure consent is/was given.
85
+ * @param bool $result
86
+ * @param EM_Booking $EM_Booking
87
+ * @return bool
88
+ */
89
+ function em_data_privacy_consent_booking_validate( $result, $EM_Booking ){
90
+ if( is_user_logged_in() ){
91
+ //check if consent was previously given and ignore if settings dictate so
92
+ $consent_given_already = get_user_meta( get_current_user_id(), 'em_data_privacy_consent', true );
93
+ if( !empty($consent_given_already) && get_option('dbem_data_privacy_consent_remember') == 1 ) return $result; //ignore if consent given as per settings
94
+ }
95
+ if( empty($EM_Booking->booking_meta['consent']) ){
96
+ $EM_Booking->add_error( sprintf(__('You must allow us to collect and store your data in order for us to process your booking.', 'events-manager')) );
97
+ $result = false;
98
+ }
99
+ return $result;
100
+ }
101
+
102
+ /**
103
+ * Updates or adds the consent date of user account meta if booking was submitted by a user and consent was given.
104
+ * @param bool $result
105
+ * @param EM_Booking $EM_Booking
106
+ * @return bool
107
+ */
108
+ function em_data_privacy_consent_booking_save( $result, $EM_Booking ){
109
+ if( $result ){
110
+ if( $EM_Booking->person_id != 0 ){
111
+ update_user_meta( $EM_Booking->person_id, 'em_data_privacy_consent', current_time('mysql') );
112
+ }
113
+ }
114
+ return $result;
115
+ }
116
+
117
+ /**
118
+ * Save consent to event or location object
119
+ * @param bool $result
120
+ * @param EM_Event|EM_Location $EM_Object
121
+ * @return bool
122
+ */
123
+ function em_data_privacy_cpt_get_post($result, $EM_Object ){
124
+ if( !empty($_REQUEST['data_privacy_consent']) ){
125
+ if( get_class($EM_Object) == 'EM_Event' ){
126
+ $EM_Object->event_attributes['_consent_given'] = 1;
127
+ $EM_Object->get_location()->location_attributes['_consent_given'] = 1;
128
+ }else{
129
+ $EM_Object->location_attributes['_consent_given'] = 1;
130
+ }
131
+ }
132
+ return $result;
133
+ }
134
+
135
+ /**
136
+ * Validate the consent provided to events and locations.
137
+ * @param bool $result
138
+ * @param EM_Event|EM_Location $EM_Object
139
+ * @return bool
140
+ */
141
+ function em_data_privacy_cpt_validate( $result, $EM_Object ){
142
+ if( !empty($EM_Object->post_id) ) return $result;
143
+ if( is_user_logged_in() ){
144
+ //check if consent was previously given and ignore if settings dictate so
145
+ $consent_given_already = get_user_meta( get_current_user_id(), 'em_data_privacy_consent', true );
146
+ if( !empty($consent_given_already) && get_option('dbem_data_privacy_consent_remember') == 1 ) return $result; //ignore if consent given as per settings
147
+ }
148
+ $attributes = get_class($EM_Object) == 'EM_Event' ? 'event_attributes':'location_attributes';
149
+ if( empty($EM_Object->{$attributes}['_consent_given']) ){
150
+ $EM_Object->add_error( sprintf(__('Please check the consent box so we can collect and store your submitted information.', 'events-manager')) );
151
+ $result = false;
152
+ }
153
+ return $result;
154
+ }
155
+
156
+ /**
157
+ * When an event or location is saved and consent is given or supplied again, update user account with latest consent date IF the object isn't associated with an anonymous user.
158
+ * @param bool $result
159
+ * @param EM_Event|EM_Location $EM_Object
160
+ * @return bool
161
+ */
162
+ function em_data_privacy_cpt_save( $result, $EM_Object ){
163
+ $attributes = get_class($EM_Object) == 'EM_Event' ? 'event_attributes':'location_attributes';
164
+ if( $result && !empty($EM_Object->{$attributes}['_consent_given'])){
165
+ if( !get_option('dbem_events_anonymous_submissions') || $EM_Object->post_author != get_option('dbem_events_anonymous_user') ){
166
+ update_user_meta( $EM_Object->post_author, 'em_data_privacy_consent', current_time('mysql') );
167
+ }
168
+ }
169
+ return $result;
170
+ }
em-events.php CHANGED
@@ -119,7 +119,7 @@ function em_content($page_content) {
119
  $content = str_replace('CONTENTS',$content,$page_content);
120
  }
121
  if(get_option('dbem_credits')){
122
- $content .= '<p style="color:#999; font-size:11px;">Powered by <a href="http://wp-events-plugin.com" style="color:#999;" target="_blank">Events Manager</a></p>';
123
  }
124
  }
125
  return apply_filters('em_content', '<div id="em-wrapper">'.$content.'</div>');
119
  $content = str_replace('CONTENTS',$content,$page_content);
120
  }
121
  if(get_option('dbem_credits')){
122
+ $content .= '<p style="color:#999; font-size:11px;">Powered by <a href="https://wp-events-plugin.com" style="color:#999;" target="_blank">Events Manager</a></p>';
123
  }
124
  }
125
  return apply_filters('em_content', '<div id="em-wrapper">'.$content.'</div>');
em-functions.php CHANGED
@@ -343,7 +343,7 @@ function em_get_attributes($lattributes = false){
343
  }
344
  }
345
  }
346
- return apply_filters('em_get_attributes', $attributes, $matches);
347
  }
348
 
349
  /**
343
  }
344
  }
345
  }
346
+ return apply_filters('em_get_attributes', $attributes, $matches, $lattributes);
347
  }
348
 
349
  /**
em-ical.php CHANGED
@@ -73,7 +73,7 @@
73
  * @return string
74
  */
75
  function em_mb_ical_wordwrap($string){
76
- if( !defined('EM_MB_ICAL_WORDWRAP') || !EM_MB_ICAL_WORDWRAP ){
77
  /*
78
  // Match anything 1 to $width chars long followed by whitespace or EOS, otherwise match anything $width chars long
79
  $search = '/(.{1,74})(?:\s|$)|(.{74})/uS';
73
  * @return string
74
  */
75
  function em_mb_ical_wordwrap($string){
76
+ if( !defined('EM_MB_ICAL_WORDWRAP') || EM_MB_ICAL_WORDWRAP ){
77
  /*
78
  // Match anything 1 to $width chars long followed by whitespace or EOS, otherwise match anything $width chars long
79
  $search = '/(.{1,74})(?:\s|$)|(.{74})/uS';
em-install.php CHANGED
@@ -496,23 +496,23 @@ function em_add_options() {
496
  'dbem_display_calendar_in_events_page' => 0,
497
  'dbem_single_event_format' => '<div style="float:right; margin:0px 0px 15px 15px;">#_LOCATIONMAP</div>
498
  <p>
499
- <strong>'.__('Date/Time','events-manager').'</strong><br/>
500
  Date(s) - #_EVENTDATES<br /><i>#_EVENTTIMES</i>
501
  </p>
502
  {has_location}
503
  <p>
504
- <strong>'.__('Location','events-manager').'</strong><br/>
505
  #_LOCATIONLINK
506
  </p>
507
  {/has_location}
508
  <p>
509
- <strong>'.__('Categories','events-manager').'</strong>
510
  #_CATEGORIES
511
  </p>
512
  <br style="clear:both" />
513
  #_EVENTNOTES
514
  {has_bookings}
515
- <h3>Bookings</h3>
516
  #_BOOKINGFORM
517
  {/has_bookings}',
518
  'dbem_event_excerpt_format' => '#_EVENTDATES @ #_EVENTTIMES - #_EVENTEXCERPT',
@@ -823,7 +823,19 @@ function em_add_options() {
823
  //feedback reminder
824
  'dbem_feedback_reminder' => time(),
825
  'dbem_events_page_ajax' => 0,
826
- 'dbem_conditional_recursions' => 1
 
 
 
 
 
 
 
 
 
 
 
 
827
  );
828
 
829
  //do date js according to locale:
@@ -1067,6 +1079,12 @@ function em_upgrade_current_installation(){
1067
  ));
1068
  EM_Admin_Notices::add($EM_Admin_Notice, is_multisite());
1069
  }
 
 
 
 
 
 
1070
  }
1071
 
1072
  function em_set_mass_caps( $roles, $caps ){
496
  'dbem_display_calendar_in_events_page' => 0,
497
  'dbem_single_event_format' => '<div style="float:right; margin:0px 0px 15px 15px;">#_LOCATIONMAP</div>
498
  <p>
499
+ <strong>'.esc_html__('Date/Time','events-manager').'</strong><br/>
500
  Date(s) - #_EVENTDATES<br /><i>#_EVENTTIMES</i>
501
  </p>
502
  {has_location}
503
  <p>
504
+ <strong>'.esc_html__('Location','events-manager').'</strong><br/>
505
  #_LOCATIONLINK
506
  </p>
507
  {/has_location}
508
  <p>
509
+ <strong>'.esc_html__('Categories','events-manager').'</strong>
510
  #_CATEGORIES
511
  </p>
512
  <br style="clear:both" />
513
  #_EVENTNOTES
514
  {has_bookings}
515
+ <h3>'.esc_html__('Bookings','events-manager').'</h3>
516
  #_BOOKINGFORM
517
  {/has_bookings}',
518
  'dbem_event_excerpt_format' => '#_EVENTDATES @ #_EVENTTIMES - #_EVENTEXCERPT',
823
  //feedback reminder
824
  'dbem_feedback_reminder' => time(),
825
  'dbem_events_page_ajax' => 0,
826
+ 'dbem_conditional_recursions' => 1,
827
+ //data privacy/protection
828
+ 'dbem_data_privacy_consent_text' => esc_html__('I consent to my submitted data being collected and stored as outlined by the site %s.','events-manager'),
829
+ 'dbem_data_privacy_consent_remember' => 1,
830
+ 'dbem_data_privacy_consent_events' => 1,
831
+ 'dbem_data_privacy_consent_locations' => 1,
832
+ 'dbem_data_privacy_consent_bookings' => 1,
833
+ 'dbem_data_privacy_export_events' => 1,
834
+ 'dbem_data_privacy_export_locations' => 1,
835
+ 'dbem_data_privacy_export_bookings' => 1,
836
+ 'dbem_data_privacy_erase_events' => 1,
837
+ 'dbem_data_privacy_erase_locations' => 1,
838
+ 'dbem_data_privacy_erase_bookings' => 1
839
  );
840
 
841
  //do date js according to locale:
1079
  ));
1080
  EM_Admin_Notices::add($EM_Admin_Notice, is_multisite());
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
+ }
1088
  }
1089
 
1090
  function em_set_mass_caps( $roles, $caps ){
events-manager.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  /*
3
  Plugin Name: Events Manager
4
- Version: 5.9.2
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.92); //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
@@ -82,6 +82,7 @@ include("em-functions.php");
82
  include("em-ical.php");
83
  include("em-shortcode.php");
84
  include("em-template-tags.php");
 
85
  include("multilingual/em-ml.php");
86
  //Widgets
87
  include("widgets/em-events.php");
@@ -124,6 +125,7 @@ if( is_admin() ){
124
  include('admin/em-docs.php');
125
  include('admin/em-help.php');
126
  include('admin/em-options.php');
 
127
  if( is_multisite() ){
128
  include('admin/em-ms-options.php');
129
  }
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
  */
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
82
  include("em-ical.php");
83
  include("em-shortcode.php");
84
  include("em-template-tags.php");
85
+ include("em-data-privacy.php");
86
  include("multilingual/em-ml.php");
87
  //Widgets
88
  include("widgets/em-events.php");
125
  include('admin/em-docs.php');
126
  include('admin/em-help.php');
127
  include('admin/em-options.php');
128
+ include('admin/em-data-privacy.php');
129
  if( is_multisite() ){
130
  include('admin/em-ms-options.php');
131
  }
includes/css/events_manager.css CHANGED
@@ -94,7 +94,8 @@ div#em-loading { position:absolute; width:100%; height:100%; background:#FFFFFF
94
  .em-booking-form label { display:block; float:left; }
95
  .em-booking-form span.input-group input { margin-left:-20px; }
96
  .em-booking-form span.input-group { display:block; margin-left:120px; }
97
- .em-booking-form label { display:inline-block; width:100px; }
 
98
  .em-booking-form-details .em-booking-submit { width:auto; }
99
  /* Tickets */
100
  .em-tickets { margin-bottom:20px; }
94
  .em-booking-form label { display:block; float:left; }
95
  .em-booking-form span.input-group input { margin-left:-20px; }
96
  .em-booking-form span.input-group { display:block; margin-left:120px; }
97
+ .em-booking-form label { display:inline-block; width:100px; }
98
+ .em-booking-form p.input-field-data_privacy_consent label { display:block; width:100%; }
99
  .em-booking-form-details .em-booking-submit { width:auto; }
100
  /* Tickets */
101
  .em-tickets { margin-bottom:20px; }
readme.txt CHANGED
@@ -4,8 +4,8 @@ Donate link: http://wp-events-plugin.com
4
  Tags: bookings, calendar, tickets, events, buddypress, event management, google maps, maps, locations, registration
5
  Text Domain: events-manager
6
  Requires at least: 3.5
7
- Tested up to: 4.9
8
- Stable tag: 5.9.2
9
 
10
  Fully featured event registration management including recurring events, locations management, calendar, Google map integration, booking management
11
 
@@ -50,13 +50,23 @@ Version 5 now makes events and locations WordPress Custom Post Types, allowing f
50
  * Lots of documentation and tutorials
51
  * And much more!
52
 
 
 
 
 
 
 
 
 
53
  = Go Pro =
54
- We have also released an add-on for Events Manager which not only demonstrates the flexibility of Events Manager, but also adds some important features:
55
 
56
  * PayPal, Authorize.net and Offline Payments
57
  * Custom booking forms
 
58
  * Coupon Codes
59
- * Faster support via private forums
 
60
 
61
  For more information or to go pro, [visit our plugin website](http://wp-events-plugin.com/features/).
62
 
@@ -100,6 +110,15 @@ See our [FAQ](http://wp-events-plugin.com/documentation/faq/) page, which is upd
100
  6. Manage attendees with various booking reports
101
 
102
  == Changelog ==
 
 
 
 
 
 
 
 
 
103
  = 5.9.2 =
104
  * fixed some instances where PHP 5.2 outputs incorrect times due to other plugins changing server timezones
105
  * fixed scope issues with PHP 5.2 when calculating start/end of month dates
@@ -109,7 +128,7 @@ See our [FAQ](http://wp-events-plugin.com/documentation/faq/) page, which is upd
109
  * added notice when viewing bookings made in another language
110
  * added booking admin table column for language used in booking
111
  * fixed some minor PHP notices preventing event submissions/edits with a new location if display_errors are enabled
112
- * updated EM_Notices to use new class names for notices output in WP Dasbhoard
113
  * added filters for all post type and custom taxonomy arrays used in initial post type and custom taxonomy registration functions (see em-posts.php)
114
 
115
  = 5.9.1 =
4
  Tags: bookings, calendar, tickets, events, buddypress, event management, google maps, maps, locations, registration
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
 
50
  * Lots of documentation and tutorials
51
  * And much more!
52
 
53
+ = Data Privacy and GDPR Compliance =
54
+ We provide the tools to [help you be GDPR compliant](http://wp-events-plugin.com/documentation/data-privacy-gdpr-compliance/), including:
55
+
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:
63
 
64
  * PayPal, Authorize.net and Offline Payments
65
  * Custom booking forms
66
+ * Individual Attendee custom forms
67
  * Coupon Codes
68
+ * Custom booking email per event and gateway
69
+ * Faster support via private Pro forums
70
 
71
  For more information or to go pro, [visit our plugin website](http://wp-events-plugin.com/features/).
72
 
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
116
+ * added location attributes array to em_get_attributes filter
117
+ * fixed EM_MB_ICAL_WORDWRAP incorrectly not applying multibyte wordwraps if set to true
118
+ * added 'not_all_day' conditional placeholder
119
+ * made EM_Taxonomy_Terms objects countable
120
+ * fixed tag placeholders not getting parsed in event format such as #_TAGIMAGE
121
+
122
  = 5.9.2 =
123
  * fixed some instances where PHP 5.2 outputs incorrect times due to other plugins changing server timezones
124
  * fixed scope issues with PHP 5.2 when calculating start/end of month dates
128
  * added notice when viewing bookings made in another language
129
  * added booking admin table column for language used in booking
130
  * fixed some minor PHP notices preventing event submissions/edits with a new location if display_errors are enabled
131
+ * updated EM_Notices to use new class names for notices output in WP Dashboard
132
  * added filters for all post type and custom taxonomy arrays used in initial post type and custom taxonomy registration functions (see em-posts.php)
133
 
134
  = 5.9.1 =
templates/forms/event-editor.php CHANGED
@@ -25,7 +25,7 @@ if( !empty($_REQUEST['success']) ){
25
  ?>
26
  <form enctype='multipart/form-data' id="event-form" class="em-event-admin-editor <?php if( $EM_Event->is_recurring() ) echo 'em-event-admin-recurring' ?>" method="post" action="<?php echo esc_url(add_query_arg(array('success'=>null))); ?>">
27
  <div class="wrap">
28
- <?php do_action('em_front_event_form_header'); ?>
29
  <?php if(get_option('dbem_events_anonymous_submissions') && !is_user_logged_in()): ?>
30
  <h3 class="event-form-submitter"><?php esc_html_e( 'Your Details', 'events-manager'); ?></h3>
31
  <div class="inside event-form-submitter">
@@ -102,7 +102,7 @@ if( !empty($_REQUEST['success']) ){
102
  <!-- END Bookings -->
103
  <?php endif; ?>
104
 
105
- <?php do_action('em_front_event_form_footer'); ?>
106
  </div>
107
  <p class="submit">
108
  <?php if( empty($EM_Event->event_id) ): ?>
25
  ?>
26
  <form enctype='multipart/form-data' id="event-form" class="em-event-admin-editor <?php if( $EM_Event->is_recurring() ) echo 'em-event-admin-recurring' ?>" method="post" action="<?php echo esc_url(add_query_arg(array('success'=>null))); ?>">
27
  <div class="wrap">
28
+ <?php do_action('em_front_event_form_header', $EM_Event); ?>
29
  <?php if(get_option('dbem_events_anonymous_submissions') && !is_user_logged_in()): ?>
30
  <h3 class="event-form-submitter"><?php esc_html_e( 'Your Details', 'events-manager'); ?></h3>
31
  <div class="inside event-form-submitter">
102
  <!-- END Bookings -->
103
  <?php endif; ?>
104
 
105
+ <?php do_action('em_front_event_form_footer', $EM_Event); ?>
106
  </div>
107
  <p class="submit">
108
  <?php if( empty($EM_Event->event_id) ): ?>
templates/forms/location-editor.php CHANGED
@@ -21,7 +21,7 @@ if(!is_admin()) echo $EM_Notices;
21
  <input type='hidden' name='_wpnonce' value='<?php echo wp_create_nonce('location_save'); ?>' />
22
  <input type='hidden' name='location_id' value='<?php echo $EM_Location->location_id ?>'/>
23
 
24
- <?php do_action('em_front_location_form_header'); ?>
25
  <h3 class="location-form-name"><?php esc_html_e( 'Location Name', 'events-manager'); ?></h3>
26
  <div class="inside location-form-name">
27
  <input name='location_name' id='location-name' type='text' value='<?php echo esc_attr($EM_Location->location_name, ENT_QUOTES); ?>' size='40' />
@@ -55,7 +55,7 @@ if(!is_admin()) echo $EM_Notices;
55
  </div>
56
  <?php endif; ?>
57
 
58
- <?php do_action('em_front_location_form_footer'); ?>
59
 
60
  <?php if( !empty($_REQUEST['redirect_to']) ): ?>
61
  <input type="hidden" name="redirect_to" value="<?php echo esc_attr($_REQUEST['redirect_to']); ?>" />
21
  <input type='hidden' name='_wpnonce' value='<?php echo wp_create_nonce('location_save'); ?>' />
22
  <input type='hidden' name='location_id' value='<?php echo $EM_Location->location_id ?>'/>
23
 
24
+ <?php do_action('em_front_location_form_header', $EM_Location); ?>
25
  <h3 class="location-form-name"><?php esc_html_e( 'Location Name', 'events-manager'); ?></h3>
26
  <div class="inside location-form-name">
27
  <input name='location_name' id='location-name' type='text' value='<?php echo esc_attr($EM_Location->location_name, ENT_QUOTES); ?>' size='40' />
55
  </div>
56
  <?php endif; ?>
57
 
58
+ <?php do_action('em_front_location_form_footer', $EM_Location); ?>
59
 
60
  <?php if( !empty($_REQUEST['redirect_to']) ): ?>
61
  <input type="hidden" name="redirect_to" value="<?php echo esc_attr($_REQUEST['redirect_to']); ?>" />