Events Manager - Version 5.9.8

Version Description

  • added Location Types including URL and (via external free add-on) Zoom support!
  • added native OAuth support for third party integrations (e.g. Zoom)
  • added $EM_Event object to booking form template actions
  • changed $EM_Booking->booking_status to protected so that status returns 1 even if approvals are disabled
  • fixed XSS vulnerability (kudos to Jakob Wierzba)
  • fixed potential SQL injection vulnerability (kudos to Antony Garand from Godaddy)
  • fixed fatal errors in BuddyPress if notifications are disabled
  • fixed minor PHP warning
  • fixed Yoast SEO 14.0 conflict
Download this release

Release Info

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

Code changes from version 5.9.7.3 to 5.9.8

admin/em-options.php CHANGED
@@ -19,6 +19,10 @@ function em_options_save(){
19
  //Do nothing, keep old setting.
20
  }elseif( ($postKey == 'dbem_category_default_color' || $postKey == 'dbem_tag_default_color') && !sanitize_hex_color($postValue) ){
21
  $EM_Notices->add_error( sprintf(esc_html_x('Colors must be in a valid %s format, such as #FF00EE.', 'hex format', 'events-manager'), '<a href="http://en.wikipedia.org/wiki/Web_colors">hex</a>').' '. esc_html__('This setting was not changed.', 'events-manager'), true);
 
 
 
 
22
  }else{
23
  //TODO slashes being added?
24
  if( is_array($postValue) ){
@@ -845,7 +849,7 @@ function em_admin_option_box_uninstall(){
845
  <th style="text-align:right;">
846
  <a href="<?php echo $export_settings_url; ?>" class="button-secondary"><?php esc_html_e('Export Settings','events-manager'); ?></a>
847
  </th>
848
- <td><?php esc_html_e('Export your Events Manager settings and restore them here or on another website running this plugin.','events-manager'); ?></td>
849
  </tr>
850
  </table>
851
 
19
  //Do nothing, keep old setting.
20
  }elseif( ($postKey == 'dbem_category_default_color' || $postKey == 'dbem_tag_default_color') && !sanitize_hex_color($postValue) ){
21
  $EM_Notices->add_error( sprintf(esc_html_x('Colors must be in a valid %s format, such as #FF00EE.', 'hex format', 'events-manager'), '<a href="http://en.wikipedia.org/wiki/Web_colors">hex</a>').' '. esc_html__('This setting was not changed.', 'events-manager'), true);
22
+ }elseif( $postKey == 'dbem_oauth' && is_array($postValue) ){
23
+ foreach($postValue as $postValue_key=>$postValue_val){
24
+ EM_Options::set($postValue_key, wp_unslash($postValue_val), 'dbem_oauth');
25
+ }
26
  }else{
27
  //TODO slashes being added?
28
  if( is_array($postValue) ){
849
  <th style="text-align:right;">
850
  <a href="<?php echo $export_settings_url; ?>" class="button-secondary"><?php esc_html_e('Export Settings','events-manager'); ?></a>
851
  </th>
852
+ <td><p><?php esc_html_e('Export your Events Manager settings and restore them here or on another website running this plugin.','events-manager'); ?></p></td>
853
  </tr>
854
  </table>
855
 
admin/settings/tabs/general.php CHANGED
@@ -50,23 +50,6 @@
50
  if( get_option('dbem_attributes_enabled') ){
51
  em_options_textarea ( sprintf(__( '%s Attributes', 'events-manager'),__('Event','events-manager')), 'dbem_placeholders_custom', sprintf(__( "You can also add event attributes here, one per line in this format <code>#_ATT{key}</code>. They will not appear on event pages unless you insert them into another template below, but you may want to store extra information about an event for other uses. <a href='%s'>More information on placeholders.</a>", 'events-manager'), EM_ADMIN_URL .'&amp;page=events-manager-help') );
52
  }
53
- if( get_option('dbem_locations_enabled') ){
54
- /*default location*/
55
- if( defined('EM_OPTIMIZE_SETTINGS_PAGE_LOCATIONS') && EM_OPTIMIZE_SETTINGS_PAGE_LOCATIONS ){
56
- em_options_input_text( __( 'Default Location', 'events-manager'), 'dbem_default_location', __('Please enter your Location ID, or leave blank for no location.','events-manager').' '.__( 'This option allows you to select the default location when adding an event.','events-manager')." ".__('(not applicable with event ownership on presently, coming soon!)','events-manager') );
57
- }else{
58
- $location_options = array();
59
- $location_options[0] = __('no default location','events-manager');
60
- $EM_Locations = EM_Locations::get();
61
- foreach($EM_Locations as $EM_Location){
62
- $location_options[$EM_Location->location_id] = $EM_Location->location_name;
63
- }
64
- em_options_select ( __( 'Default Location', 'events-manager'), 'dbem_default_location', $location_options, __('Please enter your Location ID.','events-manager').' '.__( 'This option allows you to select the default location when adding an event.','events-manager')." ".__('(not applicable with event ownership on presently, coming soon!)','events-manager') );
65
- }
66
-
67
- /*default location country*/
68
- em_options_select ( __( 'Default Location Country', 'events-manager'), 'dbem_location_default_country', em_get_countries(__('no default country', 'events-manager')), __('If you select a default country, that will be pre-selected when creating a new location.','events-manager') );
69
- }
70
  ?>
71
  <tr class="em-header">
72
  <td colspan="2">
@@ -74,17 +57,69 @@
74
  </td>
75
  </tr>
76
  <?php
77
- em_options_radio_binary ( __( 'Enable locations?', 'events-manager'), 'dbem_locations_enabled', __( 'If you disable locations, bear in mind that you should remove your location page, shortcodes and related placeholders from your <a href="#formats" class="nav-tab-link" rel="#em-menu-formats">formats</a>.','events-manager') );
78
- if( get_option('dbem_locations_enabled') ){
79
- em_options_radio_binary ( __( 'Require locations for events?', 'events-manager'), 'dbem_require_location', __( 'Setting this to no will allow you to submit events without locations. You can use the <code>{no_location}...{/no_location}</code> or <code>{has_location}..{/has_location}</code> conditional placeholder to selectively display location information.','events-manager') );
80
- em_options_radio_binary ( __( 'Use dropdown for locations?', 'events-manager'), 'dbem_use_select_for_locations', __( 'Select yes to select location from a drop-down menu; location selection will be faster, but you will lose the ability to insert locations with events','events-manager') );
81
- em_options_radio_binary ( sprintf(__( 'Enable %s attributes?', 'events-manager'),__('location','events-manager')), 'dbem_location_attributes_enabled', __( 'Select yes to enable the attributes feature','events-manager') );
82
- em_options_radio_binary ( sprintf(__( 'Enable %s custom fields?', 'events-manager'),__('location','events-manager')), 'dbem_cp_locations_custom_fields', __( 'Custom fields are the same as attributes, except you cannot restrict specific values, users can add any kind of custom field name/value pair. Only available in the WordPress admin area.','events-manager') );
83
- if( get_option('dbem_location_attributes_enabled') ){
84
- em_options_textarea ( sprintf(__( '%s Attributes', 'events-manager'),__('Location','events-manager')), 'dbem_location_placeholders_custom', sprintf(__( "You can also add location attributes here, one per line in this format <code>#_LATT{key}</code>. They will not appear on location pages unless you insert them into another template below, but you may want to store extra information about an event for other uses. <a href='%s'>More information on placeholders.</a>", 'events-manager'), EM_ADMIN_URL .'&amp;page=events-manager-help') );
85
- }
86
- }
87
  ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  <tr class="em-header">
89
  <td colspan="2">
90
  <h4><?php echo sprintf(__('%s Settings','events-manager'),__('Other','events-manager')); ?></h4>
50
  if( get_option('dbem_attributes_enabled') ){
51
  em_options_textarea ( sprintf(__( '%s Attributes', 'events-manager'),__('Event','events-manager')), 'dbem_placeholders_custom', sprintf(__( "You can also add event attributes here, one per line in this format <code>#_ATT{key}</code>. They will not appear on event pages unless you insert them into another template below, but you may want to store extra information about an event for other uses. <a href='%s'>More information on placeholders.</a>", 'events-manager'), EM_ADMIN_URL .'&amp;page=events-manager-help') );
52
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  ?>
54
  <tr class="em-header">
55
  <td colspan="2">
57
  </td>
58
  </tr>
59
  <?php
60
+ em_options_radio_binary ( __( 'Enable locations?', 'events-manager'), 'dbem_locations_enabled', __( 'If you disable locations, bear in mind that you should remove your location page, shortcodes and related placeholders from your <a href="#formats" class="nav-tab-link" rel="#em-menu-formats">formats</a>.','events-manager'), '', '.em-location-type-option' );
 
 
 
 
 
 
 
 
 
61
  ?>
62
+ <tbody class="em-location-type-option">
63
+ <?php
64
+ em_options_radio_binary ( __( 'Require locations for events?', 'events-manager'), 'dbem_require_location', __( 'Setting this to no will allow you to submit events without locations. You can use the <code>{no_location}...{/no_location}</code> or <code>{has_location}..{/has_location}</code> conditional placeholder to selectively display location information.','events-manager') );
65
+ ?>
66
+ <tr valign="top" id='dbem_location_types_row'>
67
+ <th scope="row"><?php esc_html_e('Location Types', 'events-manager'); ?></th>
68
+ <td>
69
+ <?php
70
+ $location_types = get_option('dbem_location_types', array());
71
+ ?>
72
+ <label>
73
+ <input type="checkbox" name="dbem_location_types[location]" value="1" <?php if( !empty($location_types['location']) ) echo 'checked'; ?> data-trigger=".em-location-type-option-physical" class="em-trigger">
74
+ <?php esc_html_e('Physicial Locations', 'events-manager'); ?>
75
+ </label>
76
+ <?php foreach (EM_Event_Locations\Event_Locations::get_types() as $event_location_type => $EM_Event_Location_Class): /* @var EM_Event_Locations\Event_Location $EM_Event_Location_Class */ ?>
77
+ <br>
78
+ <label>
79
+ <input type="checkbox" name="dbem_location_types[<?php echo esc_attr($event_location_type); ?>]" value="1" <?php if( !empty($location_types[$event_location_type]) ) echo 'checked'; ?> data-trigger=".em-location-type-option-<?php echo esc_attr($event_location_type); ?>" class="em-trigger">
80
+ <?php echo $EM_Event_Location_Class::get_label('plural'); ?>
81
+ </label>
82
+ <?php endforeach; ?>
83
+ <p><em><?php echo sprintf( esc_html__('You can allow different location types which can be assigned to an event. For more information see our %s.', 'events-manager'), '<a href="http://wp-events-plugin.com/documentation/location-types/" target="_blank">'.esc_html__('documentation', 'events-manager').'</a>'); ?></em></p>
84
+ </td>
85
+ </tr>
86
+ </tbody>
87
+ </table>
88
+ <table class="form-table em-location-type-option">
89
+ <tbody class="em-location-type-option-physical">
90
+ <tr class="em-subheader">
91
+ <td colspan="2">
92
+ <h5><?php esc_html_e('Physicial Locations', 'events-manager'); ?></h5>
93
+ </td>
94
+ </tr>
95
+ <?php
96
+ if( get_option('dbem_locations_enabled') ){
97
+ em_options_radio_binary ( __( 'Use dropdown for locations?', 'events-manager'), 'dbem_use_select_for_locations', __( 'Select yes to select location from a drop-down menu; location selection will be faster, but you will lose the ability to insert locations with events','events-manager') );
98
+ em_options_radio_binary ( sprintf(__( 'Enable %s attributes?', 'events-manager'),__('location','events-manager')), 'dbem_location_attributes_enabled', __( 'Select yes to enable the attributes feature','events-manager') );
99
+ em_options_radio_binary ( sprintf(__( 'Enable %s custom fields?', 'events-manager'),__('location','events-manager')), 'dbem_cp_locations_custom_fields', __( 'Custom fields are the same as attributes, except you cannot restrict specific values, users can add any kind of custom field name/value pair. Only available in the WordPress admin area.','events-manager') );
100
+ if( get_option('dbem_location_attributes_enabled') ){
101
+ em_options_textarea ( sprintf(__( '%s Attributes', 'events-manager'),__('Location','events-manager')), 'dbem_location_placeholders_custom', sprintf(__( "You can also add location attributes here, one per line in this format <code>#_LATT{key}</code>. They will not appear on location pages unless you insert them into another template below, but you may want to store extra information about an event for other uses. <a href='%s'>More information on placeholders.</a>", 'events-manager'), EM_ADMIN_URL .'&amp;page=events-manager-help') );
102
+ }
103
+ /*default location*/
104
+ if( defined('EM_OPTIMIZE_SETTINGS_PAGE_LOCATIONS') && EM_OPTIMIZE_SETTINGS_PAGE_LOCATIONS ){
105
+ em_options_input_text( __( 'Default Location', 'events-manager'), 'dbem_default_location', __('Please enter your Location ID, or leave blank for no location.','events-manager').' '.__( 'This option allows you to select the default location when adding an event.','events-manager')." ".__('(not applicable with event ownership on presently, coming soon!)','events-manager') );
106
+ }else{
107
+ $location_options = array();
108
+ $location_options[0] = __('no default location','events-manager');
109
+ $EM_Locations = EM_Locations::get();
110
+ foreach($EM_Locations as $EM_Location){
111
+ $location_options[$EM_Location->location_id] = $EM_Location->location_name;
112
+ }
113
+ em_options_select ( __( 'Default Location', 'events-manager'), 'dbem_default_location', $location_options, __('Please enter your Location ID.','events-manager').' '.__( 'This option allows you to select the default location when adding an event.','events-manager')." ".__('(not applicable with event ownership on presently, coming soon!)','events-manager') );
114
+ }
115
+
116
+ /*default location country*/
117
+ em_options_select ( __( 'Default Location Country', 'events-manager'), 'dbem_location_default_country', em_get_countries(__('no default country', 'events-manager')), __('If you select a default country, that will be pre-selected when creating a new location.','events-manager') );
118
+ }
119
+ ?>
120
+ </tbody>
121
+ </table>
122
+ <table class="form-table">
123
  <tr class="em-header">
124
  <td colspan="2">
125
  <h4><?php echo sprintf(__('%s Settings','events-manager'),__('Other','events-manager')); ?></h4>
buddypress/screens/my-bookings.php CHANGED
@@ -12,10 +12,6 @@ function bp_em_my_bookings() {
12
  bp_notifications_delete_notifications_by_type(get_current_user_id(), 'events','pending_booking');
13
  bp_notifications_delete_notifications_by_type(get_current_user_id(), 'events','confirmed_booking');
14
  bp_notifications_delete_notifications_by_type(get_current_user_id(), 'events','cancelled_booking');
15
- }else{
16
- bp_core_delete_notifications_by_type(get_current_user_id(), 'events','pending_booking');
17
- bp_core_delete_notifications_by_type(get_current_user_id(), 'events','confirmed_booking');
18
- bp_core_delete_notifications_by_type(get_current_user_id(), 'events','cancelled_booking');
19
  }
20
 
21
  em_load_event();
12
  bp_notifications_delete_notifications_by_type(get_current_user_id(), 'events','pending_booking');
13
  bp_notifications_delete_notifications_by_type(get_current_user_id(), 'events','confirmed_booking');
14
  bp_notifications_delete_notifications_by_type(get_current_user_id(), 'events','cancelled_booking');
 
 
 
 
15
  }
16
 
17
  em_load_event();
classes/em-admin-notices.php CHANGED
@@ -200,4 +200,5 @@ class EM_Admin_Notices {
200
  <?php
201
  }
202
  }
 
203
  EM_Admin_Notices::init();
200
  <?php
201
  }
202
  }
203
+ include('em-admin-notice.php');
204
  EM_Admin_Notices::init();
classes/em-booking.php CHANGED
@@ -27,6 +27,7 @@ function em_get_booking($id = false) {
27
  }
28
  /**
29
  * Contains all information and relevant functions surrounding a single booking made with Events Manager
 
30
  * @property string $language
31
  */
32
  class EM_Booking extends EM_Object{
@@ -37,7 +38,7 @@ class EM_Booking extends EM_Object{
37
  var $booking_price = null;
38
  var $booking_spaces;
39
  var $booking_comment;
40
- var $booking_status = false;
41
  var $booking_tax_rate = null;
42
  var $booking_taxes = null;
43
  var $booking_meta = array();
@@ -174,6 +175,8 @@ class EM_Booking extends EM_Object{
174
  if( !empty($this->booking_meta['lang']) ){
175
  return $this->booking_meta['lang'];
176
  }
 
 
177
  }
178
  return null;
179
  }
@@ -261,14 +264,14 @@ class EM_Booking extends EM_Object{
261
  $this->email();
262
  }
263
  $this->compat_keys();
264
- return apply_filters('em_booking_save', ( count($this->errors) == 0 ), $this);
265
  }else{
266
  $this->feedback_message = __('There was a problem saving the booking.', 'events-manager');
267
  if( !$this->can_manage() ){
268
  $this->add_error(sprintf(__('You cannot manage this %s.', 'events-manager'),__('Booking','events-manager')));
269
  }
270
  }
271
- return apply_filters('em_booking_save', false, $this);
272
  }
273
 
274
  /**
@@ -889,11 +892,12 @@ class EM_Booking extends EM_Object{
889
  $this->booking_status = false;
890
  $this->feedback_message = sprintf(__('%s deleted', 'events-manager'), __('Booking','events-manager'));
891
  $wpdb->delete( EM_META_TABLE, array('meta_key'=>'booking-note', 'object_id' => $this->booking_id), array('%s','%d'));
 
892
  }else{
893
  $this->add_error(sprintf(__('%s could not be deleted', 'events-manager'), __('Booking','events-manager')));
894
  }
895
  }
896
- do_action('em_bookings_deleted', $result, array($this->booking_id));
897
  return apply_filters('em_booking_delete',( $result !== false ), $this);
898
  }
899
 
27
  }
28
  /**
29
  * Contains all information and relevant functions surrounding a single booking made with Events Manager
30
+ * @property int|false $booking_status
31
  * @property string $language
32
  */
33
  class EM_Booking extends EM_Object{
38
  var $booking_price = null;
39
  var $booking_spaces;
40
  var $booking_comment;
41
+ protected $booking_status = false;
42
  var $booking_tax_rate = null;
43
  var $booking_taxes = null;
44
  var $booking_meta = array();
175
  if( !empty($this->booking_meta['lang']) ){
176
  return $this->booking_meta['lang'];
177
  }
178
+ }elseif( $var == 'booking_status' ){
179
+ return ($this->booking_status == 0 && !get_option('dbem_bookings_approval') ) ? 1:$this->booking_status;
180
  }
181
  return null;
182
  }
264
  $this->email();
265
  }
266
  $this->compat_keys();
267
+ return apply_filters('em_booking_save', ( count($this->errors) == 0 ), $this, $update);
268
  }else{
269
  $this->feedback_message = __('There was a problem saving the booking.', 'events-manager');
270
  if( !$this->can_manage() ){
271
  $this->add_error(sprintf(__('You cannot manage this %s.', 'events-manager'),__('Booking','events-manager')));
272
  }
273
  }
274
+ return apply_filters('em_booking_save', false, $this, false);
275
  }
276
 
277
  /**
892
  $this->booking_status = false;
893
  $this->feedback_message = sprintf(__('%s deleted', 'events-manager'), __('Booking','events-manager'));
894
  $wpdb->delete( EM_META_TABLE, array('meta_key'=>'booking-note', 'object_id' => $this->booking_id), array('%s','%d'));
895
+ do_action('em_booking_deleted', $this);
896
  }else{
897
  $this->add_error(sprintf(__('%s could not be deleted', 'events-manager'), __('Booking','events-manager')));
898
  }
899
  }
900
+ do_action('em_bookings_deleted', $result, array($this->booking_id), $this);
901
  return apply_filters('em_booking_delete',( $result !== false ), $this);
902
  }
903
 
classes/em-datetime.php CHANGED
@@ -180,6 +180,8 @@ class EM_DateTime extends DateTime {
180
  $timestamp = parent::format('U');
181
  $server_offset = date('Z', $timestamp);
182
  return date_i18n( $format, $timestamp - ($server_offset * 2) + $this->getOffset() );
 
 
183
  }else{
184
  return date_i18n( $format, $this->getTimestampWithOffset(true) );
185
  }
180
  $timestamp = parent::format('U');
181
  $server_offset = date('Z', $timestamp);
182
  return date_i18n( $format, $timestamp - ($server_offset * 2) + $this->getOffset() );
183
+ }elseif( function_exists('wp_date') ){
184
+ return wp_date( $format, $this->getTimestamp(), $this->getTimezone() );
185
  }else{
186
  return date_i18n( $format, $this->getTimestampWithOffset(true) );
187
  }
classes/em-event-posts-admin.php CHANGED
@@ -208,7 +208,7 @@ class EM_Event_Posts_Admin{
208
  case 'location':
209
  //get meta value to see if post has location, otherwise
210
  $EM_Location = $EM_Event->get_location();
211
- if( !empty($EM_Location->location_id) ){
212
  $actions = array();
213
  $actions[] = "<a href='". esc_url($EM_Location->get_permalink())."'>". esc_html__('View') ."</a>";
214
  if( $EM_Location->can_manage('edit_locations', 'edit_others_locations') ) {
@@ -217,6 +217,8 @@ class EM_Event_Posts_Admin{
217
  echo "<strong><a href='". $EM_Location->get_permalink()."'>" . $EM_Location->location_name . "</a></strong>";
218
  echo "<span class='row-actions'> - ". implode(' | ', $actions) . "</span>";
219
  echo "<br/>" . $EM_Location->location_address . " - " . $EM_Location->location_town;
 
 
220
  }else{
221
  echo __('None','events-manager');
222
  }
208
  case 'location':
209
  //get meta value to see if post has location, otherwise
210
  $EM_Location = $EM_Event->get_location();
211
+ if( $EM_Event->has_location() ){
212
  $actions = array();
213
  $actions[] = "<a href='". esc_url($EM_Location->get_permalink())."'>". esc_html__('View') ."</a>";
214
  if( $EM_Location->can_manage('edit_locations', 'edit_others_locations') ) {
217
  echo "<strong><a href='". $EM_Location->get_permalink()."'>" . $EM_Location->location_name . "</a></strong>";
218
  echo "<span class='row-actions'> - ". implode(' | ', $actions) . "</span>";
219
  echo "<br/>" . $EM_Location->location_address . " - " . $EM_Location->location_town;
220
+ }elseif( $EM_Event->has_event_location() ) {
221
+ echo $EM_Event->get_event_location()->get_admin_column();
222
  }else{
223
  echo __('None','events-manager');
224
  }
classes/em-event.php CHANGED
@@ -124,6 +124,14 @@ class EM_Event extends EM_Object{
124
  var $event_spaces;
125
  var $event_private;
126
  var $location_id;
 
 
 
 
 
 
 
 
127
  var $recurrence_id;
128
  var $event_status;
129
  var $blog_id = 0;
@@ -173,6 +181,7 @@ class EM_Event extends EM_Object{
173
  'event_rsvp_spaces' => array( 'name'=>'rsvp_spaces', 'type'=>'%d', 'null'=>true ),
174
  'event_spaces' => array( 'name'=>'spaces', 'type'=>'%d', 'null'=>true),
175
  'location_id' => array( 'name'=>'location_id', 'type'=>'%d', 'null'=>true ),
 
176
  'recurrence_id' => array( 'name'=>'recurrence_id', 'type'=>'%d', 'null'=>true ),
177
  'event_status' => array( 'name'=>'status', 'type'=>'%d', 'null'=>true ),
178
  'event_private' => array( 'name'=>'status', 'type'=>'%d', 'null'=>true ),
@@ -227,6 +236,10 @@ class EM_Event extends EM_Object{
227
  * @var EM_Location
228
  */
229
  var $location;
 
 
 
 
230
  /**
231
  * @var EM_Bookings
232
  */
@@ -489,11 +502,12 @@ class EM_Event extends EM_Object{
489
  //load meta data and other related information
490
  if( $event_post->post_status != 'auto-draft' ){
491
  $event_meta = $this->get_event_meta($search_by);
 
492
  //Get custom fields and post meta
493
  foreach($event_meta as $event_meta_key => $event_meta_val){
494
  $field_name = substr($event_meta_key, 1);
495
  if($event_meta_key[0] != '_'){
496
- $this->event_attributes[$event_meta_key] = ( is_array($event_meta_val) ) ? $event_meta_val[0]:$event_meta_val;
497
  }elseif( is_string($field_name) && !in_array($field_name, $this->post_fields) ){
498
  if( array_key_exists($field_name, $this->fields) ){
499
  $this->$field_name = $event_meta_val[0];
@@ -502,6 +516,7 @@ class EM_Event extends EM_Object{
502
  }
503
  }
504
  }
 
505
  //quick compatability fix in case _event_id isn't loaded or somehow got erased in post meta
506
  if( empty($this->event_id) && !$this->is_recurring() ){
507
  global $wpdb;
@@ -653,16 +668,40 @@ class EM_Event extends EM_Object{
653
  //reset start and end objects so they are recreated with the new dates/times if and when needed
654
  $this->start = $this->end = null;
655
 
656
- //Get Location info
657
- if( !get_option('dbem_locations_enabled') || (!empty($_POST['no_location']) && !get_option('dbem_require_location',true)) || (empty($_POST['location_id']) && !get_option('dbem_require_location',true) && get_option('dbem_use_select_for_locations')) ){
658
- $this->location_id = 0;
659
- }elseif( !empty($_POST['location_id']) && is_numeric($_POST['location_id']) ){
660
- $this->location_id = absint($_POST['location_id']);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
661
  }else{
662
- //we're adding a new location, so create an empty location and populate
663
- $this->location_id = null;
664
- $this->get_location()->get_post(false);
665
- $this->get_location()->post_content = ''; //reset post content, as it'll grab the event description otherwise
666
  }
667
 
668
  //Bookings
@@ -709,7 +748,7 @@ class EM_Event extends EM_Object{
709
  if(get_option('dbem_attributes_enabled')){
710
  global $allowedtags;
711
  if( !is_array($this->event_attributes) ){ $this->event_attributes = array(); }
712
- $event_available_attributes = em_get_attributes();
713
  if( !empty($_POST['em_attributes']) && is_array($_POST['em_attributes']) ){
714
  foreach($_POST['em_attributes'] as $att_key => $att_value ){
715
  if( (in_array($att_key, $event_available_attributes['names']) || array_key_exists($att_key, $this->event_attributes) ) ){
@@ -898,11 +937,22 @@ class EM_Event extends EM_Object{
898
  $this->add_error(__('Dates must have correct formatting. Please use the date picker provided.','events-manager'));
899
  }
900
  }
901
- if( get_option('dbem_locations_enabled') && empty($this->location_id) ){ //location ids don't need validating as we're not saving a location
902
- if( get_option('dbem_require_location',true) || $this->location_id !== 0 ){
903
- if( !$this->get_location()->validate() ){
 
 
 
 
 
904
  $this->add_error($this->get_location()->get_errors());
 
 
 
905
  }
 
 
 
906
  }
907
  }
908
  if ( count($missing_fields) > 0){
@@ -1086,6 +1136,12 @@ class EM_Event extends EM_Object{
1086
  }
1087
  }
1088
  }
 
 
 
 
 
 
1089
  //update timestamps, dates and times
1090
  update_post_meta($this->post_id, '_event_start_local', $this->start()->getDateTime());
1091
  update_post_meta($this->post_id, '_event_end_local', $this->end()->getDateTime());
@@ -1280,9 +1336,10 @@ class EM_Event extends EM_Object{
1280
 
1281
  /**
1282
  * Delete whole event, including bookings, tickets, etc.
 
1283
  * @return boolean
1284
  */
1285
- function delete($force_delete = false){ //atm wp seems to force cp deletions anyway
1286
  if( $this->can_manage('delete_events', 'delete_others_events') ){
1287
  if( !is_admin() ){
1288
  include_once('em-event-post-admin.php');
@@ -1579,7 +1636,7 @@ class EM_Event extends EM_Object{
1579
  }
1580
 
1581
  /**
1582
- * Returns the location object this event belongs to.
1583
  * @return EM_Location
1584
  */
1585
  function get_location() {
@@ -1594,6 +1651,40 @@ class EM_Event extends EM_Object{
1594
  return $this->location;
1595
  }
1596
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1597
  /**
1598
  * Returns the location object this event belongs to.
1599
  * @return EM_Person
@@ -1841,10 +1932,28 @@ class EM_Event extends EM_Object{
1841
  $show_condition = (!$this->event_rsvp && get_option('dbem_rsvp_enabled'));
1842
  }elseif ($condition == 'no_location'){
1843
  //does this event have a valid location?
1844
- $show_condition = ( empty($this->location_id) || !$this->get_location()->location_status );
1845
  }elseif ($condition == 'has_location'){
1846
  //does this event have a valid location?
1847
- $show_condition = ( !empty($this->location_id) && $this->get_location()->location_status );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1848
  }elseif ($condition == 'has_image'){
1849
  //does this event have an image?
1850
  $show_condition = ( $this->get_image_url() != '' );
@@ -1961,6 +2070,12 @@ class EM_Event extends EM_Object{
1961
  }elseif ( preg_match('/^no_tag_([a-zA-Z0-9_\-,]+)$/', $condition, $tag_match)){
1962
  //event doesn't have this tag
1963
  $show_condition = !has_term(explode(',', $tag_match[1]), EM_TAXONOMY_TAG, $this->post_id);
 
 
 
 
 
 
1964
  }
1965
  //other potential ones - has_attribute_... no_attribute_... has_categories_...
1966
  $show_condition = apply_filters('em_event_output_show_condition', $show_condition, $condition, $conditionals[0][$key], $this);
@@ -2380,6 +2495,14 @@ class EM_Event extends EM_Object{
2380
  $replace = '<a href="'.esc_url($replace).'" target="_blank"><img src="'.esc_url($img_url).'" alt="0" border="0"></a>';
2381
  }
2382
  break;
 
 
 
 
 
 
 
 
2383
  default:
2384
  $replace = $full_result;
2385
  break;
124
  var $event_spaces;
125
  var $event_private;
126
  var $location_id;
127
+ /**
128
+ * Key name of event location type associated to this event.
129
+ *
130
+ * Events can have an event-specific location type, such as a url, webinar or another custom type instead of a regular geographical location. If this value is set, then a registered event location type is loaded and relevant saved event meta is used.
131
+ *
132
+ * @var string
133
+ */
134
+ public $event_location_type;
135
  var $recurrence_id;
136
  var $event_status;
137
  var $blog_id = 0;
181
  'event_rsvp_spaces' => array( 'name'=>'rsvp_spaces', 'type'=>'%d', 'null'=>true ),
182
  'event_spaces' => array( 'name'=>'spaces', 'type'=>'%d', 'null'=>true),
183
  'location_id' => array( 'name'=>'location_id', 'type'=>'%d', 'null'=>true ),
184
+ 'event_location_type' => array( 'type'=>'%s', 'null'=>true ),
185
  'recurrence_id' => array( 'name'=>'recurrence_id', 'type'=>'%d', 'null'=>true ),
186
  'event_status' => array( 'name'=>'status', 'type'=>'%d', 'null'=>true ),
187
  'event_private' => array( 'name'=>'status', 'type'=>'%d', 'null'=>true ),
236
  * @var EM_Location
237
  */
238
  var $location;
239
+ /**
240
+ * @var array
241
+ */
242
+ var $event_location_data = array();
243
  /**
244
  * @var EM_Bookings
245
  */
502
  //load meta data and other related information
503
  if( $event_post->post_status != 'auto-draft' ){
504
  $event_meta = $this->get_event_meta($search_by);
505
+ if( !empty($even_meta['_event_location_type']) ) $this->event_location_type = $even_meta['_event_location_type']; //load this directly so we know further down whether this has an event location type to load
506
  //Get custom fields and post meta
507
  foreach($event_meta as $event_meta_key => $event_meta_val){
508
  $field_name = substr($event_meta_key, 1);
509
  if($event_meta_key[0] != '_'){
510
+ $this->event_attributes[$event_meta_key] = ( is_array($event_meta_val) ) ? $event_meta_val[0]:$event_meta_val;
511
  }elseif( is_string($field_name) && !in_array($field_name, $this->post_fields) ){
512
  if( array_key_exists($field_name, $this->fields) ){
513
  $this->$field_name = $event_meta_val[0];
516
  }
517
  }
518
  }
519
+ if( $this->has_event_location() ) $this->get_event_location()->load_postdata($event_meta);
520
  //quick compatability fix in case _event_id isn't loaded or somehow got erased in post meta
521
  if( empty($this->event_id) && !$this->is_recurring() ){
522
  global $wpdb;
668
  //reset start and end objects so they are recreated with the new dates/times if and when needed
669
  $this->start = $this->end = null;
670
 
671
+ //Get Location Info
672
+ if( get_option('dbem_locations_enabled') ){
673
+ // determine location type, with backward compatibility considerations for those overriding the location forms
674
+ $location_type = isset($_POST['location_type']) ? sanitize_key($_POST['location_type']) : 'location';
675
+ if( !empty($_POST['no_location']) ) $location_type = 0; //backwards compat
676
+ if( $location_type == 'location' && empty($_POST['location_id']) && get_option('dbem_use_select_for_locations')) $location_type = 0; //backward compat
677
+ // assign location data
678
+ if( $location_type === 0 || $location_type === '0' ){
679
+ // no location
680
+ $this->location_id = 0;
681
+ $this->event_location_type = null;
682
+ }elseif( $location_type == 'location' && EM_Locations::is_enabled() ){
683
+ // a physical location, old school
684
+ $this->event_location_type = null; // if location resides in locations table, location type is null since we have a location_id table value
685
+ if( !empty($_POST['location_id']) && is_numeric($_POST['location_id']) ){
686
+ // we're using a previously created location
687
+ $this->location_id = absint($_POST['location_id']);
688
+ }else{
689
+ $this->location_id = null;
690
+ //we're adding a new location place, so create an empty location and populate
691
+ $this->get_location()->get_post(false);
692
+ $this->get_location()->post_content = ''; //reset post content, as it'll grab the event description otherwise
693
+ }
694
+ }else{
695
+ // we're dealing with an event location such as a url or webinar
696
+ $this->location_id = null; // no location ID
697
+ $this->event_location_type = $location_type;
698
+ if( EM_Event_Locations\Event_Locations::is_enabled($location_type) ){
699
+ $this->get_event_location()->get_post();
700
+ }
701
+ }
702
  }else{
703
+ $this->location_id = 0;
704
+ $this->event_location_type = null;
 
 
705
  }
706
 
707
  //Bookings
748
  if(get_option('dbem_attributes_enabled')){
749
  global $allowedtags;
750
  if( !is_array($this->event_attributes) ){ $this->event_attributes = array(); }
751
+ $event_available_attributes = !empty($event_available_attributes) ? $event_available_attributes : em_get_attributes(); //we use this in locations, no need to repeat if needed
752
  if( !empty($_POST['em_attributes']) && is_array($_POST['em_attributes']) ){
753
  foreach($_POST['em_attributes'] as $att_key => $att_value ){
754
  if( (in_array($att_key, $event_available_attributes['names']) || array_key_exists($att_key, $this->event_attributes) ) ){
937
  $this->add_error(__('Dates must have correct formatting. Please use the date picker provided.','events-manager'));
938
  }
939
  }
940
+ if( get_option('dbem_locations_enabled') ){
941
+ if( $this->location_id === 0 && get_option('dbem_require_location',true) ){
942
+ // no location chosen, yet we require a location
943
+ $this->add_error(__('No location associated with this event.', 'events-manager'));
944
+ }elseif( $this->has_location() ){
945
+ // physical location
946
+ if( empty($this->location_id) && !$this->get_location()->validate() ){
947
+ // new location doesn't validate
948
  $this->add_error($this->get_location()->get_errors());
949
+ }elseif( !empty($this->location_id) && !$this->get_location()->location_id ){
950
+ // non-existent location selected
951
+ $this->add_error( __('Please select a valid location.', 'events-manager') );
952
  }
953
+ }elseif( $this->has_event_location() ){
954
+ // event location, validation applies errors directly to $this
955
+ $this->get_event_location()->validate();
956
  }
957
  }
958
  if ( count($missing_fields) > 0){
1136
  }
1137
  }
1138
  }
1139
+ //update event location via post meta
1140
+ if( $this->has_event_location() ){
1141
+ $this->get_event_location()->save();
1142
+ }else{
1143
+ $this->get_event_location()->reset_data();
1144
+ }
1145
  //update timestamps, dates and times
1146
  update_post_meta($this->post_id, '_event_start_local', $this->start()->getDateTime());
1147
  update_post_meta($this->post_id, '_event_end_local', $this->end()->getDateTime());
1336
 
1337
  /**
1338
  * Delete whole event, including bookings, tickets, etc.
1339
+ * @param boolean $force_delete
1340
  * @return boolean
1341
  */
1342
+ function delete( $force_delete = false ){
1343
  if( $this->can_manage('delete_events', 'delete_others_events') ){
1344
  if( !is_admin() ){
1345
  include_once('em-event-post-admin.php');
1636
  }
1637
 
1638
  /**
1639
+ * Returns the physical location object this event belongs to.
1640
  * @return EM_Location
1641
  */
1642
  function get_location() {
1651
  return $this->location;
1652
  }
1653
 
1654
+ /**
1655
+ * Returns whether this event has a phyisical location assigned to it.
1656
+ * @return bool
1657
+ */
1658
+ public function has_location(){
1659
+ return !empty($this->location_id) || (!empty($this->location) && !empty($this->location->location_name));
1660
+ }
1661
+
1662
+ /**
1663
+ * Gets the event's event location (note, different from a regular event location, which uses get_location())
1664
+ * Returns implementation of Event_Location or false if no event location assigned.
1665
+ * @return EM_Event_Locations\URL|EM_Event_Locations\Event_Location|false
1666
+ */
1667
+ public function get_event_location(){
1668
+ if( $this->has_event_location() ){
1669
+ $EM_Location_Type = EM_Event_Locations\Event_Locations::get( $this->event_location_type, $this );
1670
+ }else{
1671
+ $EM_Location_Type = new EM_Event_Locations\Event_Location( $this );
1672
+ }
1673
+ return apply_filters('em_event_get_event_location', $EM_Location_Type, $this);
1674
+ }
1675
+
1676
+ /**
1677
+ * Returns whether the event has an event location associated with it (different from a physical location). If supplied, can check against a specific type.
1678
+ * @param string $event_location_type
1679
+ * @return bool
1680
+ */
1681
+ public function has_event_location( $event_location_type = null ){
1682
+ if( !empty($event_location_type) ){
1683
+ return !empty($this->event_location_type) && $this->event_location_type === $event_location_type;
1684
+ }
1685
+ return !empty($this->event_location_type);
1686
+ }
1687
+
1688
  /**
1689
  * Returns the location object this event belongs to.
1690
  * @return EM_Person
1932
  $show_condition = (!$this->event_rsvp && get_option('dbem_rsvp_enabled'));
1933
  }elseif ($condition == 'no_location'){
1934
  //does this event have a valid location?
1935
+ $show_condition = !$this->has_event_location() && !$this->has_location();
1936
  }elseif ($condition == 'has_location'){
1937
  //does this event have a valid location?
1938
+ $show_condition = ( $this->has_location() && $this->get_location()->location_status ) || $this->has_event_location();
1939
+ }elseif ($condition == 'has_location_venue'){
1940
+ //does this event have a valid physical location?
1941
+ $show_condition = ( $this->has_location() && $this->get_location()->location_status ) || $this->has_event_location();
1942
+ }elseif ($condition == 'no_location_venue'){
1943
+ //does this event NOT have a valid physical location?
1944
+ $show_condition = !$this->has_location();
1945
+ }elseif ($condition == 'has_event_location'){
1946
+ //does this event have a valid event location?
1947
+ $show_condition = $this->has_event_location();
1948
+ }elseif ( preg_match('/^has_event_location_([a-zA-Z0-9_\-]+)$/', $condition, $type_match)){
1949
+ //event has a specific category
1950
+ $show_condition = $this->has_event_location($type_match[1]);
1951
+ }elseif ($condition == 'no_event_location'){
1952
+ //does this event not have a valid event location?
1953
+ $show_condition = !$this->has_event_location();
1954
+ }elseif ( preg_match('/^no_event_location_([a-zA-Z0-9_\-]+)$/', $condition, $type_match)){
1955
+ //does this event NOT have a specific event location?
1956
+ $show_condition = !$this->has_event_location($type_match[1]);
1957
  }elseif ($condition == 'has_image'){
1958
  //does this event have an image?
1959
  $show_condition = ( $this->get_image_url() != '' );
2070
  }elseif ( preg_match('/^no_tag_([a-zA-Z0-9_\-,]+)$/', $condition, $tag_match)){
2071
  //event doesn't have this tag
2072
  $show_condition = !has_term(explode(',', $tag_match[1]), EM_TAXONOMY_TAG, $this->post_id);
2073
+ }elseif ( preg_match('/^has_att_([a-zA-Z0-9_\-,]+)$/', $condition, $att_match)){
2074
+ //event has a specific custom field
2075
+ $show_condition = !empty($this->event_attributes[$att_match[1]]) || !empty($this->event_attributes[str_replace('_', ' ', $att_match[1])]);
2076
+ }elseif ( preg_match('/^no_att_([a-zA-Z0-9_\-,]+)$/', $condition, $att_match)){
2077
+ //event has a specific custom field
2078
+ $show_condition = empty($this->event_attributes[$att_match[1]]) && empty($this->event_attributes[str_replace('_', ' ', $att_match[1])]);
2079
  }
2080
  //other potential ones - has_attribute_... no_attribute_... has_categories_...
2081
  $show_condition = apply_filters('em_event_output_show_condition', $show_condition, $condition, $conditionals[0][$key], $this);
2495
  $replace = '<a href="'.esc_url($replace).'" target="_blank"><img src="'.esc_url($img_url).'" alt="0" border="0"></a>';
2496
  }
2497
  break;
2498
+ //Event location (not physical location)
2499
+ case '#_EVENTLOCATION':
2500
+ if( !empty($placeholders[3][$key]) ){
2501
+ $replace = $this->get_event_location()->output($placeholders[3][$key]);
2502
+ }else{
2503
+ $replace = $this->get_event_location()->output();
2504
+ }
2505
+ break;
2506
  default:
2507
  $replace = $full_result;
2508
  break;
classes/em-events.php CHANGED
@@ -1,4 +1,5 @@
1
  <?php
 
2
  //TODO EM_Events is currently static, better we make this non-static so we can loop sets of events, and standardize with other objects.
3
  /**
4
  * Use this class to query and manipulate sets of events. If dealing with more than one event, you probably want to use this class in some way.
@@ -579,6 +580,34 @@ $limit $offset";
579
  $conditions['post_id'] = "(".EM_EVENTS_TABLE.".post_id={$args['post_id']})";
580
  }
581
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
582
  return apply_filters( 'em_events_build_sql_conditions', $conditions, $args );
583
  }
584
 
@@ -661,7 +690,9 @@ $limit $offset";
661
  //event-specific search attributes
662
  'has_location' => false, //search events with a location
663
  'no_location' => false, //search events without a location
664
- 'location_status' => false //search events with locations of a specific publish status
 
 
665
  );
666
  //sort out whether defaults were supplied or just the array of search values
667
  if( empty($array) ){
1
  <?php
2
+ use EM_Event_Locations\Event_Locations;
3
  //TODO EM_Events is currently static, better we make this non-static so we can loop sets of events, and standardize with other objects.
4
  /**
5
  * Use this class to query and manipulate sets of events. If dealing with more than one event, you probably want to use this class in some way.
580
  $conditions['post_id'] = "(".EM_EVENTS_TABLE.".post_id={$args['post_id']})";
581
  }
582
  }
583
+ // event locations
584
+ if( !empty($args['event_location_type']) ){
585
+ $event_location_types = explode(',', $args['event_location_type']);
586
+ $event_locations_search = array();
587
+ // generate array of clean and enabled event location types
588
+ foreach( $event_location_types as $event_location_type ){
589
+ $event_location_type = trim($event_location_type);
590
+ if( Event_Locations::is_enabled($event_location_type) ){
591
+ $event_locations_search[] = $event_location_type;
592
+ }
593
+ }
594
+ // add condition if at least one valid/clean type supplied
595
+ if( !empty($event_locations_search) ){
596
+ if( count($event_locations_search) === 1 ){
597
+ $event_location = current($event_locations_search);
598
+ $conditions['event_location'] = "event_location_type='$event_location'";
599
+ }else{
600
+ $conditions['event_location'] = "event_location_type IN ('". implode("','", $event_locations_search) ."')";
601
+ }
602
+ }
603
+ }
604
+ if( isset($args['has_event_location']) && $args['has_event_location'] !== false ){
605
+ if( $args['has_event_location'] ){
606
+ $conditions['has_event_location'] = "event_location_type IS NOT NULL";
607
+ }else{
608
+ $conditions['has_event_location'] = "event_location_type IS NULL";
609
+ }
610
+ }
611
  return apply_filters( 'em_events_build_sql_conditions', $conditions, $args );
612
  }
613
 
690
  //event-specific search attributes
691
  'has_location' => false, //search events with a location
692
  'no_location' => false, //search events without a location
693
+ 'location_status' => false, //search events with locations of a specific publish status
694
+ 'event_location_type' => false,
695
+ 'has_event_location' => false,
696
  );
697
  //sort out whether defaults were supplied or just the array of search values
698
  if( empty($array) ){
classes/em-exception.php ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //in case we include it in EM core code
3
+ if( !class_exists('EM_Exception') ){
4
+ /**
5
+ * Extended Exception class that allows for creating multiple error messages in an exception as an array and outputting them together at once.
6
+ * Class Exception
7
+ */
8
+ class EM_Exception extends Exception {
9
+
10
+ /**
11
+ * @var WP_Error
12
+ */
13
+ public $wp_error;
14
+ /**
15
+ * @var array
16
+ */
17
+ public $error_messages = array();
18
+ /**
19
+ * @var int|string Allows for a custom code to be used rather than an integer.
20
+ */
21
+ public $error_code;
22
+
23
+ /**
24
+ * Exception constructor.
25
+ * @param string $error
26
+ * @param int $code
27
+ * @param null $previous
28
+ */
29
+ public function __construct($error = '', $code = 0, $previous = null ){
30
+ if( is_array($error) ) {
31
+ $this->error_messages = $error;
32
+ $message = $this->get_message();
33
+ }elseif( is_wp_error($error) ){ /* @var WP_Error $error */
34
+ $this->wp_error = $error;
35
+ $code = $error->get_error_code();
36
+ $message = $error->get_error_message();
37
+ }else{
38
+ $message = $error;
39
+ }
40
+ if( !is_numeric($code) ){
41
+ $this->error_code = $code;
42
+ $code = 0;
43
+ }
44
+ parent::__construct($message, $code, $previous);
45
+ }
46
+
47
+ /**
48
+ * Returns either a string code reference, or a regular Exception code number.
49
+ * @return int|string
50
+ */
51
+ public function get_error_code(){
52
+ if( $this->error_code ){
53
+ return $this->error_code;
54
+ }
55
+ return $this->getCode();
56
+ }
57
+
58
+ /**
59
+ * Provides a paragraph-formatted message which may contain multiple paragraphs for multiple errors.
60
+ * @return string
61
+ */
62
+ public function get_message(){
63
+ if( $this->is_wp_error() ){
64
+ $message = '<p>' . implode('</p><p>', $this->wp_error->get_error_messages()) . '</p>';
65
+ }elseif( !empty($this->error_messages) ){
66
+ $message = '<p>' . implode('</p><p>', $this->error_messages) . '</p>';
67
+ }else{
68
+ $message = '<p>' . $this->getMessage() . '</p>';
69
+ }
70
+ return $message;
71
+ }
72
+
73
+ /**
74
+ * @return array|string
75
+ */
76
+ public function get_messages(){
77
+ if( $this->is_wp_error() ){
78
+ return $this->wp_error->get_error_messages();
79
+ }elseif( !empty($this->error_messages) ){
80
+ return $this->error_messages;
81
+ }else{
82
+ return array($this->getMessage());
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Whether or not this exception was triggered by a WP_Error
88
+ * @return bool
89
+ */
90
+ public function is_wp_error(){
91
+ return is_wp_error( $this->wp_error );
92
+ }
93
+
94
+ /**
95
+ * Returns exception in WP_Error format, whether or not it was originally a WP_Error in the first place.
96
+ * @return WP_Error
97
+ */
98
+ public function get_wp_error(){
99
+ if( $this->is_wp_error() ){
100
+ return $this->wp_error;
101
+ }
102
+ $WP_Error = new WP_Error();
103
+ $WP_Error->add_data( $this->get_messages(), $this->getCode() );
104
+ return $WP_Error;
105
+ }
106
+ }
107
+ }
classes/em-locations.php CHANGED
@@ -22,6 +22,16 @@ class EM_Locations extends EM_Object {
22
 
23
  protected static $context = 'location';
24
 
 
 
 
 
 
 
 
 
 
 
25
  /**
26
  * Returns an array of EM_Location objects
27
  * @param array $args
22
 
23
  protected static $context = 'location';
24
 
25
+
26
+ /**
27
+ * Returns whether or not locations are enabled for use with events.
28
+ * @return bool
29
+ */
30
+ public static function is_enabled(){
31
+ $location_types = get_option('dbem_location_types', array());
32
+ return !empty($location_types['location']);
33
+ }
34
+
35
  /**
36
  * Returns an array of EM_Location objects
37
  * @param array $args
classes/em-oauth/oauth-admin-settings.php ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace EM_OAuth;
3
+ use EM_Options, EM_Exception;
4
+ use Matrix\Exception;
5
+
6
+ class OAuth_API_Admin_Settings {
7
+
8
+ public static $option_name = 'em_oauth';
9
+ public static $service_name = 'EM OAuth 2.0';
10
+ /**
11
+ * @var OAuth_API Class name of base API class for this service type, used for static variable referencing
12
+ */
13
+ public static $api_class = 'EM_OAuth\OAuth_API';
14
+ public static $service_url = 'http://example.com';
15
+ public static $icon_url = '';
16
+
17
+
18
+ public static function init(){
19
+ $class = get_called_class(); //get child class name to call
20
+ self::$icon_url = plugin_dir_url(__FILE__). 'icon.png';
21
+ //handle service app creds
22
+ add_action('em_options_page_footer', "$class::em_settings_apps");
23
+ add_action('em_options_save', "$class::em_settings_save_apps");
24
+ }
25
+
26
+ /**
27
+ * @return OAuth_API
28
+ */
29
+ public static function get_api_class(){
30
+ //set default API class name if not defined by parent
31
+ if( self::$api_class === static::$api_class && class_exists(str_replace('_Admin_Settings', '', get_called_class())) ){
32
+ static::$api_class = str_replace('_Admin_Settings', '', get_called_class());
33
+ }
34
+ return static::$api_class;
35
+ }
36
+
37
+ public static function em_settings_save_apps(){
38
+ $api = static::get_api_class();
39
+ if( $api::get_option_dataset() == 'dbem_oauth' ) return;
40
+ $option_names = array($api::get_option_name().'_app_id', $api::get_option_name().'_app_secret');
41
+ foreach( $option_names as $option_name ){
42
+ $value = !empty($_REQUEST[$api::get_option_dataset()][$option_name]) ? $_REQUEST[$api::get_option_dataset()][$option_name] : '';
43
+ EM_Options::set($option_name, $value, $api::get_option_dataset());
44
+ }
45
+ }
46
+
47
+ public static function em_settings_apps_header(){
48
+ // override this for extra content above settings meta box
49
+ }
50
+
51
+ public static function em_settings_apps(){
52
+ $desc = esc_html__("You'll need to create an App with %s and obtain the App credentials by going to %s.", 'events-manager');
53
+ $desc_url = esc_html__('You will also need supply an OAuth redirect url to %s. Your url for this site is : %s', 'events-manager');
54
+ $api = static::get_api_class(); /* @var OAuth_API $api */
55
+ $api_client_class = $api::get_client_class();
56
+ $callback_url = $api_client_class::get_oauth_callback_url();
57
+ $service_name = $api::get_service_name();
58
+ ?>
59
+ <div class="postbox em-oaut-connect em-oauth-connect-<?php echo esc_attr($api::get_authorization_scope()); ?>" id="em-opt-<?php echo esc_attr($api::get_option_name()); ?>-app" >
60
+ <div class="handlediv" title="<?php __('Click to toggle', 'events-manager'); ?>"><br /></div><h3><span><?php echo esc_html($api::get_service_name()) ?></span></h3>
61
+ <div class="inside">
62
+ <?php static::em_settings_apps_header(); ?>
63
+ <h4><?php esc_html_e('Server API Credentials', 'events-manager-zoom'); ?></h4>
64
+ <p><?php printf( $desc, $service_name, '<a href="'. $api::get_service_url() .'">'. $api::get_service_url() .'</a>'); ?></p>
65
+ <p><?php printf( $desc_url, $service_name, "<code>$callback_url</code>") ?></p>
66
+ <p><?php printf( esc_html__('Once you have entered valid API information, you will see a button below to connect your site to %s.', 'events-manager-zoom'), $service_name); ?></p>
67
+ <table class='form-table'>
68
+ <?php
69
+ em_options_input_text(sprintf(__('%s App ID', 'events-manager'), $service_name), $api::get_option_dataset().'['.$api::get_option_name().'_app_id]', '');
70
+ em_options_input_text(sprintf(__('%s App Secret', 'events-manager'), $service_name), $api::get_option_dataset().'['.$api::get_option_name().'_app_secret]', '');
71
+ ?>
72
+ </table>
73
+ <?php
74
+ static::em_settings_user_auth();
75
+ static::em_settings_apps_footer();
76
+ ?>
77
+ </div> <!-- . inside -->
78
+ </div> <!-- .postbox -->
79
+ <?php
80
+ }
81
+
82
+ public static function em_settings_apps_footer(){
83
+ // override this for extra content above settings meta box
84
+ }
85
+
86
+ public static function em_settings_user_auth(){
87
+ //a fresh client with no token for generating oauth links
88
+ $api = static::get_api_class(); /* @var OAuth_API $api */
89
+ try{
90
+ $api_client = $api::get_client(false); /* @var OAuth_API_Client $api_client */
91
+ $service_name = $api::get_service_name();
92
+ $option_name = $api::get_option_name();
93
+ //get tokens if client is configured
94
+ if( !is_wp_error($api_client) ){ //oauth is not configured correctly...
95
+ $oauth_url = $api_client->get_oauth_url();
96
+ //we don't need to verify connections at this point, we just need to know if there are any
97
+ if( $api_client->authorization_scope == 'user' ){
98
+ $user_id = get_current_user_id();
99
+ $access_tokens = $api::get_user_tokens();
100
+ }else{
101
+ $user_id = null;
102
+ $access_tokens = $api::get_site_tokens();
103
+ }
104
+ $oauth_accounts = array();
105
+ $connected = $reconnect_required = false;
106
+ foreach( $access_tokens as $account_id => $oauth_account ){
107
+ try {
108
+ $api_client->load_token( $account_id, $user_id );
109
+ $verification = true;
110
+ } catch ( EM_Exception $e ) {
111
+ $verification = false;
112
+ }
113
+ $oauth_account['id'] = !empty($oauth_account['email']) ? $oauth_account['email'] : $account_id;
114
+ $disconnect_url_args = array( 'action' => 'em_oauth_'. $api::get_option_name(), 'callback' => 'disconnect', 'account' => $account_id, 'nonce' => wp_create_nonce('em-oauth-'. $option_name .'-disconnect-'.$account_id) );
115
+ $oauth_account['disconnect'] = add_query_arg( $disconnect_url_args, admin_url( 'admin-ajax.php' ) );
116
+ if( !$verification ){
117
+ $oauth_account['reconnect'] = true;
118
+ $reconnect_required = true;
119
+ }else{
120
+ $connected = true;
121
+ }
122
+ $oauth_accounts[] = $oauth_account;
123
+ }
124
+ if( $connected ){
125
+ $button_url = add_query_arg( array( 'action' => 'em_oauth_'. $option_name, 'callback' => 'disconnect', 'nonce' => wp_create_nonce('em-oauth-'. $option_name .'-disconnect') ), admin_url( 'admin-ajax.php' ) );
126
+ $button_text = count($oauth_accounts) > 1 ? __('Disconnect All', 'events-manager') : __('Disonnect', 'events-manager');
127
+ $button_class = 'button-secondary';
128
+ }else{
129
+ $button_url = $oauth_url;
130
+ $button_text = __('Connect', 'events-manager');
131
+ $button_class = 'button-primary';
132
+ }
133
+ }
134
+ ?>
135
+ <div class="em-oauth-service-info">
136
+ <?php if( $api::get_authorization_scope() == 'user'): ?>
137
+ <h4><?php echo $service_name; ?></h4>
138
+ <?php else: ?>
139
+ <h4><?php esc_html_e('Account Connection', 'events-manager-zoom'); ?></h4>
140
+ <?php endif; ?>
141
+ <?php if( $connected || $reconnect_required ): ?>
142
+ <p><?php echo esc_html(sprintf(_n('You are successfully connected to the following %s account:', 'You are successfully connected to the following %s accounts:', count($oauth_accounts), 'events-manager-zoom'), $service_name)); ?></p>
143
+ <ul clss="em-oauth-service-accounts">
144
+ <?php foreach ( $oauth_accounts as $oauth_account ): ?>
145
+ <li class="em-oauth-service-account em-oauth-account-<?php echo empty($oauth_account['reconnect']) ? 'connected':'disconnected'; ?>">
146
+ <img src="<?php echo esc_url($oauth_account['photo']); ?>" width="25" height="25">
147
+ <div class="em-oauth-account-description">
148
+ <span class="em-oauth-account-label">
149
+ <?php if( !empty($oauth_account['reconnect']) ): ?><span class="dashicons dashicons-warning"></span><?php endif; ?>
150
+ <?php echo esc_html($oauth_account['name']) .' <em>('. esc_html($oauth_account['id']) .')</em>'; ?>
151
+ </span>
152
+ <span class="em-oauth-account-actions">
153
+ <?php if( count($oauth_accounts) > 1 ): ?>
154
+ <a href="<?php echo esc_url($oauth_account['disconnect']); ?>"><?php esc_html_e('Disconnect', 'events-manager'); ?></a>
155
+ <?php elseif( !empty($oauth_account['reconnect']) ): ?>
156
+ <a href="<?php echo esc_url($oauth_url); ?>"><?php esc_html_e('Reconnect', 'events-manager'); ?></a> |
157
+ <a href="<?php echo esc_url($oauth_account['disconnect']); ?>"><?php esc_html_e('Remove', 'events-manager'); ?></a>
158
+ <?php endif; ?>
159
+ </span>
160
+ </div>
161
+ </li>
162
+ <?php endforeach; ?>
163
+ </ul>
164
+ <p>
165
+ <a class="<?php echo $button_class; ?> em-oauth-connect-button" href="<?php echo esc_url($button_url); ?>"><?php echo esc_html($button_text); ?></a>
166
+ <?php if( $api::supports_multiple_tokens() ): ?>
167
+ <a class="button-secondary" href="<?php echo esc_url($oauth_url); ?>"><?php esc_html_e('Connect additional account') ?></a>
168
+ <?php endif; ?>
169
+ </p>
170
+ <?php do_action('em_settings_user_auth_after_connect_additional_'.$option_name); ?>
171
+ <p><em><?php esc_html_e('If you are experiencing errors when trying to use any of these accounts, try disconnecting and connecting again.', 'events-manager'); ?></em></p>
172
+ <?php else: ?>
173
+ <p><em><?php echo sprintf(esc_html__('Connect to import events and locations from %s.','events-manager'), $service_name); ?></em></p>
174
+ <a class="<?php echo $button_class; ?> em-oauth-connect-button" href="<?php echo esc_url($button_url); ?>"><?php echo esc_html($button_text); ?></a>
175
+ <?php endif; ?>
176
+ </div>
177
+ <?php
178
+ }catch( EM_Exception $ex ){
179
+ ?>
180
+ <div class="em-oauth-service-info">
181
+ <p><em><?php echo $ex->get_message(); ?></em></p>
182
+ </div>
183
+ <?php
184
+ }
185
+ }
186
+ }
classes/em-oauth/oauth-api-client.php ADDED
@@ -0,0 +1,594 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace EM_OAuth;
3
+ use EM_Options, EM_Exception, stdClass;
4
+
5
+ /**
6
+ * Class OAuth_API_Client
7
+ * @package EM_OAuth
8
+ * @property-read $service_name
9
+ * @property-read $option_name
10
+ * @property-read $option_dataset
11
+ * @property-read $authorization_scope
12
+ * @property-read $multiple_tokens
13
+ * @property-read $token_class
14
+ */
15
+ class OAuth_API_Client {
16
+ /**
17
+ * The base class for this package of classes, used for accessing infromation such as $option_name, etc.
18
+ * If naming conventions are followed, for example SomeService_API > SomeService_API_Client, this will be automatically deduced.
19
+ * @var OAuth_API
20
+ */
21
+ protected static $api_class;
22
+ /**
23
+ * @var string
24
+ */
25
+ public $id;
26
+ /**
27
+ * @var string
28
+ */
29
+ public $secret;
30
+ /**
31
+ * @var string
32
+ */
33
+ public $scope;
34
+ /**
35
+ * @var
36
+ */
37
+ public $client;
38
+
39
+ /**
40
+ * @var OAuth_API_Token
41
+ */
42
+ public $token;
43
+ /**
44
+ * @var string
45
+ */
46
+ public $user_id;
47
+ /**
48
+ * @var bool
49
+ */
50
+ public $authorized = false;
51
+
52
+ /**
53
+ * The URL without trailing slash for the API base URL, to which endpoints can be appended to.
54
+ * @var string
55
+ */
56
+ public $api_base = 'https://api.oauth.com';
57
+ /**
58
+ * The URL that'll be used to request an authorization code from the user. Can include strings CLIENT_ID, ACCESS_SCOPE, REDIRECT_URI, and STATE which will be replaced dynamically.
59
+ * @var string
60
+ */
61
+ public $oauth_authorize_url = 'https://api.oauth.com/authorize';
62
+ /**
63
+ * Required by child class unless it overrides the request_access_token() method.
64
+ * @var string
65
+ */
66
+ public $oauth_request_token_url = 'https://api.oauth.com/token';
67
+ /**
68
+ * Defaults to $oauuth_request_token_url if not set.
69
+ * @var string
70
+ */
71
+ public $oauth_refresh_token_url = null;
72
+ /**
73
+ * Required by child class unless it overrides the verify_access_token() method.
74
+ * @var string
75
+ */
76
+ public $oauth_verification_url = 'https://api.oauth.com/verify_token';
77
+ /**
78
+ * Required by child class unless it overrides the revoke_access_token() method.
79
+ * @var string
80
+ */
81
+ public $oauth_revoke_url = 'https://api.oauth.com/revoke';
82
+ public $oauth_authentication = 'parameters';
83
+ /**
84
+ * Whether or not an OAuth Service should pass on the state param for security check
85
+ * @var bool
86
+ */
87
+ public $oauth_state = true;
88
+
89
+ /**
90
+ * OAuth_API_Client constructor.
91
+ *
92
+ * @throws EM_Exception
93
+ */
94
+ public function __construct(){
95
+ // check credentials
96
+ $creds = array(
97
+ 'id' => EM_Options::get( $this->option_name. '_app_id', '', $this->option_dataset),
98
+ 'secret' => EM_Options::get( $this->option_name. '_app_secret', '', $this->option_dataset)
99
+ );
100
+ foreach( array('id', 'secret', 'scope') as $k ){
101
+ if( !empty($creds[$k]) ){
102
+ $this->$k = $creds[$k];
103
+ }elseif( empty($this->$k) ) { // constructors can be overriden to add any of the above
104
+ throw new EM_Exception( __('OAuth application information incomplete.', 'events-manager') );
105
+ }
106
+ }
107
+ if( !$this->oauth_refresh_token_url ){
108
+ $this->oauth_refresh_token_url = $this->oauth_request_token_url;
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Shortcut for base class properties.
114
+ * @param $name
115
+ * @return mixed
116
+ */
117
+ public function __get( $name ){
118
+ $api = static::get_api_class();
119
+ if( $name == 'option_name' ){
120
+ return $api::get_option_name();
121
+ }elseif( $name == 'option_dataset' ){
122
+ return $api::get_option_dataset();
123
+ }elseif( $name == 'authorization_scope' ){
124
+ return $api::get_authorization_scope();
125
+ }elseif( $name == 'multiple_tokens' ){
126
+ return $api::supports_multiple_tokens();
127
+ }elseif( $name == 'token_class' ){
128
+ return $api::get_token_class();
129
+ }
130
+ return null;
131
+ }
132
+
133
+ /**
134
+ * @return OAuth_API
135
+ */
136
+ public static function get_api_class(){
137
+ // set default API class name if not defined by parent
138
+ if( self::$api_class === static::$api_class && class_exists(str_replace('_Client', '', get_called_class())) ){
139
+ static::$api_class = str_replace('_Client', '', get_called_class());
140
+ }
141
+ return static::$api_class;
142
+ }
143
+
144
+ /**
145
+ * @return mixed
146
+ */
147
+ public function get_oauth_url(){
148
+ $scope = is_array($this->scope) ? urlencode(implode('+', $this->scope)) : $this->scope;
149
+ $state_nonce = wp_create_nonce($this->option_name.'_authorize');
150
+ $replacements = array( urlencode($this->id), $scope, urlencode(static::get_oauth_callback_url()), $state_nonce );
151
+ $return = str_replace( array('CLIENT_ID','ACCESS_SCOPE','REDIRECT_URI', 'STATE'), $replacements, $this->oauth_authorize_url );
152
+ if( $this->oauth_state ){
153
+ // check there's a STATE value in the authorize url, otherwise add it proactively
154
+ if( !preg_match('/STATE/', $this->oauth_authorize_url) ){
155
+ $return = add_query_arg('state', $state_nonce, $return);
156
+ }
157
+ }
158
+ return $return;
159
+ }
160
+
161
+ /**
162
+ * @return string
163
+ */
164
+ public static function get_oauth_callback_url(){
165
+ $redirect_base_uri = defined('OAUTH_REDIRECT') ? OAUTH_REDIRECT : admin_url('admin-ajax.php'); // you can completely replace the oauth redirect link for testing locally via a proxy for example
166
+ if( defined('EM_OAUTH_TUNNEL') ){ // for local development or other reasons, you can replace the domain with a tunnel domain, which should be with http(s):// included
167
+ $redirect_base_uri = str_replace(get_home_url(), EM_OAUTH_TUNNEL, $redirect_base_uri);
168
+ }
169
+ $api = static::get_api_class();
170
+ $callback_action = 'em_oauth_'. $api::get_option_name();
171
+ return add_query_arg(array('action'=>strtolower($callback_action), 'callback'=>'authorize'), $redirect_base_uri);
172
+ }
173
+
174
+ /**
175
+ * Returns a native client for this service, in the event we want to load an SDK provided by the service.
176
+ * @return stdClass
177
+ */
178
+ public function client(){
179
+ return new stdClass();
180
+ }
181
+
182
+ // GET, POST, PUT, PATCH, DELETE functions
183
+
184
+ /**
185
+ * @param $endpoint
186
+ * @param array $request_args
187
+ * @param bool $json_decode
188
+ * @return array
189
+ * @throws EM_Exception
190
+ */
191
+ public function http_request( $endpoint, array $request_args = array(), $json_decode = true ){
192
+ // clean up whether endpoint or full URL is provided
193
+ $endpoint = str_replace($this->api_base, '', $endpoint);
194
+ $request_url = $this->api_base. $endpoint;
195
+ //$request_url = add_query_arg('access_token', $this->token->access_token, $request_url);
196
+ // add oauth and method heaeders
197
+ if( empty($request_args['headers']) ) $request_args['headers'] = array();
198
+ $request_args['headers']['authorization'] = 'Bearer '.$this->token->access_token;
199
+ $request_args['method'] = in_array($request_args['method'], array('GET','POST','PUT','PATCH','DELETE')) ? $request_args['method'] : 'GET';
200
+ // prepare JSON format if sending via that content type
201
+ if( !empty($request_args['headers']['Content-Type']) && $request_args['headers']['Content-Type'] == 'application/json' ){
202
+ if( !empty($request_args['body']) && (is_array($request_args['body']) || is_object($request_args['body'])) ){
203
+ $request_args['body'] = json_encode($request_args['body']);
204
+ }
205
+ }
206
+ // request and parse
207
+ $response = wp_remote_request($request_url, $request_args); /* @var \Requests_Response_Headers $response['headers'] */
208
+ if( is_wp_error($response) ){
209
+ throw new EM_Exception($response);
210
+ }elseif( $response['response']['code'] >= 300 ){ //anything not 20x will indicate an issue
211
+ $errors = json_decode($response['body']);
212
+ if( is_array($errors) ){
213
+ $error = current($errors);
214
+ }elseif( !empty($errors->code) ){
215
+ $error = $errors;
216
+ }else{
217
+ $error = (object) array('code' => $response['response']['code'], 'message' => $response['body']);
218
+ }
219
+ throw new EM_Exception($error->message, $error->code);
220
+ }
221
+ if( $json_decode ){
222
+ $response['body'] = json_decode($response['body']);
223
+ }
224
+ return $response;
225
+ }
226
+
227
+ /**
228
+ * Fetches event data from the given endpoint with supplied arguments according to Meetup API v3
229
+ *
230
+ * @param string $endpoint Full URL or endpoint accepted.
231
+ * @param array $args
232
+ * @param array $request_args
233
+ * @return array
234
+ * @throws EM_Exception
235
+ */
236
+ public function get($endpoint, array $args = array(), array $request_args = array() ){
237
+ $request_url = add_query_arg( $args, $this->api_base.$endpoint );
238
+ $request_args['method'] = 'GET';
239
+ return static::http_request( $request_url, $request_args );
240
+ }
241
+
242
+ /**
243
+ * @param $endpoint
244
+ * @param array $vars
245
+ * @param array $request_args
246
+ * @param bool $json Shorthand for setting Content-Type in headers to application/json
247
+ * @return array|mixed
248
+ * @throws EM_Exception
249
+ */
250
+ public function post($endpoint, array $vars = array(), array $request_args = array(), $json = false ){
251
+ $request_args['body'] = $vars;
252
+ $request_args = array_merge(array('method' => 'POST'), $request_args);
253
+ if( $json ){
254
+ if( empty($request_args['headers'])) $request_args['headers'] = array();
255
+ $request_args['headers']['Content-Type'] = 'application/json';
256
+ }
257
+ return static::http_request($endpoint, $request_args, $json);
258
+ }
259
+
260
+ /**
261
+ * @param $endpoint
262
+ * @param array $vars
263
+ * @param array $request_args
264
+ * @param bool $json
265
+ * @return array|mixed
266
+ * @throws EM_Exception
267
+ */
268
+ public function patch($endpoint, array $vars = array(), array $request_args = array(), $json = false ){
269
+ $request_args['method'] = 'PATCH';
270
+ return static::post($endpoint, $vars, $request_args, $json);
271
+ }
272
+
273
+ /**
274
+ * @param $endpoint
275
+ * @param array $vars
276
+ * @param array $request_args
277
+ * @param bool $json
278
+ * @return array|mixed
279
+ * @throws EM_Exception
280
+ */
281
+ public function put($endpoint, array $vars = array(), array $request_args = array(), $json = false ){
282
+ $request_args['method'] = 'PUT';
283
+ return static::post($endpoint, $vars, $request_args, $json);
284
+ }
285
+
286
+ /**
287
+ * @param $endpoint
288
+ * @param array $args
289
+ * @param array $request_args
290
+ * @return array|mixed
291
+ * @throws EM_Exception
292
+ */
293
+ public function delete($endpoint, array $args = array(), array $request_args = array() ){
294
+ $request_url = add_query_arg( $args, $this->api_base.$endpoint );
295
+ $request_args['method'] = 'DELETE';
296
+ return static::http_request( $request_url, $request_args );
297
+ }
298
+
299
+ // Baseic OAuth interaction functions, loading a token into client as well as requesting, refreshing, verifying and revoking tokens.
300
+
301
+ /**
302
+ * @param int $user_id
303
+ * @param int $account_id
304
+ * @throws EM_Exception
305
+ */
306
+ public function load_token( $account_id = null, $user_id = null ){
307
+ if( $this->authorization_scope !== 'user' ) $user_id = null; // user id is not relevant
308
+ // return value if already authorized
309
+ if( $this->authorized && $this->authorized = $user_id.'|'.$account_id && $this->user_id == $user_id && $this->token->id == $account_id) return;
310
+ // not authorized, re/load token
311
+ $this->authorized = $this->token = false;
312
+ $this->user_id = $user_id;
313
+ // get token information from user account
314
+ $this->get_access_token( $account_id );
315
+ // renew token if expired
316
+ if ( $this->token->is_expired() ) {
317
+ // Refresh the token if it's expired and update WP user meta.
318
+ $this->refresh();
319
+ }else{
320
+ $this->authorized = $user_id .'|'. $this->token->id;
321
+ }
322
+ }
323
+
324
+ /**
325
+ * Requests an access token from the supplied authorization code. The access token is further verified and populated with service account meta.
326
+ * If successful, token and meta information is saved for the user $user_id or current user if not specified.
327
+ * Throws an EM_Exception if unsuccessful at any stage in this process.
328
+ *
329
+ * @var string $code
330
+ * @var int $user_id
331
+ * @throws EM_Exception
332
+ */
333
+ public function request($code, $user_id = null ){
334
+ if( $this->authorization_scope == 'user' ){
335
+ $this->user_id = empty($user_id) ? get_current_user_id() : $user_id; // used in $this->save_access_token()
336
+ }else{
337
+ $this->user_id = null;
338
+ }
339
+ $access_token = $this->request_access_token($code);
340
+ $this->token = new OAuth_API_Token($access_token);
341
+ if( $this->token->refresh_token === true ) $this->token->refresh_token = false; // if no token was provided, we may be able to obtain it here, otherwise validation will fail upon refresh.
342
+ // verify the access token so we can establish the id of this account and then save it to user profile
343
+ $access_token_meta = $this->verify_access_token();
344
+ // now, check for previous tokens and save to it instead of overwriting (we do this in case ppl reauthorize the same account and get a new token with no refresh_token)
345
+ if( empty($this->token->id) ){
346
+ $token = $this->token;
347
+ try{
348
+ $this->get_access_token($access_token_meta['id']);
349
+ $this->token->refresh( $token->to_array() ); // merge in new token info to old token
350
+ } catch ( EM_Exception $ex ){
351
+ $this->token = $token; // revert back to new token
352
+ }
353
+ }
354
+ // refresh current or new token with the meta info and save
355
+ $this->token->refresh( $access_token_meta );
356
+ $this->save_access_token();
357
+ }
358
+
359
+ /**
360
+ * @throws EM_Exception
361
+ */
362
+ public function refresh(){
363
+ if ( $this->token->refresh_token ) {
364
+ try{
365
+ $access_token = $this->refresh_access_token();
366
+ $this->token->refresh($access_token, true);
367
+ $this->save_access_token();
368
+ $this->authorized = $this->user_id .'|'. $this->token->id;
369
+ }catch( EM_Exception $ex ){
370
+ throw new EM_Exception( array(
371
+ $this->option_name.'-error' => sprintf(esc_html__( 'There was an error connecting to %s: %s', 'events-manager' ), $this->service_name, "<code>{$ex->getMessage()}</code>"),
372
+ $this->option_name.'-token-expired' => $this->reauthorize_error_string()
373
+ ));
374
+ }
375
+ }else{
376
+ throw new EM_Exception( $this->reauthorize_error_string() );
377
+ }
378
+ }
379
+
380
+ /**
381
+ * Verify the token for this client by obtaining meta data of the account associated to this token and saving it to the current token.
382
+ * @var boolean $update_token
383
+ * @return boolean
384
+ * @throws EM_Exception
385
+ */
386
+ public function verify( $update_token = true ){
387
+ $access_token_meta = $this->verify_access_token();
388
+ // refresh current or new token with the meta info and save
389
+ $updated = $this->token->refresh( $access_token_meta );
390
+ if( $updated && $update_token ) $this->save_access_token();
391
+ return true; // if we get here, verification passed.
392
+ }
393
+
394
+ /**
395
+ * @return boolean
396
+ * @throws EM_Exception
397
+ */
398
+ public function revoke(){
399
+ return $this->revoke_access_token();
400
+ }
401
+
402
+ /* START OVERRIDABLE FUNCTIONS - THESE FUNCTIONS COULD BE OVERRIDDEN TO SPECIFICALLY DEAL WITH PARTICULAR OAUTH PROVIDERS */
403
+
404
+ /**
405
+ * Specific function which requests the access token from the API Service and returns the access token array, an error array if service replies.
406
+ * Throws an EM_Exception if there are any other connection issues.
407
+ *
408
+ * @var string $code
409
+ * @return array
410
+ * @throws EM_Exception
411
+ */
412
+ public function request_access_token( $code ){
413
+ $args = array(
414
+ 'body' => array(
415
+ 'client_id' => $this->id,
416
+ 'grant_type' => 'authorization_code',
417
+ 'redirect_uri' => static::get_oauth_callback_url(),
418
+ 'code' => $code
419
+ )
420
+ );
421
+ return $this->oauth_request( 'post', $this->oauth_request_token_url, $args );
422
+ }
423
+
424
+ /**
425
+ * @return array
426
+ * @throws EM_Exception
427
+ */
428
+ public function refresh_access_token(){
429
+ $args = array(
430
+ 'body' => array(
431
+ 'grant_type' => 'refresh_token',
432
+ 'refresh_token' => $this->token->refresh_token,
433
+ )
434
+ );
435
+ return $this->oauth_request('post', $this->oauth_refresh_token_url, $args);
436
+ }
437
+
438
+ /**
439
+ * Verifies an access token by obtaining further meta data about the account associated with that token.
440
+ * Expected return is an associative array containing the id (service account id the token belongs to), name, photo and email (optional).
441
+ *
442
+ * @return array
443
+ * @throws EM_Exception
444
+ */
445
+ public function verify_access_token(){
446
+ $request_url = str_replace('ACCESS_TOKEN', $this->token->access_token, $this->oauth_verification_url);
447
+ $access_token = $this->oauth_request('get', $request_url);
448
+ return $access_token; // we may want to override this depending on what's returned
449
+ }
450
+
451
+ /**
452
+ * @return bool
453
+ * @throws EM_Exception
454
+ */
455
+ public function revoke_access_token(){
456
+ if( empty($this->oauth_revoke_url) ) return false;
457
+ $request_url = str_replace('ACCESS_TOKEN', $this->token->access_token, $this->oauth_revoke_url);
458
+ return $this->oauth_request('get', $request_url); // we may want to override this depending on what's returned
459
+ }
460
+
461
+ /**
462
+ * @param string $method
463
+ * @param $request_url
464
+ * @param array $args
465
+ * @return mixed
466
+ * @throws EM_Exception
467
+ */
468
+ public function oauth_request($method, $request_url, $args = array() ){
469
+ $args = array_merge( array('headers' => array(), 'body' => array()), $args );
470
+ if( $this->oauth_authentication == 'basic' ){
471
+ $args['headers']['authorization'] = 'Basic '.base64_encode($this->id.':'.$this->secret);
472
+ }
473
+ if( $method === 'get'){
474
+ if( $this->oauth_authentication == 'parameters' ){
475
+ // add client params to URL if using get
476
+ $request_url = add_query_arg( array('client_id' => $this->id), $request_url );
477
+ }
478
+ $response = wp_remote_get($request_url, $args);
479
+ }elseif( $method === 'post' ){
480
+ if( $this->oauth_authentication == 'parameters' ){
481
+ // add auth params to body if using post
482
+ $args['body']['client_id'] = $this->id;
483
+ $args['body']['client_secret'] = $this->secret;
484
+ }
485
+ if( empty($args['Content-Type'])){
486
+ // by defaulut post will send this content type
487
+ $args['headers']['Content-Type'] = 'application/x-www-form-urlencoded';
488
+ }
489
+ $response = wp_remote_post($request_url, $args);
490
+ }else{
491
+ throw new EM_Exception('Unknown request method.');
492
+ }
493
+ if( is_wp_error($response) ){
494
+ throw new EM_Exception($response->get_error_messages());
495
+ }elseif( $response['response']['code'] != '200' ){
496
+ $errors = json_decode($response['body']);
497
+ $error = current($errors);
498
+ if( !empty($error->message) ){
499
+ $message = $error->message;
500
+ }elseif( !empty($error->error) ){
501
+ $message = $error->error;
502
+ }elseif( !empty($error->reason) ){
503
+ $message = $error->reason;
504
+ }elseif( is_string($error) ){
505
+ $message = $error;
506
+ }elseif( is_string($errors) ){
507
+ $message = $errors;
508
+ }else{
509
+ $message = var_export($errors);
510
+ }
511
+ $error_code = !empty($error->code) ? $error->code : 'oauth-error';
512
+ throw new EM_Exception($message, $error_code);
513
+ }
514
+ return json_decode($response['body'], true); // we may want to override this depending on what's returned
515
+ }
516
+
517
+ /* END OVERRIDABLE FUNCTIONS */
518
+
519
+ /**
520
+ * @param int $api_user_id
521
+ * @return string
522
+ */
523
+ public function reauthorize_error_string($api_user_id = 0 ){
524
+ $settings_page_url = '<a href="'.admin_url('admin.php?page=events-manager-options').'">'. esc_html__('settings page', 'events-manager-google').'</a>';
525
+ if( !$api_user_id && !empty($this->token->id) ){
526
+ $api_user_id = !empty($this->token->email) ? $this->token->email : $this->token->id;
527
+ }
528
+ if( $api_user_id ){
529
+ return sprintf(__('You need to reauthorize access to account %s by visiting the %s page.', 'events-manager-google'), $api_user_id, $settings_page_url);
530
+ }
531
+ return sprintf(__('You need to authorize access to your %s account by visiting the %s page.', 'events-manager-google'), $this->service_name, $settings_page_url);
532
+ }
533
+
534
+ /**
535
+ * Gets an access token for a specific account, or provides first account user has available, if any. If no access token is available, an EM_Exception is thrown.
536
+ *
537
+ * @param int $api_user_id The ID (e.g. number or email) of the OAuth account
538
+ * @return OAuth_API_Token
539
+ * @throws EM_Exception
540
+ */
541
+ public function get_access_token( $api_user_id = 0 ){
542
+ if( $this->authorization_scope == 'site' ){
543
+ $site_tokens = EM_Options::get($this->option_name.'_token', array(), $this->option_dataset);
544
+ if( !empty($site_tokens) ){
545
+ if( $this->multiple_tokens && !empty($api_user_id) && !empty($site_tokens[$api_user_id]) ){
546
+ $token_data = $site_tokens[$api_user_id];
547
+ $token_data['id'] = $api_user_id;
548
+ }else{
549
+ $token_data = current($site_tokens);
550
+ $token_data['id'] = key($site_tokens);
551
+ }
552
+ }
553
+ }elseif( $this->authorization_scope == 'user' ){
554
+ $user_tokens = get_user_meta( $this->user_id, $this->option_dataset.'_'.$this->option_name, true );
555
+ if( !empty($user_tokens) ){
556
+ if( $api_user_id ){
557
+ if( !empty($user_tokens[$api_user_id]) ){
558
+ $token_data = $user_tokens[$api_user_id];
559
+ $token_data['id'] = $api_user_id;
560
+ }
561
+ }elseif( !empty($user_tokens) ){
562
+ $token_data = current($user_tokens);
563
+ $token_data['id'] = key($user_tokens);
564
+ }
565
+ }
566
+ }
567
+ if( empty($token_data) ) throw new EM_Exception( $this->reauthorize_error_string($api_user_id) );
568
+ $this->token = new $this->token_class($token_data);
569
+ return $this->token;
570
+ }
571
+
572
+ /**
573
+ * Sets the access token to the user meta storage where all connected accounts for the user of that token are stored.
574
+ */
575
+ public function save_access_token(){
576
+ if( $this->authorization_scope == 'site' ){
577
+ $token = $this->token->to_array();
578
+ if( $this->multiple_tokens ){
579
+ $site_tokens = EM_Options::get($this->option_name.'_token', array(), $this->option_dataset);
580
+ }
581
+ if( empty($site_tokens) ) $site_tokens = array();
582
+ $site_tokens[$this->token->id] = $token;
583
+ EM_Options::set($this->option_name.'_token', $site_tokens, $this->option_dataset);
584
+ }elseif( $this->authorization_scope == 'user' ){
585
+ if( $this->multiple_tokens ){
586
+ $user_tokens = get_user_meta($this->user_id, $this->option_dataset.'_'.$this->option_name, true);
587
+ }
588
+ if( empty($user_tokens) ) $user_tokens = array();
589
+ $token = $this->token->to_array();
590
+ $user_tokens[$this->token->id] = $token;
591
+ update_user_meta($this->user_id, $this->option_dataset.'_'.$this->option_name, $user_tokens);
592
+ }
593
+ }
594
+ }
classes/em-oauth/oauth-api-token.php ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace EM_OAuth;
3
+ use EM_Exception;
4
+
5
+ class OAuth_API_Token {
6
+
7
+ public $access_token = '';
8
+ public $refresh_token = '';
9
+ public $token_type = '';
10
+ public $expires_in = 0;
11
+ /**
12
+ * @var int Timestamp when a token will expire at, which can be supplied instead of expires_in and that value will be generated from this one.
13
+ */
14
+ public $expires_at = 0;
15
+ public $created = 0;
16
+
17
+ public $id = '';
18
+ public $email = '';
19
+ public $name = '';
20
+ public $photo = '';
21
+
22
+ /**
23
+ * @param array $token
24
+ * @throws EM_Exception
25
+ */
26
+ public function __construct( $token ){
27
+ $this->refresh($token);
28
+ if( empty($token['created']) ) $this->created = time();
29
+ }
30
+
31
+ /**
32
+ * @param array $token
33
+ * @return boolean $updated
34
+ * @throws EM_Exception
35
+ */
36
+ public function refresh( $token, $reset = false ){
37
+ $updated = false;
38
+ // reset values
39
+ if( $reset ){
40
+ $this->expires_in = $this->expires_at = $this->created = 0;
41
+ $this->access_token = $this->refresh_token = $this->token_type = '';
42
+ }
43
+ // add new values
44
+ foreach( $token as $k => $v ){
45
+ if( empty($this->$k) || $this->$k != $token[$k] ){
46
+ $this->$k = $token[$k];
47
+ $updated = true;
48
+ }
49
+ }
50
+ // set values that may not have been added
51
+ if( empty($this->id) && !empty($this->email) ) $this->id = $this->email;
52
+ if( !$this->created ) $this->created = time();
53
+ // set expires_at, which is what we'll use for expiry checking
54
+ if( $this->expires_at ){
55
+ $this->expires_in = $this->expires_at - time();
56
+ }elseif( $this->created && $this->expires_in ){
57
+ $this->expires_at = $this->expires_in + $this->created;
58
+ }else{
59
+ $this->expires_in = $this->expires_at = time();
60
+ }
61
+ $this->verify();
62
+ return $updated;
63
+ }
64
+
65
+ /**
66
+ * @throws EM_Exception
67
+ */
68
+ public function verify(){
69
+ $missing = array();
70
+ foreach( array('access_token', 'expires_at') as $k ){
71
+ if( empty($this->$k) ) $missing[] = $k;
72
+ }
73
+ if( !empty($missing) ) throw new EM_Exception( sprintf(__('Involid token credentials, the folloiwng are missing: %s.', 'events-manager'), implode(', ', $missing)) );
74
+ }
75
+
76
+ public function is_expired(){
77
+ return $this->expires_at < time();
78
+ }
79
+
80
+ public function to_array(){
81
+ $array = array();
82
+ $ignore = array('id');
83
+ foreach( get_object_vars($this) as $k => $v ){
84
+ if( !in_array($k, $ignore) && !empty($this->$k) ) $array[$k] = $this->$k;
85
+ }
86
+ return $array;
87
+ }
88
+ }
classes/em-oauth/oauth-api.php ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace EM_OAuth;
3
+ use EM_Exception, EM_Notices, EM_Options;
4
+
5
+ class OAuth_API {
6
+ /**
7
+ * The name of this service to be displayed to the end user in notices etc.
8
+ * @var string
9
+ */
10
+ protected static $service_name = 'EM OAuth 2.0';
11
+ /**
12
+ * @var string
13
+ */
14
+ protected static $service_url = 'http://example.com';
15
+ /**
16
+ * Ths option/key name used to differentiate this from other OAuth objects stored in the database tables.
17
+ * @var string
18
+ */
19
+ protected static $option_name = 'oauth';
20
+ /**
21
+ * Where data about this API is stored, which is by default in a serialized array with option name 'em_oauth'
22
+ * @var string
23
+ */
24
+ protected static $option_dataset = 'dbem_oauth';
25
+ /**
26
+ * Allows overriding the default client class to be loaded, set this to the token class name you'd like to use instead.
27
+ * By default, the extended classname preceded by _Client will be used if it exists, otherwise OAuth_API_Client.
28
+ * @var string
29
+ */
30
+ protected static $client_class;
31
+ /**
32
+ * Allows overriding the default token class to be loaded, set this to the token class name you'd like to use instead.
33
+ * By default, the extended classname preceded by _Token will be used if it exists, otherwise OAuth_API_Token.
34
+ * @var string
35
+ */
36
+ protected static $token_class;
37
+ /**
38
+ * Defines whether authorization tokens are stored at a site, user or (eventually) network level.
39
+ * @var string 'site' or 'user' level (future consideration for 'network')
40
+ */
41
+ protected static $authorization_scope = 'site';
42
+ /**
43
+ * Whether or not storage destination supports multiple accounts (e.g. multiple accounts for a site or a user)
44
+ * @var bool
45
+ */
46
+ protected static $multiple_tokens = false;
47
+
48
+ /**
49
+ * @return string
50
+ */
51
+ public static function get_service_name() {
52
+ return static::$service_name;
53
+ }
54
+
55
+ /**
56
+ * @return string
57
+ */
58
+ public static function get_service_url() {
59
+ return static::$service_url;
60
+ }
61
+
62
+ /**
63
+ * @return string
64
+ */
65
+ public static function get_option_name() {
66
+ return static::$option_name;
67
+ }
68
+
69
+ /**
70
+ * @return string
71
+ */
72
+ public static function get_option_dataset() {
73
+ return static::$option_dataset;
74
+ }
75
+
76
+ /**
77
+ * @return OAuth_API_Client|string String representation of token class, used for instantiation or static function/property reference
78
+ */
79
+ public static function get_client_class() {
80
+ if( static::$client_class !== null && class_exists(static::$client_class) ) return static::$client_class;
81
+ if( static::$client_class === null && class_exists(get_called_class().'_Client') ){
82
+ static::$client_class = get_called_class().'_Client';
83
+ return static::$client_class;
84
+ }
85
+ return 'EM_OAuth\OAuth_API_Client';
86
+ }
87
+
88
+ /**
89
+ * @return OAuth_API_Token|string String representation of token class, used for instantiation or static function/property reference
90
+ */
91
+ public static function get_token_class() {
92
+ if( static::$token_class !== null && class_exists(static::$token_class) ) return static::$token_class;
93
+ if( static::$token_class === null && class_exists(get_called_class().'_Token') ){
94
+ static::$token_class = get_called_class().'_Token';
95
+ return static::$token_class;
96
+ }
97
+ return 'EM_OAuth\OAuth_API_Token';
98
+ }
99
+
100
+ /**
101
+ * @return string
102
+ */
103
+ public static function get_authorization_scope() {
104
+ return static::$authorization_scope;
105
+ }
106
+
107
+ /**
108
+ * @return bool
109
+ */
110
+ public static function supports_multiple_tokens() {
111
+ return static::$multiple_tokens;
112
+ }
113
+
114
+ /**
115
+ * Loads the service credentials into an abstract client api object. If a user ID is supplied and there's an issue retrieving an access token, an exception will be returned.
116
+ * @param int $user_id The User ID in WordPress
117
+ * @param int $api_user_id The ID of the account in Google (i.e. the email)
118
+ * @return OAuth_API_Client
119
+ * @throws EM_Exception
120
+ */
121
+ public static function get_client( $user_id = 0, $api_user_id = 0 ) {
122
+ //set up the client
123
+ $client_class = static::get_client_class();
124
+ $client = new $client_class(); /* @var OAuth_API_Client $client */
125
+ //load user access token
126
+ if( $user_id !== false ) {
127
+ if ( empty($user_id) ) $user_id = get_current_user_id();
128
+ $client->load_token( $user_id, $api_user_id );
129
+ }
130
+ return $client;
131
+ }
132
+
133
+ public static function get_user_tokens( $user_id = false ){
134
+ if( static::$authorization_scope !== 'user' ) return array();
135
+ if( empty($user_id) ) $user_id = get_current_user_id();
136
+ $user_tokens = get_user_meta( $user_id, static::$option_dataset.'_'.static::$option_name, true );
137
+ if( empty($user_tokens) ) $user_tokens = array();
138
+ return $user_tokens;
139
+ }
140
+
141
+ /**
142
+ * @return array[OAuth_API_Token]
143
+ */
144
+ public static function get_site_tokens(){
145
+ if( static::$authorization_scope !== 'site' ) return array();
146
+ $site_tokens = EM_Options::get(static::$option_name.'_token', array(), static::$option_dataset);
147
+ if( empty($site_tokens) ) $site_tokens = array();
148
+ return $site_tokens;
149
+ }
150
+
151
+ /**
152
+ * Includes and calls the code required to handle a callback from FB to store user auth token.
153
+ */
154
+ public static function oauth_authorize() {
155
+ global $EM_Notices;
156
+ if( !empty($EM_Notices) ) $EM_Notices = new EM_Notices();
157
+ if( !empty($_REQUEST['code']) ){
158
+ try{
159
+ $client = static::get_client(false);
160
+ if( !$client->oauth_state || (!empty($_REQUEST['state']) && !wp_verify_nonce( $_REQUEST['state'], static::$option_name.'_authorize')) ){
161
+ $EM_Notices->add_error( sprintf( esc_html__( 'There was an error connecting to %s: %s', 'events-manager' ), static::$service_name, '<code>No State Provided</code>'), true );
162
+ }else{
163
+ try {
164
+ $client->request( $_REQUEST['code'] );
165
+ $EM_Notices->add_confirm( sprintf( esc_html__( 'Your account has been successfully connected with %s!', 'events-manager' ), static::$service_name ), true);
166
+ } catch ( EM_Exception $e ){
167
+ $EM_Notices->add_error( sprintf( esc_html__( 'There was an error connecting to %s: %s', 'events-manager' ), static::$service_name, '<code>'.$e->getMessage().'</code>' ), true );
168
+ }
169
+ }
170
+ } catch ( EM_Exception $ex ){
171
+ $EM_Notices->add_error($ex->get_messages(), true);
172
+ }
173
+ }else{
174
+ $EM_Notices->add_error( sprintf( esc_html__( 'There was an error connecting to %s: %s', 'events-manager' ), static::$service_name, '<code>No Authorization Code Provided</code>'), true );
175
+ }
176
+ // Redirect to settings page
177
+ $query_args = array( 'page' => 'events-manager-options' );
178
+ $url = add_query_arg( $query_args, admin_url( 'admin.php' ) );
179
+ wp_redirect( $url );
180
+ die();
181
+ }
182
+
183
+ /**
184
+ * Handles disconnecting a user from one or all their connected Google accounts, attempting to revoke their key in the process.
185
+ */
186
+ public static function oauth_disconnect(){
187
+ global $EM_Notices;
188
+ if( !empty($EM_Notices) ) $EM_Notices = new EM_Notices();
189
+
190
+ if( static::$authorization_scope == 'user' ){
191
+ $account_tokens = static::get_user_tokens();
192
+ }else{
193
+ $account_tokens = static::get_site_tokens();
194
+ }
195
+ $accounts_to_disconnect = array();
196
+ if( empty($_REQUEST['user']) && !empty($_REQUEST['nonce']) && wp_verify_nonce($_REQUEST['nonce'], 'em-oauth-'. static::$option_name .'-disconnect') ){
197
+ $accounts_to_disconnect = array_keys($account_tokens);
198
+ }elseif( !empty($_REQUEST['account']) && !empty($_REQUEST['nonce']) && wp_verify_nonce($_REQUEST['nonce'], 'em-oauth-'. static::$option_name .'-disconnect-'.$_REQUEST['account']) ){
199
+ if( !empty($account_tokens[$_REQUEST['account']]) ){
200
+ $accounts_to_disconnect[] = $_REQUEST['account'];
201
+ }
202
+ }else{
203
+ $EM_Notices->add_error('Missing nonce, please contact your administrator.', true);
204
+ }
205
+ if( !empty($accounts_to_disconnect) ){
206
+ $errors = $disconnected_accounts = array();
207
+ foreach( $accounts_to_disconnect as $account_id ){
208
+ try{
209
+ $client = static::get_client( get_current_user_id(), $account_id);
210
+ $client->revoke();
211
+ } catch ( EM_Exception $ex ){
212
+ $account_name = !empty( $client->token->email ) ? $client->token->email : $client->token->name;
213
+ $errors[] = "<em>$account_name</em> - " . $ex->getMessage();
214
+ } finally{
215
+ $disconnected_accounts[] = $account_id;
216
+ unset($account_tokens[$account_id]);
217
+ }
218
+ }
219
+ if( !empty($disconnected_accounts) ){
220
+ if( static::$authorization_scope == 'user' ){
221
+ if( empty($account_tokens) ){
222
+ delete_user_meta( get_current_user_id(), 'em_oauth_'.static::$option_name );
223
+ }else{
224
+ update_user_meta( get_current_user_id(), 'em_oauth_'.static::$option_name, $account_tokens );
225
+ }
226
+ }else{
227
+ EM_Options::set(static::$option_name.'_token', $account_tokens, static::$option_dataset);
228
+ }
229
+ $success = _n('You have successfully disconnected from your %s account.', 'You have successfully disconnected from your %s accounts.', count($accounts_to_disconnect), 'events-manager');
230
+ $EM_Notices->add_confirm(sprintf($success, static::$service_name), true);
231
+ }
232
+ if( !empty($errors) ){
233
+ $error_msg = sprintf( esc_html__('There were some issues whilst disconnecting from your %s account(s) :', 'events-manager'), static::$service_name );
234
+ array_unshift( $errors, $error_msg );
235
+ $EM_Notices->add_error( $errors, true );
236
+ }
237
+ }
238
+
239
+ // Redirect to settings page
240
+ $query_args = array( 'page' => 'events-manager-options' );
241
+ $url = add_query_arg( $query_args, admin_url( 'admin.php' ) );
242
+ wp_redirect( $url );
243
+ die();
244
+ }
245
+ }
246
+ //include dependents
247
+ include('oauth-api-token.php');
248
+ include('oauth-api-client.php');
249
+ if( is_admin() ){
250
+ include('oauth-admin-settings.php');
251
+ }
classes/em-object.php CHANGED
@@ -634,7 +634,7 @@ class EM_Object {
634
  //non default taxonomy, so create new item for the taxonomies array
635
  $tax_name = str_replace('-','_',$tax_name);
636
  $prefix = !array_key_exists($tax_name, $taxonomies_array) ? '':'post_';
637
- if( is_array($tax->object_type) ){
638
  if( $event_tax || $loc_tax ){
639
  $taxonomies_array[$prefix.$tax_name] = array('name'=>$tax_name, 'context'=>array(), 'slug'=> $tax->rewrite['slug'], 'query_var'=> $tax->query_var );
640
  }
@@ -1028,6 +1028,7 @@ class EM_Object {
1028
  if( empty($request) ) $request = $_REQUEST;
1029
  if( !empty($request['em_search']) && empty($args['search']) ) $request['search'] = $request['em_search']; //em_search is included to circumvent wp search GET/POST clashes
1030
  $accepted_searches = !empty($accepted_searches) ? $accepted_searches : self::get_default_search();
 
1031
  $accepted_searches = apply_filters('em_accepted_searches', $accepted_searches, $args);
1032
  //merge variables from the $request into $args
1033
  foreach($request as $post_key => $post_value){
634
  //non default taxonomy, so create new item for the taxonomies array
635
  $tax_name = str_replace('-','_',$tax_name);
636
  $prefix = !array_key_exists($tax_name, $taxonomies_array) ? '':'post_';
637
+ if( is_array($tax->object_type) && !empty($tax->rewrite) ){
638
  if( $event_tax || $loc_tax ){
639
  $taxonomies_array[$prefix.$tax_name] = array('name'=>$tax_name, 'context'=>array(), 'slug'=> $tax->rewrite['slug'], 'query_var'=> $tax->query_var );
640
  }
1028
  if( empty($request) ) $request = $_REQUEST;
1029
  if( !empty($request['em_search']) && empty($args['search']) ) $request['search'] = $request['em_search']; //em_search is included to circumvent wp search GET/POST clashes
1030
  $accepted_searches = !empty($accepted_searches) ? $accepted_searches : self::get_default_search();
1031
+ $accepted_searches = array_diff($accepted_searches, array('format', 'format_header', 'format_footer'));
1032
  $accepted_searches = apply_filters('em_accepted_searches', $accepted_searches, $args);
1033
  //merge variables from the $request into $args
1034
  foreach($request as $post_key => $post_value){
classes/event-locations/em-event-location-url.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace EM_Event_Locations;
3
+ /**
4
+ * Adds a URL event location type by extending EM_Event_Location and registering itself with EM_Event_Locations
5
+ *
6
+ * @property string url The url of this event location.
7
+ * @property string text The text used in a link for the url.
8
+ */
9
+ class URL extends Event_Location {
10
+
11
+ public static $type = 'url';
12
+ public static $admin_template = '/forms/event/event-locations/url.php';
13
+
14
+ public $properties = array('url', 'text');
15
+
16
+ public function get_post(){
17
+ $return = parent::get_post();
18
+ if( !empty($_POST['event_location_url']) ){
19
+ $this->event->event_location_data['url'] = esc_url_raw($_POST['event_location_url']);
20
+ }
21
+ if( !empty($_POST['event_location_url_text']) ){
22
+ $this->event->event_location_data['text'] = sanitize_text_field($_POST['event_location_url_text']);
23
+ }
24
+ return $return;
25
+ }
26
+
27
+ public function validate(){
28
+ $result = false;
29
+ if( empty($this->event->event_location_data['url']) ){
30
+ $this->event->add_error( __('Please enter a valid URL for this event location.', 'events-manager') );
31
+ $result = false;
32
+ }
33
+ if( empty($this->event->event_location_data['text']) ){
34
+ $this->event->add_error( __('Please provide some link text for this event location URL.', 'events-manager') );
35
+ $result = false;
36
+ }
37
+ return $result;
38
+ }
39
+
40
+ public function get_link( $new_target = true ){
41
+ return '<a href="'.esc_url($this->url).'">'. esc_html($this->text).'</a>';
42
+ }
43
+
44
+ public function get_admin_column() {
45
+ return '<strong>'. static::get_label() . ' - ' . $this->get_link().'</strong>';
46
+ }
47
+
48
+ public static function get_label( $label_type = 'singular' ){
49
+ switch( $label_type ){
50
+ case 'plural':
51
+ return esc_html__('URLs', 'events-manager');
52
+ break;
53
+ case 'singular':
54
+ return esc_html__('URL', 'events-manager');
55
+ break;
56
+ }
57
+ return parent::get_label($label_type);
58
+ }
59
+
60
+ public function output( $what = null ){
61
+ if( $what === null ){
62
+ return '<a href="'. esc_url($this->url) .'" target="_blank">'. esc_html($this->text) .'</a>';
63
+ }elseif( $what === '_self' ){
64
+ return '<a href="'. esc_url($this->url) .'">'. esc_html($this->text) .'</a>';
65
+ }elseif( $what === '_parent' || $what === '_top' ){
66
+ return '<a href="'. esc_url($this->url) .'" target="'.$what.'">'. esc_html($this->text) .'</a>';
67
+ }else{
68
+ return parent::output($what);
69
+ }
70
+ }
71
+ }
72
+ URL::init();
classes/event-locations/em-event-location.php ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace EM_Event_Locations;
3
+ /**
4
+ * Class EM_Event_Location
5
+ * This class is to be extended by any event location type. The only time this class is not used when not extended is if
6
+ * @property-read string type
7
+ */
8
+ class Event_Location {
9
+ /**
10
+ * @var \EM_Event
11
+ */
12
+ protected $event;
13
+ /**
14
+ * The type name of this location type, used to store in the database. Use alphanmeric characters (a-z, A-Z, 0-9), dashes and underscores only.
15
+ * @var string
16
+ */
17
+ public static $type;
18
+ /**
19
+ * Represents shortcut property names for an event location child object that can be accessed safely which then refers to the EM_Event object.
20
+ * This is a map of property names as keys to custom field names in the event_attributes array property of EM_Event.
21
+ * Naming conventions should follow the lines of lowercase event_location_{static::$type}(_$property_name), for example a url would have event_location_url as a url, and event_location_url_text for link text
22
+ * @var array
23
+ */
24
+ public $properties = array();
25
+ /**
26
+ * The admin template path, if there is one.
27
+ * @var string
28
+ * @see EM_Event_Location::load_admin_template()
29
+ */
30
+ public static $admin_template;
31
+
32
+ public static function init(){
33
+ Event_Locations::register( static::$type, get_called_class() );
34
+ }
35
+
36
+ /**
37
+ * EM_Event_Location constructor.
38
+ * @param \EM_Event $EM_Event
39
+ */
40
+ public function __construct( $EM_Event ) {
41
+ if( is_object($EM_Event) && property_exists($EM_Event, 'event_id') ){
42
+ $this->event = $EM_Event;
43
+ }
44
+ }
45
+
46
+ /**
47
+ * @param $name
48
+ * @return string|null
49
+ */
50
+ public function __get( $name ) {
51
+ if( $name == 'type' ){
52
+ return static::$type;
53
+ }elseif( in_array($name, $this->properties) ){
54
+ return isset($this->event->event_location_data[$name]) ? $this->event->event_location_data[$name] : null;
55
+ }
56
+ return null;
57
+ }
58
+
59
+ public function __set($name, $value) {
60
+ if( in_array($name, $this->properties) ){
61
+ $this->event->event_location_data[$name] = $value;
62
+ }
63
+ }
64
+
65
+ public function __isset($name) {
66
+ if( in_array($name, $this->properties) ){
67
+ return isset($this->event->event_location_data[$name]);
68
+ }
69
+ return false;
70
+ }
71
+
72
+ public function load_postdata( $event_meta = array() ){
73
+ if( empty($event_meta) ) $event_meta = $this->event->get_event_meta();
74
+ $base_key = '_event_location_'.static::$type;
75
+ foreach( $event_meta as $event_meta_key => $event_meta_val ){
76
+ if( $event_meta_key == $base_key ){
77
+ $this->event->event_location_data[static::$type] = ( is_array($event_meta_val) ) ? $event_meta_val[0]:$event_meta_val;
78
+ $this->event->event_location_data[static::$type] = maybe_unserialize($this->event->event_location_data[static::$type]);
79
+ }elseif( substr($event_meta_key, 0, strlen($base_key) ) == $base_key ){
80
+ //event location data is placed directly into the event_location_data array and referenced via get_event_location()
81
+ $key = str_replace('_event_location_'.static::$type.'_', '', $event_meta_key);
82
+ $this->event->event_location_data[$key] = ( is_array($event_meta_val) ) ? $event_meta_val[0]:$event_meta_val;
83
+ $this->event->event_location_data[$key] = maybe_unserialize($this->event->event_location_data[$key]);
84
+ }
85
+ }
86
+ }
87
+
88
+ /**
89
+ * @param array $post
90
+ * @return boolean
91
+ */
92
+ public function get_post(){
93
+ $this->event->event_location_data = array();
94
+ return true;
95
+ }
96
+
97
+ /**
98
+ * @return boolean
99
+ */
100
+ public function validate(){
101
+ return false;
102
+ }
103
+
104
+ public function save(){
105
+ if( is_numeric($this->event->post_id) && $this->event->post_id > 0 && static::$type !== null ){
106
+ $this->reset_data();
107
+ foreach( $this->event->event_location_data as $prop => $value ){
108
+ $meta_key = $prop == static::$type ? '_event_location_'.$prop : '_event_location_'.static::$type.'_'.$prop;
109
+ if( $value !== null ){
110
+ update_post_meta( $this->event->post_id, $meta_key, $value );
111
+ }else{
112
+ delete_post_meta( $this->event->post_id, $meta_key );
113
+ }
114
+ }
115
+ }
116
+ return true;
117
+ }
118
+
119
+ final function reset_data( $preserve_type_data = false ){
120
+ if( is_numeric($this->event->post_id) && $this->event->post_id > 0 ){
121
+ global $wpdb;
122
+ $result = $wpdb->query( $wpdb->prepare('DELETE FROM '.$wpdb->postmeta." WHERE post_id=%d AND meta_key LIKE '_event_location_%' AND meta_key != '_event_location_type'", $this->event->post_id) );
123
+ wp_cache_delete( $this->event->post_id, 'post_meta' ); //refresh cache to prevent looking at old data
124
+ return $result;
125
+ }
126
+ return false;
127
+ }
128
+
129
+ public function get_admin_column(){
130
+ return $this->get_label('singular');
131
+ }
132
+
133
+ /**
134
+ * Returns whether or not this event location is enabled for use.
135
+ * @return bool
136
+ */
137
+ public static function is_enabled(){
138
+ $location_types = get_option('dbem_location_types', array());
139
+ return !empty($location_types[static::$type]);
140
+ }
141
+
142
+ /**
143
+ * Loads admin template automatically if static $admin_template is set to a valid path in templates folder.
144
+ * Classes with custom forms outside of template folders can override this function and provide their own HTML that will go in the loop of event location type forms.
145
+ */
146
+ public static function load_admin_template(){
147
+ if( static::$admin_template ){
148
+ em_locate_template( static::$admin_template, true );
149
+ }
150
+ }
151
+
152
+ public static function get_label( $label_type = 'siingular' ){
153
+ //override and return plural name.
154
+ return static::$type;
155
+ }
156
+
157
+ public function output( $what = null ){
158
+ if( $what !== null && $what !== 'type' ){
159
+ return esc_html($this->$what);
160
+ }else{
161
+ return static::get_label();
162
+ }
163
+ }
164
+ }
165
+
166
+ //include default Event Locations
167
+ include('em-event-location-url.php');
classes/event-locations/em-event-locations.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace EM_Event_Locations;
3
+ /**
4
+ * Handles registration and retrival of EM_Event_Location child classes for use with events.
5
+ * Class EM_Event_Locations
6
+ */
7
+ class Event_Locations {
8
+
9
+ /**
10
+ * Associative array with type key => class name.
11
+ * @var array
12
+ */
13
+ private static $types = array();
14
+
15
+ /**
16
+ * @return array[EM_Event_Location::]
17
+ */
18
+ public static function get_types(){
19
+ return static::$types;
20
+ }
21
+
22
+ /**
23
+ * @param string $type
24
+ * @param string $classname
25
+ * @return bool
26
+ */
27
+ public static function register( $type, $classname ){
28
+ self::$types[$type] = $classname;
29
+ return true;
30
+ }
31
+
32
+ /**
33
+ * @param string $type
34
+ * @return bool
35
+ */
36
+ public static function unregister( $type ){
37
+ if( !empty(self::$types[$type]) ){
38
+ unset(self::$types[$type]);
39
+ return true;
40
+ }
41
+ return false;
42
+ }
43
+
44
+ /**
45
+ * @param string $type
46
+ * @param \EM_Event $EM_Event
47
+ * @return bool
48
+ */
49
+ public static function get( $type = null, $EM_Event = null ){
50
+ if( !empty(self::$types[$type]) && class_exists(self::$types[$type]) ){
51
+ $location_type = self::$types[$type];
52
+ return new $location_type( $EM_Event );
53
+ }
54
+ return false;
55
+ }
56
+
57
+ /**
58
+ * Returns whether or not the supplied event location $type is enabled for use.
59
+ * @param string $type
60
+ * @return bool
61
+ */
62
+ public static function is_enabled( $type ){
63
+ $location_types = get_option('dbem_location_types', array());
64
+ return !empty($location_types[$type]);
65
+ }
66
+
67
+ }
68
+ require('em-event-location.php');
em-events.php CHANGED
@@ -245,7 +245,26 @@ function em_content_wp_title($title, $sep = '', $seplocation = ''){
245
  return $title;
246
  }
247
  add_filter ( 'wp_title', 'em_content_wp_title',100,3 ); //override other plugin SEO due to way EM works.
248
- add_filter( 'wpseo_title', 'em_content_wp_title', 100, 3 ); //WP SEO friendly
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
249
 
250
  /**
251
  * Makes sure we're in "THE Loop", which is determinied by a flag set when the_post() (start) is first called, and when have_posts() (end) returns false.
245
  return $title;
246
  }
247
  add_filter ( 'wp_title', 'em_content_wp_title',100,3 ); //override other plugin SEO due to way EM works.
248
+
249
+ /**
250
+ * Yoast SEO friendly short circuit, fixes issues in Yoast 14 update by changing the $sep function into the actual separator.
251
+ * @param $title
252
+ * @param string|mixed $sep
253
+ * @param string $seplocation
254
+ * @return string
255
+ */
256
+ function em_content_wpseo_title($title, $sep = '', $seplocation = ''){
257
+ if( class_exists('WPSEO_Utils') && method_exists('WPSEO_Utils', 'get_title_separator') ){
258
+ $sep = WPSEO_Utils::get_title_separator();
259
+ }elseif( !is_string( $sep ) ){
260
+ $sep = '';
261
+ }
262
+ if( !is_string($seplocation) ){
263
+ $seplocation = '';
264
+ }
265
+ return em_content_wp_title( $title, $sep, $seplocation = '' );
266
+ }
267
+ add_filter( 'wpseo_title', 'em_content_wpseo_title', 100, 2 ); //WP SEO friendly
268
 
269
  /**
270
  * Makes sure we're in "THE Loop", which is determinied by a flag set when the_post() (start) is first called, and when have_posts() (end) returns false.
em-functions.php CHANGED
@@ -653,11 +653,16 @@ function em_checkbox_items($name, $array, $saved_values, $horizontal = true) {
653
  }
654
  function em_options_input_text($title, $name, $description ='', $default='') {
655
  $translate = EM_ML::is_option_translatable($name);
 
 
 
 
 
656
  ?>
657
  <tr valign="top" id='<?php echo esc_attr($name);?>_row'>
658
  <th scope="row"><?php echo esc_html($title); ?></th>
659
  <td>
660
- <input name="<?php echo esc_attr($name) ?>" type="text" id="<?php echo esc_attr($name) ?>" value="<?php echo esc_attr(get_option($name, $default), ENT_QUOTES); ?>" size="45" />
661
  <?php if( $translate ): ?><span class="em-translatable dashicons dashicons-admin-site"></span><?php endif; ?>
662
  <br />
663
  <?php
@@ -672,7 +677,7 @@ function em_options_input_text($title, $name, $description ='', $default='') {
672
  </tr>
673
  <?php
674
  }else{
675
- $default_lang = '<input name="'.esc_attr($name).'_ml['.EM_ML::$wplang.']" type="hidden" id="'. esc_attr($name.'_'. EM_ML::$wplang) .'" value="'. esc_attr(get_option($name, $default), ENT_QUOTES).'" />';
676
  }
677
  }
678
  echo '</table>';
653
  }
654
  function em_options_input_text($title, $name, $description ='', $default='') {
655
  $translate = EM_ML::is_option_translatable($name);
656
+ if( preg_match('/^(.+)\[(.+)?\]$/', $name, $matches) ){
657
+ $value = EM_Options::get($matches[2], $default, $matches[1]);
658
+ }else{
659
+ $value = get_option($name, $default);
660
+ }
661
  ?>
662
  <tr valign="top" id='<?php echo esc_attr($name);?>_row'>
663
  <th scope="row"><?php echo esc_html($title); ?></th>
664
  <td>
665
+ <input name="<?php echo esc_attr($name) ?>" type="text" id="<?php echo esc_attr($name) ?>" value="<?php echo esc_attr($value, ENT_QUOTES); ?>" size="45" />
666
  <?php if( $translate ): ?><span class="em-translatable dashicons dashicons-admin-site"></span><?php endif; ?>
667
  <br />
668
  <?php
677
  </tr>
678
  <?php
679
  }else{
680
+ $default_lang = '<input name="'.esc_attr($name).'_ml['.EM_ML::$wplang.']" type="hidden" id="'. esc_attr($name.'_'. EM_ML::$wplang) .'" value="'. esc_attr($value, ENT_QUOTES).'" />';
681
  }
682
  }
683
  echo '</table>';
em-install.php CHANGED
@@ -156,6 +156,7 @@ function em_create_events_table() {
156
  event_spaces int(5) NULL DEFAULT 0,
157
  event_private tinyint(1) unsigned NOT NULL DEFAULT 0,
158
  location_id bigint(20) unsigned NULL DEFAULT NULL,
 
159
  recurrence_id bigint(20) unsigned NULL DEFAULT NULL,
160
  event_date_created datetime NULL DEFAULT NULL,
161
  event_date_modified datetime NULL DEFAULT NULL,
@@ -676,6 +677,7 @@ function em_add_options() {
676
  'dbem_timezone_default' => EM_DateTimeZone::create()->getName(),
677
  'dbem_require_location' => 0,
678
  'dbem_locations_enabled' => 1,
 
679
  'dbem_use_select_for_locations' => 0,
680
  'dbem_attributes_enabled' => 1,
681
  'dbem_recurrence_enabled'=> 1,
@@ -1123,6 +1125,13 @@ function em_upgrade_current_installation(){
1123
  update_option('dbem_smtp_encryption', 0);
1124
  }
1125
  }
 
 
 
 
 
 
 
1126
  }
1127
 
1128
  function em_set_mass_caps( $roles, $caps ){
156
  event_spaces int(5) NULL DEFAULT 0,
157
  event_private tinyint(1) unsigned NOT NULL DEFAULT 0,
158
  location_id bigint(20) unsigned NULL DEFAULT NULL,
159
+ event_location_type VARCHAR(15) NULL DEFAULT NULL,
160
  recurrence_id bigint(20) unsigned NULL DEFAULT NULL,
161
  event_date_created datetime NULL DEFAULT NULL,
162
  event_date_modified datetime NULL DEFAULT NULL,
677
  'dbem_timezone_default' => EM_DateTimeZone::create()->getName(),
678
  'dbem_require_location' => 0,
679
  'dbem_locations_enabled' => 1,
680
+ 'dbem_location_types' => array('location' => 1, 'url' => 1),
681
  'dbem_use_select_for_locations' => 0,
682
  'dbem_attributes_enabled' => 1,
683
  'dbem_recurrence_enabled'=> 1,
1125
  update_option('dbem_smtp_encryption', 0);
1126
  }
1127
  }
1128
+ if( get_option('dbem_version') != '' && get_option('dbem_version') < 5.975 ){
1129
+ update_option('dbem_location_types', array('location'=>1));
1130
+ $message = esc_html__('Events Manager has introduced location types, which can include online locations such as a URL or integrations with webinar platforms such as Zoom! Enable different location types in your settings page, for more information see our %s.', 'events-manager');
1131
+ $message = sprintf( $message, '<a href="http://wp-events-plugin.com/documentation/location-types/" target="_blank">'. esc_html__('documentation', 'events-manager')).'</a>';
1132
+ $EM_Admin_Notice = new EM_Admin_Notice(array( 'name' => 'location-types-update', 'who' => 'admin', 'where' => 'all', 'message' => "$message" ));
1133
+ EM_Admin_Notices::add($EM_Admin_Notice, is_multisite());
1134
+ }
1135
  }
1136
 
1137
  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.7.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.973); //self expanatory
32
  define('EM_PRO_MIN_VERSION', 2.6712); //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
@@ -66,6 +66,7 @@ function dbem_debug_mode(){
66
 
67
  // INCLUDES
68
  //Base classes
 
69
  include('classes/em-options.php');
70
  include('classes/em-object.php');
71
  include('classes/em-datetime.php');
@@ -99,6 +100,7 @@ include('classes/em-category.php');
99
  include('classes/em-categories.php');
100
  include('classes/em-categories-frontend.php');
101
  include('classes/em-event.php');
 
102
  include('classes/em-event-post.php');
103
  include('classes/em-events.php');
104
  include('classes/em-location.php');
@@ -118,7 +120,6 @@ include('classes/em-tickets-bookings.php');
118
  include('classes/em-tickets.php');
119
  //Admin Files
120
  if( is_admin() ){
121
- include('classes/em-admin-notice.php');
122
  include('classes/em-admin-notices.php');
123
  include('admin/em-admin.php');
124
  include('admin/em-bookings.php');
@@ -189,6 +190,22 @@ if( file_exists($upload_dir['basedir'].'/locations-pics' ) ){
189
  define("EM_IMAGE_DS",'/');
190
  }
191
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  /**
193
  * @author marcus
194
  * Contains functions for loading styles on both admin and public sides.
@@ -515,7 +532,8 @@ function em_load_event(){
515
  if( preg_match('/\-([0-9]+)$/', $_REQUEST['event_slug'], $matches) ){
516
  $event_id = $matches[1];
517
  }else{
518
- $event_id = $wpdb->get_var('SELECT event_id FROM '.EM_EVENTS_TABLE." WHERE event_slug='{$_REQUEST['event_slug']}' AND blog_id!=".get_current_blog_id());
 
519
  }
520
  $EM_Event = em_get_event($event_id);
521
  }
@@ -530,7 +548,8 @@ function em_load_event(){
530
  if( preg_match('/\-([0-9]+)$/', $_REQUEST['location_slug'], $matches) ){
531
  $location_id = $matches[1];
532
  }else{
533
- $location_id = $wpdb->get_var('SELECT location_id FROM '.EM_LOCATIONS_TABLE." WHERE location_slug='{$_REQUEST['location_slug']}' AND blog_id!=".get_current_blog_id());
 
534
  }
535
  $EM_Location = em_get_location($location_id);
536
  }
1
  <?php
2
  /*
3
  Plugin Name: Events Manager
4
+ Version: 5.9.8
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.98); //self expanatory
32
  define('EM_PRO_MIN_VERSION', 2.6712); //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
66
 
67
  // INCLUDES
68
  //Base classes
69
+ include('classes/em-exception.php');
70
  include('classes/em-options.php');
71
  include('classes/em-object.php');
72
  include('classes/em-datetime.php');
100
  include('classes/em-categories.php');
101
  include('classes/em-categories-frontend.php');
102
  include('classes/em-event.php');
103
+ include('classes/event-locations/em-event-locations.php');
104
  include('classes/em-event-post.php');
105
  include('classes/em-events.php');
106
  include('classes/em-location.php');
120
  include('classes/em-tickets.php');
121
  //Admin Files
122
  if( is_admin() ){
 
123
  include('classes/em-admin-notices.php');
124
  include('admin/em-admin.php');
125
  include('admin/em-bookings.php');
190
  define("EM_IMAGE_DS",'/');
191
  }
192
 
193
+ /**
194
+ * Provides a way to proactively load groups of files, once, when needed.
195
+ * @since 5.9.7.4
196
+ */
197
+ class EM_Loader {
198
+ public static $oauth = false;
199
+
200
+ public static function oauth(){
201
+ require_once('classes/em-oauth/oauth-api.php');
202
+ add_action('em_enqueue_admin_styles', function(){
203
+ wp_enqueue_style('events-manager-oauth-admin', plugins_url('includes/css/events-manager-oauth-admin.css',__FILE__), array(), EM_VERSION);
204
+ });
205
+ self::$oauth = true;
206
+ }
207
+ }
208
+
209
  /**
210
  * @author marcus
211
  * Contains functions for loading styles on both admin and public sides.
532
  if( preg_match('/\-([0-9]+)$/', $_REQUEST['event_slug'], $matches) ){
533
  $event_id = $matches[1];
534
  }else{
535
+ $query = $wpdb->prepare('SELECT event_id FROM '.EM_EVENTS_TABLE.' WHERE event_slug = %s AND blog_id != %d', $_REQUEST['event_slug'], get_current_blog_id());
536
+ $event_id = $wpdb->get_var($query);
537
  }
538
  $EM_Event = em_get_event($event_id);
539
  }
548
  if( preg_match('/\-([0-9]+)$/', $_REQUEST['location_slug'], $matches) ){
549
  $location_id = $matches[1];
550
  }else{
551
+ $query = $wpdb->prepare('SELECT location_id FROM '.EM_LOCATIONS_TABLE." WHERE location_slug = %s AND blog_id != %d", $_REQUEST['location_slug'], get_current_blog_id());
552
+ $location_id = $wpdb->get_var($query);
553
  }
554
  $EM_Location = em_get_location($location_id);
555
  }
includes/css/events-manager-oauth-admin.css ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Settings Page */
2
+ #em-options-form .postbox.em-oaut-connect-user .em-oauth-service-info { max-width:1000px; min-height: 75px; }
3
+ #em-options-form .postbox.em-oaut-connect-user .em-oauth-service-logo { float:left; width:50px; height:50px; }
4
+
5
+ #em-options-form .postbox.em-oaut-connect li.em-oauth-service-account { clear:both; height:16px; padding-bottom:10px; }
6
+ #em-options-form .postbox.em-oaut-connect li.em-oauth-service-account img, #em-options-form .postbox.em-oaut-connect li.em-oauth-service-account > div { float:left; }
7
+ #em-options-form .postbox.em-oaut-connect li.em-oauth-service-account > div { padding: 5px 0 5px 8px; }
8
+ #em-options-form .postbox.em-oaut-connect li.em-oauth-account-disconnected img, #em-options-form .postbox.em-oaut-connect li.em-oauth-account-disconnected .em-oauth-account-label { opacity:0.5; }
9
+ #em-options-form .postbox.em-oaut-connect li.em-oauth-account-disconnected .dashicons { width:16px; height: 16px; font-size: 16px; }
10
+
11
+ @media screen and (max-width: 600px) {
12
+ #em-options-form .postbox.em-oaut-connect .em-oauth-service-logo { margin:0px 10px 10px 0px; }
13
+ #em-options-form .postbox.em-oaut-connect .em-oauth-connect-button { margin: 10px 0px 10px 10px; }
14
+ #em-options-form .postbox.em-oaut-connect .em-oauth-service-info h2 { margin: 0px 0px 5px; padding:0px 5px; }
15
+ #em-options-form .postbox.em-oaut-connect .em-oauth-service-info { clear:both; margin:0px; }
16
+ }
includes/css/events_manager.css CHANGED
@@ -161,6 +161,14 @@ div#em-loading { position:absolute; width:100%; height:100%; background:#FFFFFF
161
  /* The editor */
162
  #wp-em-editor-content-wrap table { margin-bottom:0px; }
163
  /* Location form */
 
 
 
 
 
 
 
 
164
  #em-location-data table.em-location-data td, #em-location-data table.em-location-data th { vertical-align:top; border:none; }
165
  #em-location-data table.em-location-data select { width:100%; }
166
  #em-location-data table.em-location-data { width:50%; float:left; border:none; }
161
  /* The editor */
162
  #wp-em-editor-content-wrap table { margin-bottom:0px; }
163
  /* Location form */
164
+ #event-form .em-location-type { border-top: 1px solid #dedede; margin-top:20px; padding-top:20px; }
165
+ #event-form .em-location-type p:first-child { margin-top:0; }
166
+ #event-form div.em-location-data table { float:left; margin:0px 15px 0px 0px; }
167
+ #event-form .em-event-location-data h4 { padding-bottom:5px; border-bottom:1px solid #dedede; margin-bottom:15px; }
168
+ #event-form .em-input-field { margin-bottom:10px; }
169
+ #event-form .em-input-field label { display: block; margin-bottom:5px; }
170
+ #event-form .em-input-field.em-input-field-boolean label { display: inline-block; }
171
+ #event-form .em-input-field em { display: block; margin-top:2px; }
172
  #em-location-data table.em-location-data td, #em-location-data table.em-location-data th { vertical-align:top; border:none; }
173
  #em-location-data table.em-location-data select { width:100%; }
174
  #em-location-data table.em-location-data { width:50%; float:left; border:none; }
includes/css/events_manager_admin.css CHANGED
@@ -32,10 +32,19 @@ table.events-table .category { color:#888; }
32
  #event-form .recurrence-reschedule-warning, #post .recurrence-reschedule-warning { margin-bottom:25px; color:#c45500; }
33
  #event-form .recurrence-reschedule-warning p, #post .recurrence-reschedule-warning p { margin-top:0px; font-size:14px; }
34
  /*Locations*/
 
 
35
  div.em-location-data table { float:left; margin:0px 15px 0px 0px; }
 
 
 
 
 
36
  /* Location form */
 
37
  div.em-location-data table.em-location-data td, div.em-location-data table.em-location-data th { vertical-align:top; border:none; }
38
  div.em-location-data table.em-location-data { width:50%; float:left; border:none; }
 
39
  div.em-location-data .em-location-map-container, div.em-location-data .em-location-map-404, div.em-location-data .em-location-map-content { width: 400px; height: 300px; float:left; }
40
  div.em-location-data .em-location-map-404 { vertical-align:middle; text-align: center; }
41
  /* MS Global Categories */
@@ -151,7 +160,7 @@ table.em-tickets-bookings-table tbody td { text-align:center; }
151
  #em-options-form th { padding: 15px 20px; margin:0 !important; font-size:0.97em; }
152
  #em-options-form .em-boxheader { font-style:italic; margin:0; padding:10px 5px; }
153
  #em-options-form tr.em-header td { font-style:italic; padding:10px 5px; margin:0; }
154
- #em-options-form tr.em-header h4 { font-weight:bold; font-size:15px; font-style:normal; border-bottom: 1px solid #dedede; margin:0 0 10px; padding:0 0 10px; }
155
  #em-options-form tr.em-subheader td { font-style:italic; margin:0; padding:5px 20px 2px; }
156
  #em-options-form tr.em-subheader h5 { font-style:normal; margin:10px 0; padding:0 0 5px; font-weight:bold; font-size:15px; border-bottom: 1px solid #efefef; color:#000; }
157
  #em-options-form tbody.em-subsection th { padding-left:35px; }
32
  #event-form .recurrence-reschedule-warning, #post .recurrence-reschedule-warning { margin-bottom:25px; color:#c45500; }
33
  #event-form .recurrence-reschedule-warning p, #post .recurrence-reschedule-warning p { margin-top:0px; font-size:14px; }
34
  /*Locations*/
35
+ .em-location-type { border-top: 1px solid #dedede; margin-top:20px; padding-top:20px; }
36
+ .em-location-type p:first-child { margin-top:0; }
37
  div.em-location-data table { float:left; margin:0px 15px 0px 0px; }
38
+ .em-event-location-data h4 { padding-bottom:5px; border-bottom:1px solid #dedede; margin-bottom:15px; }
39
+ .em-input-field { margin-bottom:10px; }
40
+ .em-input-field label { display: block; margin-bottom:5px; }
41
+ .em-input-field.em-input-field-boolean label { display: inline-block; }
42
+ .em-input-field em { display: block; margin-top:2px; }
43
  /* Location form */
44
+ div.em-location-types-single { display:none; }
45
  div.em-location-data table.em-location-data td, div.em-location-data table.em-location-data th { vertical-align:top; border:none; }
46
  div.em-location-data table.em-location-data { width:50%; float:left; border:none; }
47
+ div.em-location-data table.em-location-data .em-location-data-url input { width:100%; }
48
  div.em-location-data .em-location-map-container, div.em-location-data .em-location-map-404, div.em-location-data .em-location-map-content { width: 400px; height: 300px; float:left; }
49
  div.em-location-data .em-location-map-404 { vertical-align:middle; text-align: center; }
50
  /* MS Global Categories */
160
  #em-options-form th { padding: 15px 20px; margin:0 !important; font-size:0.97em; }
161
  #em-options-form .em-boxheader { font-style:italic; margin:0; padding:10px 5px; }
162
  #em-options-form tr.em-header td { font-style:italic; padding:10px 5px; margin:0; }
163
+ #em-options-form tr.em-header h4, #em-options-form .postbox h4 { font-weight:bold; font-size:15px; font-style:normal; border-bottom: 1px solid #dedede; margin:0 0 10px; padding:0 0 10px; }
164
  #em-options-form tr.em-subheader td { font-style:italic; margin:0; padding:5px 20px 2px; }
165
  #em-options-form tr.em-subheader h5 { font-style:normal; margin:10px 0; padding:0 0 5px; font-weight:bold; font-size:15px; border-bottom: 1px solid #efefef; color:#000; }
166
  #em-options-form tbody.em-subsection th { padding-left:35px; }
includes/js/admin-settings.js CHANGED
@@ -104,16 +104,27 @@ jQuery(document).ready(function($){
104
  $(this).nextAll('.em-ml-options').toggle();
105
  });
106
  //radio triggers
107
- $('input.em-trigger').change(function(e){
108
  var el = $(this);
109
  el.val() == '1' ? $(el.attr('data-trigger')).show() : $(el.attr('data-trigger')).hide();
110
  });
111
- $('input.em-trigger:checked').trigger('change');
112
- $('input.em-untrigger').change(function(e){
113
  var el = $(this);
114
  el.val() == '0' ? $(el.attr('data-trigger')).show() : $(el.attr('data-trigger')).hide();
115
  });
116
- $('input.em-untrigger:checked').trigger('change');
 
 
 
 
 
 
 
 
 
 
 
117
  //admin tools confirm
118
  $('a.admin-tools-db-cleanup').click( function( e ){
119
  if( !confirm(EM.admin_db_cleanup_warning) ){
104
  $(this).nextAll('.em-ml-options').toggle();
105
  });
106
  //radio triggers
107
+ $('input[type="radio"].em-trigger').change(function(e){
108
  var el = $(this);
109
  el.val() == '1' ? $(el.attr('data-trigger')).show() : $(el.attr('data-trigger')).hide();
110
  });
111
+ $('input[type="radio"].em-trigger:checked').trigger('change');
112
+ $('input[type="radio"].em-untrigger').change(function(e){
113
  var el = $(this);
114
  el.val() == '0' ? $(el.attr('data-trigger')).show() : $(el.attr('data-trigger')).hide();
115
  });
116
+ $('input[type="radio"].em-untrigger:checked').trigger('change');
117
+ //checkbox triggers
118
+ $('input[type="checkbox"].em-trigger').change(function(e){
119
+ var el = $(this);
120
+ el.prop('checked') ? $(el.attr('data-trigger')).show() : $(el.attr('data-trigger')).hide();
121
+ });
122
+ $('input[type="checkbox"].em-trigger').trigger('change');
123
+ $('input[type="checkbox"].em-untrigger').change(function(e){
124
+ var el = $(this);
125
+ !el.prop('checked') ? $(el.attr('data-trigger')).show() : $(el.attr('data-trigger')).hide();
126
+ });
127
+ $('input[type="checkbox"].em-untrigger').trigger('change');
128
  //admin tools confirm
129
  $('a.admin-tools-db-cleanup').click( function( e ){
130
  if( !confirm(EM.admin_db_cleanup_warning) ){
includes/js/events-manager.js CHANGED
@@ -703,7 +703,8 @@ jQuery(document).ready( function($){
703
  }else{
704
  jQuery('select#location-country option[value="'+ui.item.country+'"]').attr('selected', 'selected');
705
  }
706
- jQuery('div.em-location-data input, div.em-location-data select').css('background-color','#ccc').prop('readonly', true);
 
707
  jQuery('#em-location-reset').show();
708
  jQuery('#em-location-search-tip').hide();
709
  jQuery(document).triggerHandler('em_locations_autocomplete_selected', [event, ui]);
@@ -715,7 +716,7 @@ jQuery(document).ready( function($){
715
  };
716
  jQuery('#em-location-reset a').click( function(){
717
  jQuery('div.em-location-data input').css('background-color','#fff').val('').prop('readonly', false);
718
- jQuery('div.em-location-data select').css('background-color','#fff');
719
  jQuery('div.em-location-data option:selected').removeAttr('selected');
720
  jQuery('input#location-id').val('');
721
  jQuery('#em-location-reset').hide();
@@ -730,7 +731,8 @@ jQuery(document).ready( function($){
730
  return false;
731
  });
732
  if( jQuery('input#location-id').val() != '0' && jQuery('input#location-id').val() != '' ){
733
- jQuery('div.em-location-data input, div.em-location-data select').css('background-color','#ccc').prop('readonly', true);
 
734
  jQuery('#em-location-reset').show();
735
  jQuery('#em-location-search-tip').hide();
736
  }
703
  }else{
704
  jQuery('select#location-country option[value="'+ui.item.country+'"]').attr('selected', 'selected');
705
  }
706
+ jQuery('div.em-location-data input').css('background-color','#ccc').prop('readonly', true);
707
+ jQuery('div.em-location-data select').css('background-color','#ccc').css('color', '#666666').prop('disabled', true);
708
  jQuery('#em-location-reset').show();
709
  jQuery('#em-location-search-tip').hide();
710
  jQuery(document).triggerHandler('em_locations_autocomplete_selected', [event, ui]);
716
  };
717
  jQuery('#em-location-reset a').click( function(){
718
  jQuery('div.em-location-data input').css('background-color','#fff').val('').prop('readonly', false);
719
+ jQuery('div.em-location-data select').css('background-color','#fff').css('color', 'auto').prop('disabled', false);
720
  jQuery('div.em-location-data option:selected').removeAttr('selected');
721
  jQuery('input#location-id').val('');
722
  jQuery('#em-location-reset').hide();
731
  return false;
732
  });
733
  if( jQuery('input#location-id').val() != '0' && jQuery('input#location-id').val() != '' ){
734
+ jQuery('div.em-location-data input').css('background-color','#ccc').prop('readonly', true);
735
+ jQuery('div.em-location-data select').css('background-color','#ccc').css('color', '#666666').prop('disabled', true);
736
  jQuery('#em-location-reset').show();
737
  jQuery('#em-location-search-tip').hide();
738
  }
multilingual/em-ml-admin.php CHANGED
@@ -17,11 +17,13 @@ class EM_ML_Admin{
17
  }
18
 
19
  public static function meta_boxes(){
20
- global $EM_Event, $EM_Location;
21
  //decide if it's a master event, if not then hide the meta boxes
22
  if( !empty($EM_Event) && !EM_ML::is_original($EM_Event) ){
23
  //remove meta boxes for events
24
  remove_meta_box('em-event-when', EM_POST_TYPE_EVENT, 'side');
 
 
25
  remove_meta_box('em-event-recurring', 'event-recurring', 'normal');
26
  remove_meta_box('em-event-when-recurring', 'event-recurring', 'side');
27
  remove_meta_box('em-event-where', EM_POST_TYPE_EVENT, 'normal');
17
  }
18
 
19
  public static function meta_boxes(){
20
+ global $EM_Event, $EM_Location, $wp_meta_boxes;
21
  //decide if it's a master event, if not then hide the meta boxes
22
  if( !empty($EM_Event) && !EM_ML::is_original($EM_Event) ){
23
  //remove meta boxes for events
24
  remove_meta_box('em-event-when', EM_POST_TYPE_EVENT, 'side');
25
+ //if( isset($wp_meta_boxes[convert_to_screen(EM_POST_TYPE_EVENT)->id]['side']['high']['em-event-when']) ) unset($wp_meta_boxes[convert_to_screen(EM_POST_TYPE_EVENT)->id]['side']['high']['em-event-when']);
26
+ //if( isset($wp_meta_boxes[convert_to_screen(EM_POST_TYPE_EVENT)->id]['side']['core']['em-event-when']) ) unset($wp_meta_boxes[convert_to_screen(EM_POST_TYPE_EVENT)->id]['side']['core']['em-event-when']);
27
  remove_meta_box('em-event-recurring', 'event-recurring', 'normal');
28
  remove_meta_box('em-event-when-recurring', 'event-recurring', 'side');
29
  remove_meta_box('em-event-where', EM_POST_TYPE_EVENT, 'normal');
readme.txt CHANGED
@@ -1,11 +1,11 @@
1
  === Events Manager ===
2
  Contributors: netweblogic, nutsmuggler
3
  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: 4.8
7
  Tested up to: 5.4
8
- Stable tag: 5.9.7.3
9
  Requires PHP: 5.3
10
 
11
  Fully featured event registration management including recurring events, locations management, calendar, Google map integration, booking management
@@ -27,6 +27,10 @@ Version 5 now makes events and locations WordPress Custom Post Types, allowing f
27
  * Bookings Management (including approval/rejections, export CVS, and more!)
28
  * Multiple Tickets
29
  * MultiSite Support
 
 
 
 
30
  * BuddyPress Support
31
  * Submit Events
32
  * Group Events
@@ -62,7 +66,7 @@ We provide the tools to [help you be GDPR compliant](http://wp-events-plugin.com
62
  = Go Pro =
63
  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:
64
 
65
- * PayPal, Authorize.net and Offline Payments
66
  * Custom booking forms
67
  * Individual Attendee custom forms
68
  * Coupon Codes
@@ -111,6 +115,17 @@ See our [FAQ](http://wp-events-plugin.com/documentation/faq/) page, which is upd
111
  6. Manage attendees with various booking reports
112
 
113
  == Changelog ==
 
 
 
 
 
 
 
 
 
 
 
114
  = 5.9.7.3 =
115
  * minor JS conflict fix for those overriding the tickets admin template (or those incorrectly overriding ALL EM templates) in their theme with older HTML structure
116
 
1
  === Events Manager ===
2
  Contributors: netweblogic, nutsmuggler
3
  Donate link: http://wp-events-plugin.com
4
+ Tags: bookings, calendar, tickets, events, buddypress, event management, google maps, maps, locations, registration, zoom
5
  Text Domain: events-manager
6
+ Requires at least: 5.2
7
  Tested up to: 5.4
8
+ Stable tag: 5.9.8
9
  Requires PHP: 5.3
10
 
11
  Fully featured event registration management including recurring events, locations management, calendar, Google map integration, booking management
27
  * Bookings Management (including approval/rejections, export CVS, and more!)
28
  * Multiple Tickets
29
  * MultiSite Support
30
+ * Multiple Location Types
31
+ * Physical Locations
32
+ * Online Events (URLs)
33
+ * [Zoom Webinars/Meetings Integration](https://wordpress.org/plugins/events-manager-zoom/)
34
  * BuddyPress Support
35
  * Submit Events
36
  * Group Events
66
  = Go Pro =
67
  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:
68
 
69
+ * PayPal, Stripe, Authorize.net and Offline Payments
70
  * Custom booking forms
71
  * Individual Attendee custom forms
72
  * Coupon Codes
115
  6. Manage attendees with various booking reports
116
 
117
  == Changelog ==
118
+ = 5.9.8 =
119
+ * added Location Types including URL and (via external free add-on) Zoom support!
120
+ * added native OAuth support for third party integrations (e.g. Zoom)
121
+ * added $EM_Event object to booking form template actions
122
+ * changed $EM_Booking->booking_status to protected so that status returns 1 even if approvals are disabled
123
+ * fixed XSS vulnerability (kudos to Jakob Wierzba)
124
+ * fixed potential SQL injection vulnerability (kudos to Antony Garand from Godaddy)
125
+ * fixed fatal errors in BuddyPress if notifications are disabled
126
+ * fixed minor PHP warning
127
+ * fixed Yoast SEO 14.0 conflict
128
+
129
  = 5.9.7.3 =
130
  * minor JS conflict fix for those overriding the tickets admin template (or those incorrectly overriding ALL EM templates) in their theme with older HTML structure
131
 
templates/forms/event/event-locations/url.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ global $EM_Event;
3
+ ?>
4
+ <div class="em-input-field em-input-field-text em-location-data-url">
5
+ <label for="event_location_url"><?php esc_html_e( 'URL', 'events-manager')?></label>
6
+ <input id="event_location_url" type="text" name="event_location_url" value="<?php echo esc_attr($EM_Event->get_event_location()->url); ; ?>" />
7
+ </div>
8
+ <div class="em-input-field em-input-field-text em-location-data-url-text">
9
+ <label for="event_location_url_text"><?php esc_html_e( 'Link Text', 'events-manager')?></label>
10
+ <input id="event_location_url_text" type="text" name="event_location_url_text" value="<?php echo esc_attr($EM_Event->get_event_location()->text); ; ?>" />
11
+ </div>
templates/forms/event/location.php CHANGED
@@ -1,66 +1,100 @@
1
  <?php
2
  global $EM_Event;
3
  $required = apply_filters('em_required_html','<i>*</i>');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  ?>
5
- <?php if( !get_option('dbem_require_location') && !get_option('dbem_use_select_for_locations') ): ?>
6
- <div class="em-location-data-nolocation">
7
- <p>
8
- <input type="checkbox" name="no_location" id="no-location" value="1" <?php if( $EM_Event->location_id === '0' || $EM_Event->location_id === 0 ) echo 'checked="checked"'; ?> />
9
- <?php _e('This event does not have a physical location.','events-manager'); ?>
10
- </p>
 
 
 
11
  <script type="text/javascript">
12
  jQuery(document).ready(function($){
13
- $('#no-location').change(function(){
14
- if( $('#no-location').is(':checked') ){
15
- $('#em-location-data').hide();
 
16
  }else{
17
- $('#em-location-data').show();
 
 
 
 
 
18
  }
19
  }).trigger('change');
20
  });
21
  </script>
22
  </div>
23
- <?php endif; ?>
24
- <div id="em-location-data" class="em-location-data">
25
  <div id="location_coordinates" style='display: none;'>
26
  <input id='location-latitude' name='location_latitude' type='text' value='<?php echo esc_attr($EM_Event->get_location()->location_latitude); ?>' size='15' />
27
  <input id='location-longitude' name='location_longitude' type='text' value='<?php echo esc_attr($EM_Event->get_location()->location_longitude); ?>' size='15' />
28
  </div>
29
- <?php if( get_option('dbem_use_select_for_locations') || !$EM_Event->can_manage('edit_locations','edit_others_locations') ) : ?>
30
  <table class="em-location-data">
31
- <tr class="em-location-data-select">
32
- <th><?php esc_html_e('Location:','events-manager') ?> </th>
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');
41
- foreach($locations as $EM_Location) {
42
- $selected = ($selected_location == $EM_Location->location_id) ? "selected='selected' " : '';
43
- if( $selected ) $found_location = true;
44
- ?>
45
- <option value="<?php echo esc_attr($EM_Location->location_id) ?>" title="<?php echo esc_attr("{$EM_Location->location_latitude},{$EM_Location->location_longitude}"); ?>" <?php echo $selected ?>><?php echo esc_html($EM_Location->location_name); ?></option>
46
- <?php
47
- }
48
- if( empty($found_location) && !empty($EM_Event->location_id) ){
49
- $EM_Location = $EM_Event->get_location();
50
- if( $EM_Location->post_id ){
51
- ?>
52
- <option value="<?php echo esc_attr($EM_Location->location_id) ?>" title="<?php echo esc_attr("{$EM_Location->location_latitude},{$EM_Location->location_longitude}"); ?>" selected="selected"><?php echo esc_html($EM_Location->location_name); ?></option>
53
- <?php
54
  }
55
- }
56
- ?>
57
- </select>
58
- </td>
59
- </tr>
60
- </table>
61
- <?php else : ?>
62
- <table class="em-location-data">
63
- <?php
 
 
 
 
 
 
 
64
  global $EM_Location;
65
  if( $EM_Event->location_id !== 0 ){
66
  $EM_Location = $EM_Event->get_location();
@@ -69,60 +103,79 @@ $required = apply_filters('em_required_html','<i>*</i>');
69
  }else{
70
  $EM_Location = new EM_Location();
71
  }
72
- ?>
73
- <tr class="em-location-data-name">
74
- <th><?php _e ( 'Location Name:', 'events-manager')?></th>
75
- <td>
76
- <input id='location-id' name='location_id' type='hidden' value='<?php echo esc_attr($EM_Location->location_id); ?>' size='15' />
77
- <input id="location-name" type="text" name="location_name" value="<?php echo esc_attr($EM_Location->location_name, ENT_QUOTES); ?>" /><?php echo $required; ?>
78
- <br />
79
- <em id="em-location-search-tip"><?php esc_html_e( 'Create a location or start typing to search a previously created location.', 'events-manager')?></em>
80
- <em id="em-location-reset" style="display:none;"><?php esc_html_e('You cannot edit saved locations here.', 'events-manager'); ?> <a href="#"><?php esc_html_e('Reset this form to create a location or search again.', 'events-manager')?></a></em>
81
- </td>
82
- </tr>
83
- <tr class="em-location-data-address">
84
- <th><?php _e ( 'Address:', 'events-manager')?>&nbsp;</th>
85
- <td>
86
- <input id="location-address" type="text" name="location_address" value="<?php echo esc_attr($EM_Location->location_address); ; ?>" /><?php echo $required; ?>
87
- </td>
88
- </tr>
89
- <tr class="em-location-data-town">
90
- <th><?php _e ( 'City/Town:', 'events-manager')?>&nbsp;</th>
91
- <td>
92
- <input id="location-town" type="text" name="location_town" value="<?php echo esc_attr($EM_Location->location_town); ?>" /><?php echo $required; ?>
93
- </td>
94
- </tr>
95
- <tr class="em-location-data-state">
96
- <th><?php _e ( 'State/County:', 'events-manager')?>&nbsp;</th>
97
- <td>
98
- <input id="location-state" type="text" name="location_state" value="<?php echo esc_attr($EM_Location->location_state); ?>" />
99
- </td>
100
- </tr>
101
- <tr class="em-location-data-postcode">
102
- <th><?php _e ( 'Postcode:', 'events-manager')?>&nbsp;</th>
103
- <td>
104
- <input id="location-postcode" type="text" name="location_postcode" value="<?php echo esc_attr($EM_Location->location_postcode); ?>" />
105
- </td>
106
- </tr>
107
- <tr class="em-location-data-region">
108
- <th><?php _e ( 'Region:', 'events-manager')?>&nbsp;</th>
109
- <td>
110
- <input id="location-region" type="text" name="location_region" value="<?php echo esc_attr($EM_Location->location_region); ?>" />
111
- </td>
112
- </tr>
113
- <tr class="em-location-data-country">
114
- <th><?php _e ( 'Country:', 'events-manager')?>&nbsp;</th>
115
- <td>
116
- <select id="location-country" name="location_country">
117
- <option value="0" <?php echo ( $EM_Location->location_country == '' && $EM_Location->location_id == '' && get_option('dbem_location_default_country') == '' ) ? 'selected="selected"':''; ?>><?php _e('none selected','events-manager'); ?></option>
118
- <?php foreach(em_get_countries() as $country_key => $country_name): ?>
119
- <option value="<?php echo esc_attr($country_key); ?>" <?php echo ( $EM_Location->location_country == $country_key || ($EM_Location->location_country == '' && $EM_Location->location_id == '' && get_option('dbem_location_default_country')==$country_key) ) ? 'selected="selected"':''; ?>><?php echo esc_html($country_name); ?></option>
120
- <?php endforeach; ?>
121
- </select><?php echo $required; ?>
122
- </td>
123
- </tr>
 
 
 
 
 
 
 
124
  </table>
 
 
125
  <?php endif; ?>
126
- <?php if ( get_option( 'dbem_gmap_is_active' ) ) em_locate_template('forms/map-container.php',true); ?>
127
  <br style="clear:both;" />
 
 
 
 
 
 
 
 
 
 
 
128
  </div>
1
  <?php
2
  global $EM_Event;
3
  $required = apply_filters('em_required_html','<i>*</i>');
4
+
5
+ //determine location types (if neexed)
6
+ $location_types = array();
7
+ if( !get_option('dbem_require_location') && !get_option('dbem_use_select_for_locations') ){
8
+ $location_types[0] = array(
9
+ 'selected' => $EM_Event->location_id === '0' || $EM_Event->location_id === 0,
10
+ 'description' => esc_html__('No Location','events-manager'),
11
+ );
12
+ }
13
+ if( EM_Locations::is_enabled() ){
14
+ $location_types['location'] = array(
15
+ 'selected' => !empty($EM_Event->location_id),
16
+ 'display-class' => 'em-location-type-place',
17
+ 'description' => esc_html__('Physical Location','events-manager'),
18
+ );
19
+ }
20
+ foreach( EM_Event_Locations\Event_Locations::get_types() as $event_location_type => $EM_Event_Location_Class ){ /* @var EM_Event_Locations\Event_Location $EM_Event_Location_Class */
21
+ if( $EM_Event_Location_Class::is_enabled() ){
22
+ $location_types[$EM_Event_Location_Class::$type] = array(
23
+ 'display-class' => 'em-event-location-type-'. $EM_Event_Location_Class::$type,
24
+ 'selected' => $EM_Event_Location_Class::$type == $EM_Event->event_location_type,
25
+ 'description' => $EM_Event_Location_Class::get_label(),
26
+ );
27
+ }
28
+ }
29
  ?>
30
+ <div class="em-input-field em-input-field-select em-location-types <?php if( count($location_types) == 1 ) echo 'em-location-types-single'; ?>">
31
+ <label><?php esc_html_e ( 'Location Type', 'events-manager')?></label>
32
+ <select name="location_type" class="em-location-types-select">
33
+ <?php foreach( $location_types as $location_type => $location_type_option ): ?>
34
+ <option value="<?php echo esc_attr($location_type); ?>" <?php if( !empty($location_type_option['selected']) ) echo 'selected="selected"'; ?> data-display-class="<?php if( !empty($location_type_option['display-class']) ) echo esc_attr($location_type_option['display-class']); ?>">
35
+ <?php echo esc_html($location_type_option['description']); ?>
36
+ </option>
37
+ <?php endforeach; ?>
38
+ </select>
39
  <script type="text/javascript">
40
  jQuery(document).ready(function($){
41
+ $('.em-location-types .em-location-types-select').change(function(){
42
+ let el = $(this);
43
+ if( el.val() == 0 ){
44
+ $('.em-location-type').hide();
45
  }else{
46
+ let location_type = el.find('option:selected').data('display-class');
47
+ $('.em-location-type').hide();
48
+ $('.em-location-type.'+location_type).show();
49
+ if( location_type != 'em-location-type-place' ){
50
+ jQuery('#em-location-reset a').trigger('click');
51
+ }
52
  }
53
  }).trigger('change');
54
  });
55
  </script>
56
  </div>
57
+ <?php if( EM_Locations::is_enabled() ): ?>
58
+ <div id="em-location-data" class="em-location-data em-location-type em-location-type-place">
59
  <div id="location_coordinates" style='display: none;'>
60
  <input id='location-latitude' name='location_latitude' type='text' value='<?php echo esc_attr($EM_Event->get_location()->location_latitude); ?>' size='15' />
61
  <input id='location-longitude' name='location_longitude' type='text' value='<?php echo esc_attr($EM_Event->get_location()->location_longitude); ?>' size='15' />
62
  </div>
 
63
  <table class="em-location-data">
64
+ <?php if( get_option('dbem_use_select_for_locations') || !$EM_Event->can_manage('edit_locations','edit_others_locations') ) : ?>
65
+ <tbody class="em-location-data">
66
+ <tr class="em-location-data-select">
67
+ <th><?php esc_html_e('Location:','events-manager') ?> </th>
68
+ <td>
69
+ <select name="location_id" id='location-select-id' size="1">
70
+ <?php
71
+ $ddm_args = array('private'=>$EM_Event->can_manage('read_private_locations'));
72
+ $ddm_args['owner'] = (is_user_logged_in() && !current_user_can('read_others_locations')) ? get_current_user_id() : false;
73
+ $locations = EM_Locations::get($ddm_args);
74
+ $selected_location = !empty($EM_Event->location_id) || !empty($EM_Event->event_id) ? $EM_Event->location_id:get_option('dbem_default_location');
75
+ foreach($locations as $EM_Location) {
76
+ $selected = ($selected_location == $EM_Location->location_id) ? "selected='selected' " : '';
77
+ if( $selected ) $found_location = true;
78
+ ?>
79
+ <option value="<?php echo esc_attr($EM_Location->location_id) ?>" title="<?php echo esc_attr("{$EM_Location->location_latitude},{$EM_Location->location_longitude}"); ?>" <?php echo $selected ?>><?php echo esc_html($EM_Location->location_name); ?></option>
80
+ <?php
 
 
 
 
 
 
81
  }
82
+ if( empty($found_location) && !empty($EM_Event->location_id) ){
83
+ $EM_Location = $EM_Event->get_location();
84
+ if( $EM_Location->post_id ){
85
+ ?>
86
+ <option value="<?php echo esc_attr($EM_Location->location_id) ?>" title="<?php echo esc_attr("{$EM_Location->location_latitude},{$EM_Location->location_longitude}"); ?>" selected="selected"><?php echo esc_html($EM_Location->location_name); ?></option>
87
+ <?php
88
+ }
89
+ }
90
+ ?>
91
+ </select>
92
+ </td>
93
+ </tr>
94
+ </tbody>
95
+ <?php else : ?>
96
+ <tbody class="em-location-data">
97
+ <?php
98
  global $EM_Location;
99
  if( $EM_Event->location_id !== 0 ){
100
  $EM_Location = $EM_Event->get_location();
103
  }else{
104
  $EM_Location = new EM_Location();
105
  }
106
+ ?>
107
+ <tr class="em-location-data-name">
108
+ <th><?php _e ( 'Location Name:', 'events-manager')?></th>
109
+ <td>
110
+ <input id='location-id' name='location_id' type='hidden' value='<?php echo esc_attr($EM_Location->location_id); ?>' size='15' />
111
+ <input id="location-name" type="text" name="location_name" value="<?php echo esc_attr($EM_Location->location_name, ENT_QUOTES); ?>" /><?php echo $required; ?>
112
+ <br />
113
+ <em id="em-location-search-tip"><?php esc_html_e( 'Create a location or start typing to search a previously created location.', 'events-manager')?></em>
114
+ <em id="em-location-reset" style="display:none;"><?php esc_html_e('You cannot edit saved locations here.', 'events-manager'); ?> <a href="#"><?php esc_html_e('Reset this form to create a location or search again.', 'events-manager')?></a></em>
115
+ </td>
116
+ </tr>
117
+ <tr class="em-location-data-address">
118
+ <th><?php _e ( 'Address:', 'events-manager')?>&nbsp;</th>
119
+ <td>
120
+ <input id="location-address" type="text" name="location_address" value="<?php echo esc_attr($EM_Location->location_address); ; ?>" /><?php echo $required; ?>
121
+ </td>
122
+ </tr>
123
+ <tr class="em-location-data-town">
124
+ <th><?php _e ( 'City/Town:', 'events-manager')?>&nbsp;</th>
125
+ <td>
126
+ <input id="location-town" type="text" name="location_town" value="<?php echo esc_attr($EM_Location->location_town); ?>" /><?php echo $required; ?>
127
+ </td>
128
+ </tr>
129
+ <tr class="em-location-data-state">
130
+ <th><?php _e ( 'State/County:', 'events-manager')?>&nbsp;</th>
131
+ <td>
132
+ <input id="location-state" type="text" name="location_state" value="<?php echo esc_attr($EM_Location->location_state); ?>" />
133
+ </td>
134
+ </tr>
135
+ <tr class="em-location-data-postcode">
136
+ <th><?php _e ( 'Postcode:', 'events-manager')?>&nbsp;</th>
137
+ <td>
138
+ <input id="location-postcode" type="text" name="location_postcode" value="<?php echo esc_attr($EM_Location->location_postcode); ?>" />
139
+ </td>
140
+ </tr>
141
+ <tr class="em-location-data-region">
142
+ <th><?php _e ( 'Region:', 'events-manager')?>&nbsp;</th>
143
+ <td>
144
+ <input id="location-region" type="text" name="location_region" value="<?php echo esc_attr($EM_Location->location_region); ?>" />
145
+ </td>
146
+ </tr>
147
+ <tr class="em-location-data-country">
148
+ <th><?php _e ( 'Country:', 'events-manager')?>&nbsp;</th>
149
+ <td>
150
+ <select id="location-country" name="location_country">
151
+ <option value="0" <?php echo ( $EM_Location->location_country == '' && $EM_Location->location_id == '' && get_option('dbem_location_default_country') == '' ) ? 'selected="selected"':''; ?>><?php _e('none selected','events-manager'); ?></option>
152
+ <?php foreach(em_get_countries() as $country_key => $country_name): ?>
153
+ <option value="<?php echo esc_attr($country_key); ?>" <?php echo ( $EM_Location->location_country == $country_key || ($EM_Location->location_country == '' && $EM_Location->location_id == '' && get_option('dbem_location_default_country')==$country_key) ) ? 'selected="selected"':''; ?>><?php echo esc_html($country_name); ?></option>
154
+ <?php endforeach; ?>
155
+ </select><?php echo $required; ?>
156
+ </td>
157
+ </tr>
158
+ <tr class="em-location-data-url">
159
+ <th><?php esc_html_e( 'URL:', 'events-manager')?>&nbsp;</th>
160
+ <td>
161
+ <input id="location-url" type="text" name="location_url" value="<?php echo esc_attr($EM_Location->location_url); ; ?>" />
162
+ </td>
163
+ </tr>
164
+ </tbody>
165
  </table>
166
+ <?php if ( get_option( 'dbem_gmap_is_active' ) ):?>
167
+ <?php em_locate_template('forms/map-container.php',true); ?>
168
  <?php endif; ?>
 
169
  <br style="clear:both;" />
170
+ <?php endif; ?>
171
+ </div>
172
+ <?php endif; ?>
173
+ <div class="em-event-location-data">
174
+ <?php foreach( EM_Event_Locations\Event_Locations::get_types() as $event_location_type => $EM_Event_Location_Class ): /* @var EM_Event_Locations\Event_Location $EM_Event_Location_Class */ ?>
175
+ <?php if( $EM_Event_Location_Class::is_enabled() ): ?>
176
+ <div class="em-location-type em-event-location-type-<?php echo esc_attr($event_location_type); ?>">
177
+ <?php $EM_Event_Location_Class::load_admin_template(); ?>
178
+ </div>
179
+ <?php endif; ?>
180
+ <?php endforeach; ?>
181
  </div>
templates/placeholders/bookingform.php CHANGED
@@ -29,7 +29,7 @@ if( !$is_open && !is_user_logged_in() && $EM_Event->get_bookings()->is_open(true
29
  <?php
30
  // We are firstly checking if the user has already booked a ticket at this event, if so offer a link to view their bookings.
31
  $EM_Booking = $EM_Event->get_bookings()->has_booking();
32
- do_action('em_booking_form_top');
33
  ?>
34
  <?php if( is_object($EM_Booking) && !get_option('dbem_bookings_double') ): //Double bookings not allowed ?>
35
  <p>
@@ -47,7 +47,7 @@ if( !$is_open && !is_user_logged_in() && $EM_Event->get_bookings()->is_open(true
47
  <?php if( $tickets_count > 0) : ?>
48
  <?php //Tickets exist, so we show a booking form. ?>
49
  <form class="em-booking-form" name='booking-form' method='post' action='<?php echo apply_filters('em_booking_form_action_url',''); ?>#em-booking'>
50
- <?php do_action('em_booking_form_header'); ?>
51
  <input type='hidden' name='action' value='booking_add'/>
52
  <input type='hidden' name='event_id' value='<?php echo $EM_Event->get_bookings()->event_id; ?>'/>
53
  <input type='hidden' name='_wpnonce' value='<?php echo wp_create_nonce('booking_add'); ?>'/>
@@ -107,5 +107,7 @@ if( !$is_open && !is_user_logged_in() && $EM_Event->get_bookings()->is_open(true
107
  <br class="clear" style="clear:left;" />
108
  <?php endif; ?>
109
  <?php endif; ?>
110
- <?php do_action('em_booking_form_bottom'); ?>
 
 
111
  </div>
29
  <?php
30
  // We are firstly checking if the user has already booked a ticket at this event, if so offer a link to view their bookings.
31
  $EM_Booking = $EM_Event->get_bookings()->has_booking();
32
+ do_action('em_booking_form_top', $EM_Event);
33
  ?>
34
  <?php if( is_object($EM_Booking) && !get_option('dbem_bookings_double') ): //Double bookings not allowed ?>
35
  <p>
47
  <?php if( $tickets_count > 0) : ?>
48
  <?php //Tickets exist, so we show a booking form. ?>
49
  <form class="em-booking-form" name='booking-form' method='post' action='<?php echo apply_filters('em_booking_form_action_url',''); ?>#em-booking'>
50
+ <?php do_action('em_booking_form_header', $EM_Event); ?>
51
  <input type='hidden' name='action' value='booking_add'/>
52
  <input type='hidden' name='event_id' value='<?php echo $EM_Event->get_bookings()->event_id; ?>'/>
53
  <input type='hidden' name='_wpnonce' value='<?php echo wp_create_nonce('booking_add'); ?>'/>
107
  <br class="clear" style="clear:left;" />
108
  <?php endif; ?>
109
  <?php endif; ?>
110
+ <?php
111
+ do_action('em_booking_form_bottom', $EM_Event);
112
+ ?>
113
  </div>
templates/tables/events.php CHANGED
@@ -118,9 +118,17 @@
118
  </a>
119
  </td>
120
  <?php if( get_option('dbem_locations_enabled') ): ?>
121
- <td>
122
- <?php echo "<b>" . esc_html($EM_Event->get_location()->location_name) . "</b><br/>" . esc_html($EM_Event->get_location()->location_address) . " - " . esc_html($EM_Event->get_location()->location_town); ?>
123
- </td>
 
 
 
 
 
 
 
 
124
  <?php endif; ?>
125
  <td>
126
  <?php echo $EM_Event->output_dates(); ?>
118
  </a>
119
  </td>
120
  <?php if( get_option('dbem_locations_enabled') ): ?>
121
+ <td>
122
+ <?php
123
+ if( $EM_Event->has_location() ){
124
+ echo "<b>" . esc_html($EM_Event->get_location()->location_name) . "</b><br/>" . esc_html($EM_Event->get_location()->location_address) . " - " . esc_html($EM_Event->get_location()->location_town);
125
+ }elseif( $EM_Event->has_event_location() ) {
126
+ echo $EM_Event->get_event_location()->get_admin_column();
127
+ }else{
128
+ echo __('None','events-manager');
129
+ }
130
+ ?>
131
+ </td>
132
  <?php endif; ?>
133
  <td>
134
  <?php echo $EM_Event->output_dates(); ?>