Events Manager - Version 5.8.0.1

Version Description

  • fixed category color picker and image uploader problems
  • created base classes for EM taxonomies to make adding custom EM taxonomies even easier in the future,
  • added tag image and color settings/data
  • added sortable option for date columns of events and recurring events in the admin area
  • fixed saving an event recreating ticket_meta and wiping out ML settings
  • fixed multilingual translations of event in WPML not hiding/showing right meta boxes
  • fixed saving multilingual translation of event overwriting original language ticket names
  • changed admin headers to use new and accessible WP inline HTML structure
  • fixed serialization problems for deprecated event/location attributes and attributes with multiple post meta entries for one post
  • removed deprecated attributes editor from admin area as this is confusing and error prone when combined with other plugins manipulating custom fields
  • fixed #_EVENTEXCERPT without arguments stripping HTML since 5.7
  • added ability to list multiple categories/tags in conditional placeholders e.g. {has_tag_123,1234,tagname}...
  • added #_EVENTPRICEMINALL and #_EVENTPRICEMAXALL to show prices of unavailable tickets as well
  • fixed JS issues with MS Global mode and JS file limiting when displaying subsite single event pages on main blog
  • fixed single initial abbreviation issues in Chinese calendars
  • fixed duplicate events not being published to social networks via jetpack publicize (kudos @gnaag)
  • fixed potential incompatibilities with other plugins using wp_query_reset() on category pages, preventing our page formats from showing
  • fixed taxonomy archive pages returning zero results if taxonomy formatting is disabled and events are excluded from searches (WP Bug workaround)
  • fixed inconsistent line ending causing warnings with PHP compatibility checker
  • unified Tag and Category class functions into sets of parent class functionn
  • fixed PHP fatal error with BP when disabling notifications
  • fixed calendar day links being incorrect if another plugin adds querystring params to permalinks
  • added groupby, groupby_orderby and groupby_order arguments allowing grouping in search results for events and locations
  • improved validation and sanitization of orderby arguments to avoid ambiguous field SQL errors
  • added optimization to optionally join event/location tables when needed for grouped searches or if EM_DISABLE_OPTIONAL_JOINS is defined and set to true
  • moved condition of when argument 'bookings'
Download this release

Release Info

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

Code changes from version 5.7.3 to 5.8.0.1

Files changed (67) hide show
  1. admin/bookings/em-events.php +2 -2
  2. admin/em-admin.php +3 -3
  3. admin/em-bookings.php +9 -6
  4. admin/em-docs.php +35 -10
  5. admin/em-ms-options.php +1 -1
  6. admin/em-options.php +19 -14
  7. admin/settings/tabs/emails.php +8 -8
  8. admin/settings/tabs/formats.php +11 -8
  9. admin/settings/tabs/general.php +1 -1
  10. admin/settings/tabs/pages.php +119 -29
  11. buddypress/bp-em-notifications.php +1 -3
  12. classes/em-booking.php +21 -17
  13. classes/em-bookings-table.php +2 -2
  14. classes/em-bookings.php +144 -60
  15. classes/em-calendar.php +27 -18
  16. classes/em-categories-admin.php +61 -0
  17. classes/em-categories-frontend.php +125 -0
  18. classes/em-categories-taxonomy.php +0 -126
  19. classes/em-categories.php +41 -277
  20. classes/em-category-taxonomy.php +0 -170
  21. classes/em-category.php +24 -291
  22. classes/em-event-post-admin.php +6 -1
  23. classes/em-event-post.php +18 -4
  24. classes/em-event-posts-admin.php +18 -7
  25. classes/em-event.php +179 -92
  26. classes/em-events.php +280 -103
  27. classes/em-location-post-admin.php +1 -1
  28. classes/em-location-post.php +15 -2
  29. classes/em-location.php +96 -49
  30. classes/em-locations.php +199 -65
  31. classes/em-object.php +208 -55
  32. classes/em-permalinks.php +1 -1
  33. classes/em-tag-taxonomy.php +0 -103
  34. classes/em-tag.php +24 -185
  35. classes/em-tags-admin.php +61 -0
  36. classes/em-tags-frontend.php +56 -0
  37. classes/em-tags.php +45 -268
  38. classes/em-taxonomy-admin.php +177 -0
  39. classes/em-taxonomy-frontend.php +170 -0
  40. classes/em-taxonomy-term.php +352 -0
  41. classes/em-taxonomy-terms.php +323 -0
  42. classes/em-ticket.php +55 -24
  43. classes/em-tickets.php +27 -9
  44. em-actions.php +8 -10
  45. em-events.php +1 -1
  46. em-functions.php +27 -5
  47. em-install.php +15 -8
  48. em-posts.php +21 -5
  49. em-pro-compatibility.php +1 -1
  50. em-shortcode.php +3 -1
  51. events-manager.php +27 -20
  52. includes/css/events_manager_admin.css +1 -0
  53. includes/js/admin-settings.js +16 -7
  54. includes/js/categories-admin.js +0 -69
  55. includes/js/taxonomies-admin.js +50 -0
  56. multilingual/em-ml-admin.php +3 -1
  57. multilingual/em-ml-bookings.php +12 -9
  58. readme.txt +74 -2
  59. templates/buddypress/profile.php +4 -3
  60. templates/forms/event/attributes.php +0 -32
  61. templates/forms/event/bookings.php +1 -1
  62. templates/forms/event/group.php +2 -2
  63. templates/forms/event/when-with-recurring.php +1 -1
  64. templates/forms/location/attributes.php +0 -32
  65. templates/templates/search/search.php +1 -1
  66. widgets/em-events.php +1 -1
  67. widgets/em-locations.php +1 -1
admin/bookings/em-events.php CHANGED
@@ -36,8 +36,8 @@ function em_bookings_events_table() {
36
  $scope = "future";
37
  }
38
  $owner = !current_user_can('manage_others_bookings') ? get_current_user_id() : false;
39
- $events = EM_Events::get( array('scope'=>$scope, 'limit'=>$limit, 'offset' => $offset, 'order'=>$order, 'bookings'=>true, 'owner' => $owner ) );
40
- $events_count = EM_Events::count( array('scope'=>$scope, 'limit'=>0, 'order'=>$order, 'bookings'=>true, 'owner' => $owner ) );
41
 
42
  $use_events_end = get_option ( 'dbem_use_event_end' );
43
  ?>
36
  $scope = "future";
37
  }
38
  $owner = !current_user_can('manage_others_bookings') ? get_current_user_id() : false;
39
+ $events = EM_Events::get( array('scope'=>$scope, 'limit'=>$limit, 'offset' => $offset, 'order'=>$order, 'bookings'=>true, 'owner' => $owner, 'pagination' => 1 ) );
40
+ $events_count = EM_Events::$num_rows_found;
41
 
42
  $use_events_end = get_option ( 'dbem_use_event_end' );
43
  ?>
admin/em-admin.php CHANGED
@@ -7,7 +7,7 @@ function em_admin_menu(){
7
  $bookings_num = '';
8
  $bookings_pending_count = apply_filters('em_bookings_pending_count',0);
9
  if( get_option('dbem_bookings_approval') == 1){
10
- $bookings_pending_count += count(EM_Bookings::get(array('status'=>'0', 'blog'=>get_current_blog_id()))->bookings);
11
  }
12
  if($bookings_pending_count > 0){
13
  $bookings_num = '<span class="update-plugins count-'.$bookings_pending_count.'"><span class="plugin-count">'.$bookings_pending_count.'</span></span>';
@@ -173,7 +173,7 @@ function em_admin_warnings() {
173
  <?php
174
  }
175
 
176
- if( is_multisite() && !empty($_REQUEST['page']) && $_REQUEST['page']=='events-manager-options' && is_super_admin() && get_option('dbem_ms_update_nag') ){
177
  if( !empty($_GET['disable_dbem_ms_update_nag']) ){
178
  delete_site_option('dbem_ms_update_nag');
179
  }else{
@@ -185,7 +185,7 @@ function em_admin_warnings() {
185
  }
186
  }
187
 
188
- if( is_super_admin() && get_option('dbem_migrate_images_nag') ){
189
  if( !empty($_GET['disable_dbem_migrate_images_nag']) ){
190
  delete_site_option('dbem_migrate_images_nag');
191
  }else{
7
  $bookings_num = '';
8
  $bookings_pending_count = apply_filters('em_bookings_pending_count',0);
9
  if( get_option('dbem_bookings_approval') == 1){
10
+ $bookings_pending_count += EM_Bookings::count(array('status'=>'0', 'blog'=>get_current_blog_id()));
11
  }
12
  if($bookings_pending_count > 0){
13
  $bookings_num = '<span class="update-plugins count-'.$bookings_pending_count.'"><span class="plugin-count">'.$bookings_pending_count.'</span></span>';
173
  <?php
174
  }
175
 
176
+ if( is_multisite() && !empty($_REQUEST['page']) && $_REQUEST['page']=='events-manager-options' && em_wp_is_super_admin() && get_option('dbem_ms_update_nag') ){
177
  if( !empty($_GET['disable_dbem_ms_update_nag']) ){
178
  delete_site_option('dbem_ms_update_nag');
179
  }else{
185
  }
186
  }
187
 
188
+ if( em_wp_is_super_admin() && get_option('dbem_migrate_images_nag') ){
189
  if( !empty($_GET['disable_dbem_migrate_images_nag']) ){
190
  delete_site_option('dbem_migrate_images_nag');
191
  }else{
admin/em-bookings.php CHANGED
@@ -84,15 +84,16 @@ function em_bookings_event(){
84
  $header_button_classes = is_admin() ? 'page-title-action':'button add-new-h2';
85
  ?>
86
  <div class='wrap'>
87
- <?php if( is_admin() ): ?><h1><?php else: ?><h2><?php endif; ?>
88
  <?php echo sprintf(__('Manage %s Bookings', 'events-manager'), "'{$EM_Event->event_name}'"); ?>
 
89
  <a href="<?php echo $EM_Event->get_permalink(); ?>" class="<?php echo $header_button_classes; ?>"><?php echo sprintf(__('View %s','events-manager'), __('Event', 'events-manager')) ?></a>
90
  <a href="<?php echo $EM_Event->get_edit_url(); ?>" class="<?php echo $header_button_classes; ?>"><?php echo sprintf(__('Edit %s','events-manager'), __('Event', 'events-manager')) ?></a>
91
  <?php if( locate_template('plugins/events-manager/templates/csv-event-bookings.php', false) ): //support for legacy template ?>
92
  <a href='<?php echo EM_ADMIN_URL ."&amp;page=events-manager-bookings&amp;action=bookings_export_csv&amp;_wpnonce=".wp_create_nonce('bookings_export_csv')."&amp;event_id=".$EM_Event->event_id ?>' class="<?php echo $header_button_classes; ?>"><?php esc_html_e('Export CSV','events-manager')?></a>
93
  <?php endif; ?>
94
  <?php do_action('em_admin_event_booking_options_buttons'); ?>
95
- <?php if( !is_admin() ): ?></h2><?php else: ?></h1><?php endif; ?>
96
  <?php if( !is_admin() ) echo $EM_Notices; ?>
97
  <div>
98
  <p><strong><?php esc_html_e('Event Name','events-manager'); ?></strong> : <?php echo esc_html($EM_Event->event_name); ?></p>
@@ -141,12 +142,13 @@ function em_bookings_ticket(){
141
  $header_button_classes = is_admin() ? 'page-title-action':'button add-new-h2';
142
  ?>
143
  <div class='wrap'>
144
- <?php if( is_admin() ): ?><h1><?php else: ?><h2><?php endif; ?>
145
  <?php echo sprintf(__('Ticket for %s', 'events-manager'), "'{$EM_Event->name}'"); ?>
 
146
  <a href="<?php echo $EM_Event->get_edit_url(); ?>" class="<?php echo $header_button_classes; ?>"><?php esc_html_e('View/Edit Event','events-manager') ?></a>
147
  <a href="<?php echo $EM_Event->get_bookings_url(); ?>" class="<?php echo $header_button_classes; ?>"><?php esc_html_e('View Event Bookings','events-manager') ?></a>
148
 
149
- <?php if( !is_admin() ): ?></h2><?php else: ?></h1><?php endif; ?>
150
  <?php if( !is_admin() ) echo $EM_Notices; ?>
151
  <div>
152
  <table>
@@ -508,15 +510,16 @@ function em_bookings_person(){
508
  $header_button_classes = is_admin() ? 'page-title-action':'button add-new-h2';
509
  ?>
510
  <div class='wrap'>
511
- <?php if( is_admin() ): ?><h1><?php else: ?><h2><?php endif; ?>
512
  <?php esc_html_e('Manage Person\'s Booking', 'events-manager'); ?>
 
513
  <?php if( current_user_can('edit_users') ) : ?>
514
  <a href="<?php echo admin_url('user-edit.php?user_id='.$EM_Person->ID); ?>" class="<?php echo $header_button_classes; ?>"><?php esc_html_e('Edit User','events-manager') ?></a>
515
  <?php endif; ?>
516
  <?php if( current_user_can('delete_users') ) : ?>
517
  <a href="<?php echo wp_nonce_url( admin_url("users.php?action=delete&amp;user=$EM_Person->ID"), 'bulk-users' ); ?>" class="<?php echo $header_button_classes; ?>"><?php esc_html_e('Delete User','events-manager') ?></a>
518
  <?php endif; ?>
519
- <?php if( !is_admin() ): ?></h2><?php else: ?></h1><?php endif; ?>
520
  <?php if( !is_admin() ) echo $EM_Notices; ?>
521
  <?php do_action('em_bookings_person_header'); ?>
522
  <div id="poststuff" class="metabox-holder has-right-sidebar">
84
  $header_button_classes = is_admin() ? 'page-title-action':'button add-new-h2';
85
  ?>
86
  <div class='wrap'>
87
+ <?php if( is_admin() ): ?><h1 class="wp-heading-inline"><?php else: ?><h2><?php endif; ?>
88
  <?php echo sprintf(__('Manage %s Bookings', 'events-manager'), "'{$EM_Event->event_name}'"); ?>
89
+ <?php if( is_admin() ): ?></h1><?php endif; ?>
90
  <a href="<?php echo $EM_Event->get_permalink(); ?>" class="<?php echo $header_button_classes; ?>"><?php echo sprintf(__('View %s','events-manager'), __('Event', 'events-manager')) ?></a>
91
  <a href="<?php echo $EM_Event->get_edit_url(); ?>" class="<?php echo $header_button_classes; ?>"><?php echo sprintf(__('Edit %s','events-manager'), __('Event', 'events-manager')) ?></a>
92
  <?php if( locate_template('plugins/events-manager/templates/csv-event-bookings.php', false) ): //support for legacy template ?>
93
  <a href='<?php echo EM_ADMIN_URL ."&amp;page=events-manager-bookings&amp;action=bookings_export_csv&amp;_wpnonce=".wp_create_nonce('bookings_export_csv')."&amp;event_id=".$EM_Event->event_id ?>' class="<?php echo $header_button_classes; ?>"><?php esc_html_e('Export CSV','events-manager')?></a>
94
  <?php endif; ?>
95
  <?php do_action('em_admin_event_booking_options_buttons'); ?>
96
+ <?php if( !is_admin() ): ?></h2><?php else: ?><hr class="wp-header-end" /><?php endif; ?>
97
  <?php if( !is_admin() ) echo $EM_Notices; ?>
98
  <div>
99
  <p><strong><?php esc_html_e('Event Name','events-manager'); ?></strong> : <?php echo esc_html($EM_Event->event_name); ?></p>
142
  $header_button_classes = is_admin() ? 'page-title-action':'button add-new-h2';
143
  ?>
144
  <div class='wrap'>
145
+ <?php if( is_admin() ): ?><h1 class="wp-heading-inline"><?php else: ?><h2><?php endif; ?>
146
  <?php echo sprintf(__('Ticket for %s', 'events-manager'), "'{$EM_Event->name}'"); ?>
147
+ <?php if( is_admin() ): ?></h1><?php endif; ?>
148
  <a href="<?php echo $EM_Event->get_edit_url(); ?>" class="<?php echo $header_button_classes; ?>"><?php esc_html_e('View/Edit Event','events-manager') ?></a>
149
  <a href="<?php echo $EM_Event->get_bookings_url(); ?>" class="<?php echo $header_button_classes; ?>"><?php esc_html_e('View Event Bookings','events-manager') ?></a>
150
 
151
+ <?php if( !is_admin() ): ?></h2><?php else: ?><hr class="wp-header-end" /><?php endif; ?>
152
  <?php if( !is_admin() ) echo $EM_Notices; ?>
153
  <div>
154
  <table>
510
  $header_button_classes = is_admin() ? 'page-title-action':'button add-new-h2';
511
  ?>
512
  <div class='wrap'>
513
+ <?php if( is_admin() ): ?><h1 class="wp-heading-inline"><?php else: ?><h2><?php endif; ?>
514
  <?php esc_html_e('Manage Person\'s Booking', 'events-manager'); ?>
515
+ <?php if( is_admin() ): ?></h1><?php endif; ?>
516
  <?php if( current_user_can('edit_users') ) : ?>
517
  <a href="<?php echo admin_url('user-edit.php?user_id='.$EM_Person->ID); ?>" class="<?php echo $header_button_classes; ?>"><?php esc_html_e('Edit User','events-manager') ?></a>
518
  <?php endif; ?>
519
  <?php if( current_user_can('delete_users') ) : ?>
520
  <a href="<?php echo wp_nonce_url( admin_url("users.php?action=delete&amp;user=$EM_Person->ID"), 'bulk-users' ); ?>" class="<?php echo $header_button_classes; ?>"><?php esc_html_e('Delete User','events-manager') ?></a>
521
  <?php endif; ?>
522
+ <?php if( !is_admin() ): ?></h2><?php else: ?><hr class="wp-header-end" /><?php endif; ?>
523
  <?php if( !is_admin() ) echo $EM_Notices; ?>
524
  <?php do_action('em_bookings_person_header'); ?>
525
  <div id="poststuff" class="metabox-holder has-right-sidebar">
admin/em-docs.php CHANGED
@@ -33,7 +33,10 @@ function em_docs_init($force_init = false){
33
  'tag' => array( 'desc'=> str_replace('%s', 'tags', 'Supply a single id, slug or comma-separated ids or slugs (e.g. "1,%s-slug,3") to limit the search to events in any of these %s. You can also use negative numbers and slugs to exclude specific %s (e.g. -1,-exclude-%s,-3). If you mix inclusions and exclusions, all events with included %s AND without excluded %s will be shown. You can also use &amp; to separate ids and slugs, in which case events must contain (or not contain) both %s to be shown.'), 'default'=>0),
34
  'year' => array( 'desc'=> 'If set to a year (e.g. 2010) only events that start or end during this year/month will be returned. Does not work as intended if used with scope.', 'default'=>''),
35
  'has_location' => array( 'desc'=> 'When set to true, only events WITH an assigned location will be shown, has priority over no_location.', 'default'=>''),
36
- 'no_location' => array( 'desc'=> 'When set to true, only events WITHOUT an assigned location will be shown.', 'default'=>'')
 
 
 
37
  ),
38
  'locations' => array(
39
  'blog' => array( 'desc' => sprintf('Limit search to %s created in a specific blog id (MultiSite only)','locations')),
@@ -53,7 +56,10 @@ function em_docs_init($force_init = false){
53
  'scope' => array( 'default' => 'all'),
54
  'state' => array( 'desc'=> sprintf('Search for %s in this %s (no partial matches, case sensitive).','locations','State'), 'default' => 'none'),
55
  'status' => array( 'desc' => sprintf('Limit search to %s with a spefic status (1 is active, 0 is pending approval)','locations'), 'default'=>1),
56
- 'town' => array( 'desc'=> sprintf('Search for %s in this %s (no partial matches, case sensitive).','locations','Town'), 'default' => 'none')
 
 
 
57
  ),
58
  'categories' => array(
59
  '' => array( 'desc' => 'See the <a href="http://codex.wordpress.org/Function_Reference/get_terms">WordPress get_terms() Codex</a> for a list of possible search attributes/arguments.'),
@@ -149,8 +155,10 @@ function em_docs_init($force_init = false){
149
  '#_BOOKINGSLINK' => array( 'desc' => 'Shows a link to the admin, front-end or buddypress (if activated) bookings management page for this event. Only shown if user is logged in and able to manage bookings.' ),
150
  '#_EVENTPRICERANGE' => array( 'desc' => 'Shows a "maximum - minimum" price range for available tickets at the time of display, or a single price if there is no range. Once bookings are closed this will show a 0 value, if you have enabled \'Show unavailable tickets\' in your booking settings these will be included. Price is formatted according to currency formatting in your settings page.' ),
151
  '#_EVENTPRICERANGEALL' => array( 'desc' => 'Like #_EVENTPRICERANGE but shows all tickets price range whether or not bookings or individual tickets are available.' ),
152
- '#_EVENTPRICEMIN' => array( 'desc' => 'Shows the lowest ticket price for this event.' ),
153
- '#_EVENTPRICEMAX' => array( 'desc' => 'Shows the highest ticket price for this event.' ),
 
 
154
  )
155
  ),
156
  'Contact Details' => array(
@@ -172,6 +180,8 @@ function em_docs_init($force_init = false){
172
  'placeholders' => array(
173
  '#_EVENTICALURL' => array( 'desc' => 'Displays the URL of the event ical feed (ics file format).' ),
174
  '#_EVENTICALLINK' => array( 'desc' => 'Displays an html link to the event ical feed (ics file format).' ),
 
 
175
  '#_EVENTGCALURL' => array( 'desc' => 'Displays URL which would take the user to Google Calendar and pre-fill their add new event form.' ),
176
  '#_EVENTGCALLINK' => array( 'desc' => 'Displays a button which would take the user to Google Calendar and pre-fill their add new event form.' )
177
  )
@@ -184,7 +194,9 @@ function em_docs_init($force_init = false){
184
  '#_CATEGORYNAME' => array( 'desc' => 'Shows the category name.' ),
185
  '#_CATEGORYID' => array( 'desc' => 'Shows the category ID.' ),
186
  '#_CATEGORYSLUG' => array( 'desc' => 'Shows the category slug.' ),
187
- '#_CATEGORYCOLOR' => array( 'desc' => 'Shows the category color (useful for inline styling), in hex format, if no color is defined #FFFFFF (white) will be used.' ),
 
 
188
  '#_CATEGORYIMAGE' => array( 'desc' => 'Shows the category image, if available.' ),
189
  '#_CATEGORYIMAGE{x,y}' => array( 'desc' => 'Shows the category image thumbnail if available, x and y are width and height respectively, both being numbers e.g. <code>#_CATEGORYIMAGE{100,100}</code>. If 0 is used for either width or height, the corresponding dimension will be proportionally sized.' ),
190
  '#_CATEGORYIMAGEURL' => array( 'desc' => 'Shows the category image url, if available.' ),
@@ -202,8 +214,10 @@ function em_docs_init($force_init = false){
202
  ),
203
  'iCal/RSS Feeds' => array(
204
  'placeholders' => array(
205
- '#_CATEGORYICALURL' => array( 'desc' => 'Displays the URL of the event ical feed (ics file format) which shows all events happening in this category.', 'since'=>'5.5.2' ),
206
- '#_CATEGORYICALLINK' => array( 'desc' => 'Displays an html link to the event ical feed (ics file format) which shows all events happening in this category.', 'since'=>'5.5.2' ),
 
 
207
  '#_CATEGORYRSSURL' => array( 'desc' => 'Displays the URL of an RSS feed showing all upcoming events happening in this category.', 'since'=>'5.5.2' ),
208
  '#_CATEGORYRSSLINK' => array( 'desc' => 'Displays an html link to an RSS feed showing all upcoming events happening in this category.', 'since'=>'5.5.2' )
209
  )
@@ -216,7 +230,13 @@ function em_docs_init($force_init = false){
216
  '#_TAGNAME' => array( 'desc' => 'Shows the tag name.' ),
217
  '#_TAGID' => array( 'desc' => 'Shows the tag ID.' ),
218
  '#_TAGSLUG' => array( 'desc' => 'Shows the tag slug.' ),
219
- '#_TAGNOTES' => array( 'desc' => 'Shows the tag description.' )
 
 
 
 
 
 
220
  )
221
  ),
222
  'Related Events' => array(
@@ -230,8 +250,10 @@ function em_docs_init($force_init = false){
230
  ),
231
  'iCal/RSS Feeds' => array(
232
  'placeholders' => array(
233
- '#_TAGICALURL' => array( 'desc' => 'Displays the URL of the event ical feed (ics file format) which shows all events happening in this tag.', 'since'=>'5.5.2' ),
234
- '#_TAGICALLINK' => array( 'desc' => 'Displays an html link to the event ical feed (ics file format) which shows all events happening in this tag.' , 'since'=>'5.5.2'),
 
 
235
  '#_TAGRSSURL' => array( 'desc' => 'Displays the URL of an RSS feed showing all upcoming events happening in this tag.', 'since'=>'5.5.2' ),
236
  '#_TAGRSSLINK' => array( 'desc' => 'Displays an html link to an RSS feed showing all upcoming events happening in this tag.', 'since'=>'5.5.2' )
237
  )
@@ -256,6 +278,7 @@ function em_docs_init($force_init = false){
256
  '#_LOCATIONNOTES' => array( 'desc' => 'Shows the location description.' ),
257
  '#_LOCATIONEXCERPT' => array( 'desc' => 'If an excerpt has been added to the location, it will be used. If you added a <a href="http://en.support.wordpress.com/splitting-content/more-tag/">more tag</a> to your location description, only the content before this tag will show.' ),
258
  '#_LOCATIONEXCERPT{words, ...}' => array( 'desc' => 'If an excerpt has not been added to the location, only a specific length is shown, e.g. <code>#_EVENTEXCERPT{10,...}</code> where 10 is the number of words to show and ... is what is used at the cut-off point.' ),
 
259
  '#_LOCATIONIMAGE' => array( 'desc' => 'Shows the location image.' ),
260
  '#_LOCATIONIMAGE{x,y}' => array( 'desc' => 'Shows the location image thumbnail, x and y are width and height respectively, both being numbers e.g. <code>#_LOCATIONIMAGE{100,100}</code>. If 0 is used for either width or height, the corresponding dimension will be proportionally sized.' ),
261
  '#_LOCATIONIMAGEURL' => array( 'desc' => 'Shows the location image url, if available.' ),
@@ -292,6 +315,8 @@ function em_docs_init($force_init = false){
292
  'placeholders' => array(
293
  '#_LOCATIONICALURL' => array( 'desc' => 'Displays the URL of the location ical feed (ics file format) which shows all events happening at that location.', 'since'=>'5.5.2' ),
294
  '#_LOCATIONICALLINK' => array( 'desc' => 'Displays an html link to the event ical feed (ics file format) which shows all events happening at that location.', 'since'=>'5.5.2' ),
 
 
295
  '#_LOCATIONRSSURL' => array( 'desc' => 'Displays the URL of an RSS feed showing all upcoming events happening at this location.', 'since'=>'5.5.2' ),
296
  '#_LOCATIONRSSLINK' => array( 'desc' => 'Displays an html link to an RSS feed showing all upcoming events happening at this location.', 'since'=>'5.5.2' )
297
  )
33
  'tag' => array( 'desc'=> str_replace('%s', 'tags', 'Supply a single id, slug or comma-separated ids or slugs (e.g. "1,%s-slug,3") to limit the search to events in any of these %s. You can also use negative numbers and slugs to exclude specific %s (e.g. -1,-exclude-%s,-3). If you mix inclusions and exclusions, all events with included %s AND without excluded %s will be shown. You can also use &amp; to separate ids and slugs, in which case events must contain (or not contain) both %s to be shown.'), 'default'=>0),
34
  'year' => array( 'desc'=> 'If set to a year (e.g. 2010) only events that start or end during this year/month will be returned. Does not work as intended if used with scope.', 'default'=>''),
35
  'has_location' => array( 'desc'=> 'When set to true, only events WITH an assigned location will be shown, has priority over no_location.', 'default'=>''),
36
+ 'no_location' => array( 'desc'=> 'When set to true, only events WITHOUT an assigned location will be shown.', 'default'=>''),
37
+ 'groupby' => array( 'desc'=> 'Show one event for each unique value of the provide field name, along with events contaning no defined value for that field. See our <a href="http://wp-events-plugin.com/documentation/event-search-attributes/event-location-grouping-ordering/">grouping documentation</a> for more information.', 'default'=>0, 'args'=>'Database field name from the wp_em_events or wp_em_locations tables, e.g. <code>event_name</code> or <code>location_name</code>', 'since'=>'5.8'),
38
+ 'groupby_orderby' => array( 'desc'=> 'When groupby is defined, the fields defined here will determine the sorting of events in each group. See our <a href="http://wp-events-plugin.com/documentation/event-search-attributes/event-location-grouping-ordering/">grouping documentation</a> for more information.', 'default'=>'event_start_date,event_start_time', 'args'=>'Database fields (comma-seperated) in the wp_em_events and wp_em_locations tables, e.g. <code>event_start_date,event_start_time</code> or <code>location_name</code>', 'since'=>'5.8'),
39
+ 'groupby_order' => array( 'desc'=> 'Indicates the alphabeitcal/numerical/date order of the sorting in groupby_orderby. Choose between ASC (ascending) and DESC (descending), case insensitive.', 'default'=>'ASC', 'since'=>'5.8'),
40
  ),
41
  'locations' => array(
42
  'blog' => array( 'desc' => sprintf('Limit search to %s created in a specific blog id (MultiSite only)','locations')),
56
  'scope' => array( 'default' => 'all'),
57
  'state' => array( 'desc'=> sprintf('Search for %s in this %s (no partial matches, case sensitive).','locations','State'), 'default' => 'none'),
58
  'status' => array( 'desc' => sprintf('Limit search to %s with a spefic status (1 is active, 0 is pending approval)','locations'), 'default'=>1),
59
+ 'town' => array( 'desc'=> sprintf('Search for %s in this %s (no partial matches, case sensitive).','locations','Town'), 'default' => 'none'),
60
+ 'groupby' => array( 'desc'=> 'Show one location for each unique value of the provide field name, along with locations contaning no defined value for that field. See our <a href="http://wp-events-plugin.com/documentation/event-search-attributes/event-location-grouping-ordering/">grouping documentation</a> for more information.', 'default'=>0, 'args'=>'Database field name from the wp_em_events or wp_em_locations tables, e.g. <code>event_name</code> or <code>location_name</code>', 'since'=>'5.8'),
61
+ 'groupby_orderby' => array( 'desc'=> 'When groupby is defined, the fields defined here will determine the sorting of events in each group. See our <a href="http://wp-events-plugin.com/documentation/event-search-attributes/event-location-grouping-ordering/">grouping documentation</a> for more information.', 'default'=>'event_start_date,event_start_time', 'args'=>'Database fields (comma-seperated) in the wp_em_events and wp_em_locations tables, e.g. <code>location_country,location_town</code> or <code>location_name</code>', 'since'=>'5.8'),
62
+ 'groupby_order' => array( 'desc'=> 'Indicates the alphabeitcal/numerical/date order of the sorting in groupby_orderby. Choose between ASC (ascending) and DESC (descending), case insensitive.', 'default'=>'ASC', 'since'=>'5.8'),
63
  ),
64
  'categories' => array(
65
  '' => array( 'desc' => 'See the <a href="http://codex.wordpress.org/Function_Reference/get_terms">WordPress get_terms() Codex</a> for a list of possible search attributes/arguments.'),
155
  '#_BOOKINGSLINK' => array( 'desc' => 'Shows a link to the admin, front-end or buddypress (if activated) bookings management page for this event. Only shown if user is logged in and able to manage bookings.' ),
156
  '#_EVENTPRICERANGE' => array( 'desc' => 'Shows a "maximum - minimum" price range for available tickets at the time of display, or a single price if there is no range. Once bookings are closed this will show a 0 value, if you have enabled \'Show unavailable tickets\' in your booking settings these will be included. Price is formatted according to currency formatting in your settings page.' ),
157
  '#_EVENTPRICERANGEALL' => array( 'desc' => 'Like #_EVENTPRICERANGE but shows all tickets price range whether or not bookings or individual tickets are available.' ),
158
+ '#_EVENTPRICEMIN' => array( 'desc' => 'Shows the lowest available ticket price for this event. Will display a value of 0 if no ticket is available.' ),
159
+ '#_EVENTPRICEMAX' => array( 'desc' => 'Shows the highest available ticket price for this event. Will display a value of 0 if no ticket is avaialble.' ),
160
+ '#_EVENTPRICEMINALL' => array( 'desc' => 'Shows the lowest ticket price for this event, whether available or not.' ),
161
+ '#_EVENTPRICEMAXALL' => array( 'desc' => 'Shows the highest ticket price for this event, whether available or not.' ),
162
  )
163
  ),
164
  'Contact Details' => array(
180
  'placeholders' => array(
181
  '#_EVENTICALURL' => array( 'desc' => 'Displays the URL of the event ical feed (ics file format).' ),
182
  '#_EVENTICALLINK' => array( 'desc' => 'Displays an html link to the event ical feed (ics file format).' ),
183
+ '#_EVENTWEBCALURL' => array( 'desc' => 'Same as #_EVENTICALURL, but using the <a href="https://en.wikipedia.org/wiki/Webcal">webcal:// protocol</a>, which will open up various calendar apps automatically including iCalendar, Outlook and Google Calendar.', 'since'=>'5.8' ),
184
+ '#_EVENTWEBCALLINK' => array( 'desc' => 'Same as #_EVENTICALLINK, but using the <a href="https://en.wikipedia.org/wiki/Webcal">webcal:// protocol</a>, which will open up various calendar apps automatically including iCalendar, Outlook and Google Calendar.', 'since'=>'5.8' ),
185
  '#_EVENTGCALURL' => array( 'desc' => 'Displays URL which would take the user to Google Calendar and pre-fill their add new event form.' ),
186
  '#_EVENTGCALLINK' => array( 'desc' => 'Displays a button which would take the user to Google Calendar and pre-fill their add new event form.' )
187
  )
194
  '#_CATEGORYNAME' => array( 'desc' => 'Shows the category name.' ),
195
  '#_CATEGORYID' => array( 'desc' => 'Shows the category ID.' ),
196
  '#_CATEGORYSLUG' => array( 'desc' => 'Shows the category slug.' ),
197
+ '#_CATEGORYLINK' => array( 'desc' => 'Category name with a link to the category page.' ),
198
+ '#_CATEGORYURL' => array( 'desc' => 'URL of the category page.' ),
199
+ '#_CATEGORYCOLOR' => array( 'desc' => 'Shows the category color (useful for inline styling), in hex format, if no color is defined the default category color from your settings page will be used.' ),
200
  '#_CATEGORYIMAGE' => array( 'desc' => 'Shows the category image, if available.' ),
201
  '#_CATEGORYIMAGE{x,y}' => array( 'desc' => 'Shows the category image thumbnail if available, x and y are width and height respectively, both being numbers e.g. <code>#_CATEGORYIMAGE{100,100}</code>. If 0 is used for either width or height, the corresponding dimension will be proportionally sized.' ),
202
  '#_CATEGORYIMAGEURL' => array( 'desc' => 'Shows the category image url, if available.' ),
214
  ),
215
  'iCal/RSS Feeds' => array(
216
  'placeholders' => array(
217
+ '#_CATEGORYICALURL' => array( 'desc' => 'Displays the URL of the event ical feed (ics file format) which shows all events with this category.', 'since'=>'5.5.2' ),
218
+ '#_CATEGORYICALLINK' => array( 'desc' => 'Displays an html link to the event ical feed (ics file format) which shows all events with this category.', 'since'=>'5.5.2' ),
219
+ '#_CATEGORYWEBCALURL' => array( 'desc' => 'Same as #_CATEGORYICALURL, but using the <a href="https://en.wikipedia.org/wiki/Webcal">webcal:// protocol</a>, which will open up various calendar apps automatically including iCalendar, Outlook and Google Calendar.', 'since'=>'5.8' ),
220
+ '#_CATEGORYWEBCALLINK' => array( 'desc' => 'Same as #_CATEGORYICALLINK, but using the <a href="https://en.wikipedia.org/wiki/Webcal">webcal:// protocol</a>, which will open up various calendar apps automatically including iCalendar, Outlook and Google Calendar.', 'since'=>'5.8' ),
221
  '#_CATEGORYRSSURL' => array( 'desc' => 'Displays the URL of an RSS feed showing all upcoming events happening in this category.', 'since'=>'5.5.2' ),
222
  '#_CATEGORYRSSLINK' => array( 'desc' => 'Displays an html link to an RSS feed showing all upcoming events happening in this category.', 'since'=>'5.5.2' )
223
  )
230
  '#_TAGNAME' => array( 'desc' => 'Shows the tag name.' ),
231
  '#_TAGID' => array( 'desc' => 'Shows the tag ID.' ),
232
  '#_TAGSLUG' => array( 'desc' => 'Shows the tag slug.' ),
233
+ '#_TAGLINK' => array( 'desc' => 'Tag name with a link to the tag page.' ),
234
+ '#_TAGURL' => array( 'desc' => 'URL of the tag page.' ),
235
+ '#_TAGNOTES' => array( 'desc' => 'Shows the tag description.' ),
236
+ '#_TAGCOLOR' => array( 'desc' => 'Shows the tag color (useful for inline styling), in hex format, if no color is defined the default tag color from your settings page will be used.' ),
237
+ '#_TAGIMAGE' => array( 'desc' => 'Shows the tag image, if available.' ),
238
+ '#_TAGIMAGE{x,y}' => array( 'desc' => 'Shows the tag image thumbnail if available, x and y are width and height respectively, both being numbers e.g. <code>#_TAGIMAGE{100,100}</code>. If 0 is used for either width or height, the corresponding dimension will be proportionally sized.' ),
239
+ '#_TAGIMAGEURL' => array( 'desc' => 'Shows the tag image url, if available.' ),
240
  )
241
  ),
242
  'Related Events' => array(
250
  ),
251
  'iCal/RSS Feeds' => array(
252
  'placeholders' => array(
253
+ '#_TAGICALURL' => array( 'desc' => 'Displays the URL of the event ical feed (ics file format) which shows all events with this tag.', 'since'=>'5.5.2' ),
254
+ '#_TAGICALLINK' => array( 'desc' => 'Displays an html link to the event ical feed (ics file format) which shows all events with this tag.' , 'since'=>'5.5.2'),
255
+ '#_TAGWEBCALURL' => array( 'desc' => 'Same as #_TAGICALURL, but using the <a href="https://en.wikipedia.org/wiki/Webcal">webcal:// protocol</a>, which will open up various calendar apps automatically including iCalendar, Outlook and Google Calendar.', 'since'=>'5.8' ),
256
+ '#_TAGWEBCALLINK' => array( 'desc' => 'Same as #_TAGICALLINK, but using the <a href="https://en.wikipedia.org/wiki/Webcal">webcal:// protocol</a>, which will open up various calendar apps automatically including iCalendar, Outlook and Google Calendar.', 'since'=>'5.8' ),
257
  '#_TAGRSSURL' => array( 'desc' => 'Displays the URL of an RSS feed showing all upcoming events happening in this tag.', 'since'=>'5.5.2' ),
258
  '#_TAGRSSLINK' => array( 'desc' => 'Displays an html link to an RSS feed showing all upcoming events happening in this tag.', 'since'=>'5.5.2' )
259
  )
278
  '#_LOCATIONNOTES' => array( 'desc' => 'Shows the location description.' ),
279
  '#_LOCATIONEXCERPT' => array( 'desc' => 'If an excerpt has been added to the location, it will be used. If you added a <a href="http://en.support.wordpress.com/splitting-content/more-tag/">more tag</a> to your location description, only the content before this tag will show.' ),
280
  '#_LOCATIONEXCERPT{words, ...}' => array( 'desc' => 'If an excerpt has not been added to the location, only a specific length is shown, e.g. <code>#_EVENTEXCERPT{10,...}</code> where 10 is the number of words to show and ... is what is used at the cut-off point.' ),
281
+ '#_LOCATIONEXCERPTCUT' => array( 'desc' => 'Same as <code>#_LOCATIONEXCERPT</code> (and also accepts the <code>{words,...}</code> arguments) but will also cut excerpts as well as post content. Default word limit is 55 and cut-off is <code>[...]</code>' ),
282
  '#_LOCATIONIMAGE' => array( 'desc' => 'Shows the location image.' ),
283
  '#_LOCATIONIMAGE{x,y}' => array( 'desc' => 'Shows the location image thumbnail, x and y are width and height respectively, both being numbers e.g. <code>#_LOCATIONIMAGE{100,100}</code>. If 0 is used for either width or height, the corresponding dimension will be proportionally sized.' ),
284
  '#_LOCATIONIMAGEURL' => array( 'desc' => 'Shows the location image url, if available.' ),
315
  'placeholders' => array(
316
  '#_LOCATIONICALURL' => array( 'desc' => 'Displays the URL of the location ical feed (ics file format) which shows all events happening at that location.', 'since'=>'5.5.2' ),
317
  '#_LOCATIONICALLINK' => array( 'desc' => 'Displays an html link to the event ical feed (ics file format) which shows all events happening at that location.', 'since'=>'5.5.2' ),
318
+ '#_LOCATIONWEBCALURL' => array( 'desc' => 'Same as #_LOCATIONICALURL, but using the <a href="https://en.wikipedia.org/wiki/Webcal">webcal:// protocol</a>, which will open up various calendar apps automatically including iCalendar, Outlook and Google Calendar.', 'since'=>'5.8' ),
319
+ '#_LOCATIONWEBCALLINK' => array( 'desc' => 'Same as #_LOCATIONICALLINK, but using the <a href="https://en.wikipedia.org/wiki/Webcal">webcal:// protocol</a>, which will open up various calendar apps automatically including iCalendar, Outlook and Google Calendar.', 'since'=>'5.8' ),
320
  '#_LOCATIONRSSURL' => array( 'desc' => 'Displays the URL of an RSS feed showing all upcoming events happening at this location.', 'since'=>'5.5.2' ),
321
  '#_LOCATIONRSSLINK' => array( 'desc' => 'Displays an html link to an RSS feed showing all upcoming events happening at this location.', 'since'=>'5.5.2' )
322
  )
admin/em-ms-options.php CHANGED
@@ -31,7 +31,7 @@ function em_ms_upgrade( $blog_id ){
31
  <p><?php esc_html_e('To update your network blogs with the latest Events Manager automatically, click the update button below.','events-manager'); ?></p>
32
  <input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce('em_ms_ugrade_'.get_current_user_id()); ?>" />
33
  <input type="hidden" name="action" value="upgrade" />
34
- <input type="submit" value="<?php esc_attr_e('Update','events-manager'); ?>" />
35
  </form>
36
  <?php
37
  }
31
  <p><?php esc_html_e('To update your network blogs with the latest Events Manager automatically, click the update button below.','events-manager'); ?></p>
32
  <input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce('em_ms_ugrade_'.get_current_user_id()); ?>" />
33
  <input type="hidden" name="action" value="upgrade" />
34
+ <input type="submit" value="<?php esc_attr_e('Update','events-manager'); ?>" />
35
  </form>
36
  <?php
37
  }
admin/em-options.php CHANGED
@@ -17,7 +17,7 @@ function em_options_save(){
17
  if( in_array($postKey, array('dbem_bookings_notify_admin','dbem_event_submitted_email_admin','dbem_js_limit_events_form','dbem_js_limit_search','dbem_js_limit_general','dbem_css_limit_include','dbem_css_limit_exclude','dbem_search_form_geo_distance_options')) ){ $postValue = str_replace(' ', '', $postValue); } //clean up comma separated emails, no spaces needed
18
  if( in_array($postKey,$numeric_options) && !is_numeric($postValue) ){
19
  //Do nothing, keep old setting.
20
- }elseif( $postKey == 'dbem_category_default_color' && !preg_match("/^#([abcdef0-9]{3}){1,2}?$/i",$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?
@@ -31,7 +31,7 @@ function em_options_save(){
31
  }
32
  }
33
  //set capabilities
34
- if( !empty($_POST['em_capabilities']) && is_array($_POST['em_capabilities']) && (!is_multisite() || is_multisite() && is_super_admin()) ){
35
  global $em_capabilities_array, $wp_roles;
36
  if( is_multisite() && is_network_admin() && $_POST['dbem_ms_global_caps'] == 1 ){
37
  //apply_caps_to_blog
@@ -67,7 +67,13 @@ function em_options_save(){
67
  update_option('dbem_flush_needed',1);
68
  do_action('em_options_save');
69
  $EM_Notices->add_confirm('<strong>'.__('Changes saved.', 'events-manager').'</strong>', true);
70
- wp_redirect(em_wp_get_referer());
 
 
 
 
 
 
71
  exit();
72
  }
73
  //Migration
@@ -84,7 +90,7 @@ function em_options_save(){
84
  delete_option('dbem_migrate_images');
85
  }
86
  //Uninstall
87
- if( !empty($_REQUEST['action']) && $_REQUEST['action'] == 'uninstall' && current_user_can('activate_plugins') && !empty($_REQUEST['confirmed']) && check_admin_referer('em_uninstall_'.get_current_user_id().'_wpnonce') && is_super_admin() ){
88
  if( check_admin_referer('em_uninstall_'.get_current_user_id().'_confirmed','_wpnonce2') ){
89
  //We have a go to uninstall
90
  global $wpdb;
@@ -112,7 +118,6 @@ function em_options_save(){
112
  $wpdb->query('DROP TABLE '.EM_TICKETS_TABLE);
113
  $wpdb->query('DROP TABLE '.EM_TICKETS_BOOKINGS_TABLE);
114
  $wpdb->query('DROP TABLE '.EM_RECURRENCE_TABLE);
115
- $wpdb->query('DROP TABLE '.EM_CATEGORIES_TABLE);
116
  $wpdb->query('DROP TABLE '.EM_META_TABLE);
117
 
118
  //delete options
@@ -124,7 +129,7 @@ function em_options_save(){
124
  }
125
  }
126
  //Reset
127
- if( !empty($_REQUEST['action']) && $_REQUEST['action'] == 'reset' && !empty($_REQUEST['confirmed']) && check_admin_referer('em_reset_'.get_current_user_id().'_wpnonce') && is_super_admin() ){
128
  if( check_admin_referer('em_reset_'.get_current_user_id().'_confirmed','_wpnonce2') ){
129
  //We have a go to uninstall
130
  global $wpdb;
@@ -144,7 +149,7 @@ function em_options_save(){
144
  }
145
  }
146
  //Cleanup Event Orphans
147
- if( !empty($_REQUEST['action']) && $_REQUEST['action'] == 'cleanup_event_orphans' && check_admin_referer('em_cleanup_event_orphans_'.get_current_user_id().'_wpnonce') && is_super_admin() ){
148
  //Firstly, get all orphans
149
  global $wpdb;
150
  $sql = 'SELECT event_id FROM '.EM_EVENTS_TABLE.' WHERE post_id NOT IN (SELECT ID FROM ' .$wpdb->posts. ' WHERE post_type="'. EM_POST_TYPE_EVENT .'" OR post_type="event-recurring")';
@@ -169,7 +174,7 @@ function em_options_save(){
169
  exit();
170
  }
171
  //Force Update Recheck - Workaround for now
172
- if( !empty($_REQUEST['action']) && $_REQUEST['action'] == 'recheck_updates' && check_admin_referer('em_recheck_updates_'.get_current_user_id().'_wpnonce') && is_super_admin() ){
173
  //force recheck of plugin updates, to refresh dl links
174
  delete_transient('update_plugins');
175
  delete_site_transient('update_plugins');
@@ -178,7 +183,7 @@ function em_options_save(){
178
  exit();
179
  }
180
  //Flag version checking to look at trunk, not tag
181
- if( !empty($_REQUEST['action']) && $_REQUEST['action'] == 'check_devs' && check_admin_referer('em_check_devs_wpnonce') && is_super_admin() ){
182
  //delete transients, and add a flag to recheck dev version next time round
183
  delete_transient('update_plugins');
184
  delete_site_transient('update_plugins');
@@ -188,7 +193,7 @@ function em_options_save(){
188
  exit();
189
  }
190
  //import EM settings
191
- if( !empty($_REQUEST['action']) && ( ($_REQUEST['action'] == 'import_em_settings' && check_admin_referer('import_em_settings')) || (is_multisite() && $_REQUEST['action'] == 'import_em_ms_settings' && check_admin_referer('import_em_ms_settings')) ) && is_super_admin() ){
192
  //upload uniquely named file to system for usage later
193
  if( !empty($_FILES['import_settings_file']['size']) && is_uploaded_file($_FILES['import_settings_file']['tmp_name']) ){
194
  $settings = file_get_contents($_FILES['import_settings_file']['tmp_name']);
@@ -217,7 +222,7 @@ function em_options_save(){
217
  exit();
218
  }
219
  //export EM settings
220
- if( !empty($_REQUEST['action']) && $_REQUEST['action'] == 'export_em_settings' && check_admin_referer('export_em_settings') && is_super_admin() ){
221
  global $wpdb;
222
  $results = $wpdb->get_results('SELECT option_name, option_value FROM '.$wpdb->options ." WHERE option_name LIKE 'dbem_%' OR option_name LIKE 'emp_%' OR option_name LIKE 'em_%'", ARRAY_A);
223
  $options = array();
@@ -226,7 +231,7 @@ function em_options_save(){
226
  header('Content-Disposition: attachment; filename="events-manager-settings.txt"');
227
  echo json_encode($options);
228
  exit();
229
- }elseif( !empty($_REQUEST['action']) && $_REQUEST['action'] == 'export_em_ms_settings' && check_admin_referer('export_em_ms_settings') && is_multisite() && is_super_admin() ){
230
  //delete transients, and add a flag to recheck dev version next time round
231
  global $EM_MS_Globals, $wpdb;
232
  $options = array();
@@ -284,7 +289,7 @@ function em_admin_email_test_ajax(){
284
  add_action('wp_ajax_em_admin_test_email','em_admin_email_test_ajax');
285
 
286
  function em_admin_options_reset_page(){
287
- if( check_admin_referer('em_reset_'.get_current_user_id().'_wpnonce') && is_super_admin() ){
288
  ?>
289
  <div class="wrap">
290
  <div id='icon-options-general' class='icon32'><br /></div>
@@ -300,7 +305,7 @@ function em_admin_options_reset_page(){
300
  }
301
  }
302
  function em_admin_options_uninstall_page(){
303
- if( check_admin_referer('em_uninstall_'.get_current_user_id().'_wpnonce') && is_super_admin() ){
304
  ?>
305
  <div class="wrap">
306
  <div id='icon-options-general' class='icon32'><br /></div>
17
  if( in_array($postKey, array('dbem_bookings_notify_admin','dbem_event_submitted_email_admin','dbem_js_limit_events_form','dbem_js_limit_search','dbem_js_limit_general','dbem_css_limit_include','dbem_css_limit_exclude','dbem_search_form_geo_distance_options')) ){ $postValue = str_replace(' ', '', $postValue); } //clean up comma separated emails, no spaces needed
18
  if( in_array($postKey,$numeric_options) && !is_numeric($postValue) ){
19
  //Do nothing, keep old setting.
20
+ }elseif( ($postKey == 'dbem_category_default_color' || $postKey == 'dbem_tag_default_color') && !preg_match("/^#([abcdef0-9]{3}){1,2}?$/i",$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?
31
  }
32
  }
33
  //set capabilities
34
+ if( !empty($_POST['em_capabilities']) && is_array($_POST['em_capabilities']) && (!is_multisite() || is_multisite() && em_wp_is_super_admin()) ){
35
  global $em_capabilities_array, $wp_roles;
36
  if( is_multisite() && is_network_admin() && $_POST['dbem_ms_global_caps'] == 1 ){
37
  //apply_caps_to_blog
67
  update_option('dbem_flush_needed',1);
68
  do_action('em_options_save');
69
  $EM_Notices->add_confirm('<strong>'.__('Changes saved.', 'events-manager').'</strong>', true);
70
+ $referrer = em_wp_get_referer();
71
+ //add tab hash path to url if supplied
72
+ if( !empty($_REQUEST['tab_path']) ){
73
+ $referrer_array = explode('#', $referrer);
74
+ $referrer = esc_url_raw($referrer_array[0] . '#' . $_REQUEST['tab_path']);
75
+ }
76
+ wp_redirect($referrer);
77
  exit();
78
  }
79
  //Migration
90
  delete_option('dbem_migrate_images');
91
  }
92
  //Uninstall
93
+ if( !empty($_REQUEST['action']) && $_REQUEST['action'] == 'uninstall' && current_user_can('activate_plugins') && !empty($_REQUEST['confirmed']) && check_admin_referer('em_uninstall_'.get_current_user_id().'_wpnonce') && em_wp_is_super_admin() ){
94
  if( check_admin_referer('em_uninstall_'.get_current_user_id().'_confirmed','_wpnonce2') ){
95
  //We have a go to uninstall
96
  global $wpdb;
118
  $wpdb->query('DROP TABLE '.EM_TICKETS_TABLE);
119
  $wpdb->query('DROP TABLE '.EM_TICKETS_BOOKINGS_TABLE);
120
  $wpdb->query('DROP TABLE '.EM_RECURRENCE_TABLE);
 
121
  $wpdb->query('DROP TABLE '.EM_META_TABLE);
122
 
123
  //delete options
129
  }
130
  }
131
  //Reset
132
+ if( !empty($_REQUEST['action']) && $_REQUEST['action'] == 'reset' && !empty($_REQUEST['confirmed']) && check_admin_referer('em_reset_'.get_current_user_id().'_wpnonce') && em_wp_is_super_admin() ){
133
  if( check_admin_referer('em_reset_'.get_current_user_id().'_confirmed','_wpnonce2') ){
134
  //We have a go to uninstall
135
  global $wpdb;
149
  }
150
  }
151
  //Cleanup Event Orphans
152
+ if( !empty($_REQUEST['action']) && $_REQUEST['action'] == 'cleanup_event_orphans' && check_admin_referer('em_cleanup_event_orphans_'.get_current_user_id().'_wpnonce') && em_wp_is_super_admin() ){
153
  //Firstly, get all orphans
154
  global $wpdb;
155
  $sql = 'SELECT event_id FROM '.EM_EVENTS_TABLE.' WHERE post_id NOT IN (SELECT ID FROM ' .$wpdb->posts. ' WHERE post_type="'. EM_POST_TYPE_EVENT .'" OR post_type="event-recurring")';
174
  exit();
175
  }
176
  //Force Update Recheck - Workaround for now
177
+ if( !empty($_REQUEST['action']) && $_REQUEST['action'] == 'recheck_updates' && check_admin_referer('em_recheck_updates_'.get_current_user_id().'_wpnonce') && em_wp_is_super_admin() ){
178
  //force recheck of plugin updates, to refresh dl links
179
  delete_transient('update_plugins');
180
  delete_site_transient('update_plugins');
183
  exit();
184
  }
185
  //Flag version checking to look at trunk, not tag
186
+ if( !empty($_REQUEST['action']) && $_REQUEST['action'] == 'check_devs' && check_admin_referer('em_check_devs_wpnonce') && em_wp_is_super_admin() ){
187
  //delete transients, and add a flag to recheck dev version next time round
188
  delete_transient('update_plugins');
189
  delete_site_transient('update_plugins');
193
  exit();
194
  }
195
  //import EM settings
196
+ if( !empty($_REQUEST['action']) && ( ($_REQUEST['action'] == 'import_em_settings' && check_admin_referer('import_em_settings')) || (is_multisite() && $_REQUEST['action'] == 'import_em_ms_settings' && check_admin_referer('import_em_ms_settings')) ) && em_wp_is_super_admin() ){
197
  //upload uniquely named file to system for usage later
198
  if( !empty($_FILES['import_settings_file']['size']) && is_uploaded_file($_FILES['import_settings_file']['tmp_name']) ){
199
  $settings = file_get_contents($_FILES['import_settings_file']['tmp_name']);
222
  exit();
223
  }
224
  //export EM settings
225
+ if( !empty($_REQUEST['action']) && $_REQUEST['action'] == 'export_em_settings' && check_admin_referer('export_em_settings') && em_wp_is_super_admin() ){
226
  global $wpdb;
227
  $results = $wpdb->get_results('SELECT option_name, option_value FROM '.$wpdb->options ." WHERE option_name LIKE 'dbem_%' OR option_name LIKE 'emp_%' OR option_name LIKE 'em_%'", ARRAY_A);
228
  $options = array();
231
  header('Content-Disposition: attachment; filename="events-manager-settings.txt"');
232
  echo json_encode($options);
233
  exit();
234
+ }elseif( !empty($_REQUEST['action']) && $_REQUEST['action'] == 'export_em_ms_settings' && check_admin_referer('export_em_ms_settings') && is_multisite() && em_wp_is_super_admin() ){
235
  //delete transients, and add a flag to recheck dev version next time round
236
  global $EM_MS_Globals, $wpdb;
237
  $options = array();
289
  add_action('wp_ajax_em_admin_test_email','em_admin_email_test_ajax');
290
 
291
  function em_admin_options_reset_page(){
292
+ if( check_admin_referer('em_reset_'.get_current_user_id().'_wpnonce') && em_wp_is_super_admin() ){
293
  ?>
294
  <div class="wrap">
295
  <div id='icon-options-general' class='icon32'><br /></div>
305
  }
306
  }
307
  function em_admin_options_uninstall_page(){
308
+ if( check_admin_referer('em_uninstall_'.get_current_user_id().'_wpnonce') && em_wp_is_super_admin() ){
309
  ?>
310
  <div class="wrap">
311
  <div id='icon-options-general' class='icon32'><br /></div>
admin/settings/tabs/emails.php CHANGED
@@ -68,14 +68,6 @@
68
  em_options_input_text ( __( 'Booking pending email subject', 'events-manager'), 'dbem_bookings_email_pending_subject', $email_subject_tip);
69
  em_options_textarea ( __( 'Booking pending email', 'events-manager'), 'dbem_bookings_email_pending_body','') ;
70
  ?>
71
- <tr class="em-subheader"><td colspan='2'>
72
- <h5><?php _e('Rejected booking email','events-manager') ?></h5>
73
- <em><?php echo __( 'This will be sent automatically when a booking is rejected. Not relevant if bookings don\'t require approval.', 'events-manager').$bookings_placeholder_tip ?></em>
74
- </td></tr>
75
- <?php
76
- em_options_input_text ( __( 'Booking rejected email subject', 'events-manager'), 'dbem_bookings_email_rejected_subject', $email_subject_tip );
77
- em_options_textarea ( __( 'Booking rejected email', 'events-manager'), 'dbem_bookings_email_rejected_body', '' );
78
- ?>
79
  <tr class="em-subheader"><td colspan='2'>
80
  <h5><?php _e('Booking cancelled','events-manager') ?></h5>
81
  <em><?php echo __('This will be sent when a user cancels their booking.','events-manager').$bookings_placeholder_tip ?></em>
@@ -84,6 +76,14 @@
84
  em_options_input_text ( __( 'Booking cancelled email subject', 'events-manager'), 'dbem_bookings_email_cancelled_subject', $email_subject_tip );
85
  em_options_textarea ( __( 'Booking cancelled email', 'events-manager'), 'dbem_bookings_email_cancelled_body', '' );
86
  ?>
 
 
 
 
 
 
 
 
87
  </tbody>
88
  <?php do_action('em_options_page_booking_email_templates_options_bottom'); ?>
89
  <?php echo $save_button; ?>
68
  em_options_input_text ( __( 'Booking pending email subject', 'events-manager'), 'dbem_bookings_email_pending_subject', $email_subject_tip);
69
  em_options_textarea ( __( 'Booking pending email', 'events-manager'), 'dbem_bookings_email_pending_body','') ;
70
  ?>
 
 
 
 
 
 
 
 
71
  <tr class="em-subheader"><td colspan='2'>
72
  <h5><?php _e('Booking cancelled','events-manager') ?></h5>
73
  <em><?php echo __('This will be sent when a user cancels their booking.','events-manager').$bookings_placeholder_tip ?></em>
76
  em_options_input_text ( __( 'Booking cancelled email subject', 'events-manager'), 'dbem_bookings_email_cancelled_subject', $email_subject_tip );
77
  em_options_textarea ( __( 'Booking cancelled email', 'events-manager'), 'dbem_bookings_email_cancelled_body', '' );
78
  ?>
79
+ <tr class="em-subheader"><td colspan='2'>
80
+ <h5><?php _e('Rejected booking email','events-manager') ?></h5>
81
+ <em><?php echo __( 'This will be sent automatically when a booking is rejected. Not relevant if bookings don\'t require approval.', 'events-manager').$bookings_placeholder_tip ?></em>
82
+ </td></tr>
83
+ <?php
84
+ em_options_input_text ( __( 'Booking rejected email subject', 'events-manager'), 'dbem_bookings_email_rejected_subject', $email_subject_tip );
85
+ em_options_textarea ( __( 'Booking rejected email', 'events-manager'), 'dbem_bookings_email_rejected_body', '' );
86
+ ?>
87
  </tbody>
88
  <?php do_action('em_options_page_booking_email_templates_options_bottom'); ?>
89
  <?php echo $save_button; ?>
admin/settings/tabs/formats.php CHANGED
@@ -162,6 +162,14 @@
162
  em_options_radio_binary ( __( 'Link directly to event on day with single event?', 'events-manager'), 'dbem_calendar_direct_links', __( "If a calendar day has only one event, you can force a direct link to the event (recommended to avoid duplicate content).",'events-manager') );
163
  em_options_radio_binary ( __( 'Show list on day with single event?', 'events-manager'), 'dbem_display_calendar_day_single', __( "By default, if a calendar day only has one event, it display a single event when clicking on the link of that calendar date. If you select Yes here, you will get always see a list of events.",'events-manager') );
164
  ?>
 
 
 
 
 
 
 
 
165
  <tr class="em-header"><td colspan="2"><h4><?php _e('Small Calendar','events-manager'); ?></h4></td></tr>
166
  <?php
167
  em_options_input_text ( __( 'Month format', 'events-manager'), 'dbem_small_calendar_month_format', __('The format of the month/year header of the calendar.','events-manager').' '.$date_time_format_tip);
@@ -170,14 +178,6 @@
170
  em_options_radio_binary( __( 'Abbreviated weekdays', 'events-manager'), 'dbem_small_calendar_abbreviated_weekdays', __( 'The calendar headings uses abbreviated weekdays','events-manager') );
171
  em_options_input_text ( __( 'Initial lengths', 'events-manager'), 'dbem_small_calendar_initials_length', __( 'Shorten the calendar headings containing the days of the week, use 0 for the full name.', 'events-manager').$events_placeholder_tip );
172
  em_options_radio_binary( __( 'Show Long Events?', 'events-manager'), 'dbem_small_calendar_long_events', __( 'Events with multiple dates will appear on each of those dates in the calendar.','events-manager') );
173
- ?>
174
- <tr class="em-header"><td colspan="2"><h4><?php _e('Full Calendar','events-manager'); ?></h4></td></tr>
175
- <?php
176
- em_options_input_text ( __( 'Month format', 'events-manager'), 'dbem_full_calendar_month_format', __('The format of the month/year header of the calendar.','events-manager').' '.$date_time_format_tip);
177
- em_options_input_text ( __( 'Event format', 'events-manager'), 'dbem_full_calendar_event_format', __( 'The format of each event when displayed in the full calendar. Remember to include <code>li</code> tags before and after the event.', 'events-manager').$events_placeholder_tip );
178
- em_options_radio_binary( __( 'Abbreviated weekdays?', 'events-manager'), 'dbem_full_calendar_abbreviated_weekdays', __( 'Use abbreviations, e.g. Friday = Fri. Useful for certain languages where abbreviations differ from full names.','events-manager') );
179
- em_options_input_text ( __( 'Initial lengths', 'events-manager'), 'dbem_full_calendar_initials_length', __( 'Shorten the calendar headings containing the days of the week, use 0 for the full name.', 'events-manager').$events_placeholder_tip);
180
- em_options_radio_binary( __( 'Show Long Events?', 'events-manager'), 'dbem_full_calendar_long_events', __( 'Events with multiple dates will appear on each of those dates in the calendar.','events-manager') );
181
  ?>
182
  <tr class="em-header"><td colspan="2"><h4><?php echo __('Calendar Day Event List Settings','events-manager'); ?></h4></td></tr>
183
  <tr valign="top" id='dbem_display_calendar_orderby_row'>
@@ -334,6 +334,9 @@
334
  <div class="handlediv" title="<?php __('Click to toggle', 'events-manager'); ?>"><br /></div><h3><span><?php _e ( 'Event Tags', 'events-manager'); ?> </span></h3>
335
  <div class="inside">
336
  <table class="form-table">
 
 
 
337
  <tr class="em-header"><td colspan="2"><h4><?php echo sprintf(__('%s Page','events-manager'),__('Tags','events-manager')); ?></h4></td></tr>
338
  <?php
339
  em_options_textarea ( sprintf(__('%s list header format','events-manager'),__('Tags','events-manager')), 'dbem_tags_list_item_format_header', sprintf(__( 'This content will appear just above your code for the %s list format below. Default is blank', 'events-manager'), __('tags','events-manager')) );
162
  em_options_radio_binary ( __( 'Link directly to event on day with single event?', 'events-manager'), 'dbem_calendar_direct_links', __( "If a calendar day has only one event, you can force a direct link to the event (recommended to avoid duplicate content).",'events-manager') );
163
  em_options_radio_binary ( __( 'Show list on day with single event?', 'events-manager'), 'dbem_display_calendar_day_single', __( "By default, if a calendar day only has one event, it display a single event when clicking on the link of that calendar date. If you select Yes here, you will get always see a list of events.",'events-manager') );
164
  ?>
165
+ <tr class="em-header"><td colspan="2"><h4><?php _e('Full-Size Calendar','events-manager'); ?></h4></td></tr>
166
+ <?php
167
+ em_options_input_text ( __( 'Month format', 'events-manager'), 'dbem_full_calendar_month_format', __('The format of the month/year header of the calendar.','events-manager').' '.$date_time_format_tip);
168
+ em_options_input_text ( __( 'Event format', 'events-manager'), 'dbem_full_calendar_event_format', __( 'The format of each event when displayed in the full calendar. Remember to include <code>li</code> tags before and after the event.', 'events-manager').$events_placeholder_tip );
169
+ em_options_radio_binary( __( 'Abbreviated weekdays?', 'events-manager'), 'dbem_full_calendar_abbreviated_weekdays', __( 'Use abbreviations, e.g. Friday = Fri. Useful for certain languages where abbreviations differ from full names.','events-manager') );
170
+ em_options_input_text ( __( 'Initial lengths', 'events-manager'), 'dbem_full_calendar_initials_length', __( 'Shorten the calendar headings containing the days of the week, use 0 for the full name.', 'events-manager').$events_placeholder_tip);
171
+ em_options_radio_binary( __( 'Show Long Events?', 'events-manager'), 'dbem_full_calendar_long_events', __( 'Events with multiple dates will appear on each of those dates in the calendar.','events-manager') );
172
+ ?>
173
  <tr class="em-header"><td colspan="2"><h4><?php _e('Small Calendar','events-manager'); ?></h4></td></tr>
174
  <?php
175
  em_options_input_text ( __( 'Month format', 'events-manager'), 'dbem_small_calendar_month_format', __('The format of the month/year header of the calendar.','events-manager').' '.$date_time_format_tip);
178
  em_options_radio_binary( __( 'Abbreviated weekdays', 'events-manager'), 'dbem_small_calendar_abbreviated_weekdays', __( 'The calendar headings uses abbreviated weekdays','events-manager') );
179
  em_options_input_text ( __( 'Initial lengths', 'events-manager'), 'dbem_small_calendar_initials_length', __( 'Shorten the calendar headings containing the days of the week, use 0 for the full name.', 'events-manager').$events_placeholder_tip );
180
  em_options_radio_binary( __( 'Show Long Events?', 'events-manager'), 'dbem_small_calendar_long_events', __( 'Events with multiple dates will appear on each of those dates in the calendar.','events-manager') );
 
 
 
 
 
 
 
 
181
  ?>
182
  <tr class="em-header"><td colspan="2"><h4><?php echo __('Calendar Day Event List Settings','events-manager'); ?></h4></td></tr>
183
  <tr valign="top" id='dbem_display_calendar_orderby_row'>
334
  <div class="handlediv" title="<?php __('Click to toggle', 'events-manager'); ?>"><br /></div><h3><span><?php _e ( 'Event Tags', 'events-manager'); ?> </span></h3>
335
  <div class="inside">
336
  <table class="form-table">
337
+ <?php
338
+ em_options_input_text(sprintf(esc_html__('Default %s color','events-manager'), esc_html__('tag','events-manager')), 'dbem_tag_default_color', 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>'));
339
+ ?>
340
  <tr class="em-header"><td colspan="2"><h4><?php echo sprintf(__('%s Page','events-manager'),__('Tags','events-manager')); ?></h4></td></tr>
341
  <?php
342
  em_options_textarea ( sprintf(__('%s list header format','events-manager'),__('Tags','events-manager')), 'dbem_tags_list_item_format_header', sprintf(__( 'This content will appear just above your code for the %s list format below. Default is blank', 'events-manager'), __('tags','events-manager')) );
admin/settings/tabs/general.php CHANGED
@@ -87,7 +87,7 @@
87
 
88
  <?php if ( !is_multisite() ){ em_admin_option_box_image_sizes(); } ?>
89
 
90
- <?php if ( !is_multisite() || (is_super_admin() && !get_site_option('dbem_ms_global_caps')) ){ em_admin_option_box_caps(); } ?>
91
 
92
  <div class="postbox" id="em-opt-event-submissions" >
93
  <div class="handlediv" title="<?php __('Click to toggle', 'events-manager'); ?>"><br /></div><h3><span><?php _e ( 'Event Submission Forms', 'events-manager'); ?></span></h3>
87
 
88
  <?php if ( !is_multisite() ){ em_admin_option_box_image_sizes(); } ?>
89
 
90
+ <?php if ( !is_multisite() || (em_wp_is_super_admin() && !get_site_option('dbem_ms_global_caps')) ){ em_admin_option_box_caps(); } ?>
91
 
92
  <div class="postbox" id="em-opt-event-submissions" >
93
  <div class="handlediv" title="<?php __('Click to toggle', 'events-manager'); ?>"><br /></div><h3><span><?php _e ( 'Event Submission Forms', 'events-manager'); ?></span></h3>
admin/settings/tabs/pages.php CHANGED
@@ -64,7 +64,7 @@
64
  <tbody class="em-event-page-options">
65
  <?php
66
  em_options_radio_binary ( __( 'Show events search?', 'events-manager'), 'dbem_events_page_search_form', __( "If set to yes, a search form will appear just above your list of events.", 'events-manager') );
67
- em_options_radio_binary ( __( 'Display calendar in events page?', 'events-manager'), 'dbem_display_calendar_in_events_page', __( 'This options allows to display the calendar in the events page, instead of the default list. It is recommended not to display both the calendar widget and a calendar page.','events-manager').' '.__('If you would like to show events that span over more than one day, see the Calendar section on this page.','events-manager') );
68
  em_options_radio_binary ( __( 'Disable title rewriting?', 'events-manager'), 'dbem_disable_title_rewrites', __( "Some WordPress themes don't follow best practices when generating navigation menus, and so the automatic title rewriting feature may cause problems, if your menus aren't working correctly on the event pages, try setting this to 'Yes', and provide an appropriate HTML title format below.",'events-manager') );
69
  em_options_input_text ( __( 'Event Manager titles', 'events-manager'), 'dbem_title_html', __( "This only setting only matters if you selected 'Yes' to above. You will notice the events page titles aren't being rewritten, and you have a new title underneath the default page name. This is where you control the HTML of this title. Make sure you keep the #_PAGETITLE placeholder here, as that's what is rewritten by events manager. To control what's rewritten in this title, see settings further down for page titles.", 'events-manager') );
70
  ?>
@@ -143,14 +143,14 @@
143
  <td>
144
  <select name="dbem_events_default_orderby" >
145
  <?php
146
- $orderby_options = apply_filters('em_settings_events_default_orderby_ddm', array(
147
  'event_start_date,event_start_time,event_name' => __('Order by start date, start time, then event name','events-manager'),
148
  'event_name,event_start_date,event_start_time' => __('Order by name, start date, then start time','events-manager'),
149
  'event_name,event_end_date,event_end_time' => __('Order by name, end date, then end time','events-manager'),
150
  'event_end_date,event_end_time,event_name' => __('Order by end date, end time, then event name','events-manager'),
151
  ));
152
  ?>
153
- <?php foreach($orderby_options as $key => $value) : ?>
154
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_events_default_orderby')) ? "selected='selected'" : ''; ?>>
155
  <?php echo esc_html($value); ?>
156
  </option>
@@ -160,7 +160,7 @@
160
  <?php
161
  $ascending = __('Ascending','events-manager');
162
  $descending = __('Descending','events-manager');
163
- $order_options = apply_filters('em_settings_events_default_order_ddm', array(
164
  'ASC' => __('All Ascending','events-manager'),
165
  'DESC,ASC,ASC' => __("$descending, $ascending, $ascending",'events-manager'),
166
  'DESC,DESC,ASC' => __("$descending, $descending, $ascending",'events-manager'),
@@ -171,7 +171,7 @@
171
  'DESC,ASC,DESC' => __("$descending, $ascending, $descending",'events-manager'),
172
  ));
173
  ?>
174
- <?php foreach( $order_options as $key => $value) : ?>
175
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_events_default_order')) ? "selected='selected'" : ''; ?>>
176
  <?php echo esc_html($value); ?>
177
  </option>
@@ -202,6 +202,35 @@
202
  em_options_input_text( __('Post Classes','events-manager'), 'dbem_cp_locations_post_class', $post_class_tip );
203
  em_options_radio_binary ( __( 'Override with Formats?', 'events-manager'), 'dbem_cp_locations_formats', sprintf($format_override_tip,__('locations','events-manager')));
204
  em_options_radio_binary ( __( 'Enable Comments?', 'events-manager'), 'dbem_cp_locations_comments', sprintf(__('If you would like to disable comments entirely, disable this, otherwise you can disable comments on each single %s. Note that %s with comments enabled will still be until you resave them.','events-manager'),__('location','events-manager'),__('locations','events-manager')));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  em_options_input_text ( __( 'Event List Limits', 'events-manager'), 'dbem_location_event_list_limit', sprintf(__( "Controls how many events being held at a location are shown per page when using placeholders such as %s. Leave blank for no limit.", 'events-manager'), '<code>#_LOCATIONNEXTEVENTS</code>') );
206
  echo $save_button;
207
  ?>
@@ -242,13 +271,13 @@
242
  <td>
243
  <select name="dbem_locations_default_archive_orderby" >
244
  <?php
245
- $orderby_options = apply_filters('em_settings_locations_default_archive_orderby_ddm', array(
246
  '_location_country' => sprintf(__('Order by %s','events-manager'),__('Country','events-manager')),
247
  '_location_town' => sprintf(__('Order by %s','events-manager'),__('Town','events-manager')),
248
  'title' => sprintf(__('Order by %s','events-manager'),__('Name','events-manager'))
249
  ));
250
  ?>
251
- <?php foreach($orderby_options as $key => $value) : ?>
252
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_locations_default_archive_orderby')) ? "selected='selected'" : ''; ?>>
253
  <?php echo esc_html($value) ?>
254
  </option>
@@ -258,12 +287,12 @@
258
  <?php
259
  $ascending = __('Ascending','events-manager');
260
  $descending = __('Descending','events-manager');
261
- $order_options = apply_filters('em_settings_locations_default_archive_order_ddm', array(
262
  'ASC' => __('Ascending','events-manager'),
263
  'DESC' => __('Descending','events-manager')
264
  ));
265
  ?>
266
- <?php foreach( $order_options as $key => $value) : ?>
267
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_locations_default_archive_order')) ? "selected='selected'" : ''; ?>>
268
  <?php echo esc_html($value) ?>
269
  </option>
@@ -293,13 +322,13 @@
293
  <td>
294
  <select name="dbem_locations_default_orderby" >
295
  <?php
296
- $orderby_options = apply_filters('em_settings_locations_default_orderby_ddm', array(
297
  'location_country' => sprintf(__('Order by %s','events-manager'),__('Country','events-manager')),
298
  'location_town' => sprintf(__('Order by %s','events-manager'),__('Town','events-manager')),
299
  'location_name' => sprintf(__('Order by %s','events-manager'),__('Name','events-manager'))
300
  ));
301
  ?>
302
- <?php foreach($orderby_options as $key => $value) : ?>
303
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_locations_default_orderby')) ? "selected='selected'" : ''; ?>>
304
  <?php echo esc_html($value) ?>
305
  </option>
@@ -309,12 +338,12 @@
309
  <?php
310
  $ascending = __('Ascending','events-manager');
311
  $descending = __('Descending','events-manager');
312
- $order_options = apply_filters('em_settings_locations_default_order_ddm', array(
313
  'ASC' => __('Ascending','events-manager'),
314
  'DESC' => __('Descending','events-manager')
315
  ));
316
  ?>
317
- <?php foreach( $order_options as $key => $value) : ?>
318
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_locations_default_order')) ? "selected='selected'" : ''; ?>>
319
  <?php echo esc_html($value) ?>
320
  </option>
@@ -357,13 +386,14 @@
357
  <tr class="em-header">
358
  <td colspan="2">
359
  <h4><?php echo _e('General settings','events-manager'); ?></h4>
 
360
  </td>
361
  </tr>
362
  <?php
363
- em_options_radio_binary ( __( 'Override with Formats?', 'events-manager'), 'dbem_cp_categories_formats', sprintf($format_override_tip,__('categories','events-manager'))." ".__('Setting this to yes will make categories display as a page rather than an archive.', 'events-manager'));
364
  ?>
365
- <tr valign="top">
366
- <th scope="row"><?php _e('Default archive ordering','events-manager'); ?></th>
367
  <td>
368
  <select name="dbem_categories_default_archive_orderby" >
369
  <?php foreach($event_archive_orderby_options as $key => $value) : ?>
@@ -379,7 +409,7 @@
379
  </option>
380
  <?php endforeach; ?>
381
  </select>
382
- <br /><?php echo __('When listing events for a category, this order is applied.', 'events-manager'); ?>
383
  </td>
384
  </tr>
385
  <tr class="em-header">
@@ -393,7 +423,7 @@
393
  <td>
394
  <select name="dbem_categories_default_orderby" >
395
  <?php
396
- $orderby_options = apply_filters('em_settings_categories_default_orderby_ddm', array(
397
  'id' => sprintf(__('Order by %s','events-manager'),__('ID','events-manager')),
398
  'count' => sprintf(__('Order by %s','events-manager'),__('Count','events-manager')),
399
  'name' => sprintf(__('Order by %s','events-manager'),__('Name','events-manager')),
@@ -401,7 +431,7 @@
401
  'term_group' => sprintf(__('Order by %s','events-manager'),'term_group'),
402
  ));
403
  ?>
404
- <?php foreach($orderby_options as $key => $value) : ?>
405
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_categories_default_orderby')) ? "selected='selected'" : ''; ?>>
406
  <?php echo esc_html($value) ?>
407
  </option>
@@ -411,12 +441,12 @@
411
  <?php
412
  $ascending = __('Ascending','events-manager');
413
  $descending = __('Descending','events-manager');
414
- $order_options = apply_filters('em_settings_categories_default_order_ddm', array(
415
  'ASC' => __('Ascending','events-manager'),
416
  'DESC' => __('Descending','events-manager')
417
  ));
418
  ?>
419
- <?php foreach( $order_options as $key => $value) : ?>
420
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_categories_default_order')) ? "selected='selected'" : ''; ?>>
421
  <?php echo esc_html($value) ?>
422
  </option>
@@ -427,8 +457,37 @@
427
  </tr>
428
  <?php
429
  em_options_input_text ( __( 'List Limits', 'events-manager'), 'dbem_categories_default_limit', sprintf(__( "This will control how many %s are shown on one list by default.", 'events-manager'),__('categories','events-manager')) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
  em_options_input_text ( __( 'Event List Limits', 'events-manager'), 'dbem_category_event_list_limit', sprintf(__( "Controls how many events belonging to a category are shown per page when using placeholders such as %s. Leave blank for no limit.", 'events-manager'), '<code>#_CATEGORYNEXTEVENTS</code>') );
431
- echo $save_button;
432
  ?>
433
  </table>
434
  </div> <!-- . inside -->
@@ -461,13 +520,14 @@
461
  <tr class="em-header">
462
  <td colspan="2">
463
  <h4><?php echo _e('General settings','events-manager'); ?></h4>
 
464
  </td>
465
  </tr>
466
  <?php
467
- em_options_radio_binary ( __( 'Override with Formats?', 'events-manager'), 'dbem_cp_tags_formats', sprintf($format_override_tip,__('tags','events-manager')));
468
  ?>
469
- <tr valign="top">
470
- <th scope="row"><?php _e('Default archive ordering','events-manager'); ?></th>
471
  <td>
472
  <select name="dbem_tags_default_archive_orderby" >
473
  <?php foreach($event_archive_orderby_options as $key => $value) : ?>
@@ -483,6 +543,7 @@
483
  </option>
484
  <?php endforeach; ?>
485
  </select>
 
486
  </td>
487
  </tr>
488
  <tr class="em-header">
@@ -496,7 +557,7 @@
496
  <td>
497
  <select name="dbem_tags_default_orderby" >
498
  <?php
499
- $orderby_options = apply_filters('em_settings_tags_default_orderby_ddm', array(
500
  'id' => sprintf(__('Order by %s','events-manager'),__('ID','events-manager')),
501
  'count' => sprintf(__('Order by %s','events-manager'),__('Count','events-manager')),
502
  'name' => sprintf(__('Order by %s','events-manager'),__('Name','events-manager')),
@@ -504,7 +565,7 @@
504
  'term_group' => sprintf(__('Order by %s','events-manager'),'term_group'),
505
  ));
506
  ?>
507
- <?php foreach($orderby_options as $key => $value) : ?>
508
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_tags_default_orderby')) ? "selected='selected'" : ''; ?>>
509
  <?php echo esc_html($value) ?>
510
  </option>
@@ -514,12 +575,12 @@
514
  <?php
515
  $ascending = __('Ascending','events-manager');
516
  $descending = __('Descending','events-manager');
517
- $order_options = apply_filters('em_settings_tags_default_order_ddm', array(
518
  'ASC' => __('Ascending','events-manager'),
519
  'DESC' => __('Descending','events-manager')
520
  ));
521
  ?>
522
- <?php foreach( $order_options as $key => $value) : ?>
523
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_tags_default_order')) ? "selected='selected'" : ''; ?>>
524
  <?php echo esc_html($value) ?>
525
  </option>
@@ -530,6 +591,35 @@
530
  </tr>
531
  <?php
532
  em_options_input_text ( __( 'List Limits', 'events-manager'), 'dbem_tags_default_limit', sprintf(__( "This will control how many %s are shown on one list by default.", 'events-manager'),__('tags','events-manager')) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
533
  em_options_input_text ( __( 'Event List Limits', 'events-manager'), 'dbem_tag_event_list_limit', sprintf(__( "Controls how many events belonging to a tag are shown per page when using placeholders such as %s. Leave blank for no limit.", 'events-manager'), '<code>#_TAGNEXTEVENTS</code>') );
534
  echo $save_button; ?>
535
  </table>
64
  <tbody class="em-event-page-options">
65
  <?php
66
  em_options_radio_binary ( __( 'Show events search?', 'events-manager'), 'dbem_events_page_search_form', __( "If set to yes, a search form will appear just above your list of events.", 'events-manager') );
67
+ em_options_radio_binary ( __( 'Display calendar in events page?', 'events-manager'), 'dbem_display_calendar_in_events_page', __( 'This options allows to display the full-sized calendar on the events page, instead of the default list.','events-manager') );
68
  em_options_radio_binary ( __( 'Disable title rewriting?', 'events-manager'), 'dbem_disable_title_rewrites', __( "Some WordPress themes don't follow best practices when generating navigation menus, and so the automatic title rewriting feature may cause problems, if your menus aren't working correctly on the event pages, try setting this to 'Yes', and provide an appropriate HTML title format below.",'events-manager') );
69
  em_options_input_text ( __( 'Event Manager titles', 'events-manager'), 'dbem_title_html', __( "This only setting only matters if you selected 'Yes' to above. You will notice the events page titles aren't being rewritten, and you have a new title underneath the default page name. This is where you control the HTML of this title. Make sure you keep the #_PAGETITLE placeholder here, as that's what is rewritten by events manager. To control what's rewritten in this title, see settings further down for page titles.", 'events-manager') );
70
  ?>
143
  <td>
144
  <select name="dbem_events_default_orderby" >
145
  <?php
146
+ $event_list_orderby_options = apply_filters('em_settings_events_default_orderby_ddm', array(
147
  'event_start_date,event_start_time,event_name' => __('Order by start date, start time, then event name','events-manager'),
148
  'event_name,event_start_date,event_start_time' => __('Order by name, start date, then start time','events-manager'),
149
  'event_name,event_end_date,event_end_time' => __('Order by name, end date, then end time','events-manager'),
150
  'event_end_date,event_end_time,event_name' => __('Order by end date, end time, then event name','events-manager'),
151
  ));
152
  ?>
153
+ <?php foreach($event_list_orderby_options as $key => $value) : ?>
154
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_events_default_orderby')) ? "selected='selected'" : ''; ?>>
155
  <?php echo esc_html($value); ?>
156
  </option>
160
  <?php
161
  $ascending = __('Ascending','events-manager');
162
  $descending = __('Descending','events-manager');
163
+ $event_list_order_options = apply_filters('em_settings_events_default_order_ddm', array(
164
  'ASC' => __('All Ascending','events-manager'),
165
  'DESC,ASC,ASC' => __("$descending, $ascending, $ascending",'events-manager'),
166
  'DESC,DESC,ASC' => __("$descending, $descending, $ascending",'events-manager'),
171
  'DESC,ASC,DESC' => __("$descending, $ascending, $descending",'events-manager'),
172
  ));
173
  ?>
174
+ <?php foreach( $event_list_order_options as $key => $value) : ?>
175
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_events_default_order')) ? "selected='selected'" : ''; ?>>
176
  <?php echo esc_html($value); ?>
177
  </option>
202
  em_options_input_text( __('Post Classes','events-manager'), 'dbem_cp_locations_post_class', $post_class_tip );
203
  em_options_radio_binary ( __( 'Override with Formats?', 'events-manager'), 'dbem_cp_locations_formats', sprintf($format_override_tip,__('locations','events-manager')));
204
  em_options_radio_binary ( __( 'Enable Comments?', 'events-manager'), 'dbem_cp_locations_comments', sprintf(__('If you would like to disable comments entirely, disable this, otherwise you can disable comments on each single %s. Note that %s with comments enabled will still be until you resave them.','events-manager'),__('location','events-manager'),__('locations','events-manager')));
205
+ ?>
206
+ <tr class="em-header">
207
+ <td colspan="2">
208
+ <h4><?php echo sprintf(esc_html__('Default %s list options','events-manager'), __('event','events-manager')); ?></h4>
209
+ <p><?php echo sprintf(esc_html__('The options below are applied to the %s placeholders.', 'events-manager'), '<code>#_LOCATIONNEXTEVENTS</code>, <code>#_LOCATIONPASTEVENTS</code>, <code>#_LOCATIONALLEVENTS</code>'); ?></p>
210
+ </td>
211
+ </tr>
212
+ <tr valign="top" id='dbem_location_events_default_orderby_row'>
213
+ <th scope="row"><?php _e('Default event list ordering','events-manager'); ?></th>
214
+ <td>
215
+ <select name="dbem_location_event_list_orderby" >
216
+ <?php foreach($event_list_orderby_options as $key => $value) : ?>
217
+ <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_location_event_list_orderby')) ? "selected='selected'" : ''; ?>>
218
+ <?php echo esc_html($value); ?>
219
+ </option>
220
+ <?php endforeach; ?>
221
+ </select>
222
+ <select name="dbem_location_event_list_order" >
223
+ <?php foreach( $event_list_order_options as $key => $value) : ?>
224
+ <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_location_event_list_order')) ? "selected='selected'" : ''; ?>>
225
+ <?php echo esc_html($value); ?>
226
+ </option>
227
+ <?php endforeach; ?>
228
+ </select>
229
+ <br/>
230
+ <em><?php _e('When Events Manager displays lists of events the default behavior is ordering by start date in ascending order. To change this, modify the values above.','events-manager'); ?></em>
231
+ </td>
232
+ </tr>
233
+ <?php
234
  em_options_input_text ( __( 'Event List Limits', 'events-manager'), 'dbem_location_event_list_limit', sprintf(__( "Controls how many events being held at a location are shown per page when using placeholders such as %s. Leave blank for no limit.", 'events-manager'), '<code>#_LOCATIONNEXTEVENTS</code>') );
235
  echo $save_button;
236
  ?>
271
  <td>
272
  <select name="dbem_locations_default_archive_orderby" >
273
  <?php
274
+ $locations_list_orderby_options = apply_filters('em_settings_locations_default_archive_orderby_ddm', array(
275
  '_location_country' => sprintf(__('Order by %s','events-manager'),__('Country','events-manager')),
276
  '_location_town' => sprintf(__('Order by %s','events-manager'),__('Town','events-manager')),
277
  'title' => sprintf(__('Order by %s','events-manager'),__('Name','events-manager'))
278
  ));
279
  ?>
280
+ <?php foreach($locations_list_orderby_options as $key => $value) : ?>
281
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_locations_default_archive_orderby')) ? "selected='selected'" : ''; ?>>
282
  <?php echo esc_html($value) ?>
283
  </option>
287
  <?php
288
  $ascending = __('Ascending','events-manager');
289
  $descending = __('Descending','events-manager');
290
+ $locations_list_order_options = apply_filters('em_settings_locations_default_archive_order_ddm', array(
291
  'ASC' => __('Ascending','events-manager'),
292
  'DESC' => __('Descending','events-manager')
293
  ));
294
  ?>
295
+ <?php foreach( $locations_list_order_options as $key => $value) : ?>
296
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_locations_default_archive_order')) ? "selected='selected'" : ''; ?>>
297
  <?php echo esc_html($value) ?>
298
  </option>
322
  <td>
323
  <select name="dbem_locations_default_orderby" >
324
  <?php
325
+ $locations_list_orderby_options = apply_filters('em_settings_locations_default_orderby_ddm', array(
326
  'location_country' => sprintf(__('Order by %s','events-manager'),__('Country','events-manager')),
327
  'location_town' => sprintf(__('Order by %s','events-manager'),__('Town','events-manager')),
328
  'location_name' => sprintf(__('Order by %s','events-manager'),__('Name','events-manager'))
329
  ));
330
  ?>
331
+ <?php foreach($locations_list_orderby_options as $key => $value) : ?>
332
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_locations_default_orderby')) ? "selected='selected'" : ''; ?>>
333
  <?php echo esc_html($value) ?>
334
  </option>
338
  <?php
339
  $ascending = __('Ascending','events-manager');
340
  $descending = __('Descending','events-manager');
341
+ $locations_list_order_options = apply_filters('em_settings_locations_default_order_ddm', array(
342
  'ASC' => __('Ascending','events-manager'),
343
  'DESC' => __('Descending','events-manager')
344
  ));
345
  ?>
346
+ <?php foreach( $locations_list_order_options as $key => $value) : ?>
347
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_locations_default_order')) ? "selected='selected'" : ''; ?>>
348
  <?php echo esc_html($value) ?>
349
  </option>
386
  <tr class="em-header">
387
  <td colspan="2">
388
  <h4><?php echo _e('General settings','events-manager'); ?></h4>
389
+ <p><?php echo esc_html(sprintf(__('Viewing a general WordPress taxonomy page such as %s will show a list of events just like it would regular posts for a regular category or tag. Below you can edit things such as the order events are displayed, or completely override the archive look with our formats feature.','events-manager'), __('categories', 'events-manager'))); ?></p>
390
  </td>
391
  </tr>
392
  <?php
393
+ em_options_radio_binary ( __( 'Override with Formats?', 'events-manager'), 'dbem_cp_categories_formats', sprintf($format_override_tip,__('categories','events-manager'))." ".__('Setting this to yes will make categories display as a page rather than an archive.', 'events-manager'), '', '.em-default-categories-archive-ordering', true);
394
  ?>
395
+ <tr valign="top" class="em-default-categories-archive-ordering">
396
+ <th scope="row"><?php _e('Default event archive ordering','events-manager'); ?></th>
397
  <td>
398
  <select name="dbem_categories_default_archive_orderby" >
399
  <?php foreach($event_archive_orderby_options as $key => $value) : ?>
409
  </option>
410
  <?php endforeach; ?>
411
  </select>
412
+ <br /><?php echo esc_html(sprintf(__('When listing event archives for a %s, this order is applied.', 'events-manager'), __('category', 'events-manager'))); ?>
413
  </td>
414
  </tr>
415
  <tr class="em-header">
423
  <td>
424
  <select name="dbem_categories_default_orderby" >
425
  <?php
426
+ $categories_list_orderby_options = apply_filters('em_settings_categories_default_orderby_ddm', array(
427
  'id' => sprintf(__('Order by %s','events-manager'),__('ID','events-manager')),
428
  'count' => sprintf(__('Order by %s','events-manager'),__('Count','events-manager')),
429
  'name' => sprintf(__('Order by %s','events-manager'),__('Name','events-manager')),
431
  'term_group' => sprintf(__('Order by %s','events-manager'),'term_group'),
432
  ));
433
  ?>
434
+ <?php foreach($categories_list_orderby_options as $key => $value) : ?>
435
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_categories_default_orderby')) ? "selected='selected'" : ''; ?>>
436
  <?php echo esc_html($value) ?>
437
  </option>
441
  <?php
442
  $ascending = __('Ascending','events-manager');
443
  $descending = __('Descending','events-manager');
444
+ $categories_list_order_options = apply_filters('em_settings_categories_default_order_ddm', array(
445
  'ASC' => __('Ascending','events-manager'),
446
  'DESC' => __('Descending','events-manager')
447
  ));
448
  ?>
449
+ <?php foreach( $categories_list_order_options as $key => $value) : ?>
450
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_categories_default_order')) ? "selected='selected'" : ''; ?>>
451
  <?php echo esc_html($value) ?>
452
  </option>
457
  </tr>
458
  <?php
459
  em_options_input_text ( __( 'List Limits', 'events-manager'), 'dbem_categories_default_limit', sprintf(__( "This will control how many %s are shown on one list by default.", 'events-manager'),__('categories','events-manager')) );
460
+ ?>
461
+ <tr class="em-header">
462
+ <td colspan="2">
463
+ <h4><?php echo sprintf(esc_html__('Default %s list options','events-manager'), __('event','events-manager')); ?></h4>
464
+ <p><?php echo sprintf(esc_html__('The options below are applied to the %s placeholders.', 'events-manager'), '<code>#_CATEGORYPASTEVENTS</code>, <code>#_CATEGORYNEXTEVENTS</code>, <code>#_CATEGORYALLEVENTS</code>'); ?></p>
465
+ </td>
466
+ </tr>
467
+ <tr valign="top" id='dbem_category_events_default_orderby_row'>
468
+ <th scope="row"><?php _e('Default event list ordering','events-manager'); ?></th>
469
+ <td>
470
+ <select name="dbem_category_event_list_orderby" >
471
+ <?php foreach($event_list_orderby_options as $key => $value) : ?>
472
+ <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_category_event_list_orderby')) ? "selected='selected'" : ''; ?>>
473
+ <?php echo esc_html($value); ?>
474
+ </option>
475
+ <?php endforeach; ?>
476
+ </select>
477
+ <select name="dbem_category_event_list_order" >
478
+ <?php foreach( $event_list_order_options as $key => $value) : ?>
479
+ <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_category_event_list_order')) ? "selected='selected'" : ''; ?>>
480
+ <?php echo esc_html($value); ?>
481
+ </option>
482
+ <?php endforeach; ?>
483
+ </select>
484
+ <br/>
485
+ <em><?php _e('When Events Manager displays lists of events the default behavior is ordering by start date in ascending order. To change this, modify the values above.','events-manager'); ?></em>
486
+ </td>
487
+ </tr>
488
+ <?php
489
  em_options_input_text ( __( 'Event List Limits', 'events-manager'), 'dbem_category_event_list_limit', sprintf(__( "Controls how many events belonging to a category are shown per page when using placeholders such as %s. Leave blank for no limit.", 'events-manager'), '<code>#_CATEGORYNEXTEVENTS</code>') );
490
+ echo $save_button;
491
  ?>
492
  </table>
493
  </div> <!-- . inside -->
520
  <tr class="em-header">
521
  <td colspan="2">
522
  <h4><?php echo _e('General settings','events-manager'); ?></h4>
523
+ <p><?php echo esc_html(sprintf(__('Viewing a general WordPress taxonomy page such as %s will show a list of events just like it would regular posts for a regular category or tag. Below you can edit things such as the order events are displayed, or completely override the archive look with our formats feature.','events-manager'), __('tags', 'events-manager'))); ?></p>
524
  </td>
525
  </tr>
526
  <?php
527
+ em_options_radio_binary ( __( 'Override with Formats?', 'events-manager'), 'dbem_cp_tags_formats', sprintf($format_override_tip,__('tags','events-manager')), '', '.em-default-tags-archive-ordering', true);
528
  ?>
529
+ <tr valign="top" class="em-default-tags-archive-ordering">
530
+ <th scope="row"><?php _e('Default event archive ordering','events-manager'); ?></th>
531
  <td>
532
  <select name="dbem_tags_default_archive_orderby" >
533
  <?php foreach($event_archive_orderby_options as $key => $value) : ?>
543
  </option>
544
  <?php endforeach; ?>
545
  </select>
546
+ <br /><?php echo esc_html(sprintf(__('When listing event archives for a %s, this order is applied.', 'events-manager'), __('tag', 'events-manager'))); ?>
547
  </td>
548
  </tr>
549
  <tr class="em-header">
557
  <td>
558
  <select name="dbem_tags_default_orderby" >
559
  <?php
560
+ $tags_list_orderby_options = apply_filters('em_settings_tags_default_orderby_ddm', array(
561
  'id' => sprintf(__('Order by %s','events-manager'),__('ID','events-manager')),
562
  'count' => sprintf(__('Order by %s','events-manager'),__('Count','events-manager')),
563
  'name' => sprintf(__('Order by %s','events-manager'),__('Name','events-manager')),
565
  'term_group' => sprintf(__('Order by %s','events-manager'),'term_group'),
566
  ));
567
  ?>
568
+ <?php foreach($tags_list_orderby_options as $key => $value) : ?>
569
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_tags_default_orderby')) ? "selected='selected'" : ''; ?>>
570
  <?php echo esc_html($value) ?>
571
  </option>
575
  <?php
576
  $ascending = __('Ascending','events-manager');
577
  $descending = __('Descending','events-manager');
578
+ $tags_list_order_options = apply_filters('em_settings_tags_default_order_ddm', array(
579
  'ASC' => __('Ascending','events-manager'),
580
  'DESC' => __('Descending','events-manager')
581
  ));
582
  ?>
583
+ <?php foreach( $tags_list_order_options as $key => $value) : ?>
584
  <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_tags_default_order')) ? "selected='selected'" : ''; ?>>
585
  <?php echo esc_html($value) ?>
586
  </option>
591
  </tr>
592
  <?php
593
  em_options_input_text ( __( 'List Limits', 'events-manager'), 'dbem_tags_default_limit', sprintf(__( "This will control how many %s are shown on one list by default.", 'events-manager'),__('tags','events-manager')) );
594
+ ?>
595
+ <tr class="em-header">
596
+ <td colspan="2">
597
+ <h4><?php echo sprintf(esc_html__('Default %s list options','events-manager'), __('event','events-manager')); ?></h4>
598
+ <p><?php echo sprintf(esc_html__('The options below are applied to the %s placeholders.', 'events-manager'), '<code>#_TAGPASTEVENTS</code>, <code>#_TAGNEXTEVENTS</code>, <code>#_TAGALLEVENTS</code>'); ?></p>
599
+ </td>
600
+ </tr>
601
+ <tr valign="top" id='dbem_tag_events_default_orderby_row'>
602
+ <th scope="row"><?php _e('Default event list ordering','events-manager'); ?></th>
603
+ <td>
604
+ <select name="dbem_tag_event_list_orderby" >
605
+ <?php foreach($event_list_orderby_options as $key => $value) : ?>
606
+ <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_tag_event_list_orderby')) ? "selected='selected'" : ''; ?>>
607
+ <?php echo esc_html($value); ?>
608
+ </option>
609
+ <?php endforeach; ?>
610
+ </select>
611
+ <select name="dbem_tag_event_list_order" >
612
+ <?php foreach( $event_list_order_options as $key => $value) : ?>
613
+ <option value='<?php echo esc_attr($key) ?>' <?php echo ($key == get_option('dbem_tag_event_list_order')) ? "selected='selected'" : ''; ?>>
614
+ <?php echo esc_html($value); ?>
615
+ </option>
616
+ <?php endforeach; ?>
617
+ </select>
618
+ <br/>
619
+ <em><?php _e('When Events Manager displays lists of events the default behavior is ordering by start date in ascending order. To change this, modify the values above.','events-manager'); ?></em>
620
+ </td>
621
+ </tr>
622
+ <?php
623
  em_options_input_text ( __( 'Event List Limits', 'events-manager'), 'dbem_tag_event_list_limit', sprintf(__( "Controls how many events belonging to a tag are shown per page when using placeholders such as %s. Leave blank for no limit.", 'events-manager'), '<code>#_TAGNEXTEVENTS</code>') );
624
  echo $save_button; ?>
625
  </table>
buddypress/bp-em-notifications.php CHANGED
@@ -54,10 +54,8 @@ function bp_em_format_notifications( $action, $item_id, $secondary_item_id, $tot
54
  */
55
  function bp_em_remove_screen_notifications() {
56
  global $bp;
57
- if( function_exists('bp_notifications_delete_notifications_by_type') ){ //backwards compat for BP 1.9
58
  bp_notifications_delete_notifications_by_type( $bp->loggedin_user->id, $bp->events->slug, 'attending' );
59
- }else{
60
- bp_core_delete_notifications_by_type( $bp->loggedin_user->id, $bp->events->slug, 'attending' );
61
  }
62
  }
63
  add_action( 'bp_em_my_events', 'bp_em_remove_screen_notifications' );
54
  */
55
  function bp_em_remove_screen_notifications() {
56
  global $bp;
57
+ if( function_exists('bp_notifications_delete_notifications_by_type') ){
58
  bp_notifications_delete_notifications_by_type( $bp->loggedin_user->id, $bp->events->slug, 'attending' );
 
 
59
  }
60
  }
61
  add_action( 'bp_em_my_events', 'bp_em_remove_screen_notifications' );
classes/em-booking.php CHANGED
@@ -102,7 +102,7 @@ class EM_Booking extends EM_Object{
102
  var $manage_override;
103
 
104
  /**
105
- * Creates booking object and retreives booking data (default is a blank booking object). Accepts either array of booking data (from db) or a booking id.
106
  * @param mixed $booking_data
107
  * @return null
108
  */
@@ -115,7 +115,7 @@ class EM_Booking extends EM_Object{
115
  if( is_array($booking_data) ){
116
  $booking = $booking_data;
117
  }elseif( is_numeric($booking_data) ){
118
- //Retreiving from the database
119
  $sql = "SELECT * FROM ". EM_BOOKINGS_TABLE ." WHERE booking_id ='$booking_data'";
120
  $booking = $wpdb->get_row($sql, ARRAY_A);
121
  }
@@ -140,7 +140,7 @@ class EM_Booking extends EM_Object{
140
  //do some legacy checking here for bookings made prior to 5.4, due to how taxes are calculated
141
  $this->get_tax_rate();
142
  if( !empty($this->legacy_tax_rate) ){
143
- //reset booking_price, it'll be recalculated later (if you're using this property directly, don't, use $this->get_price())
144
  $this->booking_price = $this->booking_taxes = null;
145
  }
146
  do_action('em_booking', $this, $booking_data);
@@ -224,7 +224,7 @@ class EM_Booking extends EM_Object{
224
  }
225
 
226
  /**
227
- * Load an record into this object by passing an associative array of table criterie to search for.
228
  * Returns boolean depending on whether a record is found or not.
229
  * @param $search
230
  * @return boolean
@@ -285,7 +285,7 @@ class EM_Booking extends EM_Object{
285
  //get person
286
  $this->get_person();
287
  //re-run compatiblity keys function
288
- $this->compat_keys(); //depricating in 6.0
289
  }
290
  return apply_filters('em_booking_get_post',count($this->errors) == 0,$this);
291
  }
@@ -329,7 +329,7 @@ class EM_Booking extends EM_Object{
329
  }
330
 
331
  /**
332
- * Get the total number of spaces booked in THIS booking. Seting $force_refresh to true will recheck spaces, even if previously done so.
333
  * @param unknown_type $force_refresh
334
  * @return mixed
335
  */
@@ -376,12 +376,14 @@ class EM_Booking extends EM_Object{
376
  return $price;
377
  }
378
 
379
- function get_price_pre_taxes( $format = false ){
380
  $price = $base_price = $this->get_price_base();
381
  //apply pre-tax discounts
382
- $price -= $this->get_price_adjustments_amount('discounts', 'pre', $base_price);
383
- $price += $this->get_price_adjustments_amount('surcharges', 'pre', $base_price);
384
- $price = apply_filters('em_booking_get_price_pre_taxes', $price, $base_price, $this);
 
 
385
  if( $price < 0 ){ $price = 0; } //no negative prices
386
  //return amount of taxes applied, formatted or not
387
  if( $format ) return $this->format_price($price);
@@ -393,9 +395,9 @@ class EM_Booking extends EM_Object{
393
  * @param boolean $format
394
  * @return double|string
395
  */
396
- function get_price_post_taxes( $format = false ){
397
  //get price before taxes
398
- $price = $this->get_price_pre_taxes();
399
  //add taxes to price
400
  if( $this->get_tax_rate() > 0 ){
401
  $this->booking_taxes = $price * ($this->get_tax_rate()/100); //calculate and save tax amount
@@ -404,9 +406,11 @@ class EM_Booking extends EM_Object{
404
  }
405
  //apply post-tax discounts
406
  $price_after_taxes = $price;
407
- $price -= $this->get_price_adjustments_amount('discounts', 'post', $price_after_taxes);
408
- $price += $this->get_price_adjustments_amount('surcharges', 'post', $price_after_taxes);
409
- $price = apply_filters('em_booking_get_price_post_taxes', $price, $price_after_taxes, $this);
 
 
410
  if( $price < 0 ){ $price = 0; } //no negative prices
411
  //return amount of taxes applied, formatted or not
412
  if( $format ) return $this->format_price($price);
@@ -625,7 +629,7 @@ class EM_Booking extends EM_Object{
625
  /* Get Objects linked to booking */
626
 
627
  /**
628
- * Gets the event this booking belongs to and saves a refernece in the event property
629
  * @return EM_Event
630
  */
631
  function get_event(){
@@ -843,7 +847,7 @@ class EM_Booking extends EM_Object{
843
 
844
  function cancel($email = true){
845
  if( $this->person->ID == get_current_user_id() ){
846
- $this->manage_override = true; //normally, users can't manage a bookiing, only event owners, so we allow them to mod their booking status in this case only.
847
  }
848
  return $this->set_status(3, $email);
849
  }
102
  var $manage_override;
103
 
104
  /**
105
+ * Creates booking object and retrieves booking data (default is a blank booking object). Accepts either array of booking data (from db) or a booking id.
106
  * @param mixed $booking_data
107
  * @return null
108
  */
115
  if( is_array($booking_data) ){
116
  $booking = $booking_data;
117
  }elseif( is_numeric($booking_data) ){
118
+ //Retrieving from the database
119
  $sql = "SELECT * FROM ". EM_BOOKINGS_TABLE ." WHERE booking_id ='$booking_data'";
120
  $booking = $wpdb->get_row($sql, ARRAY_A);
121
  }
140
  //do some legacy checking here for bookings made prior to 5.4, due to how taxes are calculated
141
  $this->get_tax_rate();
142
  if( !empty($this->legacy_tax_rate) ){
143
+ //reset booking_price, it'll be recalculated later (if you're using this property directly, don't use $this->get_price())
144
  $this->booking_price = $this->booking_taxes = null;
145
  }
146
  do_action('em_booking', $this, $booking_data);
224
  }
225
 
226
  /**
227
+ * Load an record into this object by passing an associative array of table criteria to search for.
228
  * Returns boolean depending on whether a record is found or not.
229
  * @param $search
230
  * @return boolean
285
  //get person
286
  $this->get_person();
287
  //re-run compatiblity keys function
288
+ $this->compat_keys(); //depracating in 6.0
289
  }
290
  return apply_filters('em_booking_get_post',count($this->errors) == 0,$this);
291
  }
329
  }
330
 
331
  /**
332
+ * Get the total number of spaces booked in THIS booking. Setting $force_refresh to true will recheck spaces, even if previously done so.
333
  * @param unknown_type $force_refresh
334
  * @return mixed
335
  */
376
  return $price;
377
  }
378
 
379
+ function get_price_pre_taxes( $format = false, $include_adjustments = true ){
380
  $price = $base_price = $this->get_price_base();
381
  //apply pre-tax discounts
382
+ if( $include_adjustments ){
383
+ $price -= $this->get_price_adjustments_amount('discounts', 'pre', $base_price);
384
+ $price += $this->get_price_adjustments_amount('surcharges', 'pre', $base_price);
385
+ }
386
+ $price = apply_filters('em_booking_get_price_pre_taxes', $price, $base_price, $this, $include_adjustments);
387
  if( $price < 0 ){ $price = 0; } //no negative prices
388
  //return amount of taxes applied, formatted or not
389
  if( $format ) return $this->format_price($price);
395
  * @param boolean $format
396
  * @return double|string
397
  */
398
+ function get_price_post_taxes( $format = false, $include_adjustments = true ){
399
  //get price before taxes
400
+ $price = $this->get_price_pre_taxes( false, $include_adjustments );
401
  //add taxes to price
402
  if( $this->get_tax_rate() > 0 ){
403
  $this->booking_taxes = $price * ($this->get_tax_rate()/100); //calculate and save tax amount
406
  }
407
  //apply post-tax discounts
408
  $price_after_taxes = $price;
409
+ if( $include_adjustments ){
410
+ $price -= $this->get_price_adjustments_amount('discounts', 'post', $price_after_taxes);
411
+ $price += $this->get_price_adjustments_amount('surcharges', 'post', $price_after_taxes);
412
+ }
413
+ $price = apply_filters('em_booking_get_price_post_taxes', $price, $price_after_taxes, $this, $include_adjustments);
414
  if( $price < 0 ){ $price = 0; } //no negative prices
415
  //return amount of taxes applied, formatted or not
416
  if( $format ) return $this->format_price($price);
629
  /* Get Objects linked to booking */
630
 
631
  /**
632
+ * Gets the event this booking belongs to and saves a reference in the event property
633
  * @return EM_Event
634
  */
635
  function get_event(){
847
 
848
  function cancel($email = true){
849
  if( $this->person->ID == get_current_user_id() ){
850
+ $this->manage_override = true; //normally, users can't manage a booking, only event owners, so we allow them to mod their booking status in this case only.
851
  }
852
  return $this->set_status(3, $email);
853
  }
classes/em-bookings-table.php CHANGED
@@ -69,8 +69,8 @@ class EM_Bookings_Table{
69
  $this->limit = ( !empty($_REQUEST['limit']) && is_numeric($_REQUEST['limit'])) ? $_REQUEST['limit'] : 20;//Default limit
70
  $this->page = ( !empty($_REQUEST['pno']) && is_numeric($_REQUEST['pno']) ) ? $_REQUEST['pno']:1;
71
  $this->offset = ( $this->page > 1 ) ? ($this->page-1)*$this->limit : 0;
72
- $this->scope = ( !empty($_REQUEST['scope']) && array_key_exists($_REQUEST ['scope'], em_get_scopes()) ) ? sanitize_text_field($_REQUEST['scope']):get_option('dbem_default_bookings_search','future');
73
- $this->status = ( !empty($_REQUEST['status']) && array_key_exists($_REQUEST['status'], $this->statuses) ) ? sanitize_text_field($_REQUEST['status']):get_option('dbem_default_bookings_search','needs-attention');
74
  //build template of possible collumns
75
  $this->cols_template = apply_filters('em_bookings_table_cols_template', array(
76
  'user_name'=>__('Name','events-manager'),
69
  $this->limit = ( !empty($_REQUEST['limit']) && is_numeric($_REQUEST['limit'])) ? $_REQUEST['limit'] : 20;//Default limit
70
  $this->page = ( !empty($_REQUEST['pno']) && is_numeric($_REQUEST['pno']) ) ? $_REQUEST['pno']:1;
71
  $this->offset = ( $this->page > 1 ) ? ($this->page-1)*$this->limit : 0;
72
+ $this->scope = ( !empty($_REQUEST['scope']) && array_key_exists($_REQUEST ['scope'], em_get_scopes()) ) ? sanitize_text_field($_REQUEST['scope']):'future';
73
+ $this->status = ( !empty($_REQUEST['status']) && array_key_exists($_REQUEST['status'], $this->statuses) ) ? sanitize_text_field($_REQUEST['status']):'needs-attention';
74
  //build template of possible collumns
75
  $this->cols_template = apply_filters('em_bookings_table_cols_template', array(
76
  'user_name'=>__('Name','events-manager'),
classes/em-bookings.php CHANGED
@@ -10,7 +10,7 @@ class EM_Bookings extends EM_Object implements Iterator{
10
  * Array of EM_Booking objects for a specific event
11
  * @var array
12
  */
13
- var $bookings = array();
14
  /**
15
  * @var EM_Tickets
16
  */
@@ -39,6 +39,10 @@ class EM_Bookings extends EM_Object implements Iterator{
39
  */
40
  public static $disable_restrictions = false;
41
 
 
 
 
 
42
  /**
43
  * Creates an EM_Bookings instance, currently accepts an EM_Event object (gets all bookings for that event) or array of any EM_Booking objects, which can be manipulated in bulk with helper functions.
44
  * @param EM_Event $event
@@ -46,9 +50,53 @@ class EM_Bookings extends EM_Object implements Iterator{
46
  */
47
  function __construct( $data = false ){
48
  if( is_object($data) && get_class($data) == "EM_Event" ){ //Creates a blank bookings object if needed
49
- global $wpdb;
50
  $this->event_id = $data->event_id;
51
- $bookings = array();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  if( $this->event_id > 0 ){
53
  $sql = "SELECT * FROM ". EM_BOOKINGS_TABLE ." WHERE event_id ='{$this->event_id}' ORDER BY booking_date";
54
  $bookings = $wpdb->get_results($sql, ARRAY_A);
@@ -56,14 +104,8 @@ class EM_Bookings extends EM_Object implements Iterator{
56
  foreach ($bookings as $booking){
57
  $this->bookings[] = em_get_booking($booking);
58
  }
59
- $this->spaces = $this->get_spaces();
60
- }elseif( is_array($data) ){
61
- foreach( $data as $EM_Booking ){
62
- if( get_class($EM_Booking) == 'EM_Booking'){
63
- $this->bookings[] = $EM_Booking;
64
- }
65
- }
66
  }
 
67
  }
68
 
69
  /**
@@ -83,7 +125,7 @@ class EM_Bookings extends EM_Object implements Iterator{
83
  if($result){
84
  //Success
85
  do_action('em_bookings_added', $EM_Booking);
86
- $this->bookings[] = $EM_Booking;
87
  $email = $EM_Booking->email();
88
  if( get_option('dbem_bookings_approval') == 1 && $EM_Booking->booking_status == 0){
89
  $this->feedback_message = get_option('dbem_booking_feedback_pending');
@@ -168,7 +210,7 @@ class EM_Bookings extends EM_Object implements Iterator{
168
  $EM_Event->rsvp_end = $EM_Ticket->end_timestamp;
169
  if( $EM_Event->is_recurring() && !empty($EM_Ticket->ticket_meta['recurrences']) ){
170
  $EM_Event->recurrence_rsvp_days = $EM_Ticket->ticket_meta['recurrences']['end_days'];
171
- }
172
  }else{
173
  //if no end date is set, use event end date (which will have defaulted to the event start date
174
  $EM_Ticket->ticket_end = $EM_Event->event_rsvp_date." ".$EM_Event->event_rsvp_time;
@@ -201,16 +243,16 @@ class EM_Bookings extends EM_Object implements Iterator{
201
  return apply_filters('em_bookings_get_available_tickets', $EM_Tickets, $this);
202
  }
203
 
 
 
 
 
204
  function get_user_list(){
205
- $users = array();
206
- foreach( $this->get_bookings()->bookings as $EM_Booking ){
207
- $users[$EM_Booking->person->ID] = $EM_Booking->person;
208
- }
209
- return $users;
210
  }
211
 
212
  /**
213
- * does this ticket exist?
214
  * @return bool
215
  */
216
  function ticket_exists($ticket_id){
@@ -251,16 +293,26 @@ class EM_Bookings extends EM_Object implements Iterator{
251
  function delete(){
252
  global $wpdb;
253
  $booking_ids = array();
254
- //get the booking ids tied to this event
255
- foreach( $this->bookings as $EM_Booking ){
256
- $booking_ids[] = $EM_Booking->booking_id;
257
- }
258
- $result_tickets = true;
259
- $result = true;
260
- if( count($booking_ids) > 0 ){
261
- //Delete bookings and ticket bookings
262
- $result_tickets = $wpdb->query("DELETE FROM ". EM_TICKETS_BOOKINGS_TABLE ." WHERE booking_id IN (".implode(',',$booking_ids).");");
263
- $result = $wpdb->query("DELETE FROM ".EM_BOOKINGS_TABLE." WHERE booking_id IN (".implode(',',$booking_ids).")");
 
 
 
 
 
 
 
 
 
 
264
  }
265
  do_action('em_bookings_deleted', $result, $booking_ids);
266
  return apply_filters('em_bookings_delete', $result !== false && $result_tickets !== false, $booking_ids, $this);
@@ -364,30 +416,31 @@ class EM_Bookings extends EM_Object implements Iterator{
364
  * @return int
365
  */
366
  function get_booked_spaces($force_refresh = false){
367
- $booked_spaces = 0;
368
- foreach ( $this->bookings as $EM_Booking ){
369
- if( $EM_Booking->booking_status == 1 || (!get_option('dbem_bookings_approval') && $EM_Booking->booking_status == 0 ) ){
370
- $booked_spaces += $EM_Booking->get_spaces($force_refresh);
371
- }
 
372
  }
373
- return apply_filters('em_bookings_get_booked_spaces', $booked_spaces, $this);
374
  }
375
 
376
  /**
377
  * Gets number of pending spaces awaiting approval. Will return 0 if booking approval is not enabled.
378
  * @return int
379
  */
380
- function get_pending_spaces(){
381
  if( get_option('dbem_bookings_approval') == 0 ){
382
  return apply_filters('em_bookings_get_pending_spaces', 0, $this);
383
  }
384
- $pending = 0;
385
- foreach ( $this->bookings as $booking ){
386
- if($booking->booking_status == 0){
387
- $pending += $booking->get_spaces();
388
- }
389
  }
390
- return apply_filters('em_bookings_get_pending_spaces', $pending, $this);
391
  }
392
 
393
  /**
@@ -396,9 +449,9 @@ class EM_Bookings extends EM_Object implements Iterator{
396
  */
397
  function get_bookings( $all_bookings = false ){
398
  $confirmed = array();
399
- foreach ( $this->bookings as $booking ){
400
- if( $booking->booking_status == 1 || (get_option('dbem_bookings_approval') == 0 && $booking->booking_status == 0) || $all_bookings ){
401
- $confirmed[] = $booking;
402
  }
403
  }
404
  $EM_Bookings = new EM_Bookings($confirmed);
@@ -414,9 +467,9 @@ class EM_Bookings extends EM_Object implements Iterator{
414
  return new EM_Bookings();
415
  }
416
  $pending = array();
417
- foreach ( $this->bookings as $booking ){
418
- if($booking->booking_status == 0){
419
- $pending[] = $booking;
420
  }
421
  }
422
  $EM_Bookings = new EM_Bookings($pending);
@@ -429,9 +482,9 @@ class EM_Bookings extends EM_Object implements Iterator{
429
  */
430
  function get_rejected_bookings(){
431
  $rejected = array();
432
- foreach ( $this->bookings as $booking ){
433
- if($booking->booking_status == 2){
434
- $rejected[] = $booking;
435
  }
436
  }
437
  $EM_Bookings = new EM_Bookings($rejected);
@@ -444,9 +497,9 @@ class EM_Bookings extends EM_Object implements Iterator{
444
  */
445
  function get_cancelled_bookings(){
446
  $cancelled = array();
447
- foreach ( $this->bookings as $booking ){
448
- if($booking->booking_status == 3){
449
- $cancelled[] = $booking;
450
  }
451
  }
452
  $EM_Bookings = new EM_Bookings($cancelled);
@@ -464,7 +517,7 @@ class EM_Bookings extends EM_Object implements Iterator{
464
  //If person exists on record, see if they've booked this event before, if so return the booking.
465
  if( is_numeric($EM_Booking->person->ID) && $EM_Booking->person->ID > 0 ){
466
  $EM_Booking->person_id = $EM_Booking->person->ID;
467
- foreach ($this->bookings as $booking){
468
  if( $booking->person_id == $EM_Booking->person->ID ){
469
  return $booking;
470
  }
@@ -482,7 +535,7 @@ class EM_Bookings extends EM_Object implements Iterator{
482
  $user_id = get_current_user_id();
483
  }
484
  if( is_numeric($user_id) && $user_id > 0 ){
485
- foreach ($this->bookings as $EM_Booking){
486
  if( $EM_Booking->person->ID == $user_id && !in_array($EM_Booking->booking_status, array(2,3)) ){
487
  return apply_filters('em_bookings_has_booking', $EM_Booking, $this);
488
  }
@@ -534,9 +587,14 @@ class EM_Bookings extends EM_Object implements Iterator{
534
  $orderby = self::build_sql_orderby($args, $accepted_fields);
535
  //Now, build orderby sql
536
  $orderby_sql = ( count($orderby) > 0 ) ? 'ORDER BY '. implode(', ', $orderby) : 'ORDER BY booking_date';
537
- //Selector
538
- $selectors = ( $count ) ? 'COUNT(*)':'*';
539
-
 
 
 
 
 
540
  //Create the SQL statement and execute
541
  $sql = apply_filters('em_bookings_get_sql',"
542
  SELECT $selectors FROM $bookings_table
@@ -681,7 +739,8 @@ class EM_Bookings extends EM_Object implements Iterator{
681
  'status' => false,
682
  'person' => true, //to add later, search by person's bookings...
683
  'blog' => get_current_blog_id(),
684
- 'ticket_id' => false
 
685
  );
686
  //sort out whether defaults were supplied or just the array of search values
687
  if( empty($array) ){
@@ -689,6 +748,25 @@ class EM_Bookings extends EM_Object implements Iterator{
689
  }else{
690
  $defaults = array_merge($defaults, $array_or_defaults);
691
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
692
  //figure out default owning permissions
693
  if( !current_user_can('edit_others_events') ){
694
  $defaults['owner'] = get_current_user_id();
@@ -703,23 +781,29 @@ class EM_Bookings extends EM_Object implements Iterator{
703
  return apply_filters('em_bookings_get_default_search', parent::get_default_search($defaults,$array), $array, $defaults);
704
  }
705
 
706
- //Iterator Implementation
 
707
  public function rewind(){
 
708
  reset($this->bookings);
709
  }
710
  public function current(){
 
711
  $var = current($this->bookings);
712
  return $var;
713
  }
714
  public function key(){
 
715
  $var = key($this->bookings);
716
  return $var;
717
  }
718
  public function next(){
 
719
  $var = next($this->bookings);
720
  return $var;
721
  }
722
  public function valid(){
 
723
  $key = key($this->bookings);
724
  $var = ($key !== NULL && $key !== FALSE);
725
  return $var;
10
  * Array of EM_Booking objects for a specific event
11
  * @var array
12
  */
13
+ protected $bookings;
14
  /**
15
  * @var EM_Tickets
16
  */
39
  */
40
  public static $disable_restrictions = false;
41
 
42
+ protected $booked_spaces;
43
+ protected $pending_spaces;
44
+ protected $available_spaces;
45
+
46
  /**
47
  * Creates an EM_Bookings instance, currently accepts an EM_Event object (gets all bookings for that event) or array of any EM_Booking objects, which can be manipulated in bulk with helper functions.
48
  * @param EM_Event $event
50
  */
51
  function __construct( $data = false ){
52
  if( is_object($data) && get_class($data) == "EM_Event" ){ //Creates a blank bookings object if needed
 
53
  $this->event_id = $data->event_id;
54
+ }elseif( is_array($data) ){
55
+ foreach( $data as $EM_Booking ){
56
+ if( get_class($EM_Booking) == 'EM_Booking'){
57
+ $this->bookings[] = $EM_Booking;
58
+ }
59
+ }
60
+ }
61
+ }
62
+
63
+ public function __get( $var ){
64
+ if( $var == 'bookings' ){
65
+ return $this->load();
66
+ }
67
+ }
68
+
69
+ public function __set( $var, $val ){
70
+ if( $var == 'bookings' ){
71
+ if( is_array($val) ){
72
+ $this->bookings = $val;
73
+ }else{
74
+ $this->bookings = null;
75
+ }
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Counter-intuitive but __isset works against isset() but for our purpose it's mainly aimed at empty() calls, which also references this function.
81
+ * We don't expect nor do we want people using isset on things like the bookings property.
82
+ * Assume every property in EM_Bookings isset() == true and avoid it, only use empty() calls to check if there's anything in that property.
83
+ * Therefore, we'd return !empty($this->bookings) because if there's bookings, isset() should return true
84
+ * @param string $var
85
+ * @return boolean
86
+ */
87
+ public function __isset( $var ){
88
+ //if isset is invoked on $EM_Bookings->bookings then we'll assume it's only set if the bookings property is empty, not if null.
89
+ $result = false;
90
+ if( $var == 'bookings' ){
91
+ $result = !empty($this->bookings);
92
+ }
93
+ return $result;
94
+ }
95
+
96
+ public function load( $refresh = false ){
97
+ if( $refresh || $this->bookings === null ){
98
+ global $wpdb;
99
+ $bookings = $this->bookings = array();
100
  if( $this->event_id > 0 ){
101
  $sql = "SELECT * FROM ". EM_BOOKINGS_TABLE ." WHERE event_id ='{$this->event_id}' ORDER BY booking_date";
102
  $bookings = $wpdb->get_results($sql, ARRAY_A);
104
  foreach ($bookings as $booking){
105
  $this->bookings[] = em_get_booking($booking);
106
  }
 
 
 
 
 
 
 
107
  }
108
+ return apply_filters('em_bookings_load', $this->bookings);
109
  }
110
 
111
  /**
125
  if($result){
126
  //Success
127
  do_action('em_bookings_added', $EM_Booking);
128
+ if( $this->bookings !== null ) $this->bookings[] = $EM_Booking;
129
  $email = $EM_Booking->email();
130
  if( get_option('dbem_bookings_approval') == 1 && $EM_Booking->booking_status == 0){
131
  $this->feedback_message = get_option('dbem_booking_feedback_pending');
210
  $EM_Event->rsvp_end = $EM_Ticket->end_timestamp;
211
  if( $EM_Event->is_recurring() && !empty($EM_Ticket->ticket_meta['recurrences']) ){
212
  $EM_Event->recurrence_rsvp_days = $EM_Ticket->ticket_meta['recurrences']['end_days'];
213
+ }
214
  }else{
215
  //if no end date is set, use event end date (which will have defaulted to the event start date
216
  $EM_Ticket->ticket_end = $EM_Event->event_rsvp_date." ".$EM_Event->event_rsvp_time;
243
  return apply_filters('em_bookings_get_available_tickets', $EM_Tickets, $this);
244
  }
245
 
246
+ /**
247
+ * Deprecated - was never used and therefore is deprecated, will always return an array() and will eventually be removed entirely.
248
+ * @return array
249
+ */
250
  function get_user_list(){
251
+ return array();
 
 
 
 
252
  }
253
 
254
  /**
255
+ * Returns a boolean indicating whether this ticket exists in this bookings context.
256
  * @return bool
257
  */
258
  function ticket_exists($ticket_id){
293
  function delete(){
294
  global $wpdb;
295
  $booking_ids = array();
296
+ if( !empty($this->bookings) ){
297
+ //get the booking ids tied to this event or preloaded into this object
298
+ foreach( $this->bookings as $EM_Booking ){
299
+ $booking_ids[] = $EM_Booking->booking_id;
300
+ }
301
+ $result_tickets = true;
302
+ $result = true;
303
+ if( count($booking_ids) > 0 ){
304
+ //Delete bookings and ticket bookings
305
+ $result_tickets = $wpdb->query("DELETE FROM ". EM_TICKETS_BOOKINGS_TABLE ." WHERE booking_id IN (".implode(',',$booking_ids).");");
306
+ $result = $wpdb->query("DELETE FROM ".EM_BOOKINGS_TABLE." WHERE booking_id IN (".implode(',',$booking_ids).")");
307
+ }
308
+ }elseif( !empty($this->event_id) ){
309
+ //faster way of deleting bookings for an event circumventing the need to load all bookings if it hasn't been loaded already
310
+ $event_id = absint($this->event_id);
311
+ $booking_ids = $wpdb->get_col("SELECT booking_id FROM ".EM_BOOKINGS_TABLE." WHERE event_id = '$event_id'");
312
+ $result_tickets = $wpdb->query("DELETE FROM ". EM_TICKETS_BOOKINGS_TABLE ." WHERE booking_id IN (SELECT booking_id FROM ".EM_BOOKINGS_TABLE." WHERE event_id = '$event_id')");
313
+ $result = $wpdb->query("DELETE FROM ".EM_BOOKINGS_TABLE." WHERE event_id = '$event_id'");
314
+ }else{
315
+ $result = $result_tickets == true;
316
  }
317
  do_action('em_bookings_deleted', $result, $booking_ids);
318
  return apply_filters('em_bookings_delete', $result !== false && $result_tickets !== false, $booking_ids, $this);
416
  * @return int
417
  */
418
  function get_booked_spaces($force_refresh = false){
419
+ global $wpdb;
420
+ if( $this->booked_spaces === null || $force_refresh ){
421
+ $status_cond = !get_option('dbem_bookings_approval') ? 'booking_status IN (0,1)' : 'booking_status = 1';
422
+ $sql = 'SELECT SUM(booking_spaces) FROM '.EM_BOOKINGS_TABLE. " WHERE $status_cond AND event_id=".absint($this->event_id);
423
+ $booked_spaces = $wpdb->get_var($sql);
424
+ $this->booked_spaces = $booked_spaces > 0 ? $booked_spaces : 0;
425
  }
426
+ return apply_filters('em_bookings_get_booked_spaces', $this->booked_spaces, $this, $force_refresh);
427
  }
428
 
429
  /**
430
  * Gets number of pending spaces awaiting approval. Will return 0 if booking approval is not enabled.
431
  * @return int
432
  */
433
+ function get_pending_spaces( $force_refresh = false ){
434
  if( get_option('dbem_bookings_approval') == 0 ){
435
  return apply_filters('em_bookings_get_pending_spaces', 0, $this);
436
  }
437
+ global $wpdb;
438
+ if( $this->pending_spaces === null || $force_refresh ){
439
+ $sql = 'SELECT SUM(booking_spaces) FROM '.EM_BOOKINGS_TABLE. ' WHERE booking_status=0 AND event_id='.absint($this->event_id);
440
+ $pending_spaces = $wpdb->get_var($sql);
441
+ $this->pending_spaces = $pending_spaces > 0 ? $pending_spaces : 0;
442
  }
443
+ return apply_filters('em_bookings_get_pending_spaces', $this->pending_spaces, $this, $force_refresh);
444
  }
445
 
446
  /**
449
  */
450
  function get_bookings( $all_bookings = false ){
451
  $confirmed = array();
452
+ foreach ( $this->load() as $EM_Booking ){
453
+ if( $EM_Booking->booking_status == 1 || (get_option('dbem_bookings_approval') == 0 && $EM_Booking->booking_status == 0) || $all_bookings ){
454
+ $confirmed[] = $EM_Booking;
455
  }
456
  }
457
  $EM_Bookings = new EM_Bookings($confirmed);
467
  return new EM_Bookings();
468
  }
469
  $pending = array();
470
+ foreach ( $this->load() as $EM_Booking ){
471
+ if($EM_Booking->booking_status == 0){
472
+ $pending[] = $EM_Booking;
473
  }
474
  }
475
  $EM_Bookings = new EM_Bookings($pending);
482
  */
483
  function get_rejected_bookings(){
484
  $rejected = array();
485
+ foreach ( $this->load() as $EM_Booking ){
486
+ if($EM_Booking->booking_status == 2){
487
+ $rejected[] = $EM_Booking;
488
  }
489
  }
490
  $EM_Bookings = new EM_Bookings($rejected);
497
  */
498
  function get_cancelled_bookings(){
499
  $cancelled = array();
500
+ foreach ( $this->load() as $EM_Booking ){
501
+ if($EM_Booking->booking_status == 3){
502
+ $cancelled[] = $EM_Booking;
503
  }
504
  }
505
  $EM_Bookings = new EM_Bookings($cancelled);
517
  //If person exists on record, see if they've booked this event before, if so return the booking.
518
  if( is_numeric($EM_Booking->person->ID) && $EM_Booking->person->ID > 0 ){
519
  $EM_Booking->person_id = $EM_Booking->person->ID;
520
+ foreach ($this->load() as $booking){
521
  if( $booking->person_id == $EM_Booking->person->ID ){
522
  return $booking;
523
  }
535
  $user_id = get_current_user_id();
536
  }
537
  if( is_numeric($user_id) && $user_id > 0 ){
538
+ foreach ($this->load() as $EM_Booking){
539
  if( $EM_Booking->person->ID == $user_id && !in_array($EM_Booking->booking_status, array(2,3)) ){
540
  return apply_filters('em_bookings_has_booking', $EM_Booking, $this);
541
  }
587
  $orderby = self::build_sql_orderby($args, $accepted_fields);
588
  //Now, build orderby sql
589
  $orderby_sql = ( count($orderby) > 0 ) ? 'ORDER BY '. implode(', ', $orderby) : 'ORDER BY booking_date';
590
+ //Selectors
591
+ if( $count ){
592
+ $selectors = 'COUNT(*)';
593
+ }elseif( is_array($args['array']) ){
594
+ $selectors = implode(',', $args['array']);
595
+ }else{
596
+ $selectors = '*';
597
+ }
598
  //Create the SQL statement and execute
599
  $sql = apply_filters('em_bookings_get_sql',"
600
  SELECT $selectors FROM $bookings_table
739
  'status' => false,
740
  'person' => true, //to add later, search by person's bookings...
741
  'blog' => get_current_blog_id(),
742
+ 'ticket_id' => false,
743
+ 'array' => false //returns an array of results if true, if an array or text it's assumed an array or single row requested
744
  );
745
  //sort out whether defaults were supplied or just the array of search values
746
  if( empty($array) ){
748
  }else{
749
  $defaults = array_merge($defaults, $array_or_defaults);
750
  }
751
+ //clean up array value
752
+ if( !empty($args['array']) ){
753
+ $EM_Booking = new EM_Booking();
754
+ if( is_array($args['array']) ){
755
+ $clean_arg = array();
756
+ foreach( $args['array'] as $k => $field ){
757
+ if( array_key_exists($field, $EM_Booking->fields) ){
758
+ $clean_arg[] = $field;
759
+ }
760
+ }
761
+ $args['array'] = !empty($clean_arg) ? $clean_arg : true; //if invalid args given, just return all fields
762
+ }elseif( is_string($args['array']) && array_key_exists($args['array'], $EM_Booking->fields) ){
763
+ $args['array'] = array($args['array']);
764
+ }else{
765
+ $args['array'] = true;
766
+ }
767
+ }else{
768
+ $args['array'] = false;
769
+ }
770
  //figure out default owning permissions
771
  if( !current_user_can('edit_others_events') ){
772
  $defaults['owner'] = get_current_user_id();
781
  return apply_filters('em_bookings_get_default_search', parent::get_default_search($defaults,$array), $array, $defaults);
782
  }
783
 
784
+ //Iterator Implementation - if we iterate this object, we automatically invoke the load() function first
785
+ //and load up all bookings to go through from the database.
786
  public function rewind(){
787
+ $this->load();
788
  reset($this->bookings);
789
  }
790
  public function current(){
791
+ $this->load();
792
  $var = current($this->bookings);
793
  return $var;
794
  }
795
  public function key(){
796
+ $this->load();
797
  $var = key($this->bookings);
798
  return $var;
799
  }
800
  public function next(){
801
+ $this->load();
802
  $var = next($this->bookings);
803
  return $var;
804
  }
805
  public function valid(){
806
+ $this->load();
807
  $key = key($this->bookings);
808
  $var = ($key !== NULL && $key !== FALSE);
809
  return $var;
classes/em-calendar.php CHANGED
@@ -7,7 +7,7 @@ class EM_Calendar extends EM_Object {
7
 
8
  public static function get( $args ){
9
 
10
- global $wpdb;
11
 
12
  $calendar_array = array();
13
  $calendar_array['cells'] = array();
@@ -32,8 +32,7 @@ class EM_Calendar extends EM_Object {
32
  $long_events = $args['long_events'];
33
  $limit = $args['limit']; //limit arg will be used per day and not for events search
34
 
35
- $week_starts_on_sunday = get_option('dbem_week_starts_sunday');
36
- $start_of_week = get_option('start_of_week');
37
 
38
  if( !(is_numeric($month) && $month <= 12 && $month > 0) ) {
39
  $month = date('m', current_time('timestamp'));
@@ -158,9 +157,16 @@ class EM_Calendar extends EM_Object {
158
  }
159
 
160
  $days_initials_array = array();
161
- foreach($weekdays as $weekday) {
162
- $days_initials_array[] = esc_html(self::translate_and_trim($weekday, $day_initials_length));
163
- }
 
 
 
 
 
 
 
164
 
165
  $calendar_array['links'] = array( 'previous_url'=>$previous_url, 'next_url'=>$next_url);
166
  $calendar_array['row_headers'] = $days_initials_array;
@@ -279,6 +285,18 @@ class EM_Calendar extends EM_Object {
279
  }
280
  //generate a link argument string containing event search only
281
  $day_link_args = self::get_query_args( array_intersect_key($original_args, EM_Events::get_post_search($args, true) ));
 
 
 
 
 
 
 
 
 
 
 
 
282
  foreach($eventful_days as $day_key => $events) {
283
  if( array_key_exists($day_key, $calendar_array['cells']) ){
284
  //Get link title for this date
@@ -294,20 +312,10 @@ class EM_Calendar extends EM_Object {
294
  $calendar_array['cells'][$day_key]['link_title'] = implode( $event_title_separator_format, $events_titles);
295
 
296
  //Get the link to this calendar day
297
- global $wp_rewrite;
298
  if( $eventful_days_count[$day_key] > 1 || !get_option('dbem_calendar_direct_links') ){
299
- if( get_option("dbem_events_page") > 0 ){
300
- $event_page_link = get_permalink(get_option("dbem_events_page")); //PAGE URI OF EM
301
- }else{
302
- if( $wp_rewrite->using_permalinks() ){
303
- $event_page_link = trailingslashit(home_url()).EM_POST_TYPE_EVENT_SLUG.'/'; //don't use EM_URI here, since ajax calls this before EM_URI is defined.
304
- }else{
305
- //not needed atm anyway, but we use esc_url later on, in case you're wondering ;)
306
- $event_page_link = add_query_arg(array('post_type'=>EM_POST_TYPE_EVENT), home_url()); //don't use EM_URI here, since ajax calls this before EM_URI is defined.
307
- }
308
- }
309
  if( $wp_rewrite->using_permalinks() && !defined('EM_DISABLE_PERMALINKS') ){
310
- $calendar_array['cells'][$day_key]['link'] = trailingslashit($event_page_link).$day_key."/";
 
311
  //add query vars to end of link
312
  if( !empty($day_link_args) ){
313
  $calendar_array['cells'][$day_key]['link'] = esc_url_raw(add_query_arg($day_link_args, $calendar_array['cells'][$day_key]['link']));
@@ -434,6 +442,7 @@ class EM_Calendar extends EM_Object {
434
  public static function get_default_search( $array_or_defaults = array(), $array = array() ){
435
  //These defaults aren't for db queries, but flags for what to display in calendar output
436
  $defaults = array(
 
437
  'full' => 0, //Will display a full calendar with event names
438
  'long_events' => 0, //Events that last longer than a day
439
  'scope' => false,
7
 
8
  public static function get( $args ){
9
 
10
+ global $wpdb, $wp_rewrite;
11
 
12
  $calendar_array = array();
13
  $calendar_array['cells'] = array();
32
  $long_events = $args['long_events'];
33
  $limit = $args['limit']; //limit arg will be used per day and not for events search
34
 
35
+ $start_of_week = get_option('start_of_week');
 
36
 
37
  if( !(is_numeric($month) && $month <= 12 && $month > 0) ) {
38
  $month = date('m', current_time('timestamp'));
157
  }
158
 
159
  $days_initials_array = array();
160
+ //translate day names, some languages may have special circumstances
161
+ if( $day_initials_length == 1 && in_array(EM_ML::$current_language, array('zh_CN', 'zh_TW')) ){
162
+ //Chinese single initial day names are different
163
+ $days_initials_array = array('日','一','二','三','四','五','六');
164
+ }else{
165
+ //all other languages
166
+ foreach($weekdays as $weekday) {
167
+ $days_initials_array[] = esc_html(self::translate_and_trim($weekday, $day_initials_length));
168
+ }
169
+ }
170
 
171
  $calendar_array['links'] = array( 'previous_url'=>$previous_url, 'next_url'=>$next_url);
172
  $calendar_array['row_headers'] = $days_initials_array;
285
  }
286
  //generate a link argument string containing event search only
287
  $day_link_args = self::get_query_args( array_intersect_key($original_args, EM_Events::get_post_search($args, true) ));
288
+ //get event link
289
+ if( get_option("dbem_events_page") > 0 ){
290
+ $event_page_link = get_permalink(get_option("dbem_events_page")); //PAGE URI OF EM
291
+ }else{
292
+ if( $wp_rewrite->using_permalinks() ){
293
+ $event_page_link = trailingslashit(home_url()).EM_POST_TYPE_EVENT_SLUG.'/'; //don't use EM_URI here, since ajax calls this before EM_URI is defined.
294
+ }else{
295
+ //not needed atm anyway, but we use esc_url later on, in case you're wondering ;)
296
+ $event_page_link = add_query_arg(array('post_type'=>EM_POST_TYPE_EVENT), home_url()); //don't use EM_URI here, since ajax calls this before EM_URI is defined.
297
+ }
298
+ }
299
+ $event_page_link_parts = explode('?', $event_page_link); //in case we have other plugins (e.g. WPML) adding querystring params to the end
300
  foreach($eventful_days as $day_key => $events) {
301
  if( array_key_exists($day_key, $calendar_array['cells']) ){
302
  //Get link title for this date
312
  $calendar_array['cells'][$day_key]['link_title'] = implode( $event_title_separator_format, $events_titles);
313
 
314
  //Get the link to this calendar day
 
315
  if( $eventful_days_count[$day_key] > 1 || !get_option('dbem_calendar_direct_links') ){
 
 
 
 
 
 
 
 
 
 
316
  if( $wp_rewrite->using_permalinks() && !defined('EM_DISABLE_PERMALINKS') ){
317
+ $calendar_array['cells'][$day_key]['link'] = trailingslashit($event_page_link_parts[0]).$day_key."/";
318
+ if( !empty($event_page_link_parts[1]) ) $calendar_array['cells'][$day_key]['link'] .= '?' . $event_page_link_parts[1];
319
  //add query vars to end of link
320
  if( !empty($day_link_args) ){
321
  $calendar_array['cells'][$day_key]['link'] = esc_url_raw(add_query_arg($day_link_args, $calendar_array['cells'][$day_key]['link']));
442
  public static function get_default_search( $array_or_defaults = array(), $array = array() ){
443
  //These defaults aren't for db queries, but flags for what to display in calendar output
444
  $defaults = array(
445
+ 'recurring' => false, //we don't initially look for recurring events only events and recurrences of recurring events
446
  'full' => 0, //Will display a full calendar with event names
447
  'long_events' => 0, //Events that last longer than a day
448
  'scope' => false,
classes/em-categories-admin.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This class extends the EM_Taxonomy_Admin and adds category images and colors to the admin area.
4
+ *
5
+ * Currently, all functions here serve the purpose of getting around lack of late static binding in PHP < 5.3.
6
+ * Eventually when PHP 5.3 is enforced only certain class properties need to be defined for use in the parent class via static::
7
+ *
8
+ */
9
+ class EM_Categories_Admin extends EM_Taxonomy_Admin{
10
+
11
+ public static $taxonomy_name = 'EM_TAXONOMY_CATEGORY'; //converted into a constant value during init()
12
+ public static $this_class = 'EM_Categories_Admin'; //needed until 5.3 minimum is enforced for late static binding
13
+ public static $tax_class = 'EM_Category';
14
+ public static $option_name = 'category';
15
+ public static $name_singular = 'category';
16
+ public static $name_plural = 'categories';
17
+ public static $placeholder_image = '#_CATEGORYIMAGE';
18
+ public static $placeholder_color = '#_CATEGORYCOLOR';
19
+
20
+ public static function init(){
21
+ self::$taxonomy_name = EM_TAXONOMY_CATEGORY;
22
+ self::static_binding();
23
+ parent::init();
24
+ }
25
+
26
+ public static function form_add(){
27
+ self::static_binding();
28
+ parent::form_add();
29
+ }
30
+
31
+ public static function form_edit($tag){
32
+ self::static_binding();
33
+ parent::form_edit($tag);
34
+ }
35
+
36
+ public static function save( $term_id, $tt_id ){
37
+ self::static_binding();
38
+ parent::save( $term_id, $tt_id );
39
+ }
40
+
41
+ public static function delete( $term_id ){
42
+ self::static_binding();
43
+ parent::delete( $term_id );
44
+ }
45
+
46
+ /**
47
+ * Temporary function until WP requires PHP 5.3, so that we can make use of late static binding.
48
+ * Until then, all functions needing LST should run this function before calling the parent. If all extending classes do this we shouldn't have a problem.
49
+ */
50
+ public static function static_binding(){
51
+ EM_Taxonomy_Admin::$taxonomy_name = self::$taxonomy_name;
52
+ EM_Taxonomy_Admin::$this_class = self::$this_class;
53
+ EM_Taxonomy_Admin::$tax_class = self::$tax_class;
54
+ EM_Taxonomy_Admin::$option_name = self::$option_name;
55
+ EM_Taxonomy_Admin::$name_singular = self::$name_singular;
56
+ EM_Taxonomy_Admin::$name_plural = self::$name_plural;
57
+ EM_Taxonomy_Admin::$placeholder_image = self::$placeholder_image;
58
+ EM_Taxonomy_Admin::$placeholder_color = self::$placeholder_color;
59
+ }
60
+ }
61
+ add_action('admin_init',array('EM_Categories_Admin','init'));
classes/em-categories-frontend.php ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class EM_Categories_Frontend extends EM_Taxonomy_Frontend {
3
+
4
+ public static $taxonomy_name = 'event-category'; //converted into a constant value during init()
5
+ public static $this_class = 'EM_Categories_Frontend'; //needed until 5.3 minimum is enforced for late static binding
6
+ public static $tax_class = 'EM_Category';
7
+ public static $option_name = 'category';
8
+ public static $option_name_plural = 'categories';
9
+
10
+ public static function init(){
11
+ self::$taxonomy_name = EM_TAXONOMY_CATEGORY; //awaiting LST in PHP 5.3
12
+ self::static_binding();
13
+ parent::init();
14
+ }
15
+
16
+ //These following functions can be removed when PHP 5.3 is minimum and LSB is available
17
+
18
+ public static function template($template = ''){
19
+ self::static_binding();
20
+ return parent::template($template);
21
+ }
22
+
23
+ public static function the_content($content){
24
+ self::static_binding();
25
+ return parent::the_content($content);
26
+ }
27
+
28
+ public static function parse_query( $wp_query ){
29
+ //we do some double-checking here to prevent running self::static_binding() during the self::template() function when WP_Query is called.
30
+ if( !$wp_query->is_main_query() ) return;
31
+ if( $wp_query->is_tax(self::$taxonomy_name) || !empty($wp_query->{'em_'.self::$option_name.'_id'}) ){
32
+ self::static_binding();
33
+ return parent::parse_query( $wp_query );
34
+ }
35
+ }
36
+
37
+ public static function wpseo_breadcrumb_links( $links ){
38
+ self::static_binding();
39
+ return parent::wpseo_breadcrumb_links( $links );
40
+ }
41
+
42
+ /**
43
+ * Temporary function until WP requires PHP 5.3, so that we can make use of late static binding.
44
+ * Until then, all functions needing LST should run this function before calling the parent. If all extending classes do this we shouldn't have a problem.
45
+ */
46
+ public static function static_binding(){
47
+ EM_Taxonomy_Frontend::$taxonomy_name = self::$taxonomy_name;
48
+ EM_Taxonomy_Frontend::$this_class = self::$this_class;
49
+ EM_Taxonomy_Frontend::$tax_class = self::$tax_class;
50
+ EM_Taxonomy_Frontend::$option_name = self::$option_name;
51
+ EM_Taxonomy_Frontend::$option_name_plural = self::$option_name_plural;
52
+ }
53
+ }
54
+ class EM_Category_Taxonomy extends EM_Categories_Frontend {} //backwards compatibility
55
+
56
+ EM_Categories_Frontend::init();
57
+
58
+ //Walker classes allowing for hierarchical display of categories
59
+
60
+ /**
61
+ * Create an array of Categories. Copied from Walker_CategoryDropdown, but makes it possible for the selected argument to be an array.
62
+ *
63
+ * @package WordPress
64
+ * @since 2.1.0
65
+ * @uses Walker
66
+ */
67
+ class EM_Walker_Category extends Walker {
68
+ /**
69
+ * @see Walker::$tree_type
70
+ * @since 2.1.0
71
+ * @var string
72
+ */
73
+ var $tree_type = 'event-category';
74
+
75
+ /**
76
+ * @see Walker::$db_fields
77
+ * @since 2.1.0
78
+ * @todo Decouple this
79
+ * @var array
80
+ */
81
+ var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');
82
+
83
+ function __construct(){
84
+ $tree_type = EM_TAXONOMY_CATEGORY;
85
+ }
86
+
87
+ /**
88
+ * @see Walker::start_el()
89
+ */
90
+ function start_el( &$output, $object, $depth = 0, $args = array(), $current_object_id = 0 ) {
91
+ $pad = str_repeat('&nbsp;', $depth * 3);
92
+ $cat_name = $object->name;
93
+ $name = !empty($args['name']) ? $args['name']:'event_categories[]';
94
+ $output .= !empty($args['before']) ? $args['after']:'';
95
+ $output .= $pad."<input type=\"checkbox\" name=\"$name\" class=\"level-$depth\" value=\"".$object->term_id."\"";
96
+ if ( (is_array($args['selected']) && in_array($object->term_id, $args['selected'])) || ($object->term_id == $args['selected']) )
97
+ $output .= ' checked="checked"';
98
+ $output .= ' /> ';
99
+ $output .= $cat_name;
100
+ $output .= !empty($args['after']) ? $args['after']:'<br />';
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Create an array of Categories. Copied from Walker_CategoryDropdown, but makes it possible for the selected argument to be an array.
106
+ *
107
+ * @package WordPress
108
+ * @since 2.1.0
109
+ * @uses Walker
110
+ */
111
+ class EM_Walker_CategoryMultiselect extends EM_Walker_Category {
112
+ /**
113
+ * @see Walker::start_el()
114
+ */
115
+ function start_el( &$output, $object, $depth = 0, $args = array(), $current_object_id = 0 ) {
116
+ $pad = str_repeat('&nbsp;', $depth * 3);
117
+ $cat_name = $object->name;
118
+ $output .= "\t<option class=\"level-$depth\" value=\"".$object->term_id."\"";
119
+ if ( (is_array($args['selected']) && in_array($object->term_id, $args['selected'])) || ($object->term_id == $args['selected']) )
120
+ $output .= ' selected="selected"';
121
+ $output .= '>';
122
+ $output .= $pad.$cat_name;
123
+ $output .= "</option>\n";
124
+ }
125
+ }
classes/em-categories-taxonomy.php DELETED
@@ -1,126 +0,0 @@
1
- <?php
2
- class EM_Categories_Taxonomy{
3
- public static function init(){
4
- add_action( EM_TAXONOMY_CATEGORY.'_edit_form_fields', array('EM_Categories_Taxonomy','form'), 10, 1);
5
- add_action( EM_TAXONOMY_CATEGORY.'_add_form_fields', array('EM_Categories_Taxonomy','form'), 10, 1);
6
- add_action( 'edited_'.EM_TAXONOMY_CATEGORY, array('EM_Categories_Taxonomy','save'), 10, 2);
7
- add_action( 'create_'.EM_TAXONOMY_CATEGORY, array('EM_Categories_Taxonomy','save'), 10, 2);
8
- add_action( 'delete_'.EM_TAXONOMY_CATEGORY, array('EM_Categories_Taxonomy','delete'), 10, 2);
9
-
10
- add_filter('manage_edit-'.EM_TAXONOMY_CATEGORY.'_columns' , array('EM_Categories_Taxonomy','columns_add'));
11
- add_filter('manage_'.EM_TAXONOMY_CATEGORY.'_custom_column' , array('EM_Categories_Taxonomy','columns_output'),10,3);
12
-
13
- self::admin_init();
14
- }
15
-
16
-
17
- public static function columns_add($columns) {
18
- //prepend ID after checkbox
19
- $columns['cat-id'] = __('ID','events-manager');
20
- return $columns;
21
- }
22
-
23
- public static function columns_output( $val, $column, $term_id ) {
24
- switch ( $column ) {
25
- case 'cat-id':
26
- return $term_id;
27
- break;
28
- }
29
- return $val;
30
- }
31
-
32
- public static function admin_init(){
33
- global $pagenow;
34
- if( ($pagenow == 'edit-tags.php' || $pagenow == 'term.php') && !empty($_GET['taxonomy']) && $_GET['taxonomy'] == EM_TAXONOMY_CATEGORY){
35
- wp_enqueue_media();
36
- wp_enqueue_script( 'em-categories-admin', plugins_url().'/events-manager/includes/js/categories-admin.js', array( 'jquery','media-upload','thickbox','farbtastic' ) );
37
- }
38
- }
39
-
40
- public static function form($tag){
41
- $category_color = '#FFFFFF';
42
- $category_image = $category_image_id = '';
43
- if( $tag != EM_TAXONOMY_CATEGORY ){ //not an add new tag form
44
- $EM_Category = new EM_Category($tag);
45
- $category_color = $EM_Category->get_color();
46
- $category_image = $EM_Category->get_image_url();
47
- $category_image_id = $EM_Category->get_image_id();
48
- }
49
- ?>
50
- <tr class="form-field">
51
- <th scope="row" valign="top"><label for="category-bgcolor"><?php esc_html_e('Color','events-manager'); ?></label></th>
52
- <td>
53
- <input type="text" name="category_bgcolor" id="category-bgcolor" class="colorwell" value="<?php echo esc_attr($category_color); ?>" style="width:100px;"/><br />
54
- <p class="description"><?php echo sprintf(__('Choose a color for your category. You can access this using the %s placeholder.','events-manager'),'<code>#_CATEGORYCOLOR</code>'); ?></p>
55
- <div id="picker" style="position:absolute; display:none; background:#DEDEDE"></div>
56
- </td>
57
- </tr>
58
- <tr class="form-field">
59
- <th scope="row" valign="top"><label for="category-image"><?php esc_html_e('Image','events-manager'); ?></label></th>
60
- <td id="event-tax-image">
61
- <div class="img-container">
62
- <?php if( !empty($category_image) ): ?>
63
- <img src="<?php echo $category_image; ?>" />
64
- <?php endif; ?>
65
- </div>
66
- <input type="text" name="category_image" id="category-image" class="img-url" value="<?php echo esc_attr($category_image); ?>" />
67
- <input type="hidden" name="category_image_id" id="category-image-id" class="img-id" value="<?php echo esc_attr($category_image_id); ?>" />
68
- <p class="hide-if-no-js">
69
- <input id="upload_image_button" type="button" value="<?php _e('Choose/Upload Image','events-manager'); ?>" class="upload-img-button button-secondary" />
70
- <input id="delete_image_button" type="button" value="<?php _e('Remove Image','events-manager'); ?>" class="delete-img-button button-secondary" <?php if( empty($category_image) ) echo 'style="display:none;"'; ?> />
71
- </p>
72
- <br />
73
- <p class="description"><?php echo sprintf(__('Choose an image for your category, which can be displayed using the %s placeholder.','events-manager'),'<code>#_CATEGORYIMAGE</code>'); ?></p>
74
- </td>
75
- </tr>
76
- <?php
77
- }
78
-
79
- public static function save($term_id, $tt_id){
80
- global $wpdb;
81
- if (!$term_id) return;
82
- if( !empty($_POST['category_bgcolor']) && preg_match('/^#[a-zA-Z0-9]{6}$/', $_POST['category_bgcolor']) ){
83
- //get results and save/update
84
- $prev_settings = $wpdb->get_results('SELECT meta_value FROM '.EM_META_TABLE." WHERE object_id='{$term_id}' AND meta_key='category-bgcolor'");
85
- if( count($prev_settings) > 0 ){
86
- $wpdb->update(EM_META_TABLE, array('object_id'=>$term_id,'meta_value'=>$_POST['category_bgcolor']), array('object_id'=>$term_id,'meta_key'=>'category-bgcolor'));
87
- }else{
88
- $wpdb->insert(EM_META_TABLE, array('object_id'=>$term_id,'meta_key'=>'category-bgcolor','meta_value'=>$_POST['category_bgcolor']));
89
- }
90
- }
91
- if( !empty($_POST['category_image']) ){
92
- //get results and save/update
93
- $prev_settings = $wpdb->get_results('SELECT meta_value FROM '.EM_META_TABLE." WHERE object_id='{$term_id}' AND meta_key='category-image'");
94
- if( count($prev_settings) > 0 ){
95
- $wpdb->update(EM_META_TABLE, array('object_id'=>$term_id,'meta_value'=>$_POST['category_image']), array('object_id'=>$term_id,'meta_key'=>'category-image'));
96
- }else{
97
- $wpdb->insert(EM_META_TABLE, array('object_id'=>$term_id,'meta_key'=>'category-image','meta_value'=>$_POST['category_image']));
98
- }
99
- if( !empty($_POST['category_image_id']) && is_numeric($_POST['category_image_id']) ){
100
- //get results and save/update
101
- $prev_settings = $wpdb->get_results('SELECT meta_value FROM '.EM_META_TABLE." WHERE object_id='{$term_id}' AND meta_key='category-image-id'");
102
- if( count($prev_settings) > 0 ){
103
- $wpdb->update(EM_META_TABLE, array('object_id'=>$term_id,'meta_value'=>$_POST['category_image_id']), array('object_id'=>$term_id,'meta_key'=>'category-image-id'));
104
- }else{
105
- $wpdb->insert(EM_META_TABLE, array('object_id'=>$term_id,'meta_key'=>'category-image-id','meta_value'=>$_POST['category_image_id']));
106
- }
107
- }
108
- }else{
109
- //check if an image exists, if so remove association
110
- $prev_settings = $wpdb->get_results('SELECT meta_value FROM '.EM_META_TABLE." WHERE object_id='{$term_id}' AND meta_key='category-image'");
111
- if( count($prev_settings) > 0 ){
112
- $wpdb->delete(EM_META_TABLE, array('object_id'=>$term_id,'meta_key'=>'category-image'));
113
- $wpdb->delete(EM_META_TABLE, array('object_id'=>$term_id,'meta_key'=>'category-image-id'));
114
- }
115
- }
116
- }
117
-
118
- public static function delete( $term_id ){
119
- global $wpdb;
120
- //delete category image and color
121
- $wpdb->query('DELETE FROM '.EM_META_TABLE." WHERE object_id='$term_id' AND (meta_key='category-image' OR meta_key='category-bgcolor')");
122
- //delete all events category relations
123
- $wpdb->query('DELETE FROM '.EM_META_TABLE." WHERE meta_value='{$term_id}' AND meta_key='event-category'");
124
- }
125
- }
126
- add_action('admin_init',array('EM_Categories_Taxonomy','init'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/em-categories.php CHANGED
@@ -1,21 +1,12 @@
1
  <?php
2
- class EM_Categories extends EM_Object implements Iterator{
3
 
4
- /**
5
- * Array of EM_Category objects for a specific event
6
- * @var array
7
- */
8
- var $categories = array();
9
- /**
10
- * Event ID of this set of categories
11
- * @var int
12
- */
13
- var $event_id;
14
- /**
15
- * Post ID of this set of categories
16
- * @var int
17
- */
18
- var $post_id;
19
 
20
  /**
21
  * Creates an EM_Categories instance, currently accepts an EM_Event object (gets all Categories for that event) or array of any EM_Category objects, which can be manipulated in bulk with helper functions.
@@ -23,285 +14,58 @@ class EM_Categories extends EM_Object implements Iterator{
23
  * @return null
24
  */
25
  function __construct( $data = false ){
26
- global $wpdb;
27
- self::ms_global_switch();
28
- if( is_object($data) && get_class($data) == "EM_Event" && !empty($data->post_id) ){ //Creates a blank categories object if needed
29
- $this->event_id = $data->event_id;
30
- $this->post_id = $data->post_id;
31
- if( EM_MS_GLOBAL && !is_main_site($data->blog_id) ){
32
- $cat_ids = $wpdb->get_col('SELECT meta_value FROM '.EM_META_TABLE." WHERE object_id='{$this->event_id}' AND meta_key='event-category'");
33
- foreach($cat_ids as $cat_id){
34
- $this->categories[$cat_id] = new EM_Category($cat_id);
35
- }
36
- }else{
37
- $results = get_the_terms( $data->post_id, EM_TAXONOMY_CATEGORY );
38
- if( is_array($results) ){
39
- foreach($results as $result){
40
- $this->categories[$result->term_id] = new EM_Category($result);
41
- }
42
- }
43
- }
44
- }elseif( is_array($data) && self::array_is_numeric($data) ){
45
- foreach($data as $category_id){
46
- $this->categories[$category_id] = new EM_Category($category_id);
47
- }
48
- }elseif( is_array($data) ){
49
- foreach( $data as $EM_Category ){
50
- if( get_class($EM_Category) == 'EM_Category'){
51
- $this->categories[] = $EM_Category;
52
- }
53
- }
54
- }
55
- self::ms_global_switch_back();
56
- do_action('em_categories', $this);
57
  }
58
-
59
- function get_post(){
60
- self::ms_global_switch();
61
- $this->categories = array();
62
- if(!empty($_POST['event_categories']) && self::array_is_numeric($_POST['event_categories'])){
63
- foreach( $_POST['event_categories'] as $term ){
64
- $this->categories[$term] = new EM_Category($term);
65
- }
 
66
  }
67
- self::ms_global_switch_back();
68
- do_action('em_categories_get_post', $this);
69
  }
70
 
71
- function save(){
72
- $term_slugs = array();
73
- foreach($this->categories as $EM_Category){
74
- /* @var $EM_Category EM_Category */
75
- if( !empty($EM_Category->slug) ) $term_slugs[] = $EM_Category->slug; //save of category will soft-fail if slug is empty
76
- }
77
- if( count($term_slugs) == 0 && get_option('dbem_default_category') > 0 ){
78
- $default_term = get_term_by('id',get_option('dbem_default_category'), EM_TAXONOMY_CATEGORY);
79
- if($default_term) $term_slugs[] = $default_term->slug;
80
- }
81
- if( is_multisite() ){
82
- //In MS Global mode, we also save category meta information for global lookups
83
- if( EM_MS_GLOBAL && !empty($this->event_id) ){
84
- //delete categories
85
- $this->save_index();
86
- }
87
- if( !EM_MS_GLOBAL || is_main_site() ){
88
- wp_set_object_terms($this->post_id, $term_slugs, EM_TAXONOMY_CATEGORY);
89
- }
90
- }else{
91
- wp_set_object_terms($this->post_id, $term_slugs, EM_TAXONOMY_CATEGORY);
92
  }
93
- do_action('em_categories_save', $this);
94
  }
95
 
96
- function save_index(){
97
- global $wpdb;
98
- $wpdb->query('DELETE FROM '.EM_META_TABLE." WHERE object_id='{$this->event_id}' AND meta_key='event-category'");
99
- foreach($this->categories as $EM_Category){
100
- $wpdb->insert(EM_META_TABLE, array('meta_value'=>$EM_Category->term_id,'object_id'=>$this->event_id,'meta_key'=>'event-category'));
101
- }
102
- }
103
-
104
- public static function get( $args = array() ) {
105
- //Quick version, we can accept an array of IDs, which is easy to retrieve
106
- self::ms_global_switch();
107
- if( self::array_is_numeric($args) ){ //Array of numbers, assume they are event IDs to retreive
108
- $results = get_terms( EM_TAXONOMY_CATEGORY );
109
- $categories = array();
110
- foreach($results as $result){
111
- if( in_array($result->term_id, $args) ){
112
- $categories[$result->term_id] = new EM_Category($result);
113
- }
114
- }
115
- }else{
116
- //We assume it's either an empty array or array of search arguments to merge with defaults
117
- $term_args = self::get_default_search($args);
118
- $results = get_terms( EM_TAXONOMY_CATEGORY, $term_args);
119
-
120
- //If we want results directly in an array, why not have a shortcut here? We don't use this in code, so if you're using it and filter the em_categories_get hook, you may want to do this one too.
121
- if( !empty($args['array']) ){
122
- return apply_filters('em_categories_get_array', $results, $args);
123
- }
124
-
125
- //Make returned results EM_Category objects
126
- $results = (is_array($results)) ? $results:array();
127
- $categories = array();
128
- foreach ( $results as $category ){
129
- $categories[$category->term_id] = new EM_Category($category);
130
- }
131
- }
132
- self::ms_global_switch_back();
133
- return apply_filters('em_categories_get', $categories, $args);
134
  }
135
 
136
- public static function output( $args ){
137
- global $EM_Category;
138
- $EM_Category_old = $EM_Category; //When looping, we can replace EM_Category global with the current event in the loop
139
- //get page number if passed on by request (still needs pagination enabled to have effect)
140
- $page_queryvar = !empty($args['page_queryvar']) ? $args['page_queryvar'] : 'pno';
141
- if( !array_key_exists('page',$args) && !empty($args['pagination']) && !empty($_REQUEST[$page_queryvar]) && is_numeric($_REQUEST[$page_queryvar]) ){
142
- $page = $args['page'] = $_REQUEST[$page_queryvar];
143
- }
144
- //Can be either an array for the get search or an array of EM_Category objects
145
- if( is_object(current($args)) && get_class((current($args))) == 'EM_Category' ){
146
- $func_args = func_get_args();
147
- $categories = $func_args[0];
148
- $args = (!empty($func_args[1])) ? $func_args[1] : array();
149
- $args = apply_filters('em_categories_output_args', self::get_default_search($args), $categories);
150
- $limit = ( !empty($args['limit']) && is_numeric($args['limit']) ) ? $args['limit']:false;
151
- $offset = ( !empty($args['offset']) && is_numeric($args['offset']) ) ? $args['offset']:0;
152
- $page = ( !empty($args['page']) && is_numeric($args['page']) ) ? $args['page']:1;
153
- }else{
154
- $args = apply_filters('em_categories_output_args', self::get_default_search($args) );
155
- $limit = ( !empty($args['limit']) && is_numeric($args['limit']) ) ? $args['limit']:false;
156
- $offset = ( !empty($args['offset']) && is_numeric($args['offset']) ) ? $args['offset']:0;
157
- $page = ( !empty($args['page']) && is_numeric($args['page']) ) ? $args['page']:1;
158
- $args['limit'] = $args['offset'] = $args['page'] = false; //we count overall categories here
159
- $categories = self::get( $args );
160
- $args['limit'] = $limit;
161
- $args['offset'] = $offset;
162
- $args['page'] = $page;
163
- }
164
- //What format shall we output this to, or use default
165
- $format = ( $args['format'] == '' ) ? get_option( 'dbem_categories_list_item_format' ) : $args['format'] ;
166
-
167
- $output = "";
168
- $categories_count = count($categories);
169
- $categories = apply_filters('em_categories_output_categories', $categories);
170
- if ( count($categories) > 0 ) {
171
- $category_count = 0;
172
- $categories_shown = 0;
173
- foreach ( $categories as $EM_Category ) {
174
- if( ($categories_shown < $limit || empty($limit)) && ($category_count >= $offset || $offset === 0) ){
175
- $output .= $EM_Category->output($format);
176
- $categories_shown++;
177
- }
178
- $category_count++;
179
- }
180
- //Add headers and footers to output
181
- if( $format == get_option( 'dbem_categories_list_item_format' ) ){
182
- //we're using the default format, so if a custom format header or footer is supplied, we can override it, if not use the default
183
- $format_header = empty($args['format_header']) ? get_option('dbem_categories_list_item_format_header') : $args['format_header'];
184
- $format_footer = empty($args['format_footer']) ? get_option('dbem_categories_list_item_format_footer') : $args['format_footer'];
185
- }else{
186
- //we're using a custom format, so if a header or footer isn't specifically supplied we assume it's blank
187
- $format_header = !empty($args['format_header']) ? $args['format_header'] : '' ;
188
- $format_footer = !empty($args['format_footer']) ? $args['format_footer'] : '' ;
189
- }
190
- $output = $format_header . $output . $format_footer;
191
- //Pagination (if needed/requested)
192
- if( !empty($args['pagination']) && !empty($limit) && $categories_count >= $limit ){
193
- //Show the pagination links (unless there's less than 10 events, or the custom limit)
194
- $output .= self::get_pagination_links($args, $categories_count);
195
- }
196
- } else {
197
- $output = get_option ( 'dbem_no_categories_message' );
198
- }
199
- //FIXME check if reference is ok when restoring object, due to changes in php5 v 4
200
- $EM_Category_old= $EM_Category;
201
- return apply_filters('em_categories_output', $output, $categories, $args);
202
  }
203
 
204
  public static function get_pagination_links($args, $count, $search_action = 'search_cats', $default_args = array()){
205
- //get default args if we're in a search, supply to parent since we can't depend on late static binding until WP requires PHP 5.3 or later
206
- if( empty($default_args) && (!empty($args['ajax']) || !empty($_REQUEST['action']) && $_REQUEST['action'] == $search_action) ){
207
- $default_args = self::get_default_search();
208
- $default_args['limit'] = get_option('dbem_categories_default_limit');
209
- }
210
  return parent::get_pagination_links($args, $count, $search_action, $default_args);
211
  }
212
 
213
  public static function get_post_search($args = array(), $filter = false, $request = array(), $accepted_args = array()){
214
- //supply $accepted_args to parent argument since we can't depend on late static binding until WP requires PHP 5.3 or later
215
- $accepted_args = !empty($accepted_args) ? $accepted_args : array_keys(self::get_default_search());
216
- return apply_filters('em_tags_get_post_search', parent::get_post_search($args, $filter, $request, $accepted_args));
217
  }
218
 
219
- function has( $search ){
220
- if( is_numeric($search) ){
221
- foreach($this->categories as $EM_Category){
222
- if($EM_Category->term_id == $search) return apply_filters('em_categories_has', true, $search, $this);
223
- }
224
- }else{
225
- foreach($this->categories as $EM_Category){
226
- if($EM_Category->slug == $search || $EM_Category->name == $search ) return apply_filters('em_categories_has', true, $search, $this);
227
- }
228
- }
229
- return apply_filters('em_categories_has', false, $search, $this);
230
- }
231
-
232
- function get_first(){
233
- foreach($this->categories as $EM_Category){
234
- return $EM_Category;
235
- }
236
- return false;
237
- }
238
-
239
- function get_ids(){
240
- $ids = array();
241
- foreach($this->categories as $EM_Category){
242
- if( !empty($EM_Category->term_id) ){
243
- $ids[] = $EM_Category->term_id;
244
- }
245
- }
246
- return $ids;
247
- }
248
-
249
- /**
250
- * Gets the event for this object, or a blank event if none exists
251
- * @return EM_Event
252
- */
253
- function get_event(){
254
- if( is_numeric($this->event_id) ){
255
- return em_get_event($this->event_id);
256
- }else{
257
- return new EM_Event();
258
- }
259
- }
260
-
261
- /*
262
- * Adds custom calendar search defaults
263
- * @param array $array_or_defaults may be the array to override defaults
264
- * @param array $array
265
- * @return array
266
- * @uses EM_Object#get_default_search()
267
- */
268
  public static function get_default_search( $array_or_defaults = array(), $array = array() ){
269
- $defaults = array(
270
- //added from get_terms, so they don't get filtered out
271
- 'orderby' => get_option('dbem_categories_default_orderby'), 'order' => get_option('dbem_categories_default_order'),
272
- 'hide_empty' => false, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(),
273
- 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '',
274
- 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '',
275
- 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core'
276
- );
277
- //sort out whether defaults were supplied or just the array of search values
278
- if( empty($array) ){
279
- $array = $array_or_defaults;
280
- }else{
281
- $defaults = array_merge($defaults, $array_or_defaults);
282
- }
283
- return apply_filters('em_categories_get_default_search', parent::get_default_search($defaults,$array), $array, $defaults);
284
  }
285
-
286
- //Iterator Implementation
287
- public function rewind(){
288
- reset($this->categories);
289
- }
290
- public function current(){
291
- $var = current($this->categories);
292
- return $var;
293
- }
294
- public function key(){
295
- $var = key($this->categories);
296
- return $var;
297
- }
298
- public function next(){
299
- $var = next($this->categories);
300
- return $var;
301
- }
302
- public function valid(){
303
- $key = key($this->categories);
304
- $var = ($key !== NULL && $key !== FALSE);
305
- return $var;
306
- }
307
  }
1
  <?php
2
+ class EM_Categories extends EM_Taxonomy_Terms {
3
 
4
+ //Overridable functions
5
+ protected $is_ms_global = true;
6
+ protected $taxonomy = 'event-categories';
7
+ protected $meta_key = 'event-category';
8
+ protected $terms_name = 'categories';
9
+ protected $term_class = 'EM_Category';
 
 
 
 
 
 
 
 
 
10
 
11
  /**
12
  * Creates an EM_Categories instance, currently accepts an EM_Event object (gets all Categories for that event) or array of any EM_Category objects, which can be manipulated in bulk with helper functions.
14
  * @return null
15
  */
16
  function __construct( $data = false ){
17
+ $this->taxonomy = EM_TAXONOMY_CATEGORY;
18
+ parent::__construct($data);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  }
20
+
21
+ /**
22
+ * Legacy get overload for any use of $EM_Categories->tags
23
+ * @param string $var_name
24
+ * @return array|NULL
25
+ */
26
+ function __get( $var_name ){
27
+ if( $var_name == 'categories' ){
28
+ return $this->terms;
29
  }
30
+ return null;
 
31
  }
32
 
33
+ /**
34
+ * Legacy overload for use of empty($this->categories)
35
+ * @param string $var_name
36
+ * @return boolean
37
+ */
38
+ function __isset( $var_name ){
39
+ if( $var_name == 'categories' ){
40
+ return !empty($this->terms);
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  }
42
+ return !empty($this->$var_name);
43
  }
44
 
45
+ //Functions we won't need when PHP 5.3 minimum allows for use of LSB
46
+
47
+ public static function get( $args = array() ){
48
+ self::$instance = new EM_Categories();
49
+ return parent::get($args);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  }
51
 
52
+ public static function output( $args = array() ){
53
+ self::$instance = new EM_Categories();
54
+ return parent::output($args);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  }
56
 
57
  public static function get_pagination_links($args, $count, $search_action = 'search_cats', $default_args = array()){
58
+ self::$instance = new EM_Categories();
 
 
 
 
59
  return parent::get_pagination_links($args, $count, $search_action, $default_args);
60
  }
61
 
62
  public static function get_post_search($args = array(), $filter = false, $request = array(), $accepted_args = array()){
63
+ self::$instance = new EM_Categories();
64
+ return parent::get_post_search($args, $filter, $request, $accepted_args);
 
65
  }
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  public static function get_default_search( $array_or_defaults = array(), $array = array() ){
68
+ self::$instance = new EM_Categories();
69
+ return parent::get_default_search($defaults,$array);
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  }
classes/em-category-taxonomy.php DELETED
@@ -1,170 +0,0 @@
1
- <?php
2
- class EM_Category_Taxonomy{
3
- public static function init(){
4
- if( !is_admin() ){
5
- add_filter('taxonomy_template', array('EM_Category_Taxonomy','template'), 99);
6
- add_filter('parse_query', array('EM_Category_Taxonomy','parse_query'));
7
- }
8
- }
9
- /**
10
- * Overrides archive pages e.g. locations, events, event categories, event tags based on user settings
11
- * @param string $template
12
- * @return string
13
- */
14
- public static function template($template = ''){
15
- global $wp_query, $EM_Category, $em_category_id, $post;
16
- if( is_tax(EM_TAXONOMY_CATEGORY) && !locate_template('taxonomy-'.EM_TAXONOMY_CATEGORY.'.php') && get_option('dbem_cp_categories_formats', true) ){
17
- $EM_Category = em_get_category($wp_query->queried_object->term_id);
18
- if( get_option('dbem_categories_page') ){
19
- //less chance for things to go wrong with themes etc. so just reset the WP_Query to think it's a page rather than taxonomy
20
- $wp_query = new WP_Query(array('page_id'=> get_option('dbem_categories_page')));
21
- $wp_query->queried_object = $wp_query->post;
22
- $wp_query->queried_object_id = $wp_query->post->ID;
23
- $wp_query->post->post_title = $wp_query->posts[0]->post_title = $wp_query->queried_object->post_title = $EM_Category->output(get_option('dbem_category_page_title_format'));
24
- if( !function_exists('yoast_breadcrumb') ){ //not needed by WP SEO Breadcrumbs
25
- $wp_query->post->post_parent = $wp_query->posts[0]->post_parent = $wp_query->queried_object->post_parent = $EM_Category->output(get_option('dbem_categories_page'));
26
- }
27
- $post = $wp_query->post;
28
- }else{
29
- //we don't have a categories page, so we create a fake page
30
- $wp_query->posts = array();
31
- $wp_query->posts[0] = new stdClass();
32
- $wp_query->posts[0]->post_title = $wp_query->queried_object->post_title = $EM_Category->output(get_option('dbem_category_page_title_format'));
33
- $post_array = array('ID', 'post_author', 'post_date','post_date_gmt','post_content','post_excerpt','post_status','comment_status','ping_status','post_password','post_name','to_ping','pinged','post_modified','post_modified_gmt','post_content_filtered','post_parent','guid','menu_order','post_type','post_mime_type','comment_count','filter');
34
- foreach($post_array as $post_array_item){
35
- $wp_query->posts[0]->$post_array_item = '';
36
- }
37
- $wp_query->post = $wp_query->posts[0];
38
- $wp_query->post_count = 1;
39
- $wp_query->found_posts = 1;
40
- $wp_query->max_num_pages = 1;
41
- //tweak flags for determining page type
42
- $wp_query->is_tax = 0;
43
- $wp_query->is_page = 1;
44
- $wp_query->is_single = 0;
45
- $wp_query->is_singular = 1;
46
- $wp_query->is_archive = 0;
47
- }
48
- remove_filter('the_content', 'em_content'); //one less filter
49
- add_filter('the_content', array('EM_Category_Taxonomy','the_content')); //come in slightly early and consider other plugins
50
- add_filter('wpseo_breadcrumb_links',array('EM_Category_Taxonomy','wpseo_breadcrumb_links')); //for Yoast WP SEO
51
- $wp_query->em_category_id = $em_category_id = $EM_Category->term_id; //we assign $em_category_id just in case other themes/plugins do something out of the ordinary to WP_Query
52
- $template = locate_template(array('page.php','index.php'),false); //category becomes a page
53
- do_action('em_category_taxonomy_template');
54
- }
55
- return $template;
56
- }
57
-
58
- public static function the_content($content){
59
- global $wp_query, $EM_Category, $post, $em_category_id;
60
- $is_categories_page = $post->ID == get_option('dbem_categories_page');
61
- $category_flag = (!empty($wp_query->em_category_id) || !empty($em_category_id));
62
- if( ($is_categories_page && $category_flag) || (empty($post->ID) && $category_flag) ){
63
- $EM_Category = empty($wp_query->em_category_id) ? em_get_category($em_category_id):em_get_category($wp_query->em_category_id);
64
- ob_start();
65
- em_locate_template('templates/category-single.php',true);
66
- return ob_get_clean();
67
- }
68
- return $content;
69
- }
70
-
71
- public static function parse_query( ){
72
- global $wp_query, $post;
73
- if( is_tax(EM_TAXONOMY_CATEGORY) ){
74
- //Scope is future
75
- $today = strtotime(date('Y-m-d', current_time('timestamp')));
76
- if( get_option('dbem_events_current_are_past') ){
77
- $wp_query->query_vars['meta_query'][] = array( 'key' => '_start_ts', 'value' => $today, 'compare' => '>=' );
78
- }else{
79
- $wp_query->query_vars['meta_query'][] = array( 'key' => '_end_ts', 'value' => $today, 'compare' => '>=' );
80
- }
81
- if( get_option('dbem_categories_default_archive_orderby') == 'title'){
82
- $wp_query->query_vars['orderby'] = 'title';
83
- }else{
84
- $wp_query->query_vars['orderby'] = 'meta_value_num';
85
- $wp_query->query_vars['meta_key'] = get_option('dbem_categories_default_archive_orderby','_start_ts');
86
- }
87
- $wp_query->query_vars['order'] = get_option('dbem_categories_default_archive_order','ASC');
88
- }elseif( !empty($wp_query->em_category_id) ){
89
- $post = $wp_query->post;
90
- }
91
- }
92
-
93
- public static function wpseo_breadcrumb_links( $links ){
94
- global $wp_query;
95
- array_pop($links);
96
- if( get_option('dbem_categories_page') ){
97
- $links[] = array('id'=> get_option('dbem_categories_page'));
98
- }
99
- $links[] = array('text'=> $wp_query->posts[0]->post_title);
100
- return $links;
101
- }
102
- }
103
- EM_Category_Taxonomy::init();
104
-
105
- /**
106
- * Create an array of Categories. Copied from Walker_CategoryDropdown, but makes it possible for the selected argument to be an array.
107
- *
108
- * @package WordPress
109
- * @since 2.1.0
110
- * @uses Walker
111
- */
112
- class EM_Walker_Category extends Walker {
113
- /**
114
- * @see Walker::$tree_type
115
- * @since 2.1.0
116
- * @var string
117
- */
118
- var $tree_type = 'event-category';
119
-
120
- /**
121
- * @see Walker::$db_fields
122
- * @since 2.1.0
123
- * @todo Decouple this
124
- * @var array
125
- */
126
- var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');
127
-
128
- function __construct(){
129
- $tree_type = EM_TAXONOMY_CATEGORY;
130
- }
131
-
132
- /**
133
- * @see Walker::start_el()
134
- */
135
- function start_el( &$output, $object, $depth = 0, $args = array(), $current_object_id = 0 ) {
136
- $pad = str_repeat('&nbsp;', $depth * 3);
137
- $cat_name = $object->name;
138
- $name = !empty($args['name']) ? $args['name']:'event_categories[]';
139
- $output .= !empty($args['before']) ? $args['after']:'';
140
- $output .= $pad."<input type=\"checkbox\" name=\"$name\" class=\"level-$depth\" value=\"".$object->term_id."\"";
141
- if ( (is_array($args['selected']) && in_array($object->term_id, $args['selected'])) || ($object->term_id == $args['selected']) )
142
- $output .= ' checked="checked"';
143
- $output .= ' /> ';
144
- $output .= $cat_name;
145
- $output .= !empty($args['after']) ? $args['after']:'<br />';
146
- }
147
- }
148
-
149
- /**
150
- * Create an array of Categories. Copied from Walker_CategoryDropdown, but makes it possible for the selected argument to be an array.
151
- *
152
- * @package WordPress
153
- * @since 2.1.0
154
- * @uses Walker
155
- */
156
- class EM_Walker_CategoryMultiselect extends EM_Walker_Category {
157
- /**
158
- * @see Walker::start_el()
159
- */
160
- function start_el( &$output, $object, $depth = 0, $args = array(), $current_object_id = 0 ) {
161
- $pad = str_repeat('&nbsp;', $depth * 3);
162
- $cat_name = $object->name;
163
- $output .= "\t<option class=\"level-$depth\" value=\"".$object->term_id."\"";
164
- if ( (is_array($args['selected']) && in_array($object->term_id, $args['selected'])) || ($object->term_id == $args['selected']) )
165
- $output .= ' selected="selected"';
166
- $output .= '>';
167
- $output .= $pad.$cat_name;
168
- $output .= "</option>\n";
169
- }
170
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/em-category.php CHANGED
@@ -1,299 +1,32 @@
1
  <?php
2
- /**
3
- * Get an category in a db friendly way, by checking globals and passed variables to avoid extra class instantiations
4
- * @param mixed $id
5
- * @return EM_Category
6
- */
7
- function em_get_category($id = false) {
8
- global $EM_Category;
9
- //check if it's not already global so we don't instantiate again
10
- if( is_object($EM_Category) && get_class($EM_Category) == 'EM_Category' ){
11
- if( $EM_Category->term_id == $id ){
12
- return $EM_Category;
13
- }elseif( is_object($id) && $EM_Category->term_id == $id->term_id ){
14
- return $EM_Category;
15
- }
16
- }
17
- if( is_object($id) && get_class($id) == 'EM_Category' ){
18
- return $id;
19
- }else{
20
- return new EM_Category($id);
21
- }
22
- }
23
- class EM_Category extends EM_Object {
24
- //Taxonomy Fields
25
- var $id = '';
26
- var $term_id;
27
- var $name;
28
- var $slug;
29
- var $term_group;
30
- var $term_taxonomy_id;
31
- var $taxonomy;
32
- var $description = '';
33
- var $parent = 0;
34
- var $count;
35
- //extra attributes imposed by EM_Category
36
- var $image_url = '';
37
- var $color;
38
-
39
- /**
40
- * Gets data from POST (default), supplied array, or from the database if an ID is supplied
41
- * @param $category_data
42
- * @return null
43
- */
44
- function __construct( $category_data = false ) {
45
- global $wpdb;
46
- self::ms_global_switch();
47
- //Initialize
48
- $category = array();
49
- if( !empty($category_data) ){
50
- //Load category data
51
- if( is_object($category_data) && !empty($category_data->taxonomy) && $category_data->taxonomy == EM_TAXONOMY_CATEGORY ){
52
- $category = $category_data;
53
- }elseif( !is_numeric($category_data) ){
54
- $category = get_term_by('slug', $category_data, EM_TAXONOMY_CATEGORY);
55
- if( !$category ){
56
- $category = get_term_by('name', $category_data, EM_TAXONOMY_CATEGORY);
57
- }
58
- }else{
59
- $category = get_term_by('id', $category_data, EM_TAXONOMY_CATEGORY);
60
- }
61
- }
62
- if( is_object($category) || is_array($category) ){
63
- foreach($category as $key => $value){
64
- $this->$key = $value;
65
- }
66
- }
67
- $this->id = $this->term_id; //backward compatability
68
- self::ms_global_switch_back();
69
- do_action('em_category',$this, $category_data);
70
- }
71
-
72
- function get_color(){
73
- if( empty($this->color) ){
74
- global $wpdb;
75
- $color = $wpdb->get_var('SELECT meta_value FROM '.EM_META_TABLE." WHERE object_id='{$this->term_id}' AND meta_key='category-bgcolor' LIMIT 1");
76
- $this->color = ($color != '') ? $color:get_option('dbem_category_default_color', '#FFFFFF');
77
- }
78
- return $this->color;
79
- }
80
-
81
- function get_image_url( $size = 'full' ){
82
- if( empty($this->image_url) ){
83
- global $wpdb;
84
- $image_url = $wpdb->get_var('SELECT meta_value FROM '.EM_META_TABLE." WHERE object_id='{$this->term_id}' AND meta_key='category-image' LIMIT 1");
85
- $this->image_url = ($image_url != '') ? $image_url:'';
86
- }
87
- return $this->image_url;
88
- }
89
-
90
- function get_image_id(){
91
- if( empty($this->image_id) ){
92
- global $wpdb;
93
- $image_id = $wpdb->get_var('SELECT meta_value FROM '.EM_META_TABLE." WHERE object_id='{$this->term_id}' AND meta_key='category-image-id' LIMIT 1");
94
- $this->image_id = ($image_id != '') ? $image_id:'';
95
- }
96
- return $this->image_id;
97
- }
98
-
99
- function get_url(){
100
- if( empty($this->link) ){
101
- self::ms_global_switch();
102
- $this->link = get_term_link($this->slug, EM_TAXONOMY_CATEGORY);
103
- self::ms_global_switch_back();
104
- if ( is_wp_error($this->link) ) $this->link = '';
105
- }
106
- return apply_filters('em_category_get_url', $this->link);
107
- }
108
-
109
- function get_ical_url(){
110
- global $wp_rewrite;
111
- if( !empty($wp_rewrite) && $wp_rewrite->using_permalinks() ){
112
- $return = trailingslashit($this->get_url()).'ical/';
113
- }else{
114
- $return = em_add_get_params($this->get_url(), array('ical'=>1));
115
- }
116
- return apply_filters('em_category_get_ical_url', $return);
117
- }
118
-
119
- function get_rss_url(){
120
- global $wp_rewrite;
121
- if( !empty($wp_rewrite) && $wp_rewrite->using_permalinks() ){
122
- $return = trailingslashit($this->get_url()).'feed/';
123
- }else{
124
- $return = em_add_get_params($this->get_url(), array('feed'=>1));
125
- }
126
- return apply_filters('em_category_get_rss_url', $return);
127
- }
128
 
 
 
 
 
 
129
  /**
130
- * deprecated, don't use.
131
- * @return mixed
 
 
132
  */
133
- function has_events(){
134
- global $wpdb;
135
- return false;
136
- }
137
-
138
- function output_single($target = 'html'){
139
- $format = get_option ( 'dbem_category_page_format' );
140
- return apply_filters('em_category_output_single', $this->output($format, $target), $this, $target);
141
  }
142
 
143
- function output($format, $target="html") {
144
- preg_match_all('/\{([a-zA-Z0-9_]+)\}([^{]+)\{\/[a-zA-Z0-9_]+\}/', $format, $conditionals);
145
- if( count($conditionals[0]) > 0 ){
146
- //Check if the language we want exists, if not we take the first language there
147
- foreach($conditionals[1] as $key => $condition){
148
- $format = str_replace($conditionals[0][$key], apply_filters('em_category_output_condition', '', $condition, $conditionals[0][$key], $this), $format);
149
- }
150
- }
151
- $category_string = $format;
152
- preg_match_all("/(#@?_?[A-Za-z0-9]+)({([a-zA-Z0-9,]+)})?/", $format, $placeholders);
153
- $replaces = array();
154
- foreach($placeholders[1] as $key => $result) {
155
- $replace = '';
156
- $full_result = $placeholders[0][$key];
157
- switch( $result ){
158
- case '#_CATEGORYNAME':
159
- $replace = $this->name;
160
- break;
161
- case '#_CATEGORYID':
162
- $replace = $this->term_id;
163
- break;
164
- case '#_CATEGORYNOTES':
165
- case '#_CATEGORYDESCRIPTION':
166
- $replace = $this->description;
167
- break;
168
- case '#_CATEGORYIMAGE':
169
- case '#_CATEGORYIMAGEURL':
170
- if( $this->get_image_url() != ''){
171
- $image_url = esc_url($this->get_image_url());
172
- if($result == '#_CATEGORYIMAGEURL'){
173
- $replace = $image_url;
174
- }else{
175
- if( empty($placeholders[3][$key]) ){
176
- $replace = "<img src='".esc_url($this->get_image_url())."' alt='".esc_attr($this->name)."'/>";
177
- }else{
178
- $image_size = explode(',', $placeholders[3][$key]);
179
- if( self::array_is_numeric($image_size) && count($image_size) > 1 ){
180
- if( $this->get_image_id() ){
181
- //get a thumbnail
182
- if( get_option('dbem_disable_thumbnails') ){
183
- $image_attr = '';
184
- $image_args = array();
185
- if( empty($image_size[1]) && !empty($image_size[0]) ){
186
- $image_attr = 'width="'.$image_size[0].'"';
187
- $image_args['w'] = $image_size[0];
188
- }elseif( empty($image_size[0]) && !empty($image_size[1]) ){
189
- $image_attr = 'height="'.$image_size[1].'"';
190
- $image_args['h'] = $image_size[1];
191
- }elseif( !empty($image_size[0]) && !empty($image_size[1]) ){
192
- $image_attr = 'width="'.$image_size[0].'" height="'.$image_size[1].'"';
193
- $image_args = array('w'=>$image_size[0], 'h'=>$image_size[1]);
194
- }
195
- $replace = "<img src='".esc_url(em_add_get_params($image_url, $image_args))."' alt='".esc_attr($this->name)."' $image_attr />";
196
- }else{
197
- //since we previously didn't store image ids along with the url to the image (since taxonomies don't allow normal featured images), sometimes we won't be able to do this, which is why we check there's a valid image id first
198
- self::ms_global_switch();
199
- $replace = wp_get_attachment_image($this->get_image_id(), $image_size);
200
- self::ms_global_switch_back();
201
- }
202
- }
203
- }else{
204
- $replace = "<img src='".esc_url($this->get_image_url())."' alt='".esc_attr($this->name)."'/>";
205
- }
206
- }
207
- }
208
- }
209
- break;
210
- case '#_CATEGORYCOLOR':
211
- $replace = $this->get_color();
212
- break;
213
- case '#_CATEGORYLINK':
214
- case '#_CATEGORYURL':
215
- $link = $this->get_url();
216
- $replace = ($result == '#_CATEGORYURL') ? $link : '<a href="'.$link.'">'.esc_html($this->name).'</a>';
217
- break;
218
- case '#_CATEGORYICALURL':
219
- case '#_CATEGORYICALLINK':
220
- $replace = $this->get_ical_url();
221
- if( $result == '#_CATEGORYICALLINK' ){
222
- $replace = '<a href="'.esc_url($replace).'">iCal</a>';
223
- }
224
- break;
225
- case '#_CATEGORYRSSURL':
226
- case '#_CATEGORYRSSLINK':
227
- $replace = $this->get_rss_url();
228
- if( $result == '#_CATEGORYRSSLINK' ){
229
- $replace = '<a href="'.esc_url($replace).'">RSS</a>';
230
- }
231
- break;
232
- case '#_CATEGORYSLUG':
233
- $replace = $this->slug;
234
- break;
235
- case '#_CATEGORYEVENTSPAST': //deprecated, erroneous documentation, left for compatability
236
- case '#_CATEGORYEVENTSNEXT': //deprecated, erroneous documentation, left for compatability
237
- case '#_CATEGORYEVENTSALL': //deprecated, erroneous documentation, left for compatability
238
- case '#_CATEGORYPASTEVENTS':
239
- case '#_CATEGORYNEXTEVENTS':
240
- case '#_CATEGORYALLEVENTS':
241
- //convert deprecated placeholders for compatability
242
- $result = ($result == '#_CATEGORYEVENTSPAST') ? '#_CATEGORYPASTEVENTS':$result;
243
- $result = ($result == '#_CATEGORYEVENTSNEXT') ? '#_CATEGORYNEXTEVENTS':$result;
244
- $result = ($result == '#_CATEGORYEVENTSALL') ? '#_CATEGORYALLEVENTS':$result;
245
- //forget it ever happened? :/
246
- if ($result == '#_CATEGORYPASTEVENTS'){ $scope = 'past'; }
247
- elseif ( $result == '#_CATEGORYNEXTEVENTS' ){ $scope = 'future'; }
248
- else{ $scope = 'all'; }
249
- $events_count = EM_Events::count( array('category'=>$this->term_id, 'scope'=>$scope) );
250
- if ( $events_count > 0 ){
251
- $args = array('category'=>$this->term_id, 'scope'=>$scope, 'pagination'=>1, 'ajax'=>0);
252
- $args['format_header'] = get_option('dbem_category_event_list_item_header_format');
253
- $args['format_footer'] = get_option('dbem_category_event_list_item_footer_format');
254
- $args['format'] = get_option('dbem_category_event_list_item_format');
255
- $args['limit'] = get_option('dbem_category_event_list_limit');
256
- $args['page'] = (!empty($_REQUEST['pno']) && is_numeric($_REQUEST['pno']) )? $_REQUEST['pno'] : 1;
257
- $replace = EM_Events::output($args);
258
- } else {
259
- $replace = get_option('dbem_category_no_events_message','</ul>');
260
- }
261
- break;
262
- case '#_CATEGORYNEXTEVENT':
263
- $events = EM_Events::get( array('category'=>$this->term_id, 'scope'=>'future', 'limit'=>1, 'orderby'=>'event_start_date,event_start_time') );
264
- $replace = get_option('dbem_category_no_event_message');
265
- foreach($events as $EM_Event){
266
- $replace = $EM_Event->output(get_option('dbem_category_event_single_format'));
267
- }
268
- break;
269
- default:
270
- $replace = $full_result;
271
- break;
272
- }
273
- $replaces[$full_result] = apply_filters('em_category_output_placeholder', $replace, $this, $full_result, $target);
274
- }
275
- krsort($replaces);
276
- foreach($replaces as $full_result => $replacement){
277
- $category_string = str_replace($full_result, $replacement , $category_string );
278
- }
279
- return apply_filters('em_category_output', $category_string, $this, $format, $target);
280
- }
281
-
282
- function can_manage( $capability_owner = 'edit_categories', $capability_admin = false, $user_to_check = false ){
283
- global $em_capabilities_array;
284
- //Figure out if this is multisite and require an extra bit of validation
285
- $multisite_check = true;
286
- $can_manage = current_user_can($capability_owner);
287
- //if multisite and supoer admin, just return true
288
- if( is_multisite() && is_super_admin() ){ return true; }
289
- if( EM_MS_GLOBAL && !is_main_site() ){
290
- //User can't admin this bit, as they're on a sub-blog
291
- $can_manage = false;
292
- if(array_key_exists($capability_owner, $em_capabilities_array) ){
293
- $this->add_error( $em_capabilities_array[$capability_owner]);
294
- }
295
- }
296
- return $can_manage;
297
  }
298
  }
299
- ?>
 
 
 
 
 
 
 
 
 
1
  <?php
2
+ class EM_Category extends EM_Taxonomy_Term {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
 
4
+ //static options for EM_Category, but until PHP 5.3 is the WP minimum requirement we'll make them regular properties due to lack of late static binding
5
+ public $option_ms_global = true;
6
+ public $option_name = 'category'; //the singular name of this taxonomy which is used in option names consistent across EM taxonomies
7
+ public $taxonomy = 'EM_TAXONOMY_CATEGORY';
8
+
9
  /**
10
+ * Necessary to supply the $class_name until late static binding is reliably available on all WP sites running PHP 5.3
11
+ * @param string $id
12
+ * @param string $class_name
13
+ * @return EM_Taxonomy
14
  */
15
+ public static function get( $id = false, $class_name = 'EM_Category' ){
16
+ return parent::get($id, $class_name);
 
 
 
 
 
 
17
  }
18
 
19
+ public function can_manage( $capability_owner = 'edit_event_categories', $capability_admin = false, $user_to_check = false ){
20
+ return parent::can_manage($capability_owner, $capability_admin, $user_to_check);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  }
22
  }
23
+
24
+ /**
25
+ * Get an category in a db friendly way, by checking globals and passed variables to avoid extra class instantiations
26
+ * @param mixed $id
27
+ * @return EM_Category
28
+ * @uses EM_Category::get()
29
+ */
30
+ function em_get_category( $id = false ) {
31
+ return EM_Category::get($id);
32
+ }
classes/em-event-post-admin.php CHANGED
@@ -161,7 +161,12 @@ class EM_Event_Post_Admin{
161
  $where_array = array($EM_Event->event_name, $EM_Event->event_owner, $EM_Event->event_slug, $EM_Event->event_private, $EM_Event->event_id);
162
  $sql = $wpdb->prepare("UPDATE ".EM_EVENTS_TABLE." SET event_name=%s, event_owner=%d, event_slug=%s, event_status={$event_status}, event_private=%d WHERE event_id=%d", $where_array);
163
  $wpdb->query($sql);
164
- if( $EM_Event->is_recurring() && $EM_Event->is_published()){
 
 
 
 
 
165
  //recurrences are (re)saved only if event is published
166
  $EM_Event->save_events();
167
  }
161
  $where_array = array($EM_Event->event_name, $EM_Event->event_owner, $EM_Event->event_slug, $EM_Event->event_private, $EM_Event->event_id);
162
  $sql = $wpdb->prepare("UPDATE ".EM_EVENTS_TABLE." SET event_name=%s, event_owner=%d, event_slug=%s, event_status={$event_status}, event_private=%d WHERE event_id=%d", $where_array);
163
  $wpdb->query($sql);
164
+ //If we're saving via quick-edit in MS Global mode, then the categories need to be pushed to the ms global index
165
+ if( EM_MS_GLOBAL && get_option('dbem_categories_enabled') && is_main_site() ){
166
+ $EM_Event->get_categories()->save_index(); //just save to index, WP should have saved the taxonomy data
167
+ }
168
+ //deal with recurrences
169
+ if( $EM_Event->is_recurring() && ($EM_Event->is_published() || (defined('EM_FORCE_RECURRENCES_SAVE') && EM_FORCE_RECURRENCES_SAVE)) ){
170
  //recurrences are (re)saved only if event is published
171
  $EM_Event->save_events();
172
  }
classes/em-event-post.php CHANGED
@@ -13,8 +13,12 @@ class EM_Event_Post {
13
  //override single page with formats?
14
  add_filter('the_content', array('EM_Event_Post','the_content'));
15
  add_filter('the_excerpt_rss', array('EM_Event_Post','the_excerpt_rss'));
 
 
 
16
  if( get_option('dbem_cp_events_excerpt_formats') ){
17
- add_filter('the_excerpt', array('EM_Event_Post','the_excerpt'));
 
18
  }
19
  //display as page template?
20
  if( get_option('dbem_cp_events_template') ){
@@ -91,7 +95,7 @@ class EM_Event_Post {
91
  /**
92
  * Overrides the_excerpt if this is an event post type
93
  */
94
- public static function the_excerpt($content){
95
  global $post;
96
  if( $post->post_type == EM_POST_TYPE_EVENT ){
97
  $EM_Event = em_get_event($post);
@@ -100,6 +104,7 @@ class EM_Event_Post {
100
  }
101
  return $content;
102
  }
 
103
 
104
  public static function the_excerpt_rss( $content ){
105
  global $post;
@@ -113,6 +118,15 @@ class EM_Event_Post {
113
  return $content;
114
  }
115
 
 
 
 
 
 
 
 
 
 
116
  public static function the_content( $content ){
117
  global $post, $EM_Event;
118
  if( $post->post_type == EM_POST_TYPE_EVENT ){
@@ -330,8 +344,8 @@ class EM_Event_Post {
330
  }
331
  if( is_admin() ){
332
  //admin areas don't need special ordering, so make it simple
333
- $wp_query->query_vars['orderby'] = (!empty($_REQUEST['orderby'])) ? $_REQUEST['orderby']:'meta_value_num';
334
- $wp_query->query_vars['meta_key'] = '_start_ts';
335
  $wp_query->query_vars['order'] = (!empty($_REQUEST['order'])) ? $_REQUEST['order']:'ASC';
336
  }else{
337
  if( get_option('dbem_events_default_archive_orderby') == 'title'){
13
  //override single page with formats?
14
  add_filter('the_content', array('EM_Event_Post','the_content'));
15
  add_filter('the_excerpt_rss', array('EM_Event_Post','the_excerpt_rss'));
16
+ //excerpts can trigger the_content which isn't ideal, so we disable the_content between the first and last excerpt calls within WP logic
17
+ add_filter('get_the_excerpt', array('EM_Event_Post','disable_the_content'), 1);
18
+ add_filter('get_the_excerpt', array('EM_Event_Post','enable_the_content'), 100);
19
  if( get_option('dbem_cp_events_excerpt_formats') ){
20
+ //important add this before wp_trim_excerpt hook, as it can screw up things like wp_editor() for WordPress SEO plugin
21
+ add_filter('get_the_excerpt', array('EM_Event_Post','get_the_excerpt'));
22
  }
23
  //display as page template?
24
  if( get_option('dbem_cp_events_template') ){
95
  /**
96
  * Overrides the_excerpt if this is an event post type
97
  */
98
+ public static function get_the_excerpt($content){
99
  global $post;
100
  if( $post->post_type == EM_POST_TYPE_EVENT ){
101
  $EM_Event = em_get_event($post);
104
  }
105
  return $content;
106
  }
107
+ public static function the_excerpt($content){ return self::get_the_excerpt($content); }
108
 
109
  public static function the_excerpt_rss( $content ){
110
  global $post;
118
  return $content;
119
  }
120
 
121
+ public static function enable_the_content( $content ){
122
+ add_filter('the_content', array('EM_Event_Post','the_content'));
123
+ return $content;
124
+ }
125
+ public static function disable_the_content( $content ){
126
+ remove_filter('the_content', array('EM_Event_Post','the_content'));
127
+ return $content;
128
+ }
129
+
130
  public static function the_content( $content ){
131
  global $post, $EM_Event;
132
  if( $post->post_type == EM_POST_TYPE_EVENT ){
344
  }
345
  if( is_admin() ){
346
  //admin areas don't need special ordering, so make it simple
347
+ $wp_query->query_vars['orderby'] = (!empty($_REQUEST['orderby']) && $_REQUEST['orderby'] != 'date-time') ? $_REQUEST['orderby']:'meta_value_num';
348
+ $wp_query->query_vars['meta_key'] = '_start_ts';
349
  $wp_query->query_vars['order'] = (!empty($_REQUEST['order'])) ? $_REQUEST['order']:'ASC';
350
  }else{
351
  if( get_option('dbem_events_default_archive_orderby') == 'title'){
classes/em-event-posts-admin.php CHANGED
@@ -22,10 +22,14 @@ class EM_Event_Posts_Admin{
22
  $row_action_type = is_post_type_hierarchical( EM_POST_TYPE_EVENT ) ? 'page_row_actions' : 'post_row_actions';
23
  add_filter($row_action_type, array('EM_Event_Posts_Admin','row_actions'),10,2);
24
  add_action('admin_head', array('EM_Event_Posts_Admin','admin_head'));
25
- //collumns
26
- add_filter('manage_edit-'.EM_POST_TYPE_EVENT.'_columns' , array('EM_Event_Posts_Admin','columns_add'));
27
- add_filter('manage_'.EM_POST_TYPE_EVENT.'_posts_custom_column' , array('EM_Event_Posts_Admin','columns_output'),10,2 );
28
  }
 
 
 
 
29
  //clean up the views in the admin selection area - WIP
30
  //add_filter('views_edit-'.EM_POST_TYPE_EVENT, array('EM_Event_Posts_Admin','restrict_views'),10,2);
31
  //add_filter('views_edit-event-recurring', array('EM_Event_Posts_Admin','restrict_views'),10,2);
@@ -265,6 +269,12 @@ class EM_Event_Posts_Admin{
265
  }
266
  return $actions;
267
  }
 
 
 
 
 
 
268
  }
269
  add_action('admin_init', array('EM_Event_Posts_Admin','init'));
270
 
@@ -285,14 +295,15 @@ class EM_Event_Recurring_Posts_Admin{
285
  //notices
286
  add_action('admin_notices',array('EM_Event_Recurring_Posts_Admin','admin_notices'));
287
  add_action('admin_head', array('EM_Event_Recurring_Posts_Admin','admin_head'));
288
- //collumns
289
- add_filter('manage_edit-event-recurring_columns' , array('EM_Event_Recurring_Posts_Admin','columns_add'));
290
- add_filter('manage_posts_custom_column' , array('EM_Event_Recurring_Posts_Admin','columns_output'),10,1 );
291
- add_action('restrict_manage_posts', array('EM_Event_Posts_Admin','restrict_manage_posts'));
292
  //actions
293
  $row_action_type = is_post_type_hierarchical( EM_POST_TYPE_EVENT ) ? 'page_row_actions' : 'post_row_actions';
294
  add_filter($row_action_type, array('EM_Event_Recurring_Posts_Admin','row_actions'),10,2);
295
  }
 
 
 
 
 
296
  }
297
 
298
  public static function admin_notices(){
22
  $row_action_type = is_post_type_hierarchical( EM_POST_TYPE_EVENT ) ? 'page_row_actions' : 'post_row_actions';
23
  add_filter($row_action_type, array('EM_Event_Posts_Admin','row_actions'),10,2);
24
  add_action('admin_head', array('EM_Event_Posts_Admin','admin_head'));
25
+
26
+ if( empty($_GET['orderby']) ) $_GET['orderby'] = 'date-time';
27
+ if( empty($_GET['order']) ) $_GET['order'] = 'asc';
28
  }
29
+ //collumns
30
+ add_filter('manage_edit-'.EM_POST_TYPE_EVENT.'_columns' , array('EM_Event_Posts_Admin','columns_add'));
31
+ add_filter('manage_'.EM_POST_TYPE_EVENT.'_posts_custom_column' , array('EM_Event_Posts_Admin','columns_output'),10,2 );
32
+ add_filter('manage_edit-'.EM_POST_TYPE_EVENT.'_sortable_columns', array('EM_Event_Posts_Admin','sortable_columns') );
33
  //clean up the views in the admin selection area - WIP
34
  //add_filter('views_edit-'.EM_POST_TYPE_EVENT, array('EM_Event_Posts_Admin','restrict_views'),10,2);
35
  //add_filter('views_edit-event-recurring', array('EM_Event_Posts_Admin','restrict_views'),10,2);
269
  }
270
  return $actions;
271
  }
272
+
273
+ public static function sortable_columns( $columns ){
274
+ $columns['date-time'] = 'date-time';
275
+ return $columns;
276
+ }
277
+
278
  }
279
  add_action('admin_init', array('EM_Event_Posts_Admin','init'));
280
 
295
  //notices
296
  add_action('admin_notices',array('EM_Event_Recurring_Posts_Admin','admin_notices'));
297
  add_action('admin_head', array('EM_Event_Recurring_Posts_Admin','admin_head'));
 
 
 
 
298
  //actions
299
  $row_action_type = is_post_type_hierarchical( EM_POST_TYPE_EVENT ) ? 'page_row_actions' : 'post_row_actions';
300
  add_filter($row_action_type, array('EM_Event_Recurring_Posts_Admin','row_actions'),10,2);
301
  }
302
+ //collumns
303
+ add_filter('manage_edit-event-recurring_columns' , array('EM_Event_Recurring_Posts_Admin','columns_add'));
304
+ add_filter('manage_posts_custom_column' , array('EM_Event_Recurring_Posts_Admin','columns_output'),10,1 );
305
+ add_action('restrict_manage_posts', array('EM_Event_Posts_Admin','restrict_manage_posts'));
306
+ add_filter( 'manage_edit-event-recurring_sortable_columns', array('EM_Event_Posts_Admin','sortable_columns') );
307
  }
308
 
309
  public static function admin_notices(){
classes/em-event.php CHANGED
@@ -1,6 +1,6 @@
1
  <?php
2
  /**
3
- * Get an event in a db friendly way, by checking globals and passed variables to avoid extra class instantiations
4
  * @param mixed $id
5
  * @param mixed $search_by
6
  * @return EM_Event
@@ -22,6 +22,24 @@ function em_get_event($id = false, $search_by = 'event_id') {
22
  if( is_object($id) && get_class($id) == 'EM_Event' ){
23
  return apply_filters('em_get_event', $id);
24
  }else{
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  return apply_filters('em_get_event', new EM_Event($id,$search_by));
26
  }
27
  }
@@ -102,7 +120,6 @@ class EM_Event extends EM_Object{
102
  'recurrence_id' => array( 'name'=>'recurrence_id', 'type'=>'%d', 'null'=>true ),
103
  'event_status' => array( 'name'=>'status', 'type'=>'%d', 'null'=>true ),
104
  'event_private' => array( 'name'=>'status', 'type'=>'%d', 'null'=>true ),
105
- 'event_attributes' => array( 'name'=>'attributes', 'type'=>'%s', 'null'=>true ),
106
  'blog_id' => array( 'name'=>'blog_id', 'type'=>'%d', 'null'=>true ),
107
  'group_id' => array( 'name'=>'group_id', 'type'=>'%d', 'null'=>true ),
108
  'recurrence' => array( 'name'=>'recurrence', 'type'=>'%d', 'null'=>true ), //is this a recurring event template
@@ -113,8 +130,8 @@ class EM_Event extends EM_Object{
113
  'recurrence_byweekno' => array( 'name'=>'byweekno', 'type'=>'%d', 'null'=>true ), //if monthly which week (-1 is last)
114
  'recurrence_rsvp_days' => array( 'name'=>'recurrence_rsvp_days', 'type'=>'%d', 'null'=>true ), //days before or after start date to generat bookings cut-off date
115
  );
116
- var $post_fields = array('event_slug','event_owner','event_name','event_attributes','post_id','post_content'); //fields that won't be taken from the em_events table anymore
117
- var $recurrence_fields = array('recurrence_interval', 'recurrence_freq', 'recurrence_days', 'recurrence_byday', 'recurrence_byweekno');
118
 
119
  var $image_url = '';
120
  /**
@@ -263,6 +280,9 @@ class EM_Event extends EM_Object{
263
  $event_post = get_post($results['post_id']);
264
  }
265
  }else{
 
 
 
266
  if(!$is_post){
267
  if( is_multisite() && (is_numeric($search_by) || $search_by == '') ){
268
  if( $search_by == '' ) $search_by = get_current_site()->blog_id;
@@ -286,9 +306,22 @@ class EM_Event extends EM_Object{
286
  0 => __('Pending','events-manager'),
287
  1 => __('Approved','events-manager')
288
  );
 
 
 
 
 
289
  do_action('em_event', $this, $id, $search_by);
290
  }
291
 
 
 
 
 
 
 
 
 
292
  function load_postdata($event_post, $search_by = false){
293
  //load event post object if it's an actual object and also a post type of our event CPT names
294
  if( is_object($event_post) && ($event_post->post_type == 'event-recurring' || $event_post->post_type == EM_POST_TYPE_EVENT) ){
@@ -310,7 +343,7 @@ class EM_Event extends EM_Object{
310
  foreach($event_meta as $event_meta_key => $event_meta_val){
311
  $field_name = substr($event_meta_key, 1);
312
  if($event_meta_key[0] != '_'){
313
- $this->event_attributes[$event_meta_key] = ( count($event_meta_val) > 1 ) ? $event_meta_val:$event_meta_val[0];
314
  }elseif( is_string($field_name) && !in_array($field_name, $this->post_fields) ){
315
  if( array_key_exists($field_name, $this->fields) ){
316
  $this->$field_name = $event_meta_val[0];
@@ -370,15 +403,22 @@ class EM_Event extends EM_Object{
370
  $this->start = strtotime($this->event_start_date." ".$this->event_start_time, current_time('timestamp'));
371
  $this->end = strtotime($this->event_end_date." ".$this->event_end_time, current_time('timestamp'));
372
  }
 
 
373
  }
374
 
375
  function get_event_meta($blog_id = false){
 
376
  if( is_numeric($blog_id) && $blog_id > 0 && is_multisite() ){
377
  // if in multisite mode, switch blogs quickly to get the right post meta.
378
  switch_to_blog($blog_id);
379
  $event_meta = get_post_meta($this->post_id);
380
  restore_current_blog();
381
  $this->blog_id = $blog_id;
 
 
 
 
382
  }else{
383
  $event_meta = get_post_meta($this->post_id);
384
  }
@@ -408,7 +448,7 @@ class EM_Event extends EM_Object{
408
  //get the rest and validate (optional)
409
  $this->get_post_meta(false);
410
  $result = $validate ? $this->validate():true; //validate both post and meta, otherwise return true
411
- return apply_filters('em_event_get_post', $result, $this);
412
  }
413
 
414
  /**
@@ -543,7 +583,7 @@ class EM_Event extends EM_Object{
543
  }elseif( !$preview_autosave && ($can_manage_bookings || !$this->event_rsvp) ){
544
  if( empty($_POST['event_rsvp']) && $this->event_rsvp ) $deleting_bookings = true;
545
  $this->event_rsvp = 0;
546
- $this->event_rsvp_time = '00:00:00';
547
  }
548
 
549
  //Sort out event attributes - note that custom post meta now also gets inserted here automatically (and is overwritten by these attributes)
@@ -556,12 +596,12 @@ class EM_Event extends EM_Object{
556
  if( (in_array($att_key, $event_available_attributes['names']) || array_key_exists($att_key, $this->event_attributes) ) ){
557
  $this->event_attributes[$att_key] = '';
558
  $att_vals = isset($event_available_attributes['values'][$att_key]) ? count($event_available_attributes['values'][$att_key]) : 0;
559
- if( !empty($att_value) ){
560
  if( $att_vals <= 1 || ($att_vals > 1 && in_array($att_value, $event_available_attributes['values'][$att_key])) ){
561
  $this->event_attributes[$att_key] = wp_unslash($att_value);
562
  }
563
  }
564
- if( empty($att_value) && $att_vals > 1){
565
  $this->event_attributes[$att_key] = wp_unslash(wp_kses($event_available_attributes['values'][$att_key][0], $allowedtags));
566
  }
567
  }
@@ -647,6 +687,10 @@ class EM_Event extends EM_Object{
647
  //new event so we create everything from scratch
648
  $this->recurring_reschedule = $this->recurring_recreate_bookings = true;
649
  }
 
 
 
 
650
  }
651
  //categories in MS GLobal
652
  if(EM_MS_GLOBAL && !is_main_site() && get_option('dbem_categories_enabled') ){
@@ -840,18 +884,40 @@ class EM_Event extends EM_Object{
840
  }
841
  }
842
  //Update Post Meta
843
- foreach($this->fields as $key => $field_info){
 
 
844
  if( !in_array($key, $this->post_fields) && $key != 'event_attributes' ){
845
- update_post_meta($this->post_id, '_'.$key, $this->$key);
846
- }elseif($key == 'event_attributes'){
847
- //attributes get saved as individual keys
848
- $this->event_attributes = maybe_unserialize($this->event_attributes);
849
- foreach($this->event_attributes as $event_attribute_key => $event_attribute){
850
- if( !empty($event_attribute) ){
851
- update_post_meta($this->post_id, $event_attribute_key, $event_attribute);
852
- }else{
853
- delete_post_meta($this->post_id, $event_attribute_key);
854
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
855
  }
856
  }
857
  }
@@ -867,8 +933,6 @@ class EM_Event extends EM_Object{
867
  unset($event_array['event_id']);
868
  //decide whether or not event is private at this point
869
  $event_array['event_private'] = ( $this->post_status == 'private' ) ? 1:0;
870
- //save event_attributes just in case
871
- $event_array['event_attributes'] = serialize($this->event_attributes);
872
  //check if event truly exists, meaning the event_id is actually a valid event id
873
  if( !empty($this->event_id) ){
874
  $blog_condition = '';
@@ -942,7 +1006,7 @@ class EM_Event extends EM_Object{
942
  }
943
  $this->compat_keys(); //compatability keys, loaded before saving recurrences
944
  //build recurrences if needed
945
- if( $this->is_recurring() && $result && ($this->is_published() || $this->post_status == 'future') ){ //only save events if recurring event validates and is published or set for future
946
  global $EM_EVENT_SAVE_POST;
947
  //If we're in WP Admin and this was called by EM_Event_Post_Admin::save_post, don't save here, it'll be done later in EM_Event_Recurring_Post_Admin::save_post
948
  if( empty($EM_EVENT_SAVE_POST) ){
@@ -956,7 +1020,13 @@ class EM_Event extends EM_Object{
956
  do_action('em_event_added', $this);
957
  }
958
  }
959
- return apply_filters('em_event_save_meta', count($this->errors) == 0, $this);
 
 
 
 
 
 
960
  }
961
 
962
  /**
@@ -1000,6 +1070,7 @@ class EM_Event extends EM_Object{
1000
  $event_meta_inserts = array();
1001
  //Get custom fields and post meta - adapted from $this->load_post_meta()
1002
  foreach($event_meta as $event_meta_key => $event_meta_vals){
 
1003
  if($event_meta_key[0] == '_' && is_array($event_meta_vals)){
1004
  $field_name = substr($event_meta_key, 1);
1005
  if($field_name != 'event_attributes' && !array_key_exists($event_meta_key, $new_event_meta) && !in_array($field_name, array('edit_last', 'edit_lock', 'event_owner_name','event_owner_anonymous','event_owner_email')) ){
@@ -1151,7 +1222,7 @@ class EM_Event extends EM_Object{
1151
  }
1152
  if($set_post_status){
1153
  $wpdb->update( $wpdb->posts, array( 'post_status' => $post_status, 'post_name' => $this->post_name ), array( 'ID' => $this->post_id ) );
1154
- }elseif( $set_post_name ){
1155
  //if we've added a post slug then update wp_posts anyway
1156
  $wpdb->update( $wpdb->posts, array( 'post_name' => $this->post_name ), array( 'ID' => $this->post_id ) );
1157
  }
@@ -1293,6 +1364,7 @@ class EM_Event extends EM_Object{
1293
  }
1294
 
1295
  /**
 
1296
  * Gets number of spaces in this event, dependent on ticket spaces or hard limit, whichever is smaller.
1297
  * @param boolean $force_refresh
1298
  * @return int
@@ -1398,7 +1470,7 @@ class EM_Event extends EM_Object{
1398
  }
1399
  }
1400
  }
1401
- return apply_filters('em_event_is_free',$free,$this);
1402
  }
1403
 
1404
  /**
@@ -1443,6 +1515,11 @@ class EM_Event extends EM_Object{
1443
  preg_match_all('/#_ATT\{([^}]+)\}(\{([^}]+\}?)\})?/', $event_string, $results);
1444
  $attributes = em_get_attributes();
1445
  foreach($results[0] as $resultKey => $result) {
 
 
 
 
 
1446
  //Strip string of placeholder and just leave the reference
1447
  $attRef = substr( substr($result, 0, strpos($result, '}')), 6 );
1448
  $attString = '';
@@ -1459,7 +1536,7 @@ class EM_Event extends EM_Object{
1459
  }
1460
  //First let's do some conditional placeholder removals
1461
  for ($i = 0 ; $i < EM_CONDITIONAL_RECURSIONS; $i++){ //you can add nested recursions by modifying this setting in your wp_options table
1462
- preg_match_all('/\{([a-zA-Z0-9_\-]+)\}(.+?)\{\/\1\}/s', $event_string, $conditionals);
1463
  if( count($conditionals[0]) > 0 ){
1464
  //Check if the language we want exists, if not we take the first language there
1465
  foreach($conditionals[1] as $key => $condition){
@@ -1572,18 +1649,18 @@ class EM_Event extends EM_Object{
1572
  $show_condition = !in_array($attendee_booking_status, $user_bookings);
1573
  }
1574
  }
1575
- }elseif ( preg_match('/^has_category_([a-zA-Z0-9_\-]+)$/', $condition, $category_match)){
1576
  //event is in this category
1577
- $show_condition = has_term($category_match[1], EM_TAXONOMY_CATEGORY, $this->post_id);
1578
- }elseif ( preg_match('/^no_category_([a-zA-Z0-9_\-]+)$/', $condition, $category_match)){
1579
  //event is NOT in this category
1580
- $show_condition = !has_term($category_match[1], EM_TAXONOMY_CATEGORY, $this->post_id);
1581
- }elseif ( preg_match('/^has_tag_([a-zA-Z0-9_\-]+)$/', $condition, $tag_match)){
1582
  //event has this tag
1583
- $show_condition = has_term($tag_match[1], EM_TAXONOMY_TAG, $this->post_id);
1584
- }elseif ( preg_match('/^no_tag_([a-zA-Z0-9_\-]+)$/', $condition, $tag_match)){
1585
  //event doesn't have this tag
1586
- $show_condition = !has_term($tag_match[1], EM_TAXONOMY_TAG, $this->post_id);
1587
  }
1588
  //other potential ones - has_attribute_... no_attribute_... has_categories_...
1589
  $show_condition = apply_filters('em_event_output_show_condition', $show_condition, $condition, $conditionals[0][$key], $this);
@@ -1618,25 +1695,23 @@ class EM_Event extends EM_Object{
1618
  $replace = $this->event_name;
1619
  break;
1620
  case '#_NOTES': //deprecated
1621
- case '#_EXCERPT': //deprecated
1622
  case '#_EVENTNOTES':
 
 
 
1623
  case '#_EVENTEXCERPT':
1624
  case '#_EVENTEXCERPTCUT':
1625
- $replace = $this->post_content;
1626
- if($result == "#_EXCERPT" || $result == "#_EVENTEXCERPT" || $result == "#_EVENTEXCERPTCUT" ){
1627
- if( !empty($this->post_excerpt) && $result != "#_EVENTEXCERPTCUT" ){
1628
- $replace = $this->post_excerpt;
1629
- }else{
1630
- $excerpt_length = 55;
1631
- $excerpt_more = apply_filters('em_excerpt_more', ' ' . '[...]');
1632
- if( !empty($placeholders[3][$key]) ){
1633
- $trim = true;
1634
- $ph_args = explode(',', $placeholders[3][$key]);
1635
- if( is_numeric($ph_args[0]) ) $excerpt_length = $ph_args[0];
1636
- if( !empty($ph_args[1]) ) $excerpt_more = $ph_args[1];
1637
- }
1638
- $replace = $this->output_excerpt($excerpt_length, $excerpt_more);
1639
  }
 
1640
  }
1641
  break;
1642
  case '#_EVENTIMAGEURL':
@@ -1773,11 +1848,12 @@ class EM_Event extends EM_Object{
1773
  }
1774
  break;
1775
  case '#_EVENTPRICEMIN':
 
1776
  //get the range of prices
1777
  $min = false;
1778
  foreach( $this->get_tickets()->tickets as $EM_Ticket ){
1779
  /* @var $EM_Ticket EM_Ticket */
1780
- if( $EM_Ticket->is_available()|| get_option('dbem_bookings_tickets_show_unavailable') ){
1781
  if( $EM_Ticket->get_price() < $min || $min === false){
1782
  $min = $EM_Ticket->get_price();
1783
  }
@@ -1787,11 +1863,12 @@ class EM_Event extends EM_Object{
1787
  $replace = em_get_currency_formatted($min);
1788
  break;
1789
  case '#_EVENTPRICEMAX':
 
1790
  //get the range of prices
1791
  $max = 0;
1792
  foreach( $this->get_tickets()->tickets as $EM_Ticket ){
1793
  /* @var $EM_Ticket EM_Ticket */
1794
- if( $EM_Ticket->is_available()|| get_option('dbem_bookings_tickets_show_unavailable') ){
1795
  if( $EM_Ticket->get_price() > $max ){
1796
  $max = $EM_Ticket->get_price();
1797
  }
@@ -1939,6 +2016,14 @@ class EM_Event extends EM_Object{
1939
  $replace = '<a href="'.esc_url($replace).'">iCal</a>';
1940
  }
1941
  break;
 
 
 
 
 
 
 
 
1942
  case '#_EVENTGCALURL':
1943
  case '#_EVENTGCALLINK':
1944
  //get dates in UTC/GMT time
@@ -2073,27 +2158,6 @@ class EM_Event extends EM_Object{
2073
  return $replace;
2074
  }
2075
 
2076
- function output_excerpt($excerpt_length = 55, $excerpt_more = '[...]', $cut_excerpt = true){
2077
- if( !empty($this->post_excerpt) ){
2078
- $replace = $this->post_excerpt;
2079
- }else{
2080
- $replace = $this->post_content;
2081
- }
2082
- if( empty($this->post_excerpt) || $cut_excerpt ){
2083
- if ( preg_match('/<!--more(.*?)?-->/', $replace, $matches) ) {
2084
- $content = explode($matches[0], $replace, 2);
2085
- $replace = force_balance_tags($content[0]);
2086
- }
2087
- if( !empty($excerpt_length) ){
2088
- //shorten content by supplied number - copied from wp_trim_excerpt
2089
- $replace = strip_shortcodes( $replace );
2090
- $replace = str_replace(']]>', ']]&gt;', $replace);
2091
- $replace = wp_trim_words( $replace, $excerpt_length, $excerpt_more );
2092
- }
2093
- }
2094
- return $replace;
2095
- }
2096
-
2097
  /**********************************************************
2098
  * RECURRENCE METHODS
2099
  ***********************************************************/
@@ -2121,12 +2185,12 @@ class EM_Event extends EM_Object{
2121
  }
2122
 
2123
  /**
2124
- * Gets the event recurrence template, which is an EM_Event_Recurrence object (based off an event-recurring post)
2125
- * @return EM_Event_Recurrence
2126
  */
2127
  function get_event_recurrence(){
2128
  if(!$this->is_recurring()){
2129
- return new EM_Event($this->recurrence_id); //remember, recurrence_id is a post!
2130
  }else{
2131
  return $this;
2132
  }
@@ -2149,8 +2213,8 @@ class EM_Event extends EM_Object{
2149
  if( $this->is_recurrence() && !$this->is_recurring() && $this->can_manage('edit_recurring_events','edit_others_recurring_events') ){
2150
  //remove recurrence id from post meta and index table
2151
  $url = $this->get_attach_url($this->recurrence_id);
2152
- $wpdb->update(EM_EVENTS_TABLE, array('recurrence_id'=>0), array('event_id' => $this->event_id));
2153
- update_post_meta($this->post_id, '_recurrence_id', 0);
2154
  $this->feedback_message = __('Event detached.','events-manager') . ' <a href="'.$url.'">'.__('Undo','events-manager').'</a>';
2155
  $this->recurrence_id = 0;
2156
  return true;
@@ -2182,8 +2246,9 @@ class EM_Event extends EM_Object{
2182
  */
2183
  function save_events() {
2184
  global $wpdb;
2185
- $event_ids = $post_ids = array();
2186
- if( $this->can_manage('edit_events','edit_others_events') && ($this->is_published() || 'future' == $this->post_status) ){
 
2187
  //check if there's any events already created, if not (such as when an event is first submitted for approval and then published), force a reschedule.
2188
  if( $wpdb->get_var('SELECT COUNT(event_id) FROM '.EM_EVENTS_TABLE.' WHERE recurrence_id='. absint($this->event_id)) == 0 ){
2189
  $this->recurring_reschedule = true;
@@ -2209,13 +2274,13 @@ class EM_Event extends EM_Object{
2209
  unset($meta_fields['_event_id']);
2210
  if( isset($meta_fields['_post_id']) ) unset($meta_fields['_post_id']); //legacy bugfix, post_id was never needed in meta table
2211
  //remove recurrence meta info we won't need in events
2212
- foreach( array_keys($this->recurrence_fields) as $recurrence_field){
2213
- unset($event[$recurrence_field]);
2214
  unset($meta_fields['_'.$recurrence_field]);
2215
  }
2216
  //Set the recurrence ID
2217
  $event['recurrence_id'] = $meta_fields['_recurrence_id'] = $this->event_id;
2218
- $event['recurrence'] = $meta_fields['_recurrence'] = 0;
2219
 
2220
  //Let's start saving!
2221
  $event_saves = array();
@@ -2230,7 +2295,7 @@ class EM_Event extends EM_Object{
2230
  //first save event post data
2231
  foreach( $matching_days as $day ) {
2232
  //rewrite post fields if needed
2233
- $post_fields['post_name'] = $event['event_slug'] = $meta_fields['_event_slug'] = apply_filters('em_event_save_events_slug', $post_name.'-'.date($recurring_date_format, $day), $post_fields, $day, $matching_days, $this);
2234
  //adjust certain meta information
2235
  $event['event_start_date'] = $meta_fields['_event_start_date'] = date("Y-m-d", $day);
2236
  $meta_fields['_start_ts'] = strtotime($event['event_start_date'].' '.$event['event_start_time']);
@@ -2290,9 +2355,10 @@ class EM_Event extends EM_Object{
2290
  $EM_Events = EM_Events::get( array('recurrence'=>$this->event_id, 'scope'=>'all', 'status'=>'everything' ) );
2291
  foreach($EM_Events as $EM_Event){ /* @var $EM_Event EM_Event */
2292
  $event_ids[$EM_Event->post_id] = $EM_Event->event_id;
 
2293
  $post_ids[] = $EM_Event->post_id;
2294
  //do we need to change the slugs?
2295
- $post_fields['post_name'] = $event['event_slug'] = $meta_fields['_event_slug'] = apply_filters('em_event_save_events_slug', $post_name.'-'.date($recurring_date_format, $EM_Event->start), $post_fields, $EM_Event->start, array(), $this);
2296
  //adjust certain meta information relativv
2297
  if( !empty($event['recurrence_rsvp_days']) && is_numeric($event['recurrence_rsvp_days']) ){
2298
  $event_rsvp_days = $event['recurrence_rsvp_days'] >= 0 ? '+'. $event['recurrence_rsvp_days']: $event['recurrence_rsvp_days'];
@@ -2369,12 +2435,12 @@ class EM_Event extends EM_Object{
2369
  $ticket['ticket_start'] = $ticket['ticket_end'] = 'NULL';
2370
  //sort out cut-of dates
2371
  if( !empty($ticket_meta_recurrences) ){
2372
- if( array_key_exists('start_days', $ticket_meta_recurrences) ){
2373
  $ticket_start_days = $ticket_meta_recurrences['start_days'] >= 0 ? '+'. $ticket_meta_recurrences['start_days']: $ticket_meta_recurrences['start_days'];
2374
  $ticket_start_date = date('Y-m-d', strtotime($ticket_start_days.' days', $event_dates[$event_id]));
2375
  $ticket['ticket_start'] = "'". $ticket_start_date . ' '. $ticket_meta_recurrences['start_time'] ."'";
2376
  }
2377
- if( array_key_exists('end_days', $ticket_meta_recurrences) ){
2378
  $ticket_end_days = $ticket_meta_recurrences['end_days'] >= 0 ? '+'. $ticket_meta_recurrences['end_days']: $ticket_meta_recurrences['end_days'];
2379
  $ticket_end_date = date('Y-m-d', strtotime($ticket_end_days.' days', $event_dates[$event_id]));
2380
  $ticket['ticket_end'] = "'". $ticket_end_date . ' '. $ticket_meta_recurrences['end_time'] . "'";
@@ -2435,6 +2501,8 @@ class EM_Event extends EM_Object{
2435
  }
2436
  }
2437
  return apply_filters('em_event_save_events', !in_array(false, $event_saves) && $result !== false, $this, $event_ids, $post_ids);
 
 
2438
  }
2439
  return apply_filters('em_event_save_events', false, $this, $event_ids, $post_ids);
2440
  }
@@ -2579,13 +2647,32 @@ class EM_Event extends EM_Object{
2579
  */
2580
  function set_status_events($status){
2581
  //give sub events same status
2582
- $events_array = EM_Events::get( array('recurrence_id'=>$this->post_id, 'scope'=>'all', 'status'=>false ) );
2583
- foreach($events_array as $EM_Event){
2584
- /* @var $EM_Event EM_Event */
2585
- if($EM_Event->recurrence_id == $this->event_id){
2586
- $EM_Event->set_status($status);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2587
  }
 
 
 
 
2588
  }
 
2589
  }
2590
 
2591
  /**
1
  <?php
2
  /**
3
+ * Get an event in a db friendly way, by checking globals, cache and passed variables to avoid extra class instantiations.
4
  * @param mixed $id
5
  * @param mixed $search_by
6
  * @return EM_Event
22
  if( is_object($id) && get_class($id) == 'EM_Event' ){
23
  return apply_filters('em_get_event', $id);
24
  }else{
25
+ //check the cache first
26
+ $event_id = false;
27
+ if( is_numeric($id) ){
28
+ if( $search_by == 'event_id' ){
29
+ $event_id = $id;
30
+ }elseif( $search_by == 'post_id' ){
31
+ $event_id = wp_cache_get($id, 'em_events_ids');
32
+ }
33
+ }elseif( !empty($id->ID) && !empty($id->post_type) && ($id->post_type == EM_POST_TYPE_EVENT || $id->post_type == 'event-recurring') ){
34
+ $event_id = wp_cache_get($id->ID, 'em_events_ids');
35
+ }
36
+ if( $event_id ){
37
+ $event = wp_cache_get($event_id, 'em_events');
38
+ if( is_object($event) && !empty($event->event_id) && $event->event_id){
39
+ return apply_filters('em_get_event', $event);
40
+ }
41
+ }
42
+ //if we get this far, just create a new event
43
  return apply_filters('em_get_event', new EM_Event($id,$search_by));
44
  }
45
  }
120
  'recurrence_id' => array( 'name'=>'recurrence_id', 'type'=>'%d', 'null'=>true ),
121
  'event_status' => array( 'name'=>'status', 'type'=>'%d', 'null'=>true ),
122
  'event_private' => array( 'name'=>'status', 'type'=>'%d', 'null'=>true ),
 
123
  'blog_id' => array( 'name'=>'blog_id', 'type'=>'%d', 'null'=>true ),
124
  'group_id' => array( 'name'=>'group_id', 'type'=>'%d', 'null'=>true ),
125
  'recurrence' => array( 'name'=>'recurrence', 'type'=>'%d', 'null'=>true ), //is this a recurring event template
130
  'recurrence_byweekno' => array( 'name'=>'byweekno', 'type'=>'%d', 'null'=>true ), //if monthly which week (-1 is last)
131
  'recurrence_rsvp_days' => array( 'name'=>'recurrence_rsvp_days', 'type'=>'%d', 'null'=>true ), //days before or after start date to generat bookings cut-off date
132
  );
133
+ var $post_fields = array('event_slug','event_owner','event_name','event_private','event_status','event_attributes','post_id','post_content'); //fields that won't be taken from the em_events table anymore
134
+ var $recurrence_fields = array('recurrence', 'recurrence_interval', 'recurrence_freq', 'recurrence_days', 'recurrence_byday', 'recurrence_byweekno', 'recurrence_rsvp_days');
135
 
136
  var $image_url = '';
137
  /**
280
  $event_post = get_post($results['post_id']);
281
  }
282
  }else{
283
+ //if searching specifically by post_id and in MS Global mode, then assume we're looking in the current blog we're in
284
+ if( $search_by == 'post_id' && EM_MS_GLOBAL ) $search_by = get_current_blog_id();
285
+ //get post data based on ID and search context
286
  if(!$is_post){
287
  if( is_multisite() && (is_numeric($search_by) || $search_by == '') ){
288
  if( $search_by == '' ) $search_by = get_current_site()->blog_id;
306
  0 => __('Pending','events-manager'),
307
  1 => __('Approved','events-manager')
308
  );
309
+ //add this event to the cache
310
+ if( $this->event_id && $this->post_id ){
311
+ wp_cache_set($this->event_id, $this, 'em_events');
312
+ wp_cache_set($this->post_id, $this->event_id, 'em_events_ids');
313
+ }
314
  do_action('em_event', $this, $id, $search_by);
315
  }
316
 
317
+ /**
318
+ * When cloning this event, we get rid of the bookings and location objects, since they can be retrieved again from the cache instead.
319
+ */
320
+ public function __clone(){
321
+ $this->bookings = null;
322
+ $this->location = null;
323
+ }
324
+
325
  function load_postdata($event_post, $search_by = false){
326
  //load event post object if it's an actual object and also a post type of our event CPT names
327
  if( is_object($event_post) && ($event_post->post_type == 'event-recurring' || $event_post->post_type == EM_POST_TYPE_EVENT) ){
343
  foreach($event_meta as $event_meta_key => $event_meta_val){
344
  $field_name = substr($event_meta_key, 1);
345
  if($event_meta_key[0] != '_'){
346
+ $this->event_attributes[$event_meta_key] = ( is_array($event_meta_val) ) ? $event_meta_val[0]:$event_meta_val;
347
  }elseif( is_string($field_name) && !in_array($field_name, $this->post_fields) ){
348
  if( array_key_exists($field_name, $this->fields) ){
349
  $this->$field_name = $event_meta_val[0];
403
  $this->start = strtotime($this->event_start_date." ".$this->event_start_time, current_time('timestamp'));
404
  $this->end = strtotime($this->event_end_date." ".$this->event_end_time, current_time('timestamp'));
405
  }
406
+ if( empty($this->location_id) && !empty($this->event_id) ) $this->location_id = 0; //just set location_id to 0 and avoid any doubt
407
+ if( EM_MS_GLOBAL && empty($this->blog_id) ) $this->blog_id = get_current_site()->blog_id; //events created before going multisite may have null values, so we set it to main site id
408
  }
409
 
410
  function get_event_meta($blog_id = false){
411
+ if( !empty($this->blog_id) ) $blog_id = $this->blog_id; //if there's a blog id already, there's no doubt where to look for
412
  if( is_numeric($blog_id) && $blog_id > 0 && is_multisite() ){
413
  // if in multisite mode, switch blogs quickly to get the right post meta.
414
  switch_to_blog($blog_id);
415
  $event_meta = get_post_meta($this->post_id);
416
  restore_current_blog();
417
  $this->blog_id = $blog_id;
418
+ }elseif( EM_MS_GLOBAL ){
419
+ $this->ms_global_switch();
420
+ $event_meta = get_post_meta($this->post_id);
421
+ $this->ms_global_switch_back();
422
  }else{
423
  $event_meta = get_post_meta($this->post_id);
424
  }
448
  //get the rest and validate (optional)
449
  $this->get_post_meta(false);
450
  $result = $validate ? $this->validate():true; //validate both post and meta, otherwise return true
451
+ return apply_filters('em_event_get_post', $result, $this);
452
  }
453
 
454
  /**
583
  }elseif( !$preview_autosave && ($can_manage_bookings || !$this->event_rsvp) ){
584
  if( empty($_POST['event_rsvp']) && $this->event_rsvp ) $deleting_bookings = true;
585
  $this->event_rsvp = 0;
586
+ $this->event_rsvp_time = null;
587
  }
588
 
589
  //Sort out event attributes - note that custom post meta now also gets inserted here automatically (and is overwritten by these attributes)
596
  if( (in_array($att_key, $event_available_attributes['names']) || array_key_exists($att_key, $this->event_attributes) ) ){
597
  $this->event_attributes[$att_key] = '';
598
  $att_vals = isset($event_available_attributes['values'][$att_key]) ? count($event_available_attributes['values'][$att_key]) : 0;
599
+ if( $att_value != '' ){
600
  if( $att_vals <= 1 || ($att_vals > 1 && in_array($att_value, $event_available_attributes['values'][$att_key])) ){
601
  $this->event_attributes[$att_key] = wp_unslash($att_value);
602
  }
603
  }
604
+ if( $att_value != '' && $att_vals > 1){
605
  $this->event_attributes[$att_key] = wp_unslash(wp_kses($event_available_attributes['values'][$att_key][0], $allowedtags));
606
  }
607
  }
687
  //new event so we create everything from scratch
688
  $this->recurring_reschedule = $this->recurring_recreate_bookings = true;
689
  }
690
+ }else{
691
+ foreach( $this->recurrence_fields as $recurrence_field ){
692
+ $this->$recurrence_field = null;
693
+ }
694
  }
695
  //categories in MS GLobal
696
  if(EM_MS_GLOBAL && !is_main_site() && get_option('dbem_categories_enabled') ){
884
  }
885
  }
886
  //Update Post Meta
887
+ $current_meta_values = $this->get_event_meta();
888
+ foreach( $this->fields as $key => $field_info ){
889
+ //certain keys will not be saved if not needed, including flags with a 0 value. Older databases using custom WP_Query calls will need to use an array of meta_query items using NOT EXISTS - OR - value=0
890
  if( !in_array($key, $this->post_fields) && $key != 'event_attributes' ){
891
+ //ignore certain fields and delete if not new
892
+ $save_meta_key = true;
893
+ $recurrence_array = array('recurrence', 'recurrence_interval', 'recurrence_freq', 'recurrence_days', 'recurrence_byday', 'recurrence_byweekno', 'recurrence_rsvp_days');
894
+ if( !$this->is_recurring() && in_array($key, $recurrence_array) ) $save_meta_key = false;
895
+ if( !$this->is_recurrence() && $key == 'recurrence_id' ) $save_meta_key = false;
896
+ $ignore_zero_keys = array('location_id', 'group_id', 'event_all_day' );
897
+ if( in_array($key, $ignore_zero_keys) && empty($this->$key) ) $save_meta_key = false;
898
+ if( $key == 'blog_id' ) $save_meta_key = false; //not needed, given postmeta is stored on the actual blog table in MultiSite
899
+ //we don't need rsvp info if rsvp is not set, including the RSVP flag too
900
+ if( empty($this->event_rsvp) && in_array($key, array('event_rsvp','event_rsvp_date', 'event_rsvp_time', 'event_rsvp_spaces', 'event_spaces')) ) $save_meta_key = false;
901
+ //save key or ignore/delete key
902
+ if( $save_meta_key ){
903
+ update_post_meta($this->post_id, '_'.$key, $this->$key);
904
+ }elseif( array_key_exists('_'.$key, $current_meta_values) ){
905
+ //delete if this event already existed, in case this event already had the values before
906
+ delete_post_meta($this->post_id, '_'.$key);
907
+ }
908
+ }elseif( array_key_exists('_'.$key, $current_meta_values) && $key != 'event_attributes' ){ //we should delete event_attributes, but maybe something else uses it without us knowing
909
+ delete_post_meta($this->post_id, '_'.$key);
910
+ }
911
+ }
912
+ if( get_option('dbem_attributes_enabled') ){
913
+ //attributes get saved as individual keys
914
+ $atts = em_get_attributes(); //get available attributes that EM manages
915
+ $this->event_attributes = maybe_unserialize($this->event_attributes);
916
+ foreach( $atts['names'] as $event_attribute_key ){
917
+ if( array_key_exists($event_attribute_key, $this->event_attributes) && $this->event_attributes[$event_attribute_key] != '' ){
918
+ update_post_meta($this->post_id, $event_attribute_key, $this->event_attributes[$event_attribute_key]);
919
+ }else{
920
+ delete_post_meta($this->post_id, $event_attribute_key);
921
  }
922
  }
923
  }
933
  unset($event_array['event_id']);
934
  //decide whether or not event is private at this point
935
  $event_array['event_private'] = ( $this->post_status == 'private' ) ? 1:0;
 
 
936
  //check if event truly exists, meaning the event_id is actually a valid event id
937
  if( !empty($this->event_id) ){
938
  $blog_condition = '';
1006
  }
1007
  $this->compat_keys(); //compatability keys, loaded before saving recurrences
1008
  //build recurrences if needed
1009
+ if( $this->is_recurring() && $result && ($this->is_published() || $this->post_status == 'future' || (defined('EM_FORCE_RECURRENCES_SAVE') && EM_FORCE_RECURRENCES_SAVE)) ){ //only save events if recurring event validates and is published or set for future
1010
  global $EM_EVENT_SAVE_POST;
1011
  //If we're in WP Admin and this was called by EM_Event_Post_Admin::save_post, don't save here, it'll be done later in EM_Event_Recurring_Post_Admin::save_post
1012
  if( empty($EM_EVENT_SAVE_POST) ){
1020
  do_action('em_event_added', $this);
1021
  }
1022
  }
1023
+ $result = count($this->errors) == 0;
1024
+ //add this event to the cache
1025
+ if( $result ){
1026
+ wp_cache_set($this->event_id, $this, 'em_events');
1027
+ wp_cache_set($this->post_id, $this->event_id, 'em_events_ids');
1028
+ }
1029
+ return apply_filters('em_event_save_meta', $result, $this);
1030
  }
1031
 
1032
  /**
1070
  $event_meta_inserts = array();
1071
  //Get custom fields and post meta - adapted from $this->load_post_meta()
1072
  foreach($event_meta as $event_meta_key => $event_meta_vals){
1073
+ if( $event_meta_key == '_wpas_' ) continue; //allow JetPack Publicize to detect this as a new post when published
1074
  if($event_meta_key[0] == '_' && is_array($event_meta_vals)){
1075
  $field_name = substr($event_meta_key, 1);
1076
  if($field_name != 'event_attributes' && !array_key_exists($event_meta_key, $new_event_meta) && !in_array($field_name, array('edit_last', 'edit_lock', 'event_owner_name','event_owner_anonymous','event_owner_email')) ){
1222
  }
1223
  if($set_post_status){
1224
  $wpdb->update( $wpdb->posts, array( 'post_status' => $post_status, 'post_name' => $this->post_name ), array( 'ID' => $this->post_id ) );
1225
+ }elseif( !empty($set_post_name) ){
1226
  //if we've added a post slug then update wp_posts anyway
1227
  $wpdb->update( $wpdb->posts, array( 'post_name' => $this->post_name ), array( 'ID' => $this->post_id ) );
1228
  }
1364
  }
1365
 
1366
  /**
1367
+ * Deprecated - use $this->get_bookings()->get_spaces() instead.
1368
  * Gets number of spaces in this event, dependent on ticket spaces or hard limit, whichever is smaller.
1369
  * @param boolean $force_refresh
1370
  * @return int
1470
  }
1471
  }
1472
  }
1473
+ return apply_filters('em_event_is_free',$free, $this, $now);
1474
  }
1475
 
1476
  /**
1515
  preg_match_all('/#_ATT\{([^}]+)\}(\{([^}]+\}?)\})?/', $event_string, $results);
1516
  $attributes = em_get_attributes();
1517
  foreach($results[0] as $resultKey => $result) {
1518
+ //check that we haven't mistakenly captured a closing bracket in second bracket set
1519
+ if( !empty($results[3][$resultKey]) && $results[3][$resultKey][0] == '/' ){
1520
+ $result = $results[0][$resultKey] = str_replace($results[2][$resultKey], '', $result);
1521
+ $results[3][$resultKey] = $results[2][$resultKey] = '';
1522
+ }
1523
  //Strip string of placeholder and just leave the reference
1524
  $attRef = substr( substr($result, 0, strpos($result, '}')), 6 );
1525
  $attString = '';
1536
  }
1537
  //First let's do some conditional placeholder removals
1538
  for ($i = 0 ; $i < EM_CONDITIONAL_RECURSIONS; $i++){ //you can add nested recursions by modifying this setting in your wp_options table
1539
+ preg_match_all('/\{([a-zA-Z0-9_\-,]+)\}(.+?)\{\/\1\}/s', $event_string, $conditionals);
1540
  if( count($conditionals[0]) > 0 ){
1541
  //Check if the language we want exists, if not we take the first language there
1542
  foreach($conditionals[1] as $key => $condition){
1649
  $show_condition = !in_array($attendee_booking_status, $user_bookings);
1650
  }
1651
  }
1652
+ }elseif ( preg_match('/^has_category_([a-zA-Z0-9_\-,]+)$/', $condition, $category_match)){
1653
  //event is in this category
1654
+ $show_condition = has_term(explode(',', $category_match[1]), EM_TAXONOMY_CATEGORY, $this->post_id);
1655
+ }elseif ( preg_match('/^no_category_([a-zA-Z0-9_\-,]+)$/', $condition, $category_match)){
1656
  //event is NOT in this category
1657
+ $show_condition = !has_term(explode(',', $category_match[1]), EM_TAXONOMY_CATEGORY, $this->post_id);
1658
+ }elseif ( preg_match('/^has_tag_([a-zA-Z0-9_\-,]+)$/', $condition, $tag_match)){
1659
  //event has this tag
1660
+ $show_condition = has_term(explode(',', $tag_match[1]), EM_TAXONOMY_TAG, $this->post_id);
1661
+ }elseif ( preg_match('/^no_tag_([a-zA-Z0-9_\-,]+)$/', $condition, $tag_match)){
1662
  //event doesn't have this tag
1663
+ $show_condition = !has_term(explode(',', $tag_match[1]), EM_TAXONOMY_TAG, $this->post_id);
1664
  }
1665
  //other potential ones - has_attribute_... no_attribute_... has_categories_...
1666
  $show_condition = apply_filters('em_event_output_show_condition', $show_condition, $condition, $conditionals[0][$key], $this);
1695
  $replace = $this->event_name;
1696
  break;
1697
  case '#_NOTES': //deprecated
 
1698
  case '#_EVENTNOTES':
1699
+ $replace = $this->post_content;
1700
+ break;
1701
+ case '#_EXCERPT': //deprecated
1702
  case '#_EVENTEXCERPT':
1703
  case '#_EVENTEXCERPTCUT':
1704
+ if( !empty($this->post_excerpt) && $result != "#_EVENTEXCERPTCUT" ){
1705
+ $replace = $this->post_excerpt;
1706
+ }else{
1707
+ $excerpt_length = ( $result == "#_EVENTEXCERPTCUT" ) ? 55:false;
1708
+ $excerpt_more = apply_filters('em_excerpt_more', ' ' . '[...]');
1709
+ if( !empty($placeholders[3][$key]) ){
1710
+ $ph_args = explode(',', $placeholders[3][$key]);
1711
+ if( is_numeric($ph_args[0]) || empty($ph_args[0]) ) $excerpt_length = $ph_args[0];
1712
+ if( !empty($ph_args[1]) ) $excerpt_more = $ph_args[1];
 
 
 
 
 
1713
  }
1714
+ $replace = $this->output_excerpt($excerpt_length, $excerpt_more, $result == "#_EVENTEXCERPTCUT");
1715
  }
1716
  break;
1717
  case '#_EVENTIMAGEURL':
1848
  }
1849
  break;
1850
  case '#_EVENTPRICEMIN':
1851
+ case '#_EVENTPRICEMINALL':
1852
  //get the range of prices
1853
  $min = false;
1854
  foreach( $this->get_tickets()->tickets as $EM_Ticket ){
1855
  /* @var $EM_Ticket EM_Ticket */
1856
+ if( $EM_Ticket->is_available() || $result == '#_EVENTPRICEMINALL'){
1857
  if( $EM_Ticket->get_price() < $min || $min === false){
1858
  $min = $EM_Ticket->get_price();
1859
  }
1863
  $replace = em_get_currency_formatted($min);
1864
  break;
1865
  case '#_EVENTPRICEMAX':
1866
+ case '#_EVENTPRICEMAXALL':
1867
  //get the range of prices
1868
  $max = 0;
1869
  foreach( $this->get_tickets()->tickets as $EM_Ticket ){
1870
  /* @var $EM_Ticket EM_Ticket */
1871
+ if( $EM_Ticket->is_available() || $result == '#_EVENTPRICEMAXALL'){
1872
  if( $EM_Ticket->get_price() > $max ){
1873
  $max = $EM_Ticket->get_price();
1874
  }
2016
  $replace = '<a href="'.esc_url($replace).'">iCal</a>';
2017
  }
2018
  break;
2019
+ case '#_EVENTWEBCALURL':
2020
+ case '#_EVENTWEBCALLINK':
2021
+ $replace = $this->get_ical_url();
2022
+ $replace = str_replace(array('http://','https://'), 'webcal://', $replace);
2023
+ if( $result == '#_EVENTWEBCALLINK' ){
2024
+ $replace = '<a href="'.esc_url($replace).'">webcal</a>';
2025
+ }
2026
+ break;
2027
  case '#_EVENTGCALURL':
2028
  case '#_EVENTGCALLINK':
2029
  //get dates in UTC/GMT time
2158
  return $replace;
2159
  }
2160
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2161
  /**********************************************************
2162
  * RECURRENCE METHODS
2163
  ***********************************************************/
2185
  }
2186
 
2187
  /**
2188
+ * Gets the event recurrence template, which is an EM_Event object (based off an event-recurring post)
2189
+ * @return EM_Event
2190
  */
2191
  function get_event_recurrence(){
2192
  if(!$this->is_recurring()){
2193
+ return em_get_event($this->recurrence_id, 'event_id');
2194
  }else{
2195
  return $this;
2196
  }
2213
  if( $this->is_recurrence() && !$this->is_recurring() && $this->can_manage('edit_recurring_events','edit_others_recurring_events') ){
2214
  //remove recurrence id from post meta and index table
2215
  $url = $this->get_attach_url($this->recurrence_id);
2216
+ $wpdb->update(EM_EVENTS_TABLE, array('recurrence_id'=>null), array('event_id' => $this->event_id));
2217
+ delete_post_meta($this->post_id, '_recurrence_id');
2218
  $this->feedback_message = __('Event detached.','events-manager') . ' <a href="'.$url.'">'.__('Undo','events-manager').'</a>';
2219
  $this->recurrence_id = 0;
2220
  return true;
2246
  */
2247
  function save_events() {
2248
  global $wpdb;
2249
+ $event_ids = $post_ids = $event_dates = array();
2250
+ if( !$this->can_manage('edit_events','edit_others_events') ) return apply_filters('em_event_save_events', false, $this, $event_ids, $post_ids);
2251
+ if( $this->is_published() || 'future' == $this->post_status ){
2252
  //check if there's any events already created, if not (such as when an event is first submitted for approval and then published), force a reschedule.
2253
  if( $wpdb->get_var('SELECT COUNT(event_id) FROM '.EM_EVENTS_TABLE.' WHERE recurrence_id='. absint($this->event_id)) == 0 ){
2254
  $this->recurring_reschedule = true;
2274
  unset($meta_fields['_event_id']);
2275
  if( isset($meta_fields['_post_id']) ) unset($meta_fields['_post_id']); //legacy bugfix, post_id was never needed in meta table
2276
  //remove recurrence meta info we won't need in events
2277
+ foreach( $this->recurrence_fields as $recurrence_field){
2278
+ $event[$recurrence_field] = null;
2279
  unset($meta_fields['_'.$recurrence_field]);
2280
  }
2281
  //Set the recurrence ID
2282
  $event['recurrence_id'] = $meta_fields['_recurrence_id'] = $this->event_id;
2283
+ $event['recurrence'] = 0;
2284
 
2285
  //Let's start saving!
2286
  $event_saves = array();
2295
  //first save event post data
2296
  foreach( $matching_days as $day ) {
2297
  //rewrite post fields if needed
2298
+ $post_fields['post_name'] = $event['event_slug'] = apply_filters('em_event_save_events_slug', $post_name.'-'.date($recurring_date_format, $day), $post_fields, $day, $matching_days, $this);
2299
  //adjust certain meta information
2300
  $event['event_start_date'] = $meta_fields['_event_start_date'] = date("Y-m-d", $day);
2301
  $meta_fields['_start_ts'] = strtotime($event['event_start_date'].' '.$event['event_start_time']);
2355
  $EM_Events = EM_Events::get( array('recurrence'=>$this->event_id, 'scope'=>'all', 'status'=>'everything' ) );
2356
  foreach($EM_Events as $EM_Event){ /* @var $EM_Event EM_Event */
2357
  $event_ids[$EM_Event->post_id] = $EM_Event->event_id;
2358
+ $event_dates[$EM_Event->event_id] = $EM_Event->start;
2359
  $post_ids[] = $EM_Event->post_id;
2360
  //do we need to change the slugs?
2361
+ $post_fields['post_name'] = $event['event_slug'] = apply_filters('em_event_save_events_slug', $post_name.'-'.date($recurring_date_format, $EM_Event->start), $post_fields, $EM_Event->start, array(), $this);
2362
  //adjust certain meta information relativv
2363
  if( !empty($event['recurrence_rsvp_days']) && is_numeric($event['recurrence_rsvp_days']) ){
2364
  $event_rsvp_days = $event['recurrence_rsvp_days'] >= 0 ? '+'. $event['recurrence_rsvp_days']: $event['recurrence_rsvp_days'];
2435
  $ticket['ticket_start'] = $ticket['ticket_end'] = 'NULL';
2436
  //sort out cut-of dates
2437
  if( !empty($ticket_meta_recurrences) ){
2438
+ if( array_key_exists('start_days', $ticket_meta_recurrences) && $ticket_meta_recurrences['start_days'] !== false ){
2439
  $ticket_start_days = $ticket_meta_recurrences['start_days'] >= 0 ? '+'. $ticket_meta_recurrences['start_days']: $ticket_meta_recurrences['start_days'];
2440
  $ticket_start_date = date('Y-m-d', strtotime($ticket_start_days.' days', $event_dates[$event_id]));
2441
  $ticket['ticket_start'] = "'". $ticket_start_date . ' '. $ticket_meta_recurrences['start_time'] ."'";
2442
  }
2443
+ if( array_key_exists('end_days', $ticket_meta_recurrences) && $ticket_meta_recurrences['end_days'] !== false ){
2444
  $ticket_end_days = $ticket_meta_recurrences['end_days'] >= 0 ? '+'. $ticket_meta_recurrences['end_days']: $ticket_meta_recurrences['end_days'];
2445
  $ticket_end_date = date('Y-m-d', strtotime($ticket_end_days.' days', $event_dates[$event_id]));
2446
  $ticket['ticket_end'] = "'". $ticket_end_date . ' '. $ticket_meta_recurrences['end_time'] . "'";
2501
  }
2502
  }
2503
  return apply_filters('em_event_save_events', !in_array(false, $event_saves) && $result !== false, $this, $event_ids, $post_ids);
2504
+ }elseif( !$this->is_published() && $this->get_previous_status() != $this->get_status() && defined('EM_FORCE_RECURRENCES_SAVE') && EM_FORCE_RECURRENCES_SAVE ){
2505
+ $this->set_status_events($this->get_status());
2506
  }
2507
  return apply_filters('em_event_save_events', false, $this, $event_ids, $post_ids);
2508
  }
2647
  */
2648
  function set_status_events($status){
2649
  //give sub events same status
2650
+ global $wpdb;
2651
+ //get post and event ids of recurrences
2652
+ $post_ids = $event_ids = array();
2653
+ $events_array = EM_Events::get( array('recurrence'=>$this->event_id, 'scope'=>'all', 'status'=>false, 'array'=>true) ); //only get recurrences that aren't trashed or drafted
2654
+ foreach( $events_array as $event_array ){
2655
+ $post_ids[] = absint($event_array['post_id']);
2656
+ $event_ids[] = absint($event_array['event_id']);
2657
+ }
2658
+ if( !empty($post_ids) ){
2659
+ //decide on what status to set and update wp_posts in the process
2660
+ if($status === null){
2661
+ $set_status='NULL'; //draft post
2662
+ $post_status = 'draft'; //set post status in this instance
2663
+ }elseif( $status == -1 ){ //trashed post
2664
+ $set_status = -1;
2665
+ $post_status = 'trash'; //set post status in this instance
2666
+ }else{
2667
+ $set_status = $status ? 1:0; //published or pending post
2668
+ $post_status = $set_status ? 'publish':'pending';
2669
  }
2670
+ if( EM_MS_GLOBAL ) switch_to_blog( $this->blog_id );
2671
+ $result = $wpdb->query( $wpdb->prepare("UPDATE ".$wpdb->posts." SET post_status=%s WHERE ID IN (". implode(',', $post_ids) .')', array($post_status)) );
2672
+ restore_current_blog();
2673
+ $result = $result && $wpdb->query( $wpdb->prepare("UPDATE ".EM_EVENTS_TABLE." SET event_status=%s WHERE event_id IN (". implode(',', $event_ids) .')', array($set_status)) );
2674
  }
2675
+ return apply_filters('em_event_set_status_events', $result !== false, $status, $this, $event_ids, $post_ids);
2676
  }
2677
 
2678
  /**
classes/em-events.php CHANGED
@@ -6,6 +6,20 @@
6
  */
7
  class EM_Events extends EM_Object {
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  /**
10
  * Returns an array of EM_Events that match the given specs in the argument, or returns a list of future evetnts in future
11
  * (see EM_Events::get_default_search() ) for explanation of possible search array values. You can also supply a numeric array
@@ -33,58 +47,173 @@ class EM_Events extends EM_Object {
33
  $args = self::get_default_search($args);
34
  $limit = ( $args['limit'] && is_numeric($args['limit'])) ? "LIMIT {$args['limit']}" : '';
35
  $offset = ( $limit != "" && is_numeric($args['offset']) ) ? "OFFSET {$args['offset']}" : '';
36
- $groupby_sql = array();
37
 
38
- //Get the default conditions
39
- $conditions = self::build_sql_conditions($args);
40
- //Put it all together
41
- $where = ( count($conditions) > 0 ) ? " WHERE " . implode ( " AND ", $conditions ):'';
42
-
43
- //Get ordering instructions
44
  $EM_Event = new EM_Event();
45
  $EM_Location = new EM_Location();
46
- $orderby = self::build_sql_orderby($args, array_keys(array_merge($EM_Event->fields, $EM_Location->fields)), get_option('dbem_events_default_order'));
47
- //Now, build orderby sql
48
- $orderby_sql = ( count($orderby) > 0 ) ? 'ORDER BY '. implode(', ', $orderby) : '';
49
-
50
- //Create the SQL statement and execute
51
-
52
- if( !$count && $args['array'] ){
53
- $selectors_array = array();
54
- foreach( array_keys($EM_Event->fields) as $field_selector){
55
- $selectors_array[] = $events_table.'.'.$field_selector;
56
- }
57
- $selectors = implode(',', $selectors_array);
58
- }elseif( EM_MS_GLOBAL ){
59
- $selectors = $events_table.'.post_id, '.$events_table.'.blog_id';
60
- $groupby_sql[] = $events_table.'.post_id, '. $events_table.'.blog_id';
61
  }else{
62
- $selectors = $events_table.'.post_id';
63
- $groupby_sql[] = $events_table.'.post_id'; //prevent duplicates showing in lists
64
  }
 
 
 
 
 
65
  if( $count ){
66
- $selectors = 'SQL_CALC_FOUND_ROWS *';
67
  $limit = 'LIMIT 1';
68
  $offset = 'OFFSET 0';
 
 
 
 
 
 
 
 
 
 
 
69
  }
70
 
71
- //add group_by if needed
72
- $groupby_sql = !empty($groupby_sql) && is_array($groupby_sql) ? 'GROUP BY '.implode(',', $groupby_sql):'';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
- $sql = apply_filters('em_events_get_sql',"
75
- SELECT $selectors FROM $events_table
76
- LEFT JOIN $locations_table ON {$locations_table}.location_id={$events_table}.location_id
77
- $where
78
- $groupby_sql $orderby_sql
79
- $limit $offset
80
- ", $args);
 
 
 
 
 
 
 
81
 
82
- //If we're only counting results, return the number of results
83
  if( $count ){
84
- $wpdb->query($sql);
85
- return apply_filters('em_events_get_count', $wpdb->get_var('SELECT FOUND_ROWS()'), $args);
86
  }
 
 
87
  $results = $wpdb->get_results( $sql, ARRAY_A);
 
 
 
 
 
 
88
 
89
  //If we want results directly in an array, why not have a shortcut here?
90
  if( $args['array'] == true ){
@@ -160,7 +289,7 @@ class EM_Events extends EM_Object {
160
  //get page number if passed on by request (still needs pagination enabled to have effect)
161
  $page_queryvar = !empty($args['page_queryvar']) ? $args['page_queryvar'] : 'pno';
162
  if( !empty($args['pagination']) && !array_key_exists('page',$args) && !empty($_REQUEST[$page_queryvar]) && is_numeric($_REQUEST[$page_queryvar]) ){
163
- $page = $args['page'] = $_REQUEST[$page_queryvar];
164
  }
165
  //Can be either an array for the get search or an array of EM_Event objects
166
  if( is_object(current($args)) && get_class((current($args))) == 'EM_Event' ){
@@ -169,28 +298,21 @@ class EM_Events extends EM_Object {
169
  $args = (!empty($func_args[1]) && is_array($func_args[1])) ? $func_args[1] : array();
170
  $args = apply_filters('em_events_output_args', self::get_default_search($args), $events);
171
  $limit = ( !empty($args['limit']) && is_numeric($args['limit']) ) ? $args['limit']:false;
172
- $offset = ( !empty($args['offset']) && is_numeric($args['offset']) ) ? $args['offset']:0;
173
- $page = ( !empty($args['page']) && is_numeric($args['page']) ) ? $args['page']:1;
174
  $events_count = count($events);
175
  }else{
176
  //Firstly, let's check for a limit/offset here, because if there is we need to remove it and manually do this
177
  $args = apply_filters('em_events_output_args', self::get_default_search($args));
178
  $limit = ( !empty($args['limit']) && is_numeric($args['limit']) ) ? $args['limit']:false;
179
- $offset = ( !empty($args['offset']) && is_numeric($args['offset']) ) ? $args['offset']:0;
180
- $page = ( !empty($args['page']) && is_numeric($args['page']) ) ? $args['page']:1;
181
- $args_count = $args;
182
- $args_count['limit'] = false;
183
- $args_count['offset'] = false;
184
- $args_count['page'] = false;
185
- $events_count = self::count($args_count);
186
  $events = self::get( $args );
 
187
  }
188
  //What format shall we output this to, or use default
189
  $format = ( empty($args['format']) ) ? get_option( 'dbem_event_list_item_format' ) : $args['format'] ;
190
 
191
  $output = "";
192
- $events = apply_filters('em_events_output_events', $events);
193
  if ( $events_count > 0 ) {
 
194
  foreach ( $events as $EM_Event ) {
195
  $output .= $EM_Event->output($format);
196
  }
@@ -209,9 +331,9 @@ class EM_Events extends EM_Object {
209
  if( !empty($args['pagination']) && !empty($limit) && $events_count > $limit ){
210
  $output .= self::get_pagination_links($args, $events_count);
211
  }
212
- } else {
213
- $output = get_option ( 'dbem_no_events_message' );
214
- }
215
 
216
  //TODO check if reference is ok when restoring object, due to changes in php5 v 4
217
  $EM_Event = $EM_Event_old;
@@ -241,17 +363,16 @@ class EM_Events extends EM_Object {
241
 
242
  $args['mode'] = !empty($args['mode']) ? $args['mode'] : get_option('dbem_event_list_groupby');
243
  $args['header_format'] = !empty($args['header_format']) ? $args['header_format'] : get_option('dbem_event_list_groupby_header_format', '<h2>#s</h2>');
 
 
244
  //Reset some vars for counting events and displaying set arrays of events
245
  $atts = (array) $args;
246
- $atts['pagination'] = false;
247
- $atts['limit'] = false;
248
- $atts['page'] = false;
249
- $atts['offset'] = false;
250
  //decide what form of dates to show
251
- $events_count = self::count($atts);
 
252
  ob_start();
253
  if( $events_count > 0 ){
254
- $EM_Events = self::get($args);
255
  switch ( $args['mode'] ){
256
  case 'yearly':
257
  //go through the events and put them into a monthly array
@@ -347,8 +468,8 @@ class EM_Events extends EM_Object {
347
  if( !empty($args['pagination']) && !empty($args['limit']) && $events_count > $args['limit'] ){
348
  echo self::get_pagination_links($args, $events_count, 'search_events_grouped');
349
  }
350
- }else{
351
- echo get_option ( 'dbem_no_events_message' );
352
  }
353
  return ob_get_clean();
354
  }
@@ -392,33 +513,38 @@ class EM_Events extends EM_Object {
392
  public static function build_sql_conditions( $args = array() ){
393
  self::$context = EM_POST_TYPE_EVENT;
394
  global $wpdb;
 
395
  $conditions = parent::build_sql_conditions($args);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396
  if( !empty($args['search']) ){
397
- $like_search = array('event_name',EM_EVENTS_TABLE.'.post_content','location_name','location_address','location_town','location_postcode','location_state','location_country','location_region');
 
 
 
 
398
  $like_search_string = '%'.$wpdb->esc_like($args['search']).'%';
399
  $like_search_strings = array();
400
  foreach( $like_search as $v ) $like_search_strings[] = $like_search_string;
401
  $like_search_sql = "(".implode(" LIKE %s OR ", $like_search). " LIKE %s)";
402
  $conditions['search'] = $wpdb->prepare($like_search_sql, $like_search_strings);
403
  }
404
- $conditions['status'] = "(`event_status` >= 0 )"; //shows pending & published if not defined
405
- if( array_key_exists('status',$args) ){
406
- if( is_numeric($args['status']) ){
407
- $conditions['status'] = "(`event_status`={$args['status']})"; //pending or published
408
- }elseif( $args['status'] == 'pending' ){
409
- $conditions['status'] = "(`event_status`=0)"; //pending
410
- }elseif( $args['status'] == 'publish' ){
411
- $conditions['status'] = "(`event_status`=1)"; //published
412
- }elseif( $args['status'] === null || $args['status'] == 'draft' ){
413
- $conditions['status'] = "(`event_status` IS NULL )"; //show draft items
414
- }elseif( $args['status'] == 'trash' ){
415
- $conditions['status'] = "(`event_status` = -1 )"; //show trashed items
416
- }elseif( $args['status'] == 'all'){
417
- $conditions['status'] = "(`event_status` >= 0 OR `event_status` IS NULL)"; //search all statuses that aren't trashed
418
- }elseif( $args['status'] == 'everything'){
419
- unset($conditions['status']); //search all statuses
420
- }
421
- }
422
  //private events
423
  if( empty($args['private']) ){
424
  $conditions['private'] = "(`event_private`=0)";
@@ -440,16 +566,6 @@ class EM_Events extends EM_Object {
440
  }
441
  }
442
  }
443
- if( $args['bookings'] === 'user' && is_user_logged_in()){
444
- //get bookings of user
445
- $EM_Person = new EM_Person(get_current_user_id());
446
- $booking_ids = $EM_Person->get_bookings(true);
447
- if( count($booking_ids) > 0 ){
448
- $conditions['bookings'] = "(event_id IN (SELECT event_id FROM ".EM_BOOKINGS_TABLE." WHERE booking_id IN (".implode(',',$booking_ids).")))";
449
- }else{
450
- $conditions['bookings'] = "(event_id = 0)";
451
- }
452
- }
453
  //post search
454
  if( !empty($args['post_id'])){
455
  if( is_array($args['post_id']) ){
@@ -458,23 +574,54 @@ class EM_Events extends EM_Object {
458
  $conditions['post_id'] = "(".EM_EVENTS_TABLE.".post_id={$args['post_id']})";
459
  }
460
  }
461
- //events with or without locations
462
- if( !empty($args['has_location']) ){
463
- $conditions['has_location'] = '('.EM_EVENTS_TABLE.'.location_id IS NOT NULL AND '.EM_EVENTS_TABLE.'.location_id != 0)';
464
- }elseif( !empty($args['no_location']) ){
465
- $conditions['no_location'] = '('.EM_EVENTS_TABLE.'.location_id IS NULL OR '.EM_EVENTS_TABLE.'.location_id = 0)';
466
- }
467
  return apply_filters( 'em_events_build_sql_conditions', $conditions, $args );
468
  }
469
 
470
- /* Overrides EM_Object method to apply a filter to result
471
- * @see wp-content/plugins/events-manager/classes/EM_Object#build_sql_orderby()
 
472
  */
473
  public static function build_sql_orderby( $args, $accepted_fields, $default_order = 'ASC' ){
474
- self::$context = EM_POST_TYPE_EVENT;
475
  $accepted_fields[] = 'event_date_modified';
476
  $accepted_fields[] = 'event_date_created';
477
- return apply_filters( 'em_events_build_sql_orderby', parent::build_sql_orderby($args, $accepted_fields, get_option('dbem_events_default_order')), $args, $accepted_fields, $default_order );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
478
  }
479
 
480
  /*
@@ -487,20 +634,29 @@ class EM_Events extends EM_Object {
487
  public static function get_default_search( $array_or_defaults = array(), $array = array() ){
488
  self::$context = EM_POST_TYPE_EVENT;
489
  $defaults = array(
 
490
  'orderby' => get_option('dbem_events_default_orderby'),
491
  'order' => get_option('dbem_events_default_order'),
492
- 'bookings' => false, //if set to true, only events with bookings enabled are returned
 
 
493
  'status' => 1, //approved events only
494
  'town' => false,
495
  'state' => false,
496
  'country' => false,
497
  'region' => false,
498
- 'has_location' => false,
499
- 'no_location' => false,
500
  'blog' => get_current_blog_id(),
501
  'private' => current_user_can('read_private_events'),
502
  'private_only' => false,
503
- 'post_id' => false
 
 
 
 
 
 
 
 
504
  );
505
  //sort out whether defaults were supplied or just the array of search values
506
  if( empty($array) ){
@@ -514,6 +670,7 @@ class EM_Events extends EM_Object {
514
  $array['blog'] = false;
515
  }
516
  }
 
517
  if( is_admin() && !defined('DOING_AJAX') ){
518
  //figure out default owning permissions
519
  $defaults['owner'] = !current_user_can('edit_others_events') ? get_current_user_id() : false;
@@ -521,7 +678,27 @@ class EM_Events extends EM_Object {
521
  $defaults['status'] = false; //by default, admins see pending and live events
522
  }
523
  }
524
- return apply_filters('em_events_get_default_search', parent::get_default_search($defaults,$array), $array, $defaults);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
525
  }
526
  }
527
  ?>
6
  */
7
  class EM_Events extends EM_Object {
8
 
9
+ /**
10
+ * Like WPDB->num_rows it holds the number of results found on the last query.
11
+ * @var int
12
+ */
13
+ public static $num_rows;
14
+
15
+ /**
16
+ * If $args['pagination'] is true or $args['offset'] or $args['page'] is greater than one, and a limit is imposed when using a get() query,
17
+ * this will contain the total records found without a limit for the last query.
18
+ * If no limit was used or pagination was not enabled, this will be the same as self::$num_rows
19
+ * @var int
20
+ */
21
+ public static $num_rows_found;
22
+
23
  /**
24
  * Returns an array of EM_Events that match the given specs in the argument, or returns a list of future evetnts in future
25
  * (see EM_Events::get_default_search() ) for explanation of possible search array values. You can also supply a numeric array
47
  $args = self::get_default_search($args);
48
  $limit = ( $args['limit'] && is_numeric($args['limit'])) ? "LIMIT {$args['limit']}" : '';
49
  $offset = ( $limit != "" && is_numeric($args['offset']) ) ? "OFFSET {$args['offset']}" : '';
 
50
 
51
+ //Get fields that we can use in ordering and grouping, which can be event and location (excluding ambiguous) fields
 
 
 
 
 
52
  $EM_Event = new EM_Event();
53
  $EM_Location = new EM_Location();
54
+ $event_fields = array_keys($EM_Event->fields);
55
+ $location_fields = array(); //will contain location-specific fields, not ambiguous ones
56
+ foreach( array_keys($EM_Location->fields) as $field_name ){
57
+ if( !in_array($field_name, $event_fields) ) $location_fields[] = $field_name;
58
+ }
59
+ if( get_option('dbem_locations_enabled') ){
60
+ $accepted_fields = array_merge($event_fields, $location_fields);
 
 
 
 
 
 
 
 
61
  }else{
62
+ //if locations disabled then we don't accept location-specific fields
63
+ $accepted_fields = $event_fields;
64
  }
65
+
66
+ //Start SQL statement
67
+
68
+ //Create the SQL statement selectors
69
+ $calc_found_rows = $limit && ( $args['pagination'] || $args['offset'] > 0 || $args['page'] > 0 );
70
  if( $count ){
71
+ $selectors = 'COUNT(DISTINCT '.$events_table . '.event_id)';
72
  $limit = 'LIMIT 1';
73
  $offset = 'OFFSET 0';
74
+ }else{
75
+ if( $args['array'] ){
76
+ //get all fields from table, add events table prefix to avoid ambiguous fields from location
77
+ $selectors = $events_table . '.*';
78
+ }elseif( EM_MS_GLOBAL ){
79
+ $selectors = $events_table.'.post_id, '.$events_table.'.blog_id';
80
+ }else{
81
+ $selectors = $events_table.'.post_id';
82
+ }
83
+ if( $calc_found_rows ) $selectors = 'SQL_CALC_FOUND_ROWS ' . $selectors; //for storing total rows found
84
+ $selectors = 'DISTINCT ' . $selectors; //duplicate avoidance
85
  }
86
 
87
+ //check if we need to join a location table for this search, which is necessary if any location-specific are supplied, or if certain arguments such as orderby contain location fields
88
+ if( !empty($args['groupby']) || (defined('EM_DISABLE_OPTIONAL_JOINS') && EM_DISABLE_OPTIONAL_JOINS) ){
89
+ $location_specific_args = array('town', 'state', 'country', 'region', 'near', 'geo', 'search');
90
+ $join_locations = false;
91
+ foreach( $location_specific_args as $arg ) if( !empty($args[$arg]) ) $join_locations = true;
92
+ //if set to false the following would provide a false negative in the line above
93
+ if( $args['location_status'] !== false ){ $join_locations = true; }
94
+ //check ordering and grouping arguments for precense of location fields requiring a join
95
+ if( !$join_locations ){
96
+ foreach( array('groupby', 'orderby', 'groupby_orderby') as $arg ){
97
+ if( !is_array($args[$arg]) ) continue; //ignore this argument if set to false
98
+ //we assume all these arguments are now array thanks to self::get_search_defaults() cleaning it up
99
+ foreach( $args[$arg] as $field_name ){
100
+ if( in_array($field_name, $location_fields) ){
101
+ $join_locations = true;
102
+ break; //we join, no need to keep searching
103
+ }
104
+ }
105
+ }
106
+ }
107
+ }else{ $join_locations = true; }//end temporary if( !empty($args['groupby']).... wrapper
108
+ //plugins can override this optional joining behaviour here in case they add custom WHERE conditions or something like that
109
+ $join_locations = apply_filters('em_events_get_join_locations_table', $join_locations, $args, $count);
110
+ //depending on whether to join we do certain things like add a join SQL, change specific values like status search
111
+ $location_optional_join = $join_locations ? "LEFT JOIN $locations_table ON {$locations_table}.location_id={$events_table}.location_id" : '';
112
+
113
+ //Build ORDER BY and WHERE SQL statements here, after we've done all the pre-processing necessary
114
+ $conditions = self::build_sql_conditions($args);
115
+ $where = ( count($conditions) > 0 ) ? " WHERE " . implode ( " AND ", $conditions ):'';
116
+ $orderby = self::build_sql_orderby($args, $accepted_fields, get_option('dbem_events_default_order'));
117
+ $orderby_sql = ( count($orderby) > 0 ) ? 'ORDER BY '. implode(', ', $orderby) : '';
118
+
119
+ //Build GROUP BY SQL statement, which will be very different if we group things due to how we need to filter out by event date
120
+ if( !empty($args['groupby']) ){
121
+ //get groupby field(s)
122
+ $groupby_fields = self::build_sql_groupby($args, $accepted_fields);
123
+ if( !empty($groupby_fields[0]) ){
124
+ //we can safely assume we've been passed at least one array item with index of 0 containing a valid field due to build_sql_groupby()
125
+ $groupby_field = $groupby_fields[0]; //we only support one field for events
126
+ $groupby_orderby = self::build_sql_groupby_orderby($args, $accepted_fields);
127
+ $groupby_orderby_sql = !empty($groupby_orderby) ? ', '. implode(', ', $groupby_orderby) : '';
128
+ //get minimum required selectors within the inner query to shorten query length as much as possible
129
+ $inner_selectors = $events_table . '.*';
130
+ if( $location_optional_join ){
131
+ //we're selecting all fields from events table so add only location fields required in the outer ORDER BY statement
132
+ foreach( $args['orderby'] as $orderby_field ){
133
+ if( in_array($orderby_field, $location_fields) ){
134
+ $inner_selectors .= ', '. $locations_table .'.'. $orderby_field;
135
+ }
136
+ }
137
+ }
138
+
139
+ //THE Query - Grouped
140
+ if( in_array($groupby_field, $location_fields) || count(array_intersect($location_fields, $args['groupby_orderby'])) || defined('EM_FORCE_GROUPED_DATASET_SQL') ){
141
+ //if we're grouping by any fields in the locations table, we run a different (slightly slower) query to provide reliable results
142
+ if( in_array($groupby_field, $location_fields) && !in_array($groupby_field, $args['orderby']) ){
143
+ //we may not have included the grouped field if it's not in the outer ORDER BY clause, so we add it for this specific query
144
+ $inner_selectors .= ', '. $locations_table .'.'. $groupby_field;
145
+ }
146
+ $sql = "
147
+ SELECT $selectors
148
+ FROM (
149
+ SELECT *,
150
+ @cur := IF($groupby_field = @id, @cur+1, 1) AS RowNumber,
151
+ @id := $groupby_field AS IdCache
152
+ FROM (
153
+ SELECT {$inner_selectors} FROM {$events_table}
154
+ $location_optional_join
155
+ $where
156
+ ORDER BY $groupby_field $groupby_orderby_sql
157
+ ) {$events_table}
158
+ INNER JOIN (
159
+ SELECT @id:=0, @cur:=0
160
+ ) AS lookup
161
+ ) {$events_table}
162
+ WHERE RowNumber = 1
163
+ $orderby_sql
164
+ $limit $offset";
165
+ }else{
166
+ //we'll keep this query simply because it's a little faster and still seems reliable when not grouping or group-sorting any fields in the locations table
167
+ $sql = "
168
+ SELECT $selectors
169
+ FROM (
170
+ SELECT {$inner_selectors},
171
+ @cur := IF($groupby_field = @id, @cur+1, 1) AS RowNumber,
172
+ @id := $groupby_field AS IdCache
173
+ FROM {$events_table}
174
+ INNER JOIN (
175
+ SELECT @id:=0, @cur:=0
176
+ ) AS lookup
177
+ $location_optional_join
178
+ $where
179
+ ORDER BY {$groupby_field} $groupby_orderby_sql
180
+ ) {$events_table}
181
+ WHERE RowNumber = 1
182
+ $orderby_sql
183
+ $limit $offset";
184
+ }
185
+ }
186
+ }
187
 
188
+ //Build THE Query SQL statement if not already built for a grouped query
189
+ if( empty($sql) ){
190
+ //THE Query
191
+ $sql = "
192
+ SELECT $selectors FROM $events_table
193
+ $location_optional_join
194
+ $where
195
+ $orderby_sql
196
+ $limit $offset";
197
+ }
198
+
199
+ //THE Query filter
200
+ $sql = apply_filters('em_events_get_sql', $sql, $args);
201
+ //if( em_wp_is_super_admin() && WP_DEBUG_DISPLAY ){ echo "<pre>"; print_r($sql); echo '</pre>'; }
202
 
203
+ //If we're only counting results, return the number of results and go no further
204
  if( $count ){
205
+ self::$num_rows_found = self::$num_rows = $wpdb->get_var($sql);
206
+ return apply_filters('em_events_get_count', self::$num_rows, $args);
207
  }
208
+
209
+ //get the result and count results
210
  $results = $wpdb->get_results( $sql, ARRAY_A);
211
+ self::$num_rows = $wpdb->num_rows;
212
+ if( $calc_found_rows ){
213
+ self::$num_rows_found = $wpdb->get_var('SELECT FOUND_ROWS()');
214
+ }else{
215
+ self::$num_rows_found = self::$num_rows;
216
+ }
217
 
218
  //If we want results directly in an array, why not have a shortcut here?
219
  if( $args['array'] == true ){
289
  //get page number if passed on by request (still needs pagination enabled to have effect)
290
  $page_queryvar = !empty($args['page_queryvar']) ? $args['page_queryvar'] : 'pno';
291
  if( !empty($args['pagination']) && !array_key_exists('page',$args) && !empty($_REQUEST[$page_queryvar]) && is_numeric($_REQUEST[$page_queryvar]) ){
292
+ $args['page'] = $_REQUEST[$page_queryvar];
293
  }
294
  //Can be either an array for the get search or an array of EM_Event objects
295
  if( is_object(current($args)) && get_class((current($args))) == 'EM_Event' ){
298
  $args = (!empty($func_args[1]) && is_array($func_args[1])) ? $func_args[1] : array();
299
  $args = apply_filters('em_events_output_args', self::get_default_search($args), $events);
300
  $limit = ( !empty($args['limit']) && is_numeric($args['limit']) ) ? $args['limit']:false;
 
 
301
  $events_count = count($events);
302
  }else{
303
  //Firstly, let's check for a limit/offset here, because if there is we need to remove it and manually do this
304
  $args = apply_filters('em_events_output_args', self::get_default_search($args));
305
  $limit = ( !empty($args['limit']) && is_numeric($args['limit']) ) ? $args['limit']:false;
 
 
 
 
 
 
 
306
  $events = self::get( $args );
307
+ $events_count = self::$num_rows_found;
308
  }
309
  //What format shall we output this to, or use default
310
  $format = ( empty($args['format']) ) ? get_option( 'dbem_event_list_item_format' ) : $args['format'] ;
311
 
312
  $output = "";
313
+
314
  if ( $events_count > 0 ) {
315
+ $events = apply_filters('em_events_output_events', $events);
316
  foreach ( $events as $EM_Event ) {
317
  $output .= $EM_Event->output($format);
318
  }
331
  if( !empty($args['pagination']) && !empty($limit) && $events_count > $limit ){
332
  $output .= self::get_pagination_links($args, $events_count);
333
  }
334
+ }elseif( $args['no_results_msg'] !== false ){
335
+ $output = !empty($args['no_results_msg']) ? $args['no_results_msg'] : get_option('dbem_no_events_message');
336
+ }
337
 
338
  //TODO check if reference is ok when restoring object, due to changes in php5 v 4
339
  $EM_Event = $EM_Event_old;
363
 
364
  $args['mode'] = !empty($args['mode']) ? $args['mode'] : get_option('dbem_event_list_groupby');
365
  $args['header_format'] = !empty($args['header_format']) ? $args['header_format'] : get_option('dbem_event_list_groupby_header_format', '<h2>#s</h2>');
366
+ $args['date_format'] = !empty($args['date_format']) ? $args['date_format'] : get_option('dbem_event_list_groupby_format','');
367
+ $args = apply_filters('em_events_output_grouped_args', self::get_default_search($args));
368
  //Reset some vars for counting events and displaying set arrays of events
369
  $atts = (array) $args;
370
+ $atts['pagination'] = $atts['limit'] = $atts['page'] = $atts['offset'] = false;
 
 
 
371
  //decide what form of dates to show
372
+ $EM_Events = self::get( $args );
373
+ $events_count = self::$num_rows_found;
374
  ob_start();
375
  if( $events_count > 0 ){
 
376
  switch ( $args['mode'] ){
377
  case 'yearly':
378
  //go through the events and put them into a monthly array
468
  if( !empty($args['pagination']) && !empty($args['limit']) && $events_count > $args['limit'] ){
469
  echo self::get_pagination_links($args, $events_count, 'search_events_grouped');
470
  }
471
+ }elseif( $args['no_results_msg'] !== false ){
472
+ echo !empty($args['no_results_msg']) ? $args['no_results_msg'] : get_option('dbem_no_events_message');
473
  }
474
  return ob_get_clean();
475
  }
513
  public static function build_sql_conditions( $args = array() ){
514
  self::$context = EM_POST_TYPE_EVENT;
515
  global $wpdb;
516
+ //continue with conditions
517
  $conditions = parent::build_sql_conditions($args);
518
+ //specific location query conditions if locations are enabled
519
+ if( get_option('dbem_locations_enabled') ){
520
+ //events with or without locations
521
+ if( !empty($args['has_location']) ){
522
+ $conditions['has_location'] = '('.EM_EVENTS_TABLE.'.location_id IS NOT NULL AND '.EM_EVENTS_TABLE.'.location_id != 0)';
523
+ }elseif( !empty($args['no_location']) ){
524
+ $conditions['no_location'] = '('.EM_EVENTS_TABLE.'.location_id IS NULL OR '.EM_EVENTS_TABLE.'.location_id = 0)';
525
+ }elseif( !empty($conditions['location_status']) ){
526
+ $location_specific_args = array('town', 'state', 'country', 'region', 'near', 'geo', 'search');
527
+ foreach( $location_specific_args as $location_arg ){
528
+ if( !empty($args[$location_arg]) ) $skip_location_null_condition = true;
529
+ }
530
+ if( empty($skip_location_null_condition) ){
531
+ $conditions['location_status'] = '('.$conditions['location_status'].' OR '.EM_LOCATIONS_TABLE.'.location_id IS NULL)';
532
+ }
533
+ }
534
+ }
535
+ //search conditions
536
  if( !empty($args['search']) ){
537
+ if( get_option('dbem_locations_enabled') ){
538
+ $like_search = array('event_name',EM_EVENTS_TABLE.'.post_content','location_name','location_address','location_town','location_postcode','location_state','location_country','location_region');
539
+ }else{
540
+ $like_search = array('event_name',EM_EVENTS_TABLE.'.post_content');
541
+ }
542
  $like_search_string = '%'.$wpdb->esc_like($args['search']).'%';
543
  $like_search_strings = array();
544
  foreach( $like_search as $v ) $like_search_strings[] = $like_search_string;
545
  $like_search_sql = "(".implode(" LIKE %s OR ", $like_search). " LIKE %s)";
546
  $conditions['search'] = $wpdb->prepare($like_search_sql, $like_search_strings);
547
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
548
  //private events
549
  if( empty($args['private']) ){
550
  $conditions['private'] = "(`event_private`=0)";
566
  }
567
  }
568
  }
 
 
 
 
 
 
 
 
 
 
569
  //post search
570
  if( !empty($args['post_id'])){
571
  if( is_array($args['post_id']) ){
574
  $conditions['post_id'] = "(".EM_EVENTS_TABLE.".post_id={$args['post_id']})";
575
  }
576
  }
 
 
 
 
 
 
577
  return apply_filters( 'em_events_build_sql_conditions', $conditions, $args );
578
  }
579
 
580
+ /**
581
+ * Overrides EM_Object method to clean ambiguous fields and apply a filter to result.
582
+ * @see EM_Object::build_sql_orderby()
583
  */
584
  public static function build_sql_orderby( $args, $accepted_fields, $default_order = 'ASC' ){
 
585
  $accepted_fields[] = 'event_date_modified';
586
  $accepted_fields[] = 'event_date_created';
587
+ $orderby = parent::build_sql_orderby($args, $accepted_fields, get_option('dbem_events_default_order'));
588
+ $orderby = self::build_sql_ambiguous_fields_helper($orderby); //fix ambiguous fields
589
+ return apply_filters( 'em_events_build_sql_orderby', $orderby, $args, $accepted_fields, $default_order );
590
+ }
591
+
592
+ /**
593
+ * Overrides EM_Object method to clean ambiguous fields and apply a filter to result.
594
+ * @see EM_Object::build_sql_groupby()
595
+ */
596
+ public static function build_sql_groupby( $args, $accepted_fields, $groupby_order = false, $default_order = 'ASC' ){
597
+ $accepted_fields[] = 'event_date_modified';
598
+ $accepted_fields[] = 'event_date_created';
599
+ $groupby = parent::build_sql_groupby($args, $accepted_fields);
600
+ //fix ambiguous fields and give them scope of events table
601
+ $groupby = self::build_sql_ambiguous_fields_helper($groupby);
602
+ return apply_filters( 'em_events_build_sql_groupby', $groupby, $args, $accepted_fields );
603
+ }
604
+
605
+ /**
606
+ * Overrides EM_Object method to clean ambiguous fields and apply a filter to result.
607
+ * @see EM_Object::build_sql_groupby_orderby()
608
+ */
609
+ public static function build_sql_groupby_orderby($args, $accepted_fields, $default_order = 'ASC' ){
610
+ $accepted_fields[] = 'event_date_modified';
611
+ $accepted_fields[] = 'event_date_created';
612
+ $group_orderby = parent::build_sql_groupby_orderby($args, $accepted_fields, get_option('dbem_events_default_order'));
613
+ //fix ambiguous fields and give them scope of events table
614
+ $group_orderby = self::build_sql_ambiguous_fields_helper($group_orderby);
615
+ return apply_filters( 'em_events_build_sql_groupby_orderby', $group_orderby, $args, $accepted_fields, $default_order );
616
+ }
617
+
618
+ /**
619
+ * Overrides EM_Object method to provide specific reserved fields and events table.
620
+ * @see EM_Object::build_sql_ambiguous_fields_helper()
621
+ */
622
+ protected static function build_sql_ambiguous_fields_helper( $fields, $reserved_fields = array(), $prefix = 'table_name' ){
623
+ //This will likely be removed when PHP 5.3 is the minimum and LSB is a given
624
+ return parent::build_sql_ambiguous_fields_helper($fields, array('post_id', 'location_id', 'blog_id'), EM_EVENTS_TABLE);
625
  }
626
 
627
  /*
634
  public static function get_default_search( $array_or_defaults = array(), $array = array() ){
635
  self::$context = EM_POST_TYPE_EVENT;
636
  $defaults = array(
637
+ 'recurring' => false, //we don't initially look for recurring events only events and recurrences of recurring events
638
  'orderby' => get_option('dbem_events_default_orderby'),
639
  'order' => get_option('dbem_events_default_order'),
640
+ 'groupby' => false,
641
+ 'groupby_orderby' => 'event_start_date, event_start_time', //groups according to event start time, i.e. by default shows earliest event in a scope
642
+ 'groupby_order' => 'ASC', //groups according to event start time, i.e. by default shows earliest event in a scope
643
  'status' => 1, //approved events only
644
  'town' => false,
645
  'state' => false,
646
  'country' => false,
647
  'region' => false,
 
 
648
  'blog' => get_current_blog_id(),
649
  'private' => current_user_can('read_private_events'),
650
  'private_only' => false,
651
+ 'post_id' => false,
652
+ //ouput_grouped specific arguments
653
+ 'mode' => false,
654
+ 'header_format' => false,
655
+ 'date_format' => false,
656
+ //event-specific search attributes
657
+ 'has_location' => false, //search events with a location
658
+ 'no_location' => false, //search events without a location
659
+ 'location_status' => false //search events with locations of a specific publish status
660
  );
661
  //sort out whether defaults were supplied or just the array of search values
662
  if( empty($array) ){
670
  $array['blog'] = false;
671
  }
672
  }
673
+ //admin-area specific modifiers
674
  if( is_admin() && !defined('DOING_AJAX') ){
675
  //figure out default owning permissions
676
  $defaults['owner'] = !current_user_can('edit_others_events') ? get_current_user_id() : false;
678
  $defaults['status'] = false; //by default, admins see pending and live events
679
  }
680
  }
681
+ //check if we're doing any location-specific searching, if so then we (by default) want to match the status of events
682
+ if( !empty($array['has_location']) ){
683
+ //we're looking for events with locations, so we match the status we're searching for events unless there's an argument passed on for something different
684
+ $defaults['location_status'] = true;
685
+ }elseif( !empty($array['no_location']) ){
686
+ //if no location is being searched for, we should ignore any status searches for location
687
+ $defaults['location_status'] = $array['location_status'] = false;
688
+ }else{
689
+ $location_specific_args = array('town', 'state', 'country', 'region', 'near', 'geo');
690
+ foreach( $location_specific_args as $location_arg ){
691
+ if( !empty($array[$location_arg]) ) $defaults['location_status'] = true;
692
+ }
693
+ }
694
+ $args = parent::get_default_search($defaults,$array);
695
+ //do some post-parnet cleaning up here if locations are enabled or disabled
696
+ if( !get_option('dbem_locations_enabled') ){
697
+ //locations disabled, wipe any args to do with locations so they're ignored
698
+ $location_args = array('town', 'state', 'country', 'region', 'has_location', 'no_location', 'location_status', 'location', 'geo', 'near', 'location_id');
699
+ foreach( $location_args as $arg ) $args[$arg] = false;
700
+ }
701
+ return apply_filters('em_events_get_default_search', $args, $array, $defaults);
702
  }
703
  }
704
  ?>
classes/em-location-post-admin.php CHANGED
@@ -84,7 +84,7 @@ class EM_Location_Post_Admin{
84
  $is_post_type = get_post_type($post_id) == EM_POST_TYPE_LOCATION;
85
  if(!defined('UNTRASHING_'.$post_id) && $is_post_type && $saving_status){
86
  if( !empty($_REQUEST['_emnonce']) && wp_verify_nonce($_REQUEST['_emnonce'], 'edit_location')){
87
- $EM_Location = em_get_location($post_id, 'post_id');
88
  $get_meta = $EM_Location->get_post_meta(false);
89
  $validate_meta = $EM_Location->validate_meta();
90
  do_action('em_location_save_pre', $EM_Location);
84
  $is_post_type = get_post_type($post_id) == EM_POST_TYPE_LOCATION;
85
  if(!defined('UNTRASHING_'.$post_id) && $is_post_type && $saving_status){
86
  if( !empty($_REQUEST['_emnonce']) && wp_verify_nonce($_REQUEST['_emnonce'], 'edit_location')){
87
+ $EM_Location = new EM_Location($post_id, 'post_id');
88
  $get_meta = $EM_Location->get_post_meta(false);
89
  $validate_meta = $EM_Location->validate_meta();
90
  do_action('em_location_save_pre', $EM_Location);
classes/em-location-post.php CHANGED
@@ -7,8 +7,11 @@ class EM_Location_Post {
7
  add_filter('the_content', array('EM_Location_Post','the_content'));
8
  //override excerpts?
9
  if( get_option('dbem_cp_locations_excerpt_formats') ){
10
- add_filter('the_excerpt', array('EM_Location_Post','the_excerpt'));
11
  }
 
 
 
12
  //display as page or other template?
13
  if( get_option('dbem_cp_locations_template') ){
14
  add_filter('single_template',array('EM_Location_Post','single_template'));
@@ -68,7 +71,7 @@ class EM_Location_Post {
68
  /**
69
  * Overrides the_excerpt if this is an location post type
70
  */
71
- public static function the_excerpt($content){
72
  global $post;
73
  if( $post->post_type == EM_POST_TYPE_LOCATION ){
74
  $EM_Location = em_get_location($post);
@@ -77,6 +80,16 @@ class EM_Location_Post {
77
  }
78
  return $content;
79
  }
 
 
 
 
 
 
 
 
 
 
80
 
81
  public static function the_content( $content ){
82
  global $post, $EM_Location;
7
  add_filter('the_content', array('EM_Location_Post','the_content'));
8
  //override excerpts?
9
  if( get_option('dbem_cp_locations_excerpt_formats') ){
10
+ add_filter('get_the_excerpt', array('EM_Location_Post','the_excerpt'), 9);
11
  }
12
+ //excerpts can trigger the_content which isn't ideal, so we disable the_content between the first and last excerpt calls within WP logic
13
+ add_filter('get_the_excerpt', array('EM_Location_Post','disable_the_content'), 1);
14
+ add_filter('get_the_excerpt', array('EM_Location_Post','enable_the_content'), 100);
15
  //display as page or other template?
16
  if( get_option('dbem_cp_locations_template') ){
17
  add_filter('single_template',array('EM_Location_Post','single_template'));
71
  /**
72
  * Overrides the_excerpt if this is an location post type
73
  */
74
+ public static function get_the_excerpt($content){
75
  global $post;
76
  if( $post->post_type == EM_POST_TYPE_LOCATION ){
77
  $EM_Location = em_get_location($post);
80
  }
81
  return $content;
82
  }
83
+ public static function the_excerpt($content){ return self::get_the_excerpt($content); }
84
+
85
+ public static function enable_the_content( $content ){
86
+ add_filter('the_content', array('EM_Location_Post','the_content'));
87
+ return $content;
88
+ }
89
+ public static function disable_the_content( $content ){
90
+ remove_filter('the_content', array('EM_Location_Post','the_content'));
91
+ return $content;
92
+ }
93
 
94
  public static function the_content( $content ){
95
  global $post, $EM_Location;
classes/em-location.php CHANGED
@@ -1,14 +1,44 @@
1
  <?php
2
  /**
3
- * gets a location
4
  * @param mixed $id
5
  * @param mixed $search_by
6
  * @return EM_Location
7
  */
8
  function em_get_location($id = false, $search_by = 'location_id') {
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  if( is_object($id) && get_class($id) == 'EM_Location' ){
10
  return apply_filters('em_get_location', $id);
11
  }else{
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  return apply_filters('em_get_location', new EM_Location($id,$search_by));
13
  }
14
  }
@@ -53,7 +83,7 @@ class EM_Location extends EM_Object {
53
  'location_owner' => array('name'=>'owner','type'=>'%d', 'null'=>true),
54
  'location_status' => array('name'=>'status','type'=>'%d', 'null'=>true)
55
  );
56
- var $post_fields = array('post_id','location_slug','location_name','post_content','location_owner');
57
  var $location_attributes = array();
58
  var $image_url = '';
59
  var $required_fields = array();
@@ -144,6 +174,11 @@ class EM_Location extends EM_Object {
144
  $this->load_postdata($location_post, $search_by);
145
  }
146
  $this->compat_keys();
 
 
 
 
 
147
  do_action('em_location', $this, $id, $search_by);
148
  }
149
 
@@ -169,7 +204,7 @@ class EM_Location extends EM_Object {
169
  }
170
  }
171
  if(!$found && $location_meta_key[0] != '_'){
172
- $this->location_attributes[$location_meta_key] = ( count($location_meta_val) > 1 ) ? $location_meta_val:$location_meta_val[0];
173
  }
174
  }
175
  }
@@ -365,14 +400,26 @@ class EM_Location extends EM_Object {
365
  $this->blog_id = get_current_blog_id();
366
  }
367
  //Update Post Meta
 
368
  foreach( array_keys($this->fields) as $key ){
369
- if( !in_array($key, $this->post_fields) ){
370
  update_post_meta($this->post_id, '_'.$key, $this->$key);
 
 
371
  }
372
  }
373
- //Update Post Attributes
374
- foreach($this->location_attributes as $location_attribute_key => $location_attribute){
375
- update_post_meta($this->post_id, $location_attribute_key, $location_attribute);
 
 
 
 
 
 
 
 
 
376
  }
377
  $this->get_status();
378
  $this->location_status = (count($this->errors) == 0) ? $this->location_status:null; //set status at this point, it's either the current status, or if validation fails, null
@@ -417,6 +464,12 @@ class EM_Location extends EM_Object {
417
  $this->add_error( sprintf(__('You do not have permission to create/edit %s.','events-manager'), __('locations','events-manager')) );
418
  }
419
  $this->compat_keys();
 
 
 
 
 
 
420
  return apply_filters('em_location_save_meta', count($this->errors) == 0, $this);
421
  }
422
 
@@ -496,7 +549,7 @@ class EM_Location extends EM_Object {
496
  }
497
  if($set_post_status){
498
  $wpdb->update( $wpdb->posts, array( 'post_status' => $post_status, 'post_name' => $this->post_name ), array( 'ID' => $this->post_id ) );
499
- }elseif( $set_post_name ){
500
  //if we've added a post slug then update wp_posts anyway
501
  $wpdb->update( $wpdb->posts, array( 'post_name' => $this->post_name ), array( 'ID' => $this->post_id ) );
502
  }
@@ -561,10 +614,8 @@ class EM_Location extends EM_Object {
561
 
562
  function has_events( $status = 1 ){
563
  global $wpdb;
564
- $events_table = EM_EVENTS_TABLE;
565
- $sql = $wpdb->prepare("SELECT count(event_id) as events_no FROM $events_table WHERE location_id=%d AND event_status=%d", $this->location_id, $status);
566
- $affected_events = $wpdb->get_var($sql);
567
- return apply_filters('em_location_has_events', $affected_events > 0, $this);
568
  }
569
 
570
  /**
@@ -799,34 +850,24 @@ class EM_Location extends EM_Object {
799
  case '#_LOCATIONLATITUDE':
800
  $replace = $this->location_latitude;
801
  break;
802
- case '#_DESCRIPTION': //Depricated
803
- case '#_EXCERPT': //Depricated
804
  case '#_LOCATIONNOTES':
805
- case '#_LOCATIONEXCERPT':
806
  $replace = $this->post_content;
807
- if($result == "#_EXCERPT" || $result == "#_LOCATIONEXCERPT"){
808
- if( !empty($this->post_excerpt) ){
809
- $replace = $this->post_excerpt;
810
- }else{
811
- $excerpt_length = 55;
812
- $excerpt_more = apply_filters('em_excerpt_more', ' ' . '[...]');
813
- if( !empty($placeholders[3][$key]) ){
814
- $trim = true;
815
- $ph_args = explode(',', $placeholders[3][$key]);
816
- if( is_numeric($ph_args[0]) ) $excerpt_length = $ph_args[0];
817
- if( !empty($ph_args[1]) ) $excerpt_more = $ph_args[1];
818
- }
819
- if ( preg_match('/<!--more(.*?)?-->/', $replace, $matches) ) {
820
- $content = explode($matches[0], $replace, 2);
821
- $replace = force_balance_tags($content[0]);
822
- }
823
- if( !empty($trim) ){
824
- //shorten content by supplied number - copied from wp_trim_excerpt
825
- $replace = strip_shortcodes( $replace );
826
- $replace = str_replace(']]>', ']]&gt;', $replace);
827
- $replace = wp_trim_words( $replace, $excerpt_length, $excerpt_more );
828
- }
829
  }
 
830
  }
831
  break;
832
  case '#_LOCATIONIMAGEURL':
@@ -897,6 +938,14 @@ class EM_Location extends EM_Object {
897
  $replace = '<a href="'.esc_url($replace).'">iCal</a>';
898
  }
899
  break;
 
 
 
 
 
 
 
 
900
  case '#_LOCATIONRSSURL':
901
  case '#_LOCATIONRSSLINK':
902
  $replace = $this->get_rss_url();
@@ -919,18 +968,16 @@ class EM_Location extends EM_Object {
919
  if ( $result == '#_LOCATIONPASTEVENTS'){ $scope = 'past'; }
920
  elseif ( $result == '#_LOCATIONNEXTEVENTS' ){ $scope = 'future'; }
921
  else{ $scope = 'all'; }
922
- $events_count = EM_Events::count( array('location'=>$this->location_id, 'scope'=>$scope) );
923
- if ( $events_count > 0 ){
924
- $args = array('location'=>$this->location_id, 'scope'=>$scope, 'pagination'=>1, 'ajax'=>0);
925
- $args['format_header'] = get_option('dbem_location_event_list_item_header_format');
926
- $args['format_footer'] = get_option('dbem_location_event_list_item_footer_format');
927
- $args['format'] = get_option('dbem_location_event_list_item_format');
928
- $args['limit'] = get_option('dbem_location_event_list_limit');
929
- $args['page'] = (!empty($_REQUEST['pno']) && is_numeric($_REQUEST['pno']) )? $_REQUEST['pno'] : 1;
930
- $replace = EM_Events::output($args);
931
- } else {
932
- $replace = get_option('dbem_location_no_events_message');
933
- }
934
  break;
935
  case '#_LOCATIONNEXTEVENT':
936
  $events = EM_Events::get( array('location'=>$this->location_id, 'scope'=>'future', 'limit'=>1, 'orderby'=>'event_start_date,event_start_time') );
1
  <?php
2
  /**
3
+ * Get an event in a db friendly way, by checking globals, cache and passed variables to avoid extra class instantiations.
4
  * @param mixed $id
5
  * @param mixed $search_by
6
  * @return EM_Location
7
  */
8
  function em_get_location($id = false, $search_by = 'location_id') {
9
+ global $EM_Location;
10
+ //check if it's not already global so we don't instantiate again
11
+ if( is_object($EM_Location) && get_class($EM_Location) == 'EM_Location' ){
12
+ if( is_object($id) && $EM_Location->post_id == $id->ID ){
13
+ return apply_filters('em_get_location', $EM_Location);
14
+ }elseif( !is_object($id) ){
15
+ if( $search_by == 'location_id' && $EM_Location->location_id == $id ){
16
+ return apply_filters('em_get_location', $EM_Location);
17
+ }elseif( $search_by == 'post_id' && $EM_Location->post_id == $id ){
18
+ return apply_filters('em_get_location', $EM_Location);
19
+ }
20
+ }
21
+ }
22
  if( is_object($id) && get_class($id) == 'EM_Location' ){
23
  return apply_filters('em_get_location', $id);
24
  }else{
25
+ //check the cache first
26
+ $location_id = false;
27
+ if( is_numeric($id) ){
28
+ if( $search_by == 'location_id' ){
29
+ $location_id = $id;
30
+ }elseif( $search_by == 'post_id' ){
31
+ $location_id = wp_cache_get($id, 'em_locations_ids');
32
+ }
33
+ }elseif( !empty($id->ID) && !empty($id->post_type) && $id->post_type == EM_POST_TYPE_LOCATION ){
34
+ $location_id = wp_cache_get($id->ID, 'em_locations_ids');
35
+ }
36
+ if( $location_id ){
37
+ $location = wp_cache_get($location_id, 'em_locations');
38
+ if( is_object($location) && !empty($location->location_id) && $location->location_id){
39
+ return apply_filters('em_get_location', $location);
40
+ }
41
+ }
42
  return apply_filters('em_get_location', new EM_Location($id,$search_by));
43
  }
44
  }
83
  'location_owner' => array('name'=>'owner','type'=>'%d', 'null'=>true),
84
  'location_status' => array('name'=>'status','type'=>'%d', 'null'=>true)
85
  );
86
+ var $post_fields = array('post_id','location_slug','location_status', 'location_name','post_content','location_owner');
87
  var $location_attributes = array();
88
  var $image_url = '';
89
  var $required_fields = array();
174
  $this->load_postdata($location_post, $search_by);
175
  }
176
  $this->compat_keys();
177
+ //add this location to the cache
178
+ if( $this->location_id && $this->post_id ){
179
+ wp_cache_set($this->location_id, $this, 'em_locations');
180
+ wp_cache_set($this->post_id, $this->location_id, 'em_locations_ids');
181
+ }
182
  do_action('em_location', $this, $id, $search_by);
183
  }
184
 
204
  }
205
  }
206
  if(!$found && $location_meta_key[0] != '_'){
207
+ $this->location_attributes[$location_meta_key] = ( is_array($location_meta_val) ) ? $location_meta_val[0]:$location_meta_val;
208
  }
209
  }
210
  }
400
  $this->blog_id = get_current_blog_id();
401
  }
402
  //Update Post Meta
403
+ $current_meta_values = get_post_meta($this->post_id);
404
  foreach( array_keys($this->fields) as $key ){
405
+ if( !in_array($key, $this->post_fields) && $key != 'blog_id' && $this->$key != '' ){
406
  update_post_meta($this->post_id, '_'.$key, $this->$key);
407
+ }elseif( array_key_exists('_'.$key, $current_meta_values) ){ //we should delete event_attributes, but maybe something else uses it without us knowing
408
+ delete_post_meta($this->post_id, '_'.$key);
409
  }
410
  }
411
+ //Update Post Custom Fields and attributes
412
+ if( get_option('dbem_location_attributes_enabled') ){
413
+ //attributes get saved as individual keys or deleted if non-existent anymore
414
+ $atts = em_get_attributes( true ); //get available attributes that EM manages
415
+ $this->location_attributes= maybe_unserialize($this->location_attributes);
416
+ foreach( $atts['names'] as $location_attribute_key ){
417
+ if( !empty($this->location_attributes[$location_attribute_key]) ){
418
+ update_post_meta($this->post_id, $location_attribute_key, $this->location_attributes[$location_attribute_key]);
419
+ }else{
420
+ delete_post_meta($this->post_id, $location_attribute_key);
421
+ }
422
+ }
423
  }
424
  $this->get_status();
425
  $this->location_status = (count($this->errors) == 0) ? $this->location_status:null; //set status at this point, it's either the current status, or if validation fails, null
464
  $this->add_error( sprintf(__('You do not have permission to create/edit %s.','events-manager'), __('locations','events-manager')) );
465
  }
466
  $this->compat_keys();
467
+ $result = count($this->errors) == 0;
468
+ //add this location to the cache
469
+ if( $result ){
470
+ wp_cache_set($this->location_id, $this, 'em_locations');
471
+ wp_cache_set($this->post_id, $this->location_id, 'em_locations_ids');
472
+ }
473
  return apply_filters('em_location_save_meta', count($this->errors) == 0, $this);
474
  }
475
 
549
  }
550
  if($set_post_status){
551
  $wpdb->update( $wpdb->posts, array( 'post_status' => $post_status, 'post_name' => $this->post_name ), array( 'ID' => $this->post_id ) );
552
+ }elseif( !empty($set_post_name) ){
553
  //if we've added a post slug then update wp_posts anyway
554
  $wpdb->update( $wpdb->posts, array( 'post_name' => $this->post_name ), array( 'ID' => $this->post_id ) );
555
  }
614
 
615
  function has_events( $status = 1 ){
616
  global $wpdb;
617
+ $events_count = EM_Events::count(array('location_id' => $this->location_id, 'status' => $status));
618
+ return apply_filters('em_location_has_events', $events_count > 0, $this);
 
 
619
  }
620
 
621
  /**
850
  case '#_LOCATIONLATITUDE':
851
  $replace = $this->location_latitude;
852
  break;
853
+ case '#_DESCRIPTION': //Deprecated
 
854
  case '#_LOCATIONNOTES':
 
855
  $replace = $this->post_content;
856
+ break;
857
+ case '#_EXCERPT': //Deprecated
858
+ case '#_LOCATIONEXCERPT':
859
+ case '#_LOCATIONEXCERPTCUT':
860
+ if( !empty($this->post_excerpt) && $result != "#_LOCATIONEXCERPTCUT" ){
861
+ $replace = $this->post_excerpt;
862
+ }else{
863
+ $excerpt_length = ( $result == "#_LOCATIONEXCERPTCUT" ) ? 55 : false;
864
+ $excerpt_more = apply_filters('em_excerpt_more', ' ' . '[...]');
865
+ if( !empty($placeholders[3][$key]) ){
866
+ $ph_args = explode(',', $placeholders[3][$key]);
867
+ if( is_numeric($ph_args[0]) || empty($ph_args[0]) ) $excerpt_length = $ph_args[0];
868
+ if( !empty($ph_args[1]) ) $excerpt_more = $ph_args[1];
 
 
 
 
 
 
 
 
 
869
  }
870
+ $replace = $this->output_excerpt($excerpt_length, $excerpt_more, $result == "#_LOCATIONEXCERPTCUT");
871
  }
872
  break;
873
  case '#_LOCATIONIMAGEURL':
938
  $replace = '<a href="'.esc_url($replace).'">iCal</a>';
939
  }
940
  break;
941
+ case '#_LOCATIONWEBCALURL':
942
+ case '#_LOCATIONWEBCALLINK':
943
+ $replace = $this->get_ical_url();
944
+ $replace = str_replace(array('http://','https://'), 'webcal://', $replace);
945
+ if( $result == '#_LOCATIONWEBCALLINK' ){
946
+ $replace = '<a href="'.esc_url($replace).'">Webcal</a>';
947
+ }
948
+ break;
949
  case '#_LOCATIONRSSURL':
950
  case '#_LOCATIONRSSLINK':
951
  $replace = $this->get_rss_url();
968
  if ( $result == '#_LOCATIONPASTEVENTS'){ $scope = 'past'; }
969
  elseif ( $result == '#_LOCATIONNEXTEVENTS' ){ $scope = 'future'; }
970
  else{ $scope = 'all'; }
971
+ $args = array('location'=>$this->location_id, 'scope'=>$scope, 'pagination'=>1, 'ajax'=>0);
972
+ $args['format_header'] = get_option('dbem_location_event_list_item_header_format');
973
+ $args['format_footer'] = get_option('dbem_location_event_list_item_footer_format');
974
+ $args['format'] = get_option('dbem_location_event_list_item_format');
975
+ $args['no_results_msg'] = get_option('dbem_location_no_events_message');
976
+ $args['limit'] = get_option('dbem_location_event_list_limit');
977
+ $args['orderby'] = get_option('dbem_location_event_list_orderby');
978
+ $args['order'] = get_option('dbem_location_event_list_order');
979
+ $args['page'] = (!empty($_REQUEST['pno']) && is_numeric($_REQUEST['pno']) )? $_REQUEST['pno'] : 1;
980
+ $replace = EM_Events::output($args);
 
 
981
  break;
982
  case '#_LOCATIONNEXTEVENT':
983
  $events = EM_Events::get( array('location'=>$this->location_id, 'scope'=>'future', 'limit'=>1, 'orderby'=>'event_start_date,event_start_time') );
classes/em-locations.php CHANGED
@@ -6,6 +6,20 @@
6
  */
7
  class EM_Locations extends EM_Object {
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  /**
10
  * Returns an array of EM_Location objects
11
  * @param boolean $eventful
@@ -40,42 +54,151 @@ class EM_Locations extends EM_Object {
40
  $limit = ( $args['limit'] && is_numeric($args['limit'])) ? "LIMIT {$args['limit']}" : '';
41
  $offset = ( $limit != "" && is_numeric($args['offset']) ) ? "OFFSET {$args['offset']}" : '';
42
 
43
- //Get the default conditions
44
- $conditions = self::build_sql_conditions($args);
45
-
46
- //Put it all together
47
- $where = ( count($conditions) > 0 ) ? " WHERE " . implode ( " AND ", $conditions ):'';
48
-
49
- //Get ordering instructions
50
  $EM_Event = new EM_Event(); //blank event for below
51
  $EM_Location = new EM_Location(0); //blank location for below
52
- $accepted_fields = $EM_Location->get_fields(true);
53
- $accepted_fields = array_merge($EM_Event->get_fields(true),$accepted_fields);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  $orderby = self::build_sql_orderby($args, $accepted_fields, get_option('dbem_events_default_order'));
55
- //Now, build orderby sql
56
  $orderby_sql = ( count($orderby) > 0 ) ? 'ORDER BY '. implode(', ', $orderby) : '';
57
 
58
- if( EM_MS_GLOBAL ){
59
- $selectors = ( $count ) ? 'COUNT('.$locations_table.'.location_id)':$locations_table.'.post_id, '.$locations_table.'.blog_id';
60
- }else{
61
- $selectors = ( $count ) ? 'COUNT('.$locations_table.'.location_id)':$locations_table.'.post_id';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  }
63
- //Create the SQL statement and execute
64
- $sql = apply_filters('em_locations_get_sql', "
65
- SELECT $selectors FROM $locations_table
66
- LEFT JOIN $events_table ON {$locations_table}.location_id={$events_table}.location_id
67
- $where
68
- GROUP BY {$locations_table}.location_id
69
- $orderby_sql
70
- $limit $offset
71
- ", $args);
72
 
73
  //If we're only counting results, return the number of results
74
  if( $count ){
75
- return apply_filters('em_locations_get_count', count($wpdb->get_col($sql)), $args);
 
76
  }
77
- $results = $wpdb->get_results($sql, ARRAY_A);
78
 
 
 
 
 
 
 
 
 
 
79
  //If we want results directly in an array, why not have a shortcut here?
80
  if( $args['array'] == true ){
81
  return apply_filters('em_locations_get_array', $results, $args);
@@ -109,7 +232,7 @@ class EM_Locations extends EM_Object {
109
  //Can be either an array for the get search or an array of EM_Location objects
110
  $page_queryvar = !empty($args['page_queryvar']) ? $args['page_queryvar'] : 'pno';
111
  if( !empty($args['pagination']) && !array_key_exists('page',$args) && !empty($_REQUEST[$page_queryvar]) && is_numeric($_REQUEST[$page_queryvar]) ){
112
- $page = $args['page'] = $_REQUEST[$page_queryvar];
113
  }
114
  if( is_object(current($args)) && get_class((current($args))) == 'EM_Location' ){
115
  $func_args = func_get_args();
@@ -117,20 +240,12 @@ class EM_Locations extends EM_Object {
117
  $args = (!empty($func_args[1])) ? $func_args[1] : array();
118
  $args = apply_filters('em_locations_output_args', self::get_default_search($args), $locations);
119
  $limit = ( !empty($args['limit']) && is_numeric($args['limit']) ) ? $args['limit']:false;
120
- $offset = ( !empty($args['offset']) && is_numeric($args['offset']) ) ? $args['offset']:0;
121
- $page = ( !empty($args['page']) && is_numeric($args['page']) ) ? $args['page']:1;
122
  $locations_count = count($locations);
123
  }else{
124
  $args = apply_filters('em_locations_output_args', self::get_default_search($args) );
125
  $limit = ( !empty($args['limit']) && is_numeric($args['limit']) ) ? $args['limit']:false;
126
- $offset = ( !empty($args['offset']) && is_numeric($args['offset']) ) ? $args['offset']:0;
127
- $page = ( !empty($args['page']) && is_numeric($args['page']) ) ? $args['page']:1;
128
- $args_count = $args;
129
- $args_count['limit'] = 0;
130
- $args_count['offset'] = 0;
131
- $args_count['page'] = 1;
132
- $locations_count = self::count($args_count);
133
  $locations = self::get( $args );
 
134
  }
135
  //What format shall we output this to, or use default
136
  $format = empty($args['format']) ? get_option( 'dbem_location_list_item_format' ) : $args['format'] ;
@@ -158,8 +273,8 @@ class EM_Locations extends EM_Object {
158
  //output pagination links
159
  $output .= self::get_pagination_links($args, $locations_count);
160
  }
161
- } else {
162
- $output = get_option ( 'dbem_no_locations_message' );
163
  }
164
  //FIXME check if reference is ok when restoring object, due to changes in php5 v 4
165
  $EM_Location_old= $EM_Location;
@@ -225,7 +340,7 @@ class EM_Locations extends EM_Object {
225
  }
226
  //eventful locations
227
  if( true == $args['eventful'] ){
228
- $conditions['eventful'] = "{$events_table}.event_id IS NOT NULL AND event_status=1";
229
  }elseif( true == $args['eventless'] ){
230
  $conditions['eventless'] = "{$events_table}.event_id IS NULL";
231
  if( !empty($conditions['scope']) ) unset($conditions['scope']); //scope condition would render all queries return no results
@@ -254,25 +369,6 @@ class EM_Locations extends EM_Object {
254
  }
255
  }
256
  }
257
- //status
258
- $conditions['status'] = "(`location_status` >= 0)"; //pending and published if status is not explicitly defined (Default is 1)
259
- if( array_key_exists('status',$args) ){
260
- if( is_numeric($args['status']) ){
261
- $conditions['status'] = "(`location_status`={$args['status']} )"; //trash (-1), pending, (0) or published (1)
262
- }elseif( $args['status'] == 'pending' ){
263
- $conditions['status'] = "(`location_status`=0)"; //pending
264
- }elseif( $args['status'] == 'publish' ){
265
- $conditions['status'] = "(`location_status`=1)"; //published
266
- }elseif( $args['status'] === null || $args['status'] == 'draft' ){
267
- $conditions['status'] = "(`location_status` IS NULL )"; //show draft items
268
- }elseif( $args['status'] == 'trash' ){
269
- $conditions['status'] = "(`location_status` = -1 )"; //show trashed items
270
- }elseif( $args['status'] == 'all'){
271
- $conditions['status'] = "(`location_status` >= 0 OR `location_status` IS NULL)"; //search all statuses that aren't trashed
272
- }elseif( $args['status'] == 'everything'){
273
- unset($conditions['status']); //search all statuses
274
- }
275
- }
276
  //private locations
277
  if( empty($args['private']) ){
278
  $conditions['private'] = "(`location_private`=0)";
@@ -290,12 +386,45 @@ class EM_Locations extends EM_Object {
290
  return apply_filters('em_locations_build_sql_conditions', $conditions, $args);
291
  }
292
 
293
- /* Overrides EM_Object method to apply a filter to result
294
- * @see wp-content/plugins/events-manager/classes/EM_Object#build_sql_orderby()
 
295
  */
296
- public static function build_sql_orderby( $args, $accepted_fields, $default_order = 'ASC' ){
297
- self::$context = EM_POST_TYPE_LOCATION;
298
- return apply_filters( 'em_locations_build_sql_orderby', parent::build_sql_orderby($args, $accepted_fields, get_option('dbem_events_default_order')), $args, $accepted_fields, $default_order );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  }
300
 
301
  /*
@@ -308,9 +437,10 @@ class EM_Locations extends EM_Object {
308
  public static function get_default_search( $array_or_defaults = array(), $array = array() ){
309
  self::$context = EM_POST_TYPE_LOCATION;
310
  $defaults = array(
311
- 'eventful' => false, //Locations that have an event (scope will also play a part here
312
- 'eventless' => false, //Locations WITHOUT events, eventful takes precedence
313
  'orderby' => 'location_name',
 
 
 
314
  'town' => false,
315
  'state' => false,
316
  'country' => false,
@@ -320,7 +450,11 @@ class EM_Locations extends EM_Object {
320
  'blog' => get_current_blog_id(),
321
  'private' => current_user_can('read_private_locations'),
322
  'private_only' => false,
323
- 'post_id' => false
 
 
 
 
324
  );
325
  //sort out whether defaults were supplied or just the array of search values
326
  if( empty($array) ){
6
  */
7
  class EM_Locations extends EM_Object {
8
 
9
+ /**
10
+ * Like WPDB->num_rows it holds the number of results found on the last query.
11
+ * @var int
12
+ */
13
+ public static $num_rows;
14
+
15
+ /**
16
+ * If $args['pagination'] is true or $args['offset'] or $args['page'] is greater than one, and a limit is imposed when using a get() query,
17
+ * this will contain the total records found without a limit for the last query.
18
+ * If no limit was used or pagination was not enabled, this will be the same as self::$num_rows
19
+ * @var int
20
+ */
21
+ public static $num_rows_found;
22
+
23
  /**
24
  * Returns an array of EM_Location objects
25
  * @param boolean $eventful
54
  $limit = ( $args['limit'] && is_numeric($args['limit'])) ? "LIMIT {$args['limit']}" : '';
55
  $offset = ( $limit != "" && is_numeric($args['offset']) ) ? "OFFSET {$args['offset']}" : '';
56
 
57
+ //Get fields that we can use in ordering and grouping, which can be event and location (excluding ambiguous) fields
 
 
 
 
 
 
58
  $EM_Event = new EM_Event(); //blank event for below
59
  $EM_Location = new EM_Location(0); //blank location for below
60
+ $location_fields = array_keys($EM_Location->fields);
61
+ $event_fields = array(); //will contain event-specific fields, not ambiguous ones
62
+ foreach( array_keys($EM_Event->fields) as $field_name ){
63
+ if( !in_array($field_name, $location_fields) ) $event_fields[] = $field_name;
64
+ }
65
+ $accepted_fields = array_merge($event_fields, $location_fields);
66
+
67
+ //add selectors
68
+ $calc_found_rows = $limit && ( $args['pagination'] || $args['offset'] > 0 || $args['page'] > 0 );
69
+ if( $count ){
70
+ $selectors = 'COUNT(DISTINCT '.$locations_table . '.location_id)'; //works in MS Global mode since location_id is always unique, post_id is not
71
+ $limit = 'LIMIT 1';
72
+ $offset = 'OFFSET 0';
73
+ }else{
74
+ if( $args['array'] ){
75
+ //get all fields from table, add events table prefix to avoid ambiguous fields from location
76
+ $selectors = $locations_table . '.*';
77
+ }elseif( EM_MS_GLOBAL ){
78
+ $selectors = $locations_table.'.post_id, '.$locations_table.'.blog_id';
79
+ }else{
80
+ $selectors = $locations_table.'.post_id';
81
+ }
82
+ if( $calc_found_rows ) $selectors = 'SQL_CALC_FOUND_ROWS ' . $selectors; //for storing total rows found
83
+ $selectors = 'DISTINCT ' . $selectors; //duplicate avoidance
84
+ }
85
+
86
+ //check if we need to join a location table for this search, which is necessary if any location-specific are supplied, or if certain arguments such as orderby contain location fields
87
+ $join_events_table = false;
88
+ //for we only will check optional joining by default for groupby searches, and for the original searches if EM_DISABLE_OPTIONAL_JOINS is set to true in wp-config.php
89
+ if( !empty($args['groupby']) || (defined('EM_DISABLE_OPTIONAL_JOINS') && EM_DISABLE_OPTIONAL_JOINS) ){
90
+ $event_specific_args = array('eventful', 'eventless', 'tag', 'category', 'event', 'recurrence', 'month', 'year', 'rsvp', 'bookings');
91
+ $join_events_table = $args['scope'] != 'all'; //only value where false is not default so we check that first
92
+ foreach( $event_specific_args as $arg ) if( !empty($args[$arg]) ) $join_events_table = true;
93
+ //if set to false the following would provide a false negative in the line above
94
+ if( $args['recurrences'] !== null ) $join_events_table = true;
95
+ if( $args['recurring'] !== null ) $join_events_table = true;
96
+ if( $args['event_status'] !== false ){ $join_events_table = true; }
97
+ //check ordering and grouping arguments for precense of event fields requiring a join
98
+ if( !$join_events_table ){
99
+ foreach( array('groupby', 'orderby', 'groupby_orderby') as $arg ){
100
+ if( !is_array($args[$arg]) ) continue; //ignore this argument if set to false
101
+ //we assume all these arguments are now array thanks to self::get_search_defaults() cleaning it up
102
+ foreach( $args[$arg] as $field_name ){
103
+ if( in_array($field_name, $event_fields) ){
104
+ $join_events_table = true;
105
+ break; //we join, no need to keep searching
106
+ }
107
+ }
108
+ }
109
+ }
110
+ //EM_Events has a special argument for recurring events (the template), where it automatically omits recurring event templates. If we are searching events, and recurring was not explicitly set, we set it to the same as in EM_Events default
111
+ if( $join_events_table && $args['recurring'] === null ) $args['recurring'] = false;
112
+ }else{ $join_events_table = true; }//end temporary if( !empty($args['groupby']).... wrapper
113
+ //plugins can override this optional joining behaviour here in case they add custom WHERE conditions or something like that
114
+ $join_events_table = apply_filters('em_locations_get_join_events_table', $join_events_table, $args, $count);
115
+ //depending on whether to join we do certain things like add a join SQL, change specific values like status search
116
+ $event_optional_join = $join_events_table ? "LEFT JOIN $events_table ON {$locations_table}.location_id={$events_table}.location_id" : '';
117
+ $args['event_status'] = $args['event_status'] === false ? $join_events_table : $args['event_status']; //if we're joining events table, by default we want status to match that of locations in this search
118
+
119
+ //Build ORDER BY and WHERE SQL statements here, after we've done all the pre-processing necessary
120
+ $conditions = self::build_sql_conditions($args);
121
+ $where = ( count($conditions) > 0 ) ? " WHERE " . implode ( " AND ", $conditions ):'';
122
  $orderby = self::build_sql_orderby($args, $accepted_fields, get_option('dbem_events_default_order'));
 
123
  $orderby_sql = ( count($orderby) > 0 ) ? 'ORDER BY '. implode(', ', $orderby) : '';
124
 
125
+ //Build GROUP BY SQL statement, which will be very different if we group things due to how we need to filter out by event date
126
+ if( !empty($args['groupby']) ){
127
+ //get groupby field(s)
128
+ $groupby_fields = self::build_sql_groupby($args, $accepted_fields);
129
+ if( !empty($groupby_fields[0]) ){
130
+ //we can safely assume we've been passed at least one array item with index of 0 containing a valid field due to build_sql_groupby()
131
+ $groupby_field = $groupby_fields[0]; //we only support one field for events
132
+ $groupby_orderby = self::build_sql_groupby_orderby($args, $accepted_fields);
133
+ $groupby_orderby_sql = !empty($groupby_orderby) ? ', '. implode(', ', $groupby_orderby) : '';
134
+ //get minimum required selectors within the inner query to shorten query length as much as possible
135
+ $inner_selectors = $locations_table . '.*';
136
+ if( $event_optional_join ){
137
+ //we're selecting all fields from events table so add only location fields required in the outer ORDER BY statement
138
+ if( in_array($groupby_field, $event_fields) && !in_array($groupby_field, $args['orderby']) ){
139
+ //we may not have included the grouped field if it's not in the outer ORDER BY clause, so we add it for this specific query
140
+ $inner_selectors .= ', '. $events_table .'.'. $groupby_field;
141
+ }
142
+ foreach( $args['orderby'] as $orderby_field ){
143
+ if( in_array($orderby_field, $event_fields) ){
144
+ $inner_selectors .= ', '. $events_table .'.'. $orderby_field;
145
+ }
146
+ }
147
+ }
148
+ //THE Query - Grouped
149
+ $sql = "
150
+ SELECT DISTINCT $selectors
151
+ FROM (
152
+ SELECT *,
153
+ @cur := IF($groupby_field = @id, @cur+1, 1) AS RowNumber,
154
+ @id := $groupby_field AS IdCache
155
+ FROM (
156
+ SELECT {$inner_selectors} FROM {$locations_table}
157
+ $event_optional_join
158
+ $where
159
+ ORDER BY {$groupby_field} $groupby_orderby_sql
160
+ ) DataSet
161
+ INNER JOIN (
162
+ SELECT @id:='', @cur:=0
163
+ ) AS lookup
164
+ ) {$locations_table}
165
+ WHERE RowNumber = 1
166
+ $orderby_sql
167
+ $limit $offset";
168
+ }
169
+ }
170
+
171
+ //build the SQL statement if not already built for group
172
+ if( empty($sql) ){
173
+ //THE query
174
+ $sql = "
175
+ SELECT DISTINCT $selectors FROM $locations_table
176
+ $event_optional_join
177
+ $where
178
+ $orderby_sql
179
+ $limit $offset
180
+ ";
181
  }
182
+
183
+ //the query filter
184
+ $sql = apply_filters('em_locations_get_sql', $sql, $args);
185
+ //if( em_wp_is_super_admin() && WP_DEBUG_DISPLAY ){ echo "<pre>"; print_r($sql); echo '</pre>'; }
 
 
 
 
 
186
 
187
  //If we're only counting results, return the number of results
188
  if( $count ){
189
+ self::$num_rows_found = self::$num_rows = $wpdb->get_var($sql);
190
+ return apply_filters('em_locations_get_count', self::$num_rows, $args);
191
  }
 
192
 
193
+ //get the result and count results
194
+ $results = $wpdb->get_results( $sql, ARRAY_A);
195
+ self::$num_rows = $wpdb->num_rows;
196
+ if( $calc_found_rows ){
197
+ self::$num_rows_found = $wpdb->get_var('SELECT FOUND_ROWS()');
198
+ }else{
199
+ self::$num_rows_found = self::$num_rows;
200
+ }
201
+
202
  //If we want results directly in an array, why not have a shortcut here?
203
  if( $args['array'] == true ){
204
  return apply_filters('em_locations_get_array', $results, $args);
232
  //Can be either an array for the get search or an array of EM_Location objects
233
  $page_queryvar = !empty($args['page_queryvar']) ? $args['page_queryvar'] : 'pno';
234
  if( !empty($args['pagination']) && !array_key_exists('page',$args) && !empty($_REQUEST[$page_queryvar]) && is_numeric($_REQUEST[$page_queryvar]) ){
235
+ $args['page'] = $_REQUEST[$page_queryvar];
236
  }
237
  if( is_object(current($args)) && get_class((current($args))) == 'EM_Location' ){
238
  $func_args = func_get_args();
240
  $args = (!empty($func_args[1])) ? $func_args[1] : array();
241
  $args = apply_filters('em_locations_output_args', self::get_default_search($args), $locations);
242
  $limit = ( !empty($args['limit']) && is_numeric($args['limit']) ) ? $args['limit']:false;
 
 
243
  $locations_count = count($locations);
244
  }else{
245
  $args = apply_filters('em_locations_output_args', self::get_default_search($args) );
246
  $limit = ( !empty($args['limit']) && is_numeric($args['limit']) ) ? $args['limit']:false;
 
 
 
 
 
 
 
247
  $locations = self::get( $args );
248
+ $locations_count = self::$num_rows_found;
249
  }
250
  //What format shall we output this to, or use default
251
  $format = empty($args['format']) ? get_option( 'dbem_location_list_item_format' ) : $args['format'] ;
273
  //output pagination links
274
  $output .= self::get_pagination_links($args, $locations_count);
275
  }
276
+ }elseif( $args['no_results_msg'] !== false ){
277
+ $output = !empty($args['no_results_msg']) ? $args['no_results_msg'] : get_option('dbem_no_locations_message');
278
  }
279
  //FIXME check if reference is ok when restoring object, due to changes in php5 v 4
280
  $EM_Location_old= $EM_Location;
340
  }
341
  //eventful locations
342
  if( true == $args['eventful'] ){
343
+ $conditions['eventful'] = "{$events_table}.event_id IS NOT NULL";
344
  }elseif( true == $args['eventless'] ){
345
  $conditions['eventless'] = "{$events_table}.event_id IS NULL";
346
  if( !empty($conditions['scope']) ) unset($conditions['scope']); //scope condition would render all queries return no results
369
  }
370
  }
371
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
372
  //private locations
373
  if( empty($args['private']) ){
374
  $conditions['private'] = "(`location_private`=0)";
386
  return apply_filters('em_locations_build_sql_conditions', $conditions, $args);
387
  }
388
 
389
+ /**
390
+ * Overrides EM_Object method to clean ambiguous fields and apply a filter to result.
391
+ * @see EM_Object::build_sql_orderby()
392
  */
393
+ public static function build_sql_orderby( $args, $accepted_fields, $default_order = 'ASC' ){
394
+ $orderby = parent::build_sql_orderby($args, $accepted_fields, get_option('dbem_events_default_order'));
395
+ $orderby = self::build_sql_ambiguous_fields_helper($orderby); //fix ambiguous fields
396
+ return apply_filters( 'em_locations_build_sql_orderby', $orderby, $args, $accepted_fields, $default_order );
397
+ }
398
+
399
+ /**
400
+ * Overrides EM_Object method to clean ambiguous fields and apply a filter to result.
401
+ * @see EM_Object::build_sql_groupby()
402
+ */
403
+ public static function build_sql_groupby( $args, $accepted_fields, $groupby_order = false, $default_order = 'ASC' ){
404
+ $groupby = parent::build_sql_groupby($args, $accepted_fields);
405
+ //fix ambiguous fields and give them scope of events table
406
+ $groupby = self::build_sql_ambiguous_fields_helper($groupby);
407
+ return apply_filters( 'em_locations_build_sql_groupby', $groupby, $args, $accepted_fields );
408
+ }
409
+
410
+ /**
411
+ * Overrides EM_Object method to clean ambiguous fields and apply a filter to result.
412
+ * @see EM_Object::build_sql_groupby_orderby()
413
+ */
414
+ public static function build_sql_groupby_orderby($args, $accepted_fields, $default_order = 'ASC' ){
415
+ $group_orderby = parent::build_sql_groupby_orderby($args, $accepted_fields, get_option('dbem_events_default_order'));
416
+ //fix ambiguous fields and give them scope of events table
417
+ $group_orderby = self::build_sql_ambiguous_fields_helper($group_orderby);
418
+ return apply_filters( 'em_locations_build_sql_groupby_orderby', $group_orderby, $args, $accepted_fields, $default_order );
419
+ }
420
+
421
+ /**
422
+ * Overrides EM_Object method to provide specific reserved fields and locations table.
423
+ * @see EM_Object::build_sql_ambiguous_fields_helper()
424
+ */
425
+ protected static function build_sql_ambiguous_fields_helper( $fields, $reserved_fields = array(), $prefix = 'table_name' ){
426
+ //This will likely be removed when PHP 5.3 is the minimum and LSB is a given
427
+ return parent::build_sql_ambiguous_fields_helper($fields, array('post_id', 'location_id', 'blog_id'), EM_LOCATIONS_TABLE);
428
  }
429
 
430
  /*
437
  public static function get_default_search( $array_or_defaults = array(), $array = array() ){
438
  self::$context = EM_POST_TYPE_LOCATION;
439
  $defaults = array(
 
 
440
  'orderby' => 'location_name',
441
+ 'groupby' => false,
442
+ 'groupby_orderby' => 'location_name', //groups according to event start time, i.e. by default shows earliest event in a scope
443
+ 'groupby_order' => 'ASC', //groups according to event start time, i.e. by default shows earliest event in a scope
444
  'town' => false,
445
  'state' => false,
446
  'country' => false,
450
  'blog' => get_current_blog_id(),
451
  'private' => current_user_can('read_private_locations'),
452
  'private_only' => false,
453
+ 'post_id' => false,
454
+ //location-specific attributes
455
+ 'eventful' => false, //Locations that have an event (scope will also play a part here
456
+ 'eventless' => false, //Locations WITHOUT events, eventful takes precedence
457
+ 'event_status' => false //search locations with events of a specific publish status
458
  );
459
  //sort out whether defaults were supplied or just the array of search values
460
  if( empty($array) ){
classes/em-object.php CHANGED
@@ -28,26 +28,32 @@ class EM_Object {
28
  'scope' => 'future',
29
  'order' => 'ASC', //hard-coded at end of this function
30
  'orderby' => false,
 
 
 
31
  'format' => '',
32
  'format_header' => '', //custom html above the list
33
  'format_footer' => '', //custom html below the list
 
34
  'category' => 0,
35
  'tag' => 0,
36
  'location' => false,
37
- 'event' => false,
 
 
38
  'offset'=>0,
39
  'page'=>1,//basically, if greater than 0, calculates offset at end
40
  'page_queryvar'=>null,
41
- 'recurrence'=>0,
42
- 'recurrences'=>null,
43
- 'recurring'=>false,
44
  'month'=>'',
45
  'year'=>'',
46
  'pagination'=>false,
47
  'array'=>false,
48
  'owner'=>false,
49
  'rsvp'=>false, //deprecated for bookings
50
- 'bookings'=>false,
51
  'search'=>false,
52
  'geo'=>false, //reserved for future searching via name
53
  'near'=>false, //lat,lng coordinates in array or comma-separated format
@@ -102,13 +108,6 @@ class EM_Object {
102
  if( !empty($array['country']) && is_string($array['country']) && preg_match('/^( ?.+ ?,?)+$/', $array['country']) ){
103
  $array['country'] = explode(',',$array['country']);
104
  }
105
-
106
- //OrderBy - can be a comma-separated array of field names to order by (field names of object, not db)
107
- if( array_key_exists('orderby', $array)){
108
- if( !is_array($array['orderby']) && preg_match('/,/', $array['orderby']) ) {
109
- $array['orderby'] = explode(',', $array['orderby']);
110
- }
111
- }
112
  //TODO validate search query array
113
  //Clean the supplied array, so we only have allowed keys
114
  foreach( array_keys($array) as $key){
@@ -149,22 +148,31 @@ class EM_Object {
149
  $defaults['scope'] = $super_defaults['scope'];
150
  }
151
  }
152
- //Order - it's either ASC or DESC, so let's just validate
153
- if( !is_array($defaults['order']) && preg_match('/,/', $defaults['order']) ) {
154
- $defaults['order'] = explode(',', $defaults['order']);
155
- }elseif( !in_array($defaults['order'], array('ASC','DESC','asc','desc')) ){
156
- $defaults['order'] = $super_defaults['order'];
 
 
 
157
  }
158
- //ORDER BY, split if an array
159
- if( !is_array($defaults['orderby']) && preg_match('/,/', $defaults['orderby']) ) {
160
- $defaults['orderby'] = explode(',', $defaults['orderby']);
 
 
 
 
 
 
161
  }
162
- //TODO should we clean format of malicious code over here and run everything thorugh this?
163
  $defaults['array'] = ($defaults['array'] == true);
164
  $defaults['pagination'] = ($defaults['pagination'] == true);
165
  $defaults['limit'] = (is_numeric($defaults['limit'])) ? $defaults['limit']:$super_defaults['limit'];
166
  $defaults['offset'] = (is_numeric($defaults['offset'])) ? $defaults['offset']:$super_defaults['offset'];
167
- $defaults['recurring'] = $defaults['recurring'] === 'include' ? $defaults['recurring']:($defaults['recurring'] == true);
168
  $defaults['search'] = ($defaults['search']) ? trim($defaults['search']):false;
169
  //Calculate offset if event page is set
170
  if($defaults['page'] > 1){
@@ -206,21 +214,61 @@ class EM_Object {
206
  $year = $args['year'];
207
  $today = date('Y-m-d', current_time('timestamp'));
208
  //Create the WHERE statement
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
 
210
  //Recurrences
211
- $conditions = array();
212
  if( $recurring ){
213
- //we show recurring event templates here, if 'recurring' is 'include' then we show both recurring and normal events.
214
  if( $recurring !== 'include' ){
215
  $conditions['recurring'] = "`recurrence`=1";
216
  }
217
  }elseif( $recurrence > 0 ){
218
  $conditions['recurrence'] = $wpdb->prepare("`recurrence_id`=%d", $recurrence);
219
  }else{
 
220
  if( $recurrences !== null ){
221
  $conditions['recurrences'] = $recurrences ? "(`recurrence_id` > 0 )":"(`recurrence_id` IS NULL OR `recurrence_id`=0 )";
222
  }
223
- $conditions['recurring'] = "(`recurrence`!=1 OR `recurrence` IS NULL)";
 
 
 
224
  }
225
  //Dates - first check 'month', and 'year', and adjust scope if needed
226
  if( !($month=='' && $year=='') ){
@@ -331,33 +379,34 @@ class EM_Object {
331
  }
332
 
333
  //Filter by Location - can be object, array, or id
 
334
  if ( is_numeric($location) && $location > 0 ) { //Location ID takes precedence
335
- $conditions['location'] = " {$locations_table}.location_id = $location";
336
  }elseif ( $location === 0 ) { //only helpful is searching events
337
  $conditions['location'] = " {$events_table}.location_id = $location OR {$events_table}.location_id IS NULL";
338
  }elseif ( self::array_is_numeric($location) ){
339
- $conditions['location'] = "( {$locations_table}.location_id = " . implode(" OR {$locations_table}.location_id = ", $location) .' )';
340
  }elseif ( is_object($location) && get_class($location)=='EM_Location' ){ //Now we deal with objects
341
- $conditions['location'] = " {$locations_table}.location_id = $location->location_id";
342
  }elseif ( is_array($location) && @get_class(current($location)=='EM_Location') ){ //we can accept array of ids or EM_Location objects
343
  foreach($location as $EM_Location){
344
  $location_ids[] = $EM_Location->location_id;
345
  }
346
- $conditions['location'] = "( {$locations_table}.location_id=". implode(" {$locations_table}.location_id=", $location_ids) ." )";
347
  }
348
 
349
  //Filter by Event - can be object, array, or id
350
  if ( is_numeric($event) && $event > 0 ) { //event ID takes precedence
351
  $conditions['event'] = " {$events_table}.event_id = $event";
352
  }elseif ( self::array_is_numeric($event) ){ //array of ids
353
- $conditions['event'] = "( {$events_table}.event_id = " . implode(" OR {$events_table}.event_id = ", $event) .' )';
354
  }elseif ( is_object($event) && get_class($event)=='EM_Event' ){ //Now we deal with objects
355
  $conditions['event'] = " {$events_table}.event_id = $event->event_id";
356
  }elseif ( is_array($event) && @get_class(current($event)=='EM_Event') ){ //we can accept array of ids or EM_event objects
357
  foreach($event as $EM_Event){
358
  $event_ids[] = $EM_Event->event_id;
359
  }
360
- $conditions['event'] = "( {$events_table}.event_id=". implode(" {$events_table}.event_id=", $event_ids) ." )";
361
  }
362
 
363
  //Location specific filters
@@ -502,6 +551,15 @@ class EM_Object {
502
  //If we want rsvped items, we usually check the event
503
  if( $bookings == 1 ){
504
  $conditions['bookings'] = 'event_rsvp=1';
 
 
 
 
 
 
 
 
 
505
  }
506
  //Default ownership belongs to an event, child objects can just overwrite this if needed.
507
  if( is_numeric($owner) ){
@@ -843,42 +901,114 @@ class EM_Object {
843
  return apply_filters('em_object_build_wp_query_conditions', $wp_query);
844
  }
845
 
 
 
 
 
 
 
 
 
 
846
  public static function build_sql_orderby( $args, $accepted_fields, $default_order = 'ASC' ){
847
  //First, ORDER BY
848
- $args = apply_filters('em_object_build_sql_orderby_args', $args);
849
- $orderby = array();
850
- if(is_array($args['orderby'])){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
851
  //Clean orderby array so we only have accepted values
852
- foreach( $args['orderby'] as $key => $field ){
853
  if( array_key_exists($field, $accepted_fields) ){
854
- $orderby[] = $accepted_fields[$field];
 
855
  }elseif( in_array($field,$accepted_fields) ){
856
- $orderby[] = $field;
857
  }else{
858
- unset($args['orderby'][$key]);
859
  }
860
  }
861
- }elseif( $args['orderby'] != '' && array_key_exists($args['orderby'], $accepted_fields) ){
862
- $orderby[] = $accepted_fields[$args['orderby']];
863
- }elseif( $args['orderby'] != '' && in_array($args['orderby'], $accepted_fields) ){
864
- $orderby[] = $args['orderby'];
865
  }
866
  //ORDER
867
- //If order is an array, we'll go through the orderby array and match the order values (in order of array) with orderby values
868
- //If orders don't match up, or it's not ASC/DESC, the default events search in EM settings/options page will be used.
869
- foreach($orderby as $i => $field){
870
- $orderby[$i] .= ' ';
871
- if(is_array($args['order'])){
872
- if( in_array($args['order'][$i], array('ASC','DESC','asc','desc')) ){
873
- $orderby[$i] .= $args['order'][$i];
 
 
 
 
874
  }else{
875
- $orderby[$i] .= $default_order;
876
  }
877
- }else{
878
- $orderby[$i] .= ( in_array($args['order'], array('ASC','DESC','asc','desc')) ) ? $args['order'] : $default_order;
879
  }
880
  }
881
- return apply_filters('em_object_build_sql_orderby', $orderby);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
882
  }
883
 
884
  /**
@@ -1028,7 +1158,7 @@ class EM_Object {
1028
  function can_manage( $owner_capability = false, $admin_capability = false, $user_to_check = false ){
1029
  global $em_capabilities_array;
1030
  //if multisite and super admin, just return true
1031
- if( is_multisite() && is_super_admin() ){ return true; }
1032
  //set user to the desired user we're verifying, otherwise default to current user
1033
  if( $user_to_check ){
1034
  $user = new WP_User($user_to_check);
@@ -1053,7 +1183,7 @@ class EM_Object {
1053
  }elseif( $admin_capability && array_key_exists($admin_capability, $em_capabilities_array) ){
1054
  $error_msg = $em_capabilities_array[$admin_capability];
1055
  }
1056
-
1057
  if( !$can_manage && !$is_owner && !empty($error_msg) ){
1058
  $this->add_error($error_msg);
1059
  }
@@ -1118,6 +1248,8 @@ class EM_Object {
1118
  if($db){
1119
  if( !empty($this->$key) || $this->$key === 0 || $this->$key === '0' || empty($val['null']) ){
1120
  $array[$key] = $this->$key;
 
 
1121
  }
1122
  }else{
1123
  $array[$key] = $this->$key;
@@ -1495,6 +1627,27 @@ class EM_Object {
1495
  /*
1496
  * END IMAGE UPlOAD FUNCTIONS
1497
  */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1498
 
1499
  function sanitize_time( $time ){
1500
  if( !empty($time) && preg_match ( '/^([01]\d|2[0-3]):([0-5]\d) ?(AM|PM)?$/', $time, $match ) ){
28
  'scope' => 'future',
29
  'order' => 'ASC', //hard-coded at end of this function
30
  'orderby' => false,
31
+ 'groupby' => false,
32
+ 'groupby_orderby' => false,
33
+ 'groupby_order' => 'ASC',
34
  'format' => '',
35
  'format_header' => '', //custom html above the list
36
  'format_footer' => '', //custom html below the list
37
+ 'no_results_msg' => '', //default message if no results used in output() function
38
  'category' => 0,
39
  'tag' => 0,
40
  'location' => false,
41
+ 'event' => false,
42
+ 'event_status' => false, //automatically set to 'status' value if in EM_Events, useful only for EM_Locations
43
+ 'location_status' => false, //automatically set to 'status' value if in EM_Locations, useful only for EM_Events
44
  'offset'=>0,
45
  'page'=>1,//basically, if greater than 0, calculates offset at end
46
  'page_queryvar'=>null,
47
+ 'recurrence' => 0, //look for a specific recurrence by ID
48
+ 'recurrences' => null, //if set, exclusively show (true) or omit (false) recurrences
49
+ 'recurring' => null, //if set to 'include' it'll only show recurring event templates, if set to false, it'll omit them from results, null or true will include in results
50
  'month'=>'',
51
  'year'=>'',
52
  'pagination'=>false,
53
  'array'=>false,
54
  'owner'=>false,
55
  'rsvp'=>false, //deprecated for bookings
56
+ 'bookings' => false, //if set to true, only events with bookings enabled are returned
57
  'search'=>false,
58
  'geo'=>false, //reserved for future searching via name
59
  'near'=>false, //lat,lng coordinates in array or comma-separated format
108
  if( !empty($array['country']) && is_string($array['country']) && preg_match('/^( ?.+ ?,?)+$/', $array['country']) ){
109
  $array['country'] = explode(',',$array['country']);
110
  }
 
 
 
 
 
 
 
111
  //TODO validate search query array
112
  //Clean the supplied array, so we only have allowed keys
113
  foreach( array_keys($array) as $key){
148
  $defaults['scope'] = $super_defaults['scope'];
149
  }
150
  }
151
+ //ORDER and GROUP BY ORDER - split up string array, if just text do a quick validation and set to default if upon failure
152
+ foreach( array('order', 'groupby_order') as $order_arg ){
153
+ if( !is_array($defaults[$order_arg]) && preg_match('/,/', $defaults[$order_arg]) ) {
154
+ $defaults[$order_arg] = str_replace(' ', '', $defaults[$order_arg]);
155
+ $defaults[$order_arg] = explode(',', $defaults[$order_arg]);
156
+ }elseif( !is_array($defaults[$order_arg]) && !in_array($defaults[$order_arg], array('ASC','DESC','asc','desc')) ){
157
+ $defaults[$order_arg] = $super_defaults[$order_arg];
158
+ }
159
  }
160
+ //ORDER BY, GROUP BY and GROUP BY ORDER ensure we have a valid array, splitting by commas if present
161
+ foreach( array('orderby', 'groupby', 'groupby_orderby') as $orderby_arg ){
162
+ if( !is_array($defaults[$orderby_arg]) && preg_match('/,/', $defaults[$orderby_arg]) ) {
163
+ $defaults[$orderby_arg] = str_replace(' ', '', $defaults[$orderby_arg]);
164
+ $defaults[$orderby_arg] = explode(',', $defaults[$orderby_arg]);
165
+ }elseif( !empty($defaults[$orderby_arg]) && !is_array($defaults[$orderby_arg]) ){
166
+ $defaults[$orderby_arg] = array($defaults[$orderby_arg]);
167
+ }
168
+ if( is_array($defaults[$orderby_arg]) ) $defaults[$orderby_arg] = array_values($defaults[$orderby_arg]); //reset array keys because we want an index 0 present
169
  }
170
+ //TODO should we clean format of malicious code over here and run everything through this?
171
  $defaults['array'] = ($defaults['array'] == true);
172
  $defaults['pagination'] = ($defaults['pagination'] == true);
173
  $defaults['limit'] = (is_numeric($defaults['limit'])) ? $defaults['limit']:$super_defaults['limit'];
174
  $defaults['offset'] = (is_numeric($defaults['offset'])) ? $defaults['offset']:$super_defaults['offset'];
175
+ if( $defaults['recurring'] !== null ) $defaults['recurring'] = $defaults['recurring'] === 'include' ? $defaults['recurring']:($defaults['recurring'] == true);
176
  $defaults['search'] = ($defaults['search']) ? trim($defaults['search']):false;
177
  //Calculate offset if event page is set
178
  if($defaults['page'] > 1){
214
  $year = $args['year'];
215
  $today = date('Y-m-d', current_time('timestamp'));
216
  //Create the WHERE statement
217
+ $conditions = array();
218
+
219
+ //Statuses - we search for the 'status' based on the context of current object (i.e. is it an event or location for the moment)
220
+ // if we define the alternative status such as location_status in event context, if set to true it matches the event 'status'
221
+ // if a specific status search value is given i.e. not true and not false then that's used to generate the right condition for that specific field
222
+ // e.g. if in events, search for 'publish' events and 0 location_status, it'll find events with a location pending review.
223
+ foreach( array('event_status', 'location_status') as $status_type ){
224
+ //find out whether the main status context we're after is an event or location i.e. are we running an events or location query
225
+ $is_location_status = $status_type == "location_status" && self::$context == EM_POST_TYPE_LOCATION;
226
+ $is_event_status = $status_type == "event_status" && self::$context == EM_POST_TYPE_EVENT;
227
+ //$is_joined_status decides whether this status we're dealing with is part of a joined table or the main table
228
+ $is_joined_status = (!$is_location_status || !$is_event_status) && $args[$status_type] !== false;
229
+ //we add a status condition if this is the main status context or if joining a table and joined status arg is not exactly false
230
+ if( $is_location_status || $is_event_status || $args[$status_type] !== false ){
231
+ $condition_status = $is_joined_status ? $status_type : 'status'; //the key for this condition type
232
+ //if this is the status belonging to the joined table, if set to true we match the main context status otherwise we check the specific status
233
+ $status_arg = $is_joined_status && $args[$status_type] !== true ? $args[$status_type] : $args['status'];
234
+ //if joining by event or location, we may mistakenly omit any results without a complementing event or location, we need to account for that here
235
+ //other parts of the condition can negate whether or not eventful locations or events with/without locations should be included
236
+ $conditions[$condition_status] = "(`{$status_type}` >= 0 )"; //shows pending & published if not defined
237
+ if( is_numeric($status_arg) ){
238
+ $conditions[$condition_status] = "(`{$status_type}`={$status_arg})"; //pending or published
239
+ }elseif( $status_arg == 'pending' ){
240
+ $conditions[$condition_status] = "(`{$status_type}`=0)"; //pending
241
+ }elseif( $status_arg == 'publish' ){
242
+ $conditions[$condition_status] = "(`{$status_type}`=1)"; //published
243
+ }elseif( $status_arg === null || $status_arg == 'draft' ){
244
+ $conditions[$condition_status] = "(`{$status_type}` IS NULL )"; //show draft items
245
+ }elseif( $status_arg == 'trash' ){
246
+ $conditions[$condition_status] = "(`{$status_type}` = -1 )"; //show trashed items
247
+ }elseif( $status_arg == 'all'){
248
+ $conditions[$condition_status] = "(`{$status_type}` >= 0 OR `{$status_type}` IS NULL)"; //search all statuses that aren't trashed
249
+ }elseif( $status_arg == 'everything'){
250
+ unset($conditions[$condition_status]); //search all statuses
251
+ }
252
+ }
253
+ }
254
 
255
  //Recurrences
 
256
  if( $recurring ){
257
+ //we show recurring event templates as well within results, if 'recurring' is 'include' then we show both recurring and normal events.
258
  if( $recurring !== 'include' ){
259
  $conditions['recurring'] = "`recurrence`=1";
260
  }
261
  }elseif( $recurrence > 0 ){
262
  $conditions['recurrence'] = $wpdb->prepare("`recurrence_id`=%d", $recurrence);
263
  }else{
264
+ //we choose to either exclusively show or completely omit recurrences, if not set then both are shown
265
  if( $recurrences !== null ){
266
  $conditions['recurrences'] = $recurrences ? "(`recurrence_id` > 0 )":"(`recurrence_id` IS NULL OR `recurrence_id`=0 )";
267
  }
268
+ //if we get here and $recurring is not exactly null (meaning ignored), it was set to false or 0 meaning recurring events shouldn't be included
269
+ if( $recurring !== null ){
270
+ $conditions['recurring'] = "(`recurrence`!=1 OR `recurrence` IS NULL)";
271
+ }
272
  }
273
  //Dates - first check 'month', and 'year', and adjust scope if needed
274
  if( !($month=='' && $year=='') ){
379
  }
380
 
381
  //Filter by Location - can be object, array, or id
382
+ $location_id_table = self::$context == EM_POST_TYPE_EVENT ? $events_table:$locations_table;
383
  if ( is_numeric($location) && $location > 0 ) { //Location ID takes precedence
384
+ $conditions['location'] = " {$location_id_table}.location_id = $location";
385
  }elseif ( $location === 0 ) { //only helpful is searching events
386
  $conditions['location'] = " {$events_table}.location_id = $location OR {$events_table}.location_id IS NULL";
387
  }elseif ( self::array_is_numeric($location) ){
388
+ $conditions['location'] = "{$location_id_table}.location_id IN (" . implode(',', $location) .')';
389
  }elseif ( is_object($location) && get_class($location)=='EM_Location' ){ //Now we deal with objects
390
+ $conditions['location'] = " {$location_id_table}.location_id = $location->location_id";
391
  }elseif ( is_array($location) && @get_class(current($location)=='EM_Location') ){ //we can accept array of ids or EM_Location objects
392
  foreach($location as $EM_Location){
393
  $location_ids[] = $EM_Location->location_id;
394
  }
395
+ $conditions['location'] = "{$location_id_table}.location_id IN (" . implode(',', $location_ids) .')';
396
  }
397
 
398
  //Filter by Event - can be object, array, or id
399
  if ( is_numeric($event) && $event > 0 ) { //event ID takes precedence
400
  $conditions['event'] = " {$events_table}.event_id = $event";
401
  }elseif ( self::array_is_numeric($event) ){ //array of ids
402
+ $conditions['event'] = "{$events_table}.event_id IN (" . implode(',', $event) .')';
403
  }elseif ( is_object($event) && get_class($event)=='EM_Event' ){ //Now we deal with objects
404
  $conditions['event'] = " {$events_table}.event_id = $event->event_id";
405
  }elseif ( is_array($event) && @get_class(current($event)=='EM_Event') ){ //we can accept array of ids or EM_event objects
406
  foreach($event as $EM_Event){
407
  $event_ids[] = $EM_Event->event_id;
408
  }
409
+ $conditions['event'] = "{$events_table}.event_id IN (" . implode(',', $event_ids) .')';
410
  }
411
 
412
  //Location specific filters
551
  //If we want rsvped items, we usually check the event
552
  if( $bookings == 1 ){
553
  $conditions['bookings'] = 'event_rsvp=1';
554
+ }elseif( $bookings === 'user' && is_user_logged_in()){
555
+ //get bookings of user
556
+ $EM_Person = new EM_Person(get_current_user_id());
557
+ $booking_ids = $EM_Person->get_bookings(true);
558
+ if( count($booking_ids) > 0 ){
559
+ $conditions['bookings'] = "(event_id IN (SELECT event_id FROM ".EM_BOOKINGS_TABLE." WHERE booking_id IN (".implode(',',$booking_ids).")))";
560
+ }else{
561
+ $conditions['bookings'] = "(event_id = 0)";
562
+ }
563
  }
564
  //Default ownership belongs to an event, child objects can just overwrite this if needed.
565
  if( is_numeric($owner) ){
901
  return apply_filters('em_object_build_wp_query_conditions', $wp_query);
902
  }
903
 
904
+ /**
905
+ * Sanitizes the ORDER BY part of the SQL statement so only valid fields are supplied for ordering.
906
+ * Also combines default orders which can be an array which is applied to each specific ordering field, or just one value applied to all ordering fields.
907
+ * @uses EM_Object::build_sql_x_by_helper()
908
+ * @param array $args
909
+ * @param array $accepted_fields
910
+ * @param string|array $default_order
911
+ * @return array
912
+ */
913
  public static function build_sql_orderby( $args, $accepted_fields, $default_order = 'ASC' ){
914
  //First, ORDER BY
915
+ $args = apply_filters('em_object_build_sql_orderby_args', $args, $accepted_fields, $default_order );
916
+ $orderby = self::build_sql_x_by_helper($args['orderby'], $args['order'], $accepted_fields, $default_order);
917
+ return apply_filters('em_object_build_sql_orderby', $orderby, $args, $accepted_fields, $default_order );
918
+ }
919
+
920
+ /**
921
+ * Returns a set of further fields this query should be grouped by. Not required for straight-forward GROUP BY SQL queries.
922
+ * This is supplementary for build_sql_groupby in cases such as events, where ordering and grouping are mixed due to complex SQL sub-queries and partitions.
923
+ * @uses EM_Object::build_sql_x_by_helper()
924
+ * @param unknown $args
925
+ * @param unknown $accepted_fields
926
+ * @param string $default_order
927
+ * @return mixed|unknown
928
+ */
929
+ public static function build_sql_groupby_orderby( $args, $accepted_fields, $default_order = 'ASC' ){
930
+ $args = apply_filters('em_object_build_sql_groupby_orderby_args', $args, $accepted_fields, $default_order );
931
+ $orderby = self::build_sql_x_by_helper($args['groupby_orderby'], $args['groupby_order'], $accepted_fields, $default_order);
932
+ return apply_filters('em_object_build_sql_groupby_orderby', $orderby, $args, $accepted_fields, $default_order );
933
+ }
934
+
935
+ /**
936
+ * Sanitizes the group by statement so it includes only accepted fields. Returns an array of valid field names to group by.
937
+ * Optionally, if a $groupby_order value is provided then ASC/DESC values will be added to each field similar to EM_Object::build_sql_orderby
938
+ * @uses EM_Object::build_sql_x_by_helper()
939
+ * @param unknown $args
940
+ * @param unknown $accepted_fields
941
+ * @param string $groupby_order
942
+ * @param string $default_order
943
+ * @return mixed|unknown
944
+ */
945
+ public static function build_sql_groupby( $args, $accepted_fields, $groupby_order = false, $default_order = 'ASC' ){
946
+ //First, ORDER BY
947
+ $args = apply_filters('em_object_build_sql_groupby_args', $args);
948
+ $groupby = self::build_sql_x_by_helper($args['groupby'], $groupby_order, $accepted_fields, $default_order);
949
+ return apply_filters('em_object_build_sql_groupby', $groupby, $args, $accepted_fields);
950
+ }
951
+
952
+ /**
953
+ * Helper for building arrays of fields
954
+ * @param unknown $x_by_field
955
+ * @param unknown $order
956
+ * @param unknown $accepted_fields
957
+ * @param string $default_order
958
+ * @return array
959
+ */
960
+ protected static function build_sql_x_by_helper($x_by_field, $order, $accepted_fields, $default_order = 'ASC' ){
961
+ $x_by = array();
962
+ if(is_array($x_by_field)){
963
  //Clean orderby array so we only have accepted values
964
+ foreach( $x_by_field as $key => $field ){
965
  if( array_key_exists($field, $accepted_fields) ){
966
+ //maybe cases we're given an array where keys are shortcut names e.g. id => event_id - this way will be deprecated at one point
967
+ $x_by[] = $accepted_fields[$field];
968
  }elseif( in_array($field,$accepted_fields) ){
969
+ $x_by[] = $field;
970
  }else{
971
+ unset($x_by[$key]);
972
  }
973
  }
974
+ }elseif( $x_by_field != '' && array_key_exists($x_by_field, $accepted_fields) ){
975
+ $x_by[] = $accepted_fields[$x_by_field];
976
+ }elseif( $x_by_field != '' && in_array($x_by_field, $accepted_fields) ){
977
+ $x_by[] = $x_by_field;
978
  }
979
  //ORDER
980
+ if( $order !== false ){
981
+ foreach($x_by as $i => $field){
982
+ $x_by[$i] .= ' ';
983
+ if(is_array($order)){
984
+ //If order is an array, we'll go through the orderby array and match the order values (in order of array) with orderby values
985
+ if( in_array($order[$i], array('ASC','DESC','asc','desc')) ){
986
+ $x_by[$i] .= $order[$i];
987
+ }else{
988
+ //If orders don't match up, or it's not ASC/DESC, the default events search in EM settings/options page will be used.
989
+ $x_by[$i] .= $default_order;
990
+ }
991
  }else{
992
+ $x_by[$i] .= ( in_array($order, array('ASC','DESC','asc','desc')) ) ? $order : $default_order;
993
  }
 
 
994
  }
995
  }
996
+ return $x_by;
997
+ }
998
+
999
+ /**
1000
+ * Fixes ambiguous fields in a given array (which can contain prefixed ASC/DESC arguments) and give them scope of events table
1001
+ * @param array $fields
1002
+ * @return array
1003
+ */
1004
+ protected static function build_sql_ambiguous_fields_helper( $fields, $reserved_fields = array(), $prefix = 'table_name' ){
1005
+ foreach($fields as $k => $v){
1006
+ $needle = trim(str_replace(array('ASC','DESC'), '', $v)); //remove ASC DESC for searching/comparison arrays such as order by
1007
+ if( in_array($needle, $reserved_fields) ){
1008
+ $fields[$k] = $prefix.'.'.$v;
1009
+ }
1010
+ }
1011
+ return $fields;
1012
  }
1013
 
1014
  /**
1158
  function can_manage( $owner_capability = false, $admin_capability = false, $user_to_check = false ){
1159
  global $em_capabilities_array;
1160
  //if multisite and super admin, just return true
1161
+ if( is_multisite() && em_wp_is_super_admin() ){ return true; }
1162
  //set user to the desired user we're verifying, otherwise default to current user
1163
  if( $user_to_check ){
1164
  $user = new WP_User($user_to_check);
1183
  }elseif( $admin_capability && array_key_exists($admin_capability, $em_capabilities_array) ){
1184
  $error_msg = $em_capabilities_array[$admin_capability];
1185
  }
1186
+ $can_manage = apply_filters('em_object_can_manage', $can_manage, $this, $owner_capability, $admin_capability, $user_to_check);
1187
  if( !$can_manage && !$is_owner && !empty($error_msg) ){
1188
  $this->add_error($error_msg);
1189
  }
1248
  if($db){
1249
  if( !empty($this->$key) || $this->$key === 0 || $this->$key === '0' || empty($val['null']) ){
1250
  $array[$key] = $this->$key;
1251
+ }elseif( $this->$key === null && !empty($val['null']) ){
1252
+ $array[$key] = null;
1253
  }
1254
  }else{
1255
  $array[$key] = $this->$key;
1627
  /*
1628
  * END IMAGE UPlOAD FUNCTIONS
1629
  */
1630
+
1631
+ function output_excerpt($excerpt_length = 55, $excerpt_more = '[...]', $cut_excerpt = true){
1632
+ if( !empty($this->post_excerpt) ){
1633
+ $replace = $this->post_excerpt;
1634
+ }else{
1635
+ $replace = $this->post_content;
1636
+ }
1637
+ if( empty($this->post_excerpt) || $cut_excerpt ){
1638
+ if ( preg_match('/<!--more(.*?)?-->/', $replace, $matches) ) {
1639
+ $content = explode($matches[0], $replace, 2);
1640
+ $replace = force_balance_tags($content[0]);
1641
+ }
1642
+ if( !empty($excerpt_length) ){
1643
+ //shorten content by supplied number - copied from wp_trim_excerpt
1644
+ $replace = strip_shortcodes( $replace );
1645
+ $replace = str_replace(']]>', ']]&gt;', $replace);
1646
+ $replace = wp_trim_words( $replace, $excerpt_length, $excerpt_more );
1647
+ }
1648
+ }
1649
+ return $replace;
1650
+ }
1651
 
1652
  function sanitize_time( $time ){
1653
  if( !empty($time) && preg_match ( '/^([01]\d|2[0-3]):([0-5]\d) ?(AM|PM)?$/', $time, $match ) ){
classes/em-permalinks.php CHANGED
@@ -34,7 +34,7 @@ if( !class_exists('EM_Permalinks') ){
34
  public static function flush(){
35
  global $wp_rewrite;
36
  $wp_rewrite->flush_rules();
37
- delete_option('dbem_flush_needed');
38
  }
39
 
40
  public static function post_type_archive_link($link, $post_type){
34
  public static function flush(){
35
  global $wp_rewrite;
36
  $wp_rewrite->flush_rules();
37
+ update_option('dbem_flush_needed', 0);
38
  }
39
 
40
  public static function post_type_archive_link($link, $post_type){
classes/em-tag-taxonomy.php DELETED
@@ -1,103 +0,0 @@
1
- <?php
2
- class EM_Tag_Taxonomy{
3
- public static function init(){
4
- if( !is_admin() ){
5
- add_filter('taxonomy_template', array('EM_Tag_Taxonomy','template'), 99);
6
- add_filter('parse_query', array('EM_Tag_Taxonomy','parse_query'));
7
- }
8
- }
9
- /**
10
- * Overrides archive pages e.g. locations, events, event tags, event tags based on user settings
11
- * @param string $template
12
- * @return string
13
- */
14
- public static function template($template = ''){
15
- global $wp_query, $EM_Tag, $em_tag_id, $post;
16
- if( is_tax(EM_TAXONOMY_TAG) && !locate_template('taxonomy-'.EM_TAXONOMY_TAG.'.php') && get_option('dbem_cp_tags_formats', true)){
17
- $EM_Tag = em_get_tag($wp_query->queried_object->term_id);
18
- if( get_option('dbem_tags_page') ){
19
- //less chance for things to go wrong with themes etc. so just reset the WP_Query to think it's a page rather than taxonomy
20
- $wp_query = new WP_Query(array('page_id'=> get_option('dbem_tags_page')));
21
- $wp_query->queried_object = $wp_query->post;
22
- $wp_query->queried_object_id = $wp_query->post->ID;
23
- $wp_query->post->post_title = $wp_query->posts[0]->post_title = $wp_query->queried_object->post_title = $EM_Tag->output(get_option('dbem_tag_page_title_format'));
24
- if( !function_exists('yoast_breadcrumb') ){ //not needed by WP SEO Breadcrumbs
25
- $wp_query->post->post_parent = $wp_query->posts[0]->post_parent = $wp_query->queried_object->post_parent = $EM_Tag->output(get_option('dbem_tags_page'));
26
- }
27
- $post = $wp_query->post;
28
- }else{
29
- $wp_query->em_tag_id = $em_tag_id = $EM_Tag->term_id; //we assign $em_tag_id just in case other themes/plugins do something out of the ordinary to WP_Query
30
- $wp_query->posts = array();
31
- $wp_query->posts[0] = new stdClass();
32
- $wp_query->posts[0]->post_title = $wp_query->queried_object->post_title = $EM_Tag->output(get_option('dbem_tag_page_title_format'));
33
- $post_array = array('ID', 'post_author', 'post_date','post_date_gmt','post_content','post_excerpt','post_status','comment_status','ping_status','post_password','post_name','to_ping','pinged','post_modified','post_modified_gmt','post_content_filtered','post_parent','guid','menu_order','post_type','post_mime_type','comment_count','filter');
34
- foreach($post_array as $post_array_item){
35
- $wp_query->posts[0]->$post_array_item = '';
36
- }
37
- $wp_query->post = $wp_query->posts[0];
38
- $wp_query->post_count = 1;
39
- $wp_query->found_posts = 1;
40
- $wp_query->max_num_pages = 1;
41
- //tweak flags for determining page type
42
- $wp_query->is_tax = 0;
43
- $wp_query->is_page = 1;
44
- $wp_query->is_single = 0;
45
- $wp_query->is_singular = 1;
46
- $wp_query->is_archive = 0;
47
- }
48
- remove_filter('the_content', 'em_content'); //one less filter
49
- add_filter('the_content', array('EM_Tag_Taxonomy','the_content')); //come in slightly early and consider other plugins
50
- add_filter('wpseo_breadcrumb_links',array('EM_Tag_Taxonomy','wpseo_breadcrumb_links')); //for Yoast WP SEO
51
- $wp_query->em_tag_id = $em_tag_id = $EM_Tag->term_id; //we assign $em_tag_id just in case other themes/plugins do something out of the ordinary to WP_Query
52
- $template = locate_template(array('page.php','index.php'),false); //tag becomes a page
53
- do_action('em_tag_taxonomy_template');
54
- }
55
- return $template;
56
- }
57
-
58
- public static function the_content($content){
59
- global $wp_query, $EM_Tag, $post, $em_tag_id;
60
- $is_tags_page = $post->ID == get_option('dbem_tags_page');
61
- $tag_flag = (!empty($wp_query->em_tag_id) || !empty($em_tag_id));
62
- if( ($is_tags_page && $tag_flag) || (empty($post->ID) && $tag_flag) ){
63
- $EM_Tag = empty($wp_query->em_tag_id) ? em_get_tag($em_tag_id):em_get_tag($wp_query->em_tag_id);
64
- ob_start();
65
- em_locate_template('templates/tag-single.php',true);
66
- return ob_get_clean();
67
- }
68
- return $content;
69
- }
70
-
71
- public static function parse_query(){
72
- global $wp_query, $post;
73
- if( is_tax(EM_TAXONOMY_TAG) ){
74
- //Scope is future
75
- $today = strtotime(date('Y-m-d', current_time('timestamp')));
76
- if( get_option('dbem_events_current_are_past') ){
77
- $wp_query->query_vars['meta_query'][] = array( 'key' => '_start_ts', 'value' => $today, 'compare' => '>=' );
78
- }else{
79
- $wp_query->query_vars['meta_query'][] = array( 'key' => '_end_ts', 'value' => $today, 'compare' => '>=' );
80
- }
81
- if( get_option('dbem_tags_default_archive_orderby') == 'title'){
82
- $wp_query->query_vars['orderby'] = 'title';
83
- }else{
84
- $wp_query->query_vars['orderby'] = 'meta_value_num';
85
- $wp_query->query_vars['meta_key'] = get_option('dbem_tags_default_archive_orderby','_start_ts');
86
- }
87
- $wp_query->query_vars['order'] = get_option('dbem_tags_default_archive_order','ASC');
88
- }elseif( !empty($wp_query->em_tag_id) ){
89
- $post = $wp_query->post;
90
- }
91
- }
92
-
93
- public static function wpseo_breadcrumb_links( $links ){
94
- global $wp_query;
95
- array_pop($links);
96
- if( get_option('dbem_tags_page') ){
97
- $links[] = array('id'=> get_option('dbem_tags_page'));
98
- }
99
- $links[] = array('text'=> $wp_query->posts[0]->post_title);
100
- return $links;
101
- }
102
- }
103
- EM_Tag_Taxonomy::init();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/em-tag.php CHANGED
@@ -1,195 +1,34 @@
1
  <?php
2
  /**
3
- * Get an tag in a db friendly way, by checking globals and passed variables to avoid extra class instantiations
4
- * @param mixed $id
5
- * @return EM_Tag
6
  */
7
- function em_get_tag($id = false) {
8
- global $EM_Tag;
9
- //check if it's not already global so we don't instantiate again
10
- if( is_object($EM_Tag) && get_class($EM_Tag) == 'EM_Tag' ){
11
- if( $EM_Tag->term_id == $id ){
12
- return $EM_Tag;
13
- }elseif( is_object($id) && $EM_Tag->term_id == $id->term_id ){
14
- return $EM_Tag;
15
- }
16
- }
17
- if( is_object($id) && get_class($id) == 'EM_Tag' ){
18
- return $id;
19
- }else{
20
- return new EM_Tag($id);
21
- }
22
- }
23
- class EM_Tag extends EM_Object {
24
- //Taxonomy Fields
25
- var $id = '';
26
- var $term_id;
27
- var $name;
28
- var $slug;
29
- var $term_group;
30
- var $term_taxonomy_id;
31
- var $taxonomy;
32
- var $description = '';
33
- var $parent = 0;
34
- var $count;
35
 
 
 
 
 
36
  /**
37
- * Gets data from POST (default), supplied array, or from the database if an ID is supplied
38
- * @param $tag_data
39
- * @return null
 
40
  */
41
- function __construct( $tag_data = false ) {
42
- global $wpdb;
43
- //Initialize
44
- $tag = array();
45
- if( !empty($tag_data) ){
46
- //Load tag data
47
- if( is_object($tag_data) && !empty($tag_data->taxonomy) && $tag_data->taxonomy == EM_TAXONOMY_TAG ){
48
- $tag = $tag_data;
49
- }elseif( !is_numeric($tag_data) ){
50
- $tag = get_term_by('name', $tag_data, EM_TAXONOMY_TAG);
51
- if( empty($tag) ){
52
- $tag = get_term_by('slug', $tag_data, EM_TAXONOMY_TAG);
53
- }
54
- }else{
55
- $tag = get_term_by('id', $tag_data, EM_TAXONOMY_TAG);
56
- }
57
- }
58
- if( !empty($tag) ){
59
- foreach($tag as $key => $value){
60
- $this->$key = $value;
61
- }
62
- $this->id = $this->term_id; //backward compatability
63
- }
64
- do_action('em_tag',$this, $tag_data);
65
- }
66
-
67
- function get_url(){
68
- if( empty($this->link) ){
69
- self::ms_global_switch();
70
- $this->link = get_term_link($this->slug, EM_TAXONOMY_TAG);
71
- self::ms_global_switch_back();
72
- if ( is_wp_error($this->link) ) $this->link = '';
73
- }
74
- return apply_filters('em_tag_get_url', $this->link);
75
- }
76
-
77
- function get_ical_url(){
78
- global $wp_rewrite;
79
- if( !empty($wp_rewrite) && $wp_rewrite->using_permalinks() ){
80
- $return = trailingslashit($this->get_url()).'ical/';
81
- }else{
82
- $return = em_add_get_params($this->get_url(), array('ical'=>1));
83
- }
84
- return apply_filters('em_tag_get_ical_url', $return);
85
- }
86
-
87
- function get_rss_url(){
88
- global $wp_rewrite;
89
- if( !empty($wp_rewrite) && $wp_rewrite->using_permalinks() ){
90
- $return = trailingslashit($this->get_url()).'feed/';
91
- }else{
92
- $return = em_add_get_params($this->get_url(), array('feed'=>1));
93
- }
94
- return apply_filters('em_tag_get_rss_url', $return);
95
- }
96
-
97
- function output_single($target = 'html'){
98
- $format = get_option ( 'dbem_tag_page_format' );
99
- return apply_filters('em_tag_output_single', $this->output($format, $target), $this, $target);
100
  }
101
 
102
- function output($format, $target="html") {
103
- preg_match_all('/\{([a-zA-Z0-9_]+)\}([^{]+)\{\/[a-zA-Z0-9_]+\}/', $format, $conditionals);
104
- if( count($conditionals[0]) > 0 ){
105
- foreach($conditionals[1] as $key => $condition){
106
- $format = str_replace($conditionals[0][$key], apply_filters('em_tag_output_condition', '', $condition, $conditionals[0][$key], $this), $format);
107
- }
108
- }
109
- $tag_string = $format;
110
- preg_match_all("/(#@?_?[A-Za-z0-9]+)({([a-zA-Z0-9,]+)})?/", $format, $placeholders);
111
- $replaces = array();
112
- foreach($placeholders[1] as $key => $result) {
113
- $match = true;
114
- $replace = '';
115
- $full_result = $placeholders[0][$key];
116
- switch( $result ){
117
- case '#_TAGNAME':
118
- $replace = $this->name;
119
- break;
120
- case '#_TAGID':
121
- $replace = $this->term_id;
122
- break;
123
- case '#_TAGSLUG':
124
- $replace = $this->slug;
125
- break;
126
- case '#_TAGLINK':
127
- case '#_TAGURL':
128
- $link = $this->get_url();
129
- $replace = ($result == '#_TAGURL') ? $link : '<a href="'.$link.'">'.esc_html($this->name).'</a>';
130
- break;
131
- case '#_TAGICALURL':
132
- case '#_TAGICALLINK':
133
- $replace = $this->get_ical_url();
134
- if( $result == '#_TAGICALLINK' ){
135
- $replace = '<a href="'.esc_url($replace).'">iCal</a>';
136
- }
137
- break;
138
- case '#_TAGRSSURL':
139
- case '#_TAGRSSLINK':
140
- $replace = $this->get_rss_url();
141
- if( $result == '#_TAGRSSLINK' ){
142
- $replace = '<a href="'.esc_url($replace).'">RSS</a>';
143
- }
144
- break;
145
- case '#_TAGNOTES':
146
- $replace = $this->description;
147
- break;
148
- case '#_TAGEVENTSPAST': //deprecated, erroneous documentation, left for compatability
149
- case '#_TAGEVENTSNEXT': //deprecated, erroneous documentation, left for compatability
150
- case '#_TAGEVENTSALL': //deprecated, erroneous documentation, left for compatability
151
- case '#_TAGPASTEVENTS':
152
- case '#_TAGNEXTEVENTS':
153
- case '#_TAGALLEVENTS':
154
- //convert deprecated placeholders for compatability
155
- $result = ($result == '#_TAGEVENTSPAST') ? '#_TAGPASTEVENTS':$result;
156
- $result = ($result == '#_TAGEVENTSNEXT') ? '#_TAGNEXTEVENTS':$result;
157
- $result = ($result == '#_TAGEVENTSALL') ? '#_TAGALLEVENTS':$result;
158
- //forget it ever happened? :/
159
- if ($result == '#_TAGPASTEVENTS'){ $scope = 'past'; }
160
- elseif ( $result == '#_TAGNEXTEVENTS' ){ $scope = 'future'; }
161
- else{ $scope = 'all'; }
162
- $events_count = EM_Events::count( array('tag'=>$this->term_id, 'scope'=>$scope) );
163
- if ( $events_count > 0 ){
164
- $args = array('tag'=>$this->term_id, 'scope'=>$scope, 'pagination'=>1, 'ajax'=>0);
165
- $args['format_header'] = get_option('dbem_tag_event_list_item_header_format');
166
- $args['format_footer'] = get_option('dbem_tag_event_list_item_footer_format');
167
- $args['format'] = get_option('dbem_tag_event_list_item_format');
168
- $args['limit'] = get_option('dbem_tag_event_list_limit');
169
- $args['page'] = (!empty($_REQUEST['pno']) && is_numeric($_REQUEST['pno']) )? $_REQUEST['pno'] : 1;
170
- $replace = EM_Events::output($args);
171
- } else {
172
- $replace = get_option('dbem_tag_no_events_message');
173
- }
174
- break;
175
- case '#_TAGNEXTEVENT':
176
- $events = EM_Events::get( array('tag'=>$this->term_id, 'scope'=>'future', 'limit'=>1, 'orderby'=>'event_start_date,event_start_time') );
177
- $replace = get_option('dbem_tag_no_event_message');
178
- foreach($events as $EM_Event){
179
- $replace = $EM_Event->output(get_option('dbem_tag_event_single_format'));
180
- }
181
- break;
182
- default:
183
- $replace = $full_result;
184
- break;
185
- }
186
- $replaces[$full_result] = apply_filters('em_tag_output_placeholder', $replace, $this, $full_result, $target);
187
- }
188
- krsort($replaces);
189
- foreach($replaces as $full_result => $replacement){
190
- $tag_string = str_replace($full_result, $replacement , $tag_string );
191
- }
192
- return apply_filters('em_tag_output', $tag_string, $this, $format, $target);
193
  }
194
  }
195
- ?>
 
 
 
 
 
 
 
 
 
1
  <?php
2
  /**
3
+ * A single event tag object. *
 
 
4
  */
5
+ class EM_Tag extends EM_Taxonomy_Term {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
+ //static options for EM_Category, but until PHP 5.3 is the WP minimum requirement we'll make them regular properties due to lack of late static binding
8
+ public $option_name = 'tag'; //the singular name of this taxonomy which is used in option names consistent across EM taxonomies
9
+ public $taxonomy = 'EM_TAXONOMY_TAG';
10
+
11
  /**
12
+ * Necessary to supply the $class_name until late static binding is reliably available on all WP sites running PHP 5.3
13
+ * @param string $id
14
+ * @param string $class_name
15
+ * @return EM_Taxonomy
16
  */
17
+ public static function get( $id = false, $class_name = 'EM_Tag' ){
18
+ return parent::get($id, $class_name);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  }
20
 
21
+ public function can_manage( $capability_owner = 'edit_event_tags', $capability_admin = false, $user_to_check = false ){
22
+ return parent::can_manage($capability_owner, $capability_admin, $user_to_check);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  }
24
  }
25
+
26
+ /**
27
+ * Get an category in a db friendly way, by checking globals and passed variables to avoid extra class instantiations
28
+ * @param mixed $id
29
+ * @return EM_Category
30
+ * @uses EM_Category::get()
31
+ */
32
+ function em_get_tag( $id = false ) {
33
+ return EM_Tag::get($id);
34
+ }
classes/em-tags-admin.php ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This class extends the EM_Taxonomy_Admin and adds category images and colors to the admin area.
4
+ *
5
+ * Currently, all functions here serve the purpose of getting around lack of late static binding in PHP < 5.3.
6
+ * Eventually when PHP 5.3 is enforced only certain class properties need to be defined for use in the parent class via static::
7
+ *
8
+ */
9
+ class EM_Tags_Admin extends EM_Taxonomy_Admin{
10
+
11
+ public static $taxonomy_name = 'EM_TAXONOMY_TAG'; //converted into a constant value during init()
12
+ public static $this_class = 'EM_Tags_Admin'; //needed until 5.3 minimum is enforced for late static binding
13
+ public static $tax_class = 'EM_Tag';
14
+ public static $option_name = 'tag';
15
+ public static $name_singular = 'tag';
16
+ public static $name_plural = 'tags';
17
+ public static $placeholder_image = '#_TAGIMAGE';
18
+ public static $placeholder_color = '#_TAGCOLOR';
19
+
20
+ public static function init(){
21
+ self::$taxonomy_name = EM_TAXONOMY_TAG;
22
+ self::static_binding();
23
+ parent::init();
24
+ }
25
+
26
+ public static function form_add(){
27
+ self::static_binding();
28
+ parent::form_add();
29
+ }
30
+
31
+ public static function form_edit($tag){
32
+ self::static_binding();
33
+ parent::form_edit($tag);
34
+ }
35
+
36
+ public static function save( $term_id, $tt_id ){
37
+ self::static_binding();
38
+ parent::save( $term_id, $tt_id );
39
+ }
40
+
41
+ public static function delete( $term_id ){
42
+ self::static_binding();
43
+ parent::delete( $term_id );
44
+ }
45
+
46
+ /**
47
+ * Temporary function until WP requires PHP 5.3, so that we can make use of late static binding.
48
+ * Until then, all functions needing LST should run this function before calling the parent. If all extending classes do this we shouldn't have a problem.
49
+ */
50
+ public static function static_binding(){
51
+ EM_Taxonomy_Admin::$taxonomy_name = self::$taxonomy_name;
52
+ EM_Taxonomy_Admin::$this_class = self::$this_class;
53
+ EM_Taxonomy_Admin::$tax_class = self::$tax_class;
54
+ EM_Taxonomy_Admin::$option_name = self::$option_name;
55
+ EM_Taxonomy_Admin::$name_singular = self::$name_singular;
56
+ EM_Taxonomy_Admin::$name_plural = self::$name_plural;
57
+ EM_Taxonomy_Admin::$placeholder_image = self::$placeholder_image;
58
+ EM_Taxonomy_Admin::$placeholder_color = self::$placeholder_color;
59
+ }
60
+ }
61
+ add_action('admin_init',array('EM_Tags_Admin','init'));
classes/em-tags-frontend.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class EM_Tags_Frontend extends EM_Taxonomy_Frontend {
3
+
4
+ public static $taxonomy_name = 'event-tag'; //converted into a constant value during init()
5
+ public static $this_class = 'EM_Tags_Frontend'; //needed until 5.3 minimum is enforced for late static binding
6
+ public static $tax_class = 'EM_Tag';
7
+ public static $option_name = 'tag';
8
+ public static $option_name_plural = 'tags';
9
+
10
+ public static function init(){
11
+ self::$taxonomy_name = EM_TAXONOMY_TAG; //awaiting LSB in PHP 5.3
12
+ self::static_binding();
13
+ parent::init();
14
+ }
15
+
16
+ //These following functions can be removed when PHP 5.3 is minimum and LSB is available
17
+
18
+ public static function template($template = ''){
19
+ self::static_binding();
20
+ return parent::template($template);
21
+ }
22
+
23
+ public static function the_content($content){
24
+ self::static_binding();
25
+ return parent::the_content($content);
26
+ }
27
+
28
+ public static function parse_query( $wp_query ){
29
+ //we do some double-checking here to prevent running self::static_binding() during the self::template() function when WP_Query is called.
30
+ if( !$wp_query->is_main_query() ) return;
31
+ if( $wp_query->is_tax(self::$taxonomy_name) || !empty($wp_query->{'em_'.self::$option_name.'_id'}) ){
32
+ self::static_binding();
33
+ return parent::parse_query( $wp_query );
34
+ }
35
+ }
36
+
37
+ public static function wpseo_breadcrumb_links( $links ){
38
+ self::static_binding();
39
+ return parent::wpseo_breadcrumb_links( $links );
40
+ }
41
+
42
+ /**
43
+ * Temporary function until WP requires PHP 5.3, so that we can make use of late static binding.
44
+ * Until then, all functions needing LST should run this function before calling the parent. If all extending classes do this we shouldn't have a problem.
45
+ */
46
+ public static function static_binding(){
47
+ EM_Taxonomy_Frontend::$taxonomy_name = self::$taxonomy_name;
48
+ EM_Taxonomy_Frontend::$this_class = self::$this_class;
49
+ EM_Taxonomy_Frontend::$tax_class = self::$tax_class;
50
+ EM_Taxonomy_Frontend::$option_name = self::$option_name;
51
+ EM_Taxonomy_Frontend::$option_name_plural = self::$option_name_plural;
52
+ }
53
+ }
54
+ class EM_Tag_Taxonomy extends EM_Tags_Frontend {} //backwards compatibility
55
+
56
+ EM_Tags_Frontend::init();
classes/em-tags.php CHANGED
@@ -1,292 +1,69 @@
1
  <?php
2
- class EM_Tags extends EM_Object implements Iterator{
 
 
 
 
 
3
 
4
  /**
5
- * Array of EM_Tag objects for a specific event
6
- * @var array
7
- */
8
- var $tags = array();
9
- /**
10
- * Event ID of this set of tags
11
- * @var int
12
- */
13
- var $event_id;
14
- /**
15
- * Post ID of this set of tags
16
- * @var int
17
- */
18
- var $post_id;
19
-
20
- /**
21
- * Creates an EM_Tags instance, currently accepts an EM_Event object (gets all Tags for that event) or array of any EM_Tag objects, which can be manipulated in bulk with helper functions.
22
  * @param mixed $data
23
  * @return null
24
  */
25
- function __construct( $data = false ){
26
- global $wpdb;
27
- //an EM_Event object
28
- if( is_object($data) && get_class($data) == "EM_Event" && !empty($data->post_id) ){ //Creates a blank tags object if needed
29
- $this->event_id = $data->event_id;
30
- $this->post_id = $data->post_id;
31
- if( EM_MS_GLOBAL && (get_current_blog_id() !== $data->blog_id || (!$data->blog_id && !is_main_site()) ) ){
32
- if( !$this->blog_id ) $this->blog_id = get_current_site()->blog_id;
33
- switch_to_blog($this->blog_id);
34
- $results = get_the_terms( $data->post_id, EM_TAXONOMY_TAG );
35
- restore_current_blog();
36
- }else{
37
- $results = get_the_terms( $data->post_id, EM_TAXONOMY_TAG );
38
- }
39
- if( is_array($results) ){
40
- foreach($results as $result){
41
- $this->tags[$result->term_id] = new EM_Tag($result);
42
- }
43
- }
44
- //array of EM_Tag ids
45
- }elseif( is_array($data) && self::array_is_numeric($data) ){
46
- foreach($data as $tag_id){
47
- $this->tags[$tag_id] = new EM_Tag($tag_id);
48
- }
49
- //array of EM_Tag objects
50
- }elseif( is_array($data) ){
51
- foreach( $data as $EM_Tag ){
52
- if( get_class($EM_Tag) == 'EM_Tag'){
53
- $this->tags[] = $EM_Tag;
54
- }
55
- }
56
- }
57
- do_action('em_tags', $this);
58
  }
59
 
60
- /* experimental, not tested! */
61
- function get_post(){
62
- self::ms_global_switch();
63
- $this->tags = array();
64
- if(!empty($_POST['event_tags']) && self::array_is_numeric($_POST['event_tags'])){
65
- foreach( $_POST['event_tags'] as $term ){
66
- $this->tags[$term] = new EM_Tag($term);
67
- }
68
- }
69
- self::ms_global_switch_back();
70
- do_action('em_tags_get_post', $this);
71
- }
72
-
73
- /* experimental, not tested! */
74
- function save(){
75
- $term_slugs = array();
76
- foreach($this->tags as $EM_Tag){
77
- /* @var $EM_Tag EM_Tag */
78
- if( !empty($EM_Tag->slug) ) $term_slugs[] = $EM_Tag->slug; //save of tag will soft-fail if slug is empty
79
- }
80
- if( count($term_slugs) == 0 && get_option('dbem_default_tag') > 0 ){
81
- $default_term = get_term_by('id',get_option('dbem_default_tag'), EM_TAXONOMY_TAG);
82
- if($default_term) $term_slugs[] = $default_term->slug;
83
- }
84
- if( count($term_slugs) > 0 ){
85
- wp_set_object_terms($this->post_id, $term_slugs, EM_TAXONOMY_TAG);
86
- }
87
- do_action('em_tags_save', $this);
88
- }
89
-
90
- public static function get( $args = array() ) {
91
- //Quick version, we can accept an array of IDs, which is easy to retrieve
92
- if( self::array_is_numeric($args) ){ //Array of numbers, assume they are event IDs to retreive
93
- $results = get_terms( EM_TAXONOMY_TAG );
94
- $tags = array();
95
- foreach($results as $result){
96
- if( in_array($result->term_id, $args) ){
97
- $tags[$result->term_id] = new EM_Tag($result);
98
- }
99
- }
100
- }else{
101
- //We assume it's either an empty array or array of search arguments to merge with defaults
102
- $term_args = self::get_default_search($args);
103
- $results = get_terms( EM_TAXONOMY_TAG, $term_args);
104
-
105
- //If we want results directly in an array, why not have a shortcut here? We don't use this in code, so if you're using it and filter the em_tags_get hook, you may want to do this one too.
106
- if( !empty($args['array']) ){
107
- return apply_filters('em_tags_get_array', $results, $args);
108
- }
109
-
110
- //Make returned results EM_Tag objects
111
- $results = (is_array($results)) ? $results:array();
112
- $tags = array();
113
- foreach ( $results as $tag ){
114
- $tags[$tag->term_id] = new EM_Tag($tag);
115
- }
116
- }
117
- self::ms_global_switch_back();
118
- return apply_filters('em_tags_get', $tags, $args);
119
- }
120
-
121
- public static function output( $args ){
122
- global $EM_Tag;
123
- $EM_Tag_old = $EM_Tag; //When looping, we can replace EM_Tag global with the current event in the loop
124
- //get page number if passed on by request (still needs pagination enabled to have effect)
125
- $page_queryvar = !empty($args['page_queryvar']) ? $args['page_queryvar'] : 'pno';
126
- if( !empty($args['pagination']) && !array_key_exists('page',$args) && !empty($_REQUEST[$page_queryvar]) && is_numeric($_REQUEST[$page_queryvar]) ){
127
- $page = $args['page'] = $_REQUEST[$page_queryvar];
128
- }
129
- //Can be either an array for the get search or an array of EM_Tag objects
130
- if( is_object(current($args)) && get_class((current($args))) == 'EM_Tag' ){
131
- $func_args = func_get_args();
132
- $tags = $func_args[0];
133
- $args = (!empty($func_args[1])) ? $func_args[1] : array();
134
- $args = apply_filters('em_tags_output_args', self::get_default_search($args), $tags);
135
- $limit = ( !empty($args['limit']) && is_numeric($args['limit']) ) ? $args['limit']:false;
136
- $offset = ( !empty($args['offset']) && is_numeric($args['offset']) ) ? $args['offset']:0;
137
- $page = ( !empty($args['page']) && is_numeric($args['page']) ) ? $args['page']:1;
138
- }else{
139
- $args = apply_filters('em_tags_output_args', self::get_default_search($args) );
140
- $limit = ( !empty($args['limit']) && is_numeric($args['limit']) ) ? $args['limit']:false;
141
- $offset = ( !empty($args['offset']) && is_numeric($args['offset']) ) ? $args['offset']:0;
142
- $page = ( !empty($args['page']) && is_numeric($args['page']) ) ? $args['page']:1;
143
- $args['limit'] = $args['offset'] = $args['page'] = false; //we count overall tags here
144
- $tags = self::get( $args );
145
- $args['limit'] = $limit;
146
- $args['offset'] = $offset;
147
- $args['page'] = $page;
148
- }
149
- //What format shall we output this to, or use default
150
- $format = ( $args['format'] == '' ) ? get_option( 'dbem_tags_list_item_format' ) : $args['format'] ;
151
-
152
- $output = "";
153
- $tags_count = count($tags);
154
- $tags = apply_filters('em_tags_output_tags', $tags);
155
- if ( count($tags) > 0 ) {
156
- $tag_count = 0;
157
- $tags_shown = 0;
158
- foreach ( $tags as $EM_Tag ) {
159
- if( ($tags_shown < $limit || empty($limit)) && ($tag_count >= $offset || $offset === 0) ){
160
- $output .= $EM_Tag->output($format);
161
- $tags_shown++;
162
- }
163
- $tag_count++;
164
- }
165
- //Add headers and footers to output
166
- if( $format == get_option( 'dbem_tags_list_item_format' ) ){
167
- //we're using the default format, so if a custom format header or footer is supplied, we can override it, if not use the default
168
- $format_header = empty($args['format_header']) ? get_option('dbem_tags_list_item_format_header') : $args['format_header'];
169
- $format_footer = empty($args['format_footer']) ? get_option('dbem_tags_list_item_format_footer') : $args['format_footer'];
170
- }else{
171
- //we're using a custom format, so if a header or footer isn't specifically supplied we assume it's blank
172
- $format_header = !empty($args['format_header']) ? $args['format_header'] : '' ;
173
- $format_footer = !empty($args['format_footer']) ? $args['format_footer'] : '' ;
174
- }
175
- $output = $format_header . $output . $format_footer;
176
-
177
- //Pagination (if needed/requested)
178
- if( !empty($args['pagination']) && !empty($limit) && $tags_count >= $limit ){
179
- $output .= self::get_pagination_links($args, $tags_count);
180
- }
181
- } else {
182
- $output = get_option ( 'dbem_no_tags_message' );
183
  }
184
- //FIXME check if reference is ok when restoring object, due to changes in php5 v 4
185
- $EM_Tag_old= $EM_Tag;
186
- return apply_filters('em_tags_output', $output, $tags, $args);
187
  }
188
 
189
- public static function get_pagination_links($args, $count, $search_action = 'search_tags', $default_args = array()){
190
- //get default args if we're in a search, supply to parent since we can't depend on late static binding until WP requires PHP 5.3 or later
191
- if( empty($default_args) && (!empty($args['ajax']) || !empty($_REQUEST['action']) && $_REQUEST['action'] == $search_action) ){
192
- $default_args = self::get_default_search();
193
- $default_args['limit'] = get_option('dbem_tags_default_limit');
 
 
 
194
  }
195
- return parent::get_pagination_links($args, $count, $search_action, $default_args);
196
  }
197
 
198
- public static function get_post_search($args = array(), $filter = false, $request = array(), $accepted_args = array()){
199
- //supply $accepted_args to parent argument since we can't depend on late static binding until WP requires PHP 5.3 or later
200
- $accepted_args = !empty($accepted_args) ? $accepted_args : array_keys(self::get_default_search());
201
- return apply_filters('em_tags_get_post_search', parent::get_post_search($args, $filter, $request, $accepted_args));
202
- }
203
 
204
- function has( $search ){
205
- if( is_numeric($search) ){
206
- foreach($this->tags as $EM_Tag){
207
- if($EM_Tag->term_id == $search) return apply_filters('em_tags_has', true, $search, $this);
208
- }
209
- }else{
210
- foreach($this->tags as $EM_Tag){
211
- if($EM_Tag->slug == $search || $EM_Tag->name == $search ) return apply_filters('em_tags_has', true, $search, $this);
212
- }
213
- }
214
- return apply_filters('em_tags_has', false, $search, $this);
215
  }
216
-
217
- function get_first(){
218
- foreach($this->tags as $EM_Tag){
219
- return $EM_Tag;
220
- }
221
- return false;
222
  }
223
 
224
- function get_ids(){
225
- $ids = array();
226
- foreach($this->tags as $EM_Tag){
227
- if( !empty($EM_Tag->term_id) ){
228
- $ids[] = $EM_Tag->term_id;
229
- }
230
- }
231
- return $ids;
232
  }
233
-
234
- /**
235
- * Gets the event for this object, or a blank event if none exists
236
- * @return EM_Event
237
- */
238
- function get_event(){
239
- if( is_numeric($this->event_id) ){
240
- return em_get_event($this->event_id);
241
- }else{
242
- return new EM_Event();
243
- }
244
  }
245
 
246
- /*
247
- * Adds custom tags search defaults
248
- * @param array $array_or_defaults may be the array to override defaults
249
- * @param array $array
250
- * @return array
251
- * @uses EM_Object#get_default_search()
252
- */
253
  public static function get_default_search( $array_or_defaults = array(), $array = array() ){
254
- $defaults = array(
255
- //added from get_terms, so they don't get filtered out
256
- 'orderby' => get_option('dbem_tags_default_orderby', 'name'), 'order' => get_option('dbem_tags_default_order', 'name'),
257
- 'hide_empty' => false, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(),
258
- 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '',
259
- 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '',
260
- 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core'
261
- );
262
- //sort out whether defaults were supplied or just the array of search values
263
- if( empty($array) ){
264
- $array = $array_or_defaults;
265
- }else{
266
- $defaults = array_merge($defaults, $array_or_defaults);
267
- }
268
- return apply_filters('em_tags_get_default_search', parent::get_default_search($defaults,$array), $array, $defaults);
269
  }
270
-
271
- //Iterator Implementation
272
- public function rewind(){
273
- reset($this->tags);
274
- }
275
- public function current(){
276
- $var = current($this->tags);
277
- return $var;
278
- }
279
- public function key(){
280
- $var = key($this->tags);
281
- return $var;
282
- }
283
- public function next(){
284
- $var = next($this->tags);
285
- return $var;
286
- }
287
- public function valid(){
288
- $key = key($this->tags);
289
- $var = ($key !== NULL && $key !== FALSE);
290
- return $var;
291
- }
292
  }
1
  <?php
2
+ class EM_Tags extends EM_Taxonomy_Terms {
3
+ //Overridable functions
4
+ protected $taxonomy = 'event-tags';
5
+ protected $meta_key = 'event-tags';
6
+ protected $terms_name = 'tags';
7
+ protected $term_class = 'EM_Tag';
8
 
9
  /**
10
+ * Creates an EM_Tags instance, currently accepts an EM_Event object (gets all Categories for that event) or array of any EM_Category objects, which can be manipulated in bulk with helper functions.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  * @param mixed $data
12
  * @return null
13
  */
14
+ public function __construct( $data = false ){
15
+ $this->taxonomy = EM_TAXONOMY_TAG;
16
+ parent::__construct($data);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  }
18
 
19
+ /**
20
+ * Legacy get overload for any use of $EM_Tags->tags
21
+ * @param string $var_name
22
+ * @return array|NULL
23
+ */
24
+ public function __get( $var_name ){
25
+ if( $var_name == 'tags' ){
26
+ return $this->terms;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  }
28
+ return null;
 
 
29
  }
30
 
31
+ /**
32
+ * Legacy overload for use of empty($this->tags)
33
+ * @param string $var_name
34
+ * @return boolean
35
+ */
36
+ function __isset( $var_name ){
37
+ if( $var_name == 'tags' ){
38
+ return !empty($this->terms);
39
  }
40
+ return !empty($this->$var_name);
41
  }
42
 
43
+ //Functions we won't need when PHP 5.3 minimum allows for use of LSB
 
 
 
 
44
 
45
+ public static function get( $args = array() ){
46
+ self::$instance = new EM_Tags();
47
+ return parent::get($args);
 
 
 
 
 
 
 
 
48
  }
49
+
50
+ public static function output( $args = array() ){
51
+ self::$instance = new EM_Tags();
52
+ return parent::output($args);
 
 
53
  }
54
 
55
+ public static function get_pagination_links($args, $count, $search_action = 'search_cats', $default_args = array()){
56
+ self::$instance = new EM_Tags();
57
+ return parent::get_pagination_links($args, $count, $search_action, $default_args);
 
 
 
 
 
58
  }
59
+
60
+ public static function get_post_search($args = array(), $filter = false, $request = array(), $accepted_args = array()){
61
+ self::$instance = new EM_Tags();
62
+ return parent::get_post_search($args, $filter, $request, $accepted_args);
 
 
 
 
 
 
 
63
  }
64
 
 
 
 
 
 
 
 
65
  public static function get_default_search( $array_or_defaults = array(), $array = array() ){
66
+ self::$instance = new EM_Tags();
67
+ return parent::get_default_search($defaults,$array);
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  }
classes/em-taxonomy-admin.php ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Base class for taxonomies, adding extra features to the admin area such as displaying and saving image and color options.
4
+ *
5
+ * Classes extending this one must define the right
6
+ */
7
+ class EM_Taxonomy_Admin {
8
+
9
+ /**
10
+ * The name of this taxonomy, e.g. event-categories, which is defined in child class.
11
+ * @var string
12
+ */
13
+ public static $taxonomy_name;
14
+ /**
15
+ * The name of the child class, used for now whilst late static binding isn't guaranteed since we may be running on PHP <5.3
16
+ * Once PHP 5.3 is a minimum requirement in WP, we can get rid of this one.
17
+ * @var string
18
+ */
19
+ public static $this_class = 'EM_Taxonomy_Admin';
20
+ /**
21
+ * Currently used to instantiate a class of the specific term. Eventually we could just use EM_Taxonomy since these will be standardized functions for any taxonomy.
22
+ * @var string
23
+ */
24
+ public static $tax_class = 'EM_Taxonomy';
25
+ /**
26
+ * Name of taxonomy for reference in saving to database, e.g. category will be used to save category-image.
27
+ * This may differ from the name of the taxonomy, such as event-category can be category
28
+ * @var string
29
+ */
30
+ public static $option_name = 'taxonomy';
31
+ public static $name_singular = 'taxonomy';
32
+ public static $name_plural = 'taxonomies';
33
+ public static $placeholder_image = '#_TAXONOMYIMAGE';
34
+ public static $placeholder_color = '#_TAXONOMYCOLOR';
35
+
36
+ public static function init(){
37
+ global $pagenow;
38
+ if( ($pagenow == 'edit-tags.php' || $pagenow == 'term.php') && !empty($_GET['taxonomy']) && $_GET['taxonomy'] == self::$taxonomy_name){
39
+ add_filter('admin_enqueue_scripts', 'EM_Taxonomy_Admin::admin_enqueue_scripts');
40
+ }
41
+ add_action( self::$taxonomy_name.'_edit_form_fields', array(self::$this_class, 'form_edit'), 10, 1);
42
+ add_action( self::$taxonomy_name.'_add_form_fields', array(self::$this_class, 'form_add'), 10, 1);
43
+ add_action( 'edited_'.self::$taxonomy_name, array(self::$this_class, 'save'), 10, 2);
44
+ add_action( 'create_'.self::$taxonomy_name, array(self::$this_class, 'save'), 10, 2);
45
+ add_action( 'delete_'.self::$taxonomy_name, array(self::$this_class, 'delete'), 10, 2);
46
+
47
+ add_filter('manage_edit-'.self::$taxonomy_name.'_columns' , array(self::$this_class, 'columns_add'));
48
+ add_filter('manage_'.self::$taxonomy_name.'_custom_column' , array(self::$this_class, 'columns_output'),10,3);
49
+
50
+ }
51
+
52
+ public static function columns_add($columns) {
53
+ //prepend ID after checkbox
54
+ $columns['term-id'] = __('ID','events-manager');
55
+ return $columns;
56
+ }
57
+
58
+ public static function columns_output( $val, $column, $term_id ) {
59
+ switch ( $column ) {
60
+ case 'term-id':
61
+ return $term_id;
62
+ break;
63
+ }
64
+ return $val;
65
+ }
66
+
67
+ public static function admin_enqueue_scripts(){
68
+ wp_enqueue_media();
69
+ wp_enqueue_style( 'wp-color-picker' );
70
+ wp_enqueue_script( 'em-taxonomies-admin', plugins_url().'/events-manager/includes/js/taxonomies-admin.js', array('jquery','media-upload','thickbox','farbtastic','wp-color-picker') );
71
+ }
72
+
73
+ public static function form_edit($tag){
74
+ $EM_Taxonomy = new self::$tax_class($tag);
75
+ $taxonomy_color = $EM_Taxonomy->get_color();
76
+ $taxonomy_image = $EM_Taxonomy->get_image_url();
77
+ $taxonomy_image_id = $EM_Taxonomy->get_image_id();
78
+ ?>
79
+ <tr class="form-field term-color-wrap">
80
+ <th scope="row" valign="top"><label for="term-color"><?php esc_html_e('Color','events-manager'); ?></label></th>
81
+ <td>
82
+ <input type="text" name="term_color" id="term-color" class="term-color" value="<?php echo esc_attr($taxonomy_color); ?>" /><br />
83
+ <p class="description"><?php echo sprintf(__('Choose a color for your %s. You can access this using the %s placeholder.','events-manager'), __(self::$name_singular, 'events-manager'), '<code>'. self::$placeholder_color. '</code>'); ?></p>
84
+ <div id="picker" style="position:absolute; display:none; background:#DEDEDE"></div>
85
+ </td>
86
+ </tr>
87
+ <tr class="form-field term-image-wrap">
88
+ <th scope="row" valign="top"><label for="term-image"><?php esc_html_e('Image','events-manager'); ?></label></th>
89
+ <td>
90
+ <div class="img-container">
91
+ <?php if( !empty($taxonomy_image) ): ?>
92
+ <img src="<?php echo $taxonomy_image; ?>" />
93
+ <?php endif; ?>
94
+ </div>
95
+ <input type="text" name="term_image" id="term-image" class="img-url" value="<?php echo esc_attr($taxonomy_image); ?>" />
96
+ <input type="hidden" name="term_image_id" id="term-image-id" class="img-id" value="<?php echo esc_attr($taxonomy_image_id); ?>" />
97
+ <p class="hide-if-no-js">
98
+ <input id="upload_image_button" type="button" value="<?php _e('Choose/Upload Image','events-manager'); ?>" class="upload-img-button button-secondary" />
99
+ <input id="delete_image_button" type="button" value="<?php _e('Remove Image','events-manager'); ?>" class="delete-img-button button-secondary" <?php if( empty($taxonomy_image) ) echo 'style="display:none;"'; ?> />
100
+ </p>
101
+ <br />
102
+ <p class="description"><?php echo sprintf(__('Choose an image for your %s, which can be displayed using the %s placeholder.','events-manager'), __(self::$name_singular,'events-manager'),'<code>'. self::$placeholder_image. '</code>'); ?></p>
103
+ </td>
104
+ </tr>
105
+ <?php
106
+ }
107
+
108
+ public static function form_add(){
109
+ ?>
110
+ <div class="term-color-wrap">
111
+ <label for="term-color"><?php esc_html_e('Color','events-manager'); ?></label>
112
+ <input type="text" name="term_color" id="term-color" class="term-color" value="#FFFFFF" /><br />
113
+ <p class="description"><?php echo sprintf(__('Choose a color for your %s. You can access this using the %s placeholder.','events-manager'), __(self::$name_singular,'events-manager'),'<code>'. self::$placeholder_color. '</code>'); ?></p>
114
+ </div>
115
+ <div class="form-field term-image-wrap">
116
+ <label for="term-image"><?php esc_html_e('Image','events-manager'); ?></label>
117
+ <div class="img-container"></div>
118
+ <input type="text" name="term_image" id="term-image" class="img-url" value="" />
119
+ <input type="hidden" name="term_image_id" id="term-image-id" class="img-id" value="" />
120
+ <p class="hide-if-no-js">
121
+ <input id="upload_image_button" type="button" value="<?php _e('Choose/Upload Image','events-manager'); ?>" class="upload-img-button button-secondary" />
122
+ <input id="delete_image_button" type="button" value="<?php _e('Remove Image','events-manager'); ?>" class="delete-img-button button-secondary" style="display:none;" />
123
+ </p>
124
+ <p class="description"><?php echo sprintf(__('Choose an image for your %s, which can be displayed using the %s placeholder.','events-manager'), __(self::$name_singular,'events-manager'),'<code>'. self::$placeholder_image. '</code>'); ?></p>
125
+ </div>
126
+ <?php
127
+ }
128
+
129
+ public static function save( $term_id, $tt_id ){
130
+ global $wpdb;
131
+ if (!$term_id) return;
132
+ if( !empty($_POST['term_color']) && preg_match('/^#[a-zA-Z0-9]{6}$/', $_POST['term_color']) ){
133
+ //get results and save/update
134
+ $prev_settings = $wpdb->get_results('SELECT meta_value FROM '.EM_META_TABLE." WHERE object_id='{$term_id}' AND meta_key='". self::$option_name ."-bgcolor'");
135
+ if( count($prev_settings) > 0 ){
136
+ $wpdb->update(EM_META_TABLE, array('object_id' => $term_id, 'meta_value' => $_POST['term_color']), array('object_id' => $term_id, 'meta_key' => self::$option_name .'-bgcolor'));
137
+ }else{
138
+ $wpdb->insert(EM_META_TABLE, array('object_id' => $term_id, 'meta_key' => self::$option_name .'-bgcolor', 'meta_value' => $_POST['term_color']));
139
+ }
140
+ }
141
+ if( !empty($_POST['term_image']) ){
142
+ //get results and save/update
143
+ $prev_settings = $wpdb->get_results('SELECT meta_value FROM '.EM_META_TABLE." WHERE object_id='{$term_id}' AND meta_key='". self::$option_name ."-image'");
144
+ if( count($prev_settings) > 0 ){
145
+ $wpdb->update(EM_META_TABLE, array('object_id' => $term_id, 'meta_value' => $_POST['term_image']), array('object_id' => $term_id, 'meta_key' => self::$option_name .'-image'));
146
+ }else{
147
+ $wpdb->insert(EM_META_TABLE, array('object_id' => $term_id, 'meta_key' => self::$option_name .'-image', 'meta_value' => $_POST['term_image']));
148
+ }
149
+ if( !empty($_POST['term_image_id']) && is_numeric($_POST['term_image_id']) ){
150
+ //get results and save/update
151
+ $prev_settings = $wpdb->get_results('SELECT meta_value FROM '.EM_META_TABLE." WHERE object_id='{$term_id}' AND meta_key='". self::$option_name ."-image-id'");
152
+ if( count($prev_settings) > 0 ){
153
+ $wpdb->update(EM_META_TABLE, array('object_id' => $term_id, 'meta_value' => $_POST['term_image_id']), array('object_id' => $term_id, 'meta_key'=> self::$option_name .'-image-id'));
154
+ }else{
155
+ $wpdb->insert(EM_META_TABLE, array('object_id' => $term_id, 'meta_key'=> self::$option_name .'-image-id', 'meta_value' => $_POST['term_image_id']));
156
+ }
157
+ }
158
+ }else{
159
+ //check if an image exists, if so remove association
160
+ $prev_settings = $wpdb->get_results('SELECT meta_value FROM '.EM_META_TABLE." WHERE object_id='{$term_id}' AND meta_key='". self::$option_name ."-image'");
161
+ if( count($prev_settings) > 0 ){
162
+ $wpdb->delete(EM_META_TABLE, array('object_id' => $term_id, 'meta_key' => self::$option_name .'-image'));
163
+ $wpdb->delete(EM_META_TABLE, array('object_id' => $term_id, 'meta_key' => self::$option_name .'-image-id'));
164
+ }
165
+ }
166
+ }
167
+
168
+ public static function delete( $term_id ){
169
+ global $wpdb;
170
+ //delete taxonomy image and color
171
+ $wpdb->query('DELETE FROM '.EM_META_TABLE." WHERE object_id='$term_id' AND (meta_key='". self::$option_name ."-image' OR meta_key='". self::$option_name ."-image-id' OR meta_key='". self::$option_name ."-bgcolor')");
172
+ //delete all events taxonomy relations for MultiSite Global Mode
173
+ if( EM_MS_GLOBAL ){
174
+ $wpdb->query('DELETE FROM '.EM_META_TABLE." WHERE meta_value='{$term_id}' AND meta_key='event-". self::$option_name ."'");
175
+ }
176
+ }
177
+ }
classes/em-taxonomy-frontend.php ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class EM_Taxonomy_Frontend {
3
+
4
+ /**
5
+ * The name of this taxonomy, e.g. event-categories, which is defined in child class.
6
+ * @var string
7
+ */
8
+ public static $taxonomy_name = 'EM_TAXONOMY_NAME';
9
+ /**
10
+ * The name of the child class, used for now whilst late static binding isn't guaranteed since we may be running on PHP <5.3
11
+ * Once PHP 5.3 is a minimum requirement in WP, we can get rid of this one.
12
+ * @var string
13
+ */
14
+ public static $this_class = 'EM_Taxonomy_Frontend';
15
+ /**
16
+ * Currently used to instantiate a class of the specific term. Eventually we could just use EM_Taxonomy since these will be standardized functions for any taxonomy.
17
+ * @var string
18
+ */
19
+ public static $tax_class = 'EM_Taxonomy';
20
+ /**
21
+ * Name of taxonomy for reference in saving to database, e.g. category will be used to save category-image.
22
+ * This may differ from the name of the taxonomy, such as event-category can be category
23
+ * @var string
24
+ */
25
+ public static $option_name = 'taxonomy';
26
+ public static $option_name_plural = 'taxonomies';
27
+
28
+ public static function init(){
29
+ if( !is_admin() ){
30
+ add_filter('taxonomy_template', array(self::$this_class,'template'), 99);
31
+ add_filter('parse_query', array(self::$this_class,'parse_query'));
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Overrides archive pages e.g. locations, events, event categories, event tags based on user settings
37
+ * @param string $template
38
+ * @return string
39
+ */
40
+ public static function template($template = ''){
41
+ global $wp_query, $wp_the_query, $em_the_query, $post;
42
+ if( is_tax(self::$taxonomy_name) && !locate_template('taxonomy-'.self::$taxonomy_name.'.php') && get_option('dbem_cp_'. self::$option_name_plural .'_formats', true) ){
43
+ $em_the_query = $wp_the_query; //use this for situations where other plugins need to access 'original' query data, which you can switch back/forth.
44
+ $EM_Taxonomy = $GLOBALS[self::$tax_class] = EM_Taxonomy_Term::get($wp_query->queried_object->term_id, self::$tax_class);
45
+ if( self::get_page_id() ){
46
+ //less chance for things to go wrong with themes etc. so just reset the WP_Query to think it's a page rather than taxonomy
47
+ $wp_query = new WP_Query(array('page_id'=> self::get_page_id()));
48
+ $wp_query->queried_object = $wp_query->post;
49
+ $wp_query->queried_object_id = $wp_query->post->ID;
50
+ $wp_query->post->post_title = $wp_query->posts[0]->post_title = $wp_query->queried_object->post_title = $EM_Taxonomy->output(get_option('dbem_'. self::$option_name .'_page_title_format'));
51
+ if( !function_exists('yoast_breadcrumb') ){ //not needed by WP SEO Breadcrumbs, we deal with it in a filter further down - wpseo_breadcrumb_links
52
+ $wp_query->post->post_parent = $wp_query->posts[0]->post_parent = $wp_query->queried_object->post_parent = self::get_page_id();
53
+ }
54
+ $post = $wp_query->post;
55
+ $wp_the_query = $wp_query; //we won't do this to the else section because we should deprecate it due to its instability
56
+ }else{
57
+ //we don't have a categories page, so we create a fake page
58
+ $wp_query->posts = array();
59
+ $wp_query->posts[0] = new stdClass();
60
+ $wp_query->posts[0]->post_title = $wp_query->queried_object->post_title = $EM_Taxonomy->output(get_option('dbem_'. self::$option_name .'_page_title_format'));
61
+ $post_array = array('ID', 'post_author', 'post_date','post_date_gmt','post_content','post_excerpt','post_status','comment_status','ping_status','post_password','post_name','to_ping','pinged','post_modified','post_modified_gmt','post_content_filtered','post_parent','guid','menu_order','post_type','post_mime_type','comment_count','filter');
62
+ foreach($post_array as $post_array_item){
63
+ $wp_query->posts[0]->$post_array_item = '';
64
+ }
65
+ $wp_query->post = $wp_query->posts[0];
66
+ $wp_query->post_count = 1;
67
+ $wp_query->found_posts = 1;
68
+ $wp_query->max_num_pages = 1;
69
+ //tweak flags for determining page type
70
+ $wp_query->is_tax = 0;
71
+ $wp_query->is_page = 1;
72
+ $wp_query->is_single = 0;
73
+ $wp_query->is_singular = 1;
74
+ $wp_query->is_archive = 0;
75
+ }
76
+ //set taxonomy id to globals and query object
77
+ $em_taxonomy_property = 'em_'. self::$option_name .'_id';
78
+ $wp_query->{$em_taxonomy_property} = $wp_the_query->{$em_taxonomy_property} = $GLOBALS[$em_taxonomy_property] = $EM_Taxonomy->term_id; //we assign global taxononmy id just in case other themes/plugins do something out of the ordinary to WP_Query
79
+ //set the template
80
+ $template = locate_template(array('page.php','index.php'),false); //category becomes a page
81
+ //sort out filters
82
+ remove_filter('the_content', 'em_content'); //one less filter
83
+ add_filter('the_content', array(self::$this_class,'the_content')); //come in slightly early and consider other plugins
84
+ //Yoast WP SEO Tweals
85
+ if( defined('WPSEO_VERSION') ){
86
+ add_filter('wpseo_breadcrumb_links',array(self::$this_class,'wpseo_breadcrumb_links'));
87
+ add_filter('wpseo_head', array(self::$this_class,'flip_the_query'), 1);
88
+ add_filter('wpseo_head', array(self::$this_class,'flip_the_query'), 1000000);
89
+ }
90
+ do_action('em_'. self::$option_name .'_taxonomy_template');
91
+ }
92
+ return $template;
93
+ }
94
+
95
+ public static function the_content($content){
96
+ global $wp_query, $post;
97
+ $em_taxonomy_property = 'em_'. self::$option_name .'_id'; //could be em_category_name or em_tag_name
98
+ $is_taxonomy_page = $post->ID == self::get_page_id();
99
+ if( !empty($GLOBALS[$em_taxonomy_property]) ) $taxonomy_id = $GLOBALS[$em_taxonomy_property];
100
+ if( !empty($wp_query->{$em_taxonomy_property}) ) $taxonomy_id = $wp_query->{$em_taxonomy_property};
101
+ $taxonomy_flag = (!empty($wp_query->{$em_taxonomy_property}) || !empty($GLOBALS[$em_taxonomy_property]));
102
+ if( ($is_taxonomy_page && !empty($taxonomy_id)) || (empty($post->ID) && !empty($taxonomy_id)) ){
103
+ $GLOBALS[self::$tax_class] = EM_Taxonomy_Term::get($taxonomy_id, self::$tax_class);
104
+ ob_start();
105
+ em_locate_template('templates/'.self::$option_name.'-single.php',true);
106
+ return ob_get_clean();
107
+ }
108
+ return $content;
109
+ }
110
+
111
+ /**
112
+ * Parses the query on regular taxonomy archives so events are cronologically ordered.
113
+ * @param WP_Query $wp_query
114
+ */
115
+ public static function parse_query( $wp_query ){
116
+ global $post;
117
+ if( !$wp_query->is_main_query() ) return;
118
+ if( $wp_query->is_tax(self::$taxonomy_name) ){
119
+ //Scope is future
120
+ $today = strtotime(date('Y-m-d', current_time('timestamp')));
121
+ if( get_option('dbem_events_current_are_past') ){
122
+ $wp_query->query_vars['meta_query'][] = array( 'key' => '_start_ts', 'value' => $today, 'compare' => '>=' );
123
+ }else{
124
+ $wp_query->query_vars['meta_query'][] = array( 'key' => '_end_ts', 'value' => $today, 'compare' => '>=' );
125
+ }
126
+ if( get_option('dbem_'. self::$option_name_plural .'_default_archive_orderby') == 'title'){
127
+ $wp_query->query_vars['orderby'] = 'title';
128
+ }else{
129
+ $wp_query->query_vars['orderby'] = 'meta_value_num';
130
+ $wp_query->query_vars['meta_key'] = get_option('dbem_'. self::$option_name_plural .'_default_archive_orderby','_start_ts');
131
+ }
132
+ $wp_query->query_vars['order'] = get_option('dbem_'. self::$option_name_plural .'_default_archive_order','ASC');
133
+ $post_types = $wp_query->get( 'post_type');
134
+ $post_types = is_array($post_types) ? $post_types + array(EM_POST_TYPE_EVENT) : EM_POST_TYPE_EVENT;
135
+ if( !get_option('dbem_cp_events_search_results') ) $wp_query->set( 'post_type', $post_types ); //in case events aren't publicly searchable due to 'bug' in WP - https://core.trac.wordpress.org/ticket/17592
136
+ }elseif( !empty($wp_query->{'em_'.self::$option_name.'_id'}) ){
137
+ $post = $wp_query->post;
138
+ }
139
+ }
140
+
141
+ public static function get_page_id(){
142
+ return get_option('dbem_'.self::$option_name_plural.'_page');
143
+ }
144
+
145
+ public static function wpseo_breadcrumb_links( $links ){
146
+ global $wp_query;
147
+ array_pop($links);
148
+ if( self::get_page_id() ){
149
+ $links[] = array('id'=> self::get_page_id());
150
+ }
151
+ $links[] = array('text'=> $wp_query->posts[0]->post_title);
152
+ return $links;
153
+ }
154
+
155
+ /**
156
+ * Switches the query back/forth from the original query if EM has interferred to add formatting for taxonomy pages.
157
+ * Useful if you want plugins to temporarily access the old WP_Query which indicated we were looking at a taxonomy.
158
+ * For example, with WordPress SEO by Yoast, for wpseo_head we can switch at priority 1 and switch back at a really low priority so meta data is correctly generated.
159
+ * @param string $template
160
+ * @return string
161
+ */
162
+ public static function flip_the_query(){
163
+ global $wp_query, $wp_the_query, $em_the_query;
164
+ if( !empty($em_the_query) ){
165
+ $old_query = $wp_the_query;
166
+ $wp_query = $wp_the_query = $em_the_query;
167
+ $em_the_query = $old_query;
168
+ }
169
+ }
170
+ }
classes/em-taxonomy-term.php ADDED
@@ -0,0 +1,352 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Parent class for single taxonomy objects, which are essentially WP_Term objects.
4
+ * @since 5.7.3.2
5
+ */
6
+ class EM_Taxonomy_Term extends EM_Object {
7
+
8
+ //Class-overridable options
9
+ public $option_ms_global = false;
10
+ public $option_name = 'taxonomy';
11
+
12
+ //Taxonomy Fields
13
+ var $id = '';
14
+ var $term_id;
15
+ var $name;
16
+ var $slug;
17
+ var $term_group;
18
+ var $term_taxonomy_id;
19
+ var $taxonomy; //initially used to supply string representing constant containing name of the specific taxonomy type
20
+ var $description = '';
21
+ var $parent = 0;
22
+ var $count;
23
+ //extra attributes imposed by EM Taxonomies
24
+ var $image_url = '';
25
+ var $color;
26
+
27
+ /**
28
+ * Gets data from POST (default), supplied array, or from the database if an ID is supplied
29
+ * @param $taxonomy
30
+ */
31
+ public function __construct( $taxonomy_data = false ){
32
+ if( $this->option_ms_global ) self::ms_global_switch();
33
+ //Initialize
34
+ $this->taxonomy = constant($this->taxonomy);
35
+ $taxonomy = array();
36
+ if( !empty($taxonomy_data) ){
37
+ //Load taxonomy data
38
+ if( is_object($taxonomy_data) && !empty($taxonomy_data->taxonomy) && $taxonomy_data->taxonomy == $this->taxonomy ){
39
+ $taxonomy = $taxonomy_data;
40
+ }elseif( !is_numeric($taxonomy_data) ){
41
+ $taxonomy = get_term_by('slug', $taxonomy_data, $this->taxonomy);
42
+ if( !$taxonomy ){
43
+ $taxonomy = get_term_by('name', $taxonomy_data, $this->taxonomy);
44
+ }
45
+ }else{
46
+ $taxonomy = get_term_by('id', $taxonomy_data, $this->taxonomy);
47
+ }
48
+ }
49
+ if( is_object($taxonomy) || is_array($taxonomy) ){
50
+ foreach($taxonomy as $key => $value){
51
+ $this->$key = $value;
52
+ }
53
+ }
54
+ $this->id = $this->term_id; //backward compatability
55
+ if( $this->option_ms_global ) self::ms_global_switch_back();
56
+ do_action('em_'.$this->option_name, $this, $taxonomy_data);
57
+ }
58
+
59
+ /**
60
+ * Returns a single EM_Taxonomy_Term child class instance based on supplied ID or slug and taxonony class name, such as EM_Tag or EM_Category.
61
+ *
62
+ * Shortcut using __construct since it checks globals and any other caches before executing a __construct call.
63
+ *
64
+ * @param int|string $id ID or slug. Name is not used for globals check since mixups between terms with same name/slug values.
65
+ * @param string $taxonomy_class The name of the EM class used for this taxonomy.
66
+ * @return EM_Taxonomy
67
+ */
68
+ public static function get( $id, $taxonomy_class ){
69
+ //check if it's not already global so we don't instantiate again
70
+ $EM_Taxonomy = !empty( $GLOBALS[$taxonomy_class] ) ? $GLOBALS[$taxonomy_class] : '';
71
+ if( is_object($EM_Taxonomy) && get_class($EM_Taxonomy) == $taxonomy_class ){
72
+ if( $EM_Taxonomy->term_id == $id || $EM_Taxonomy->slug == $id ){
73
+ return $EM_Taxonomy;
74
+ }elseif( is_object($id) && $EM_Taxonomy->term_id == $id->term_id ){
75
+ return $EM_Taxonomy;
76
+ }
77
+ }
78
+ if( is_object($id) && get_class($id) == '$taxonomy_class' ){
79
+ return $id;
80
+ }else{
81
+ return new $taxonomy_class($id);
82
+ }
83
+ }
84
+
85
+ public function get_url(){
86
+ if( empty($this->link) ){
87
+ self::ms_global_switch();
88
+ $this->link = get_term_link($this->slug, $this->taxonomy);
89
+ self::ms_global_switch_back();
90
+ if ( is_wp_error($this->link) ) $this->link = '';
91
+ }
92
+ return apply_filters('em_'. $this->option_name .'_get_url', $this->link);
93
+ }
94
+
95
+ public function get_ical_url(){
96
+ global $wp_rewrite;
97
+ if( !empty($wp_rewrite) && $wp_rewrite->using_permalinks() ){
98
+ $return = trailingslashit($this->get_url()).'ical/';
99
+ }else{
100
+ $return = em_add_get_params($this->get_url(), array('ical'=>1));
101
+ }
102
+ return apply_filters('em_'. $this->option_name .'_get_ical_url', $return);
103
+ }
104
+
105
+ public function get_rss_url(){
106
+ global $wp_rewrite;
107
+ if( !empty($wp_rewrite) && $wp_rewrite->using_permalinks() ){
108
+ $return = trailingslashit($this->get_url()).'feed/';
109
+ }else{
110
+ $return = em_add_get_params($this->get_url(), array('feed'=>1));
111
+ }
112
+ return apply_filters('em_'. $this->option_name .'_get_rss_url', $return);
113
+ }
114
+
115
+ public function get_color(){
116
+ if( empty($this->color) ){
117
+ $color = wp_cache_get($this->term_id, 'em_'.$this->option_name.'_colors');
118
+ if( $color ){
119
+ $this->color = $color;
120
+ }else{
121
+ global $wpdb;
122
+ $color = $wpdb->get_var('SELECT meta_value FROM '.EM_META_TABLE." WHERE object_id='{$this->term_id}' AND meta_key='". $this->option_name ."-bgcolor' LIMIT 1");
123
+ $this->color = ($color != '') ? $color:get_option('dbem_'.$this->option_name.'_default_color', '#FFFFFF');
124
+ wp_cache_set($this->term_id, $this->color, 'em_'.$this->option_name.'_colors');
125
+ }
126
+ }
127
+ return $this->color;
128
+ }
129
+
130
+ public function get_image_url( $size = 'full' ){
131
+ if( empty($this->image_url) ){
132
+ global $wpdb;
133
+ $image_url = $wpdb->get_var('SELECT meta_value FROM '.EM_META_TABLE." WHERE object_id='{$this->term_id}' AND meta_key='". $this->option_name ."-image' LIMIT 1");
134
+ $this->image_url = ($image_url != '') ? $image_url:'';
135
+ }
136
+ return $this->image_url;
137
+ }
138
+
139
+ public function get_image_id(){
140
+ if( empty($this->image_id) ){
141
+ global $wpdb;
142
+ $image_id = $wpdb->get_var('SELECT meta_value FROM '.EM_META_TABLE." WHERE object_id='{$this->term_id}' AND meta_key='". $this->option_name ."-image-id' LIMIT 1");
143
+ $this->image_id = ($image_id != '') ? $image_id:'';
144
+ }
145
+ return $this->image_id;
146
+ }
147
+
148
+ public function output_single($target = 'html'){
149
+ $format = get_option ( 'dbem_'. $this->option_name .'_page_format' );
150
+ return apply_filters('em_'. $this->option_name .'_output_single', $this->output($format, $target), $this, $target);
151
+ }
152
+
153
+ public function output($format, $target="html") {
154
+ preg_match_all('/\{([a-zA-Z0-9_]+)\}([^{]+)\{\/[a-zA-Z0-9_]+\}/', $format, $conditionals);
155
+ if( count($conditionals[0]) > 0 ){
156
+ //Check if the language we want exists, if not we take the first language there
157
+ foreach($conditionals[1] as $key => $condition){
158
+ $format = str_replace($conditionals[0][$key], apply_filters('em_'. $this->option_name .'_output_condition', '', $condition, $conditionals[0][$key], $this), $format);
159
+ }
160
+ }
161
+ $taxonomy_string = $format;
162
+ preg_match_all("/(#@?_?[A-Za-z0-9]+)({([a-zA-Z0-9,]+)})?/", $format, $placeholders);
163
+ $replaces = array();
164
+ $ph = strtoupper($this->option_name);
165
+ foreach($placeholders[1] as $key => $result) {
166
+ $replace = '';
167
+ $full_result = $placeholders[0][$key];
168
+ switch( $result ){
169
+ case '#_'. $ph .'NAME':
170
+ $replace = $this->name;
171
+ break;
172
+ case '#_'. $ph .'ID':
173
+ $replace = $this->term_id;
174
+ break;
175
+ case '#_'. $ph .'NOTES':
176
+ case '#_'. $ph .'DESCRIPTION':
177
+ $replace = $this->description;
178
+ break;
179
+ case '#_'. $ph .'IMAGEURL':
180
+ $replace = esc_url($this->get_image_url());
181
+ break;
182
+ case '#_'. $ph .'IMAGE':
183
+ if( $this->get_image_url() != ''){
184
+ $image_url = esc_url($this->get_image_url());
185
+ if( empty($placeholders[3][$key]) ){
186
+ $replace = "<img src='".esc_url($this->get_image_url())."' alt='".esc_attr($this->name)."'/>";
187
+ }else{
188
+ $image_size = explode(',', $placeholders[3][$key]);
189
+ if( self::array_is_numeric($image_size) && count($image_size) > 1 ){
190
+ if( $this->get_image_id() ){
191
+ //get a thumbnail
192
+ if( get_option('dbem_disable_thumbnails') ){
193
+ $image_attr = '';
194
+ $image_args = array();
195
+ if( empty($image_size[1]) && !empty($image_size[0]) ){
196
+ $image_attr = 'width="'.$image_size[0].'"';
197
+ $image_args['w'] = $image_size[0];
198
+ }elseif( empty($image_size[0]) && !empty($image_size[1]) ){
199
+ $image_attr = 'height="'.$image_size[1].'"';
200
+ $image_args['h'] = $image_size[1];
201
+ }elseif( !empty($image_size[0]) && !empty($image_size[1]) ){
202
+ $image_attr = 'width="'.$image_size[0].'" height="'.$image_size[1].'"';
203
+ $image_args = array('w'=>$image_size[0], 'h'=>$image_size[1]);
204
+ }
205
+ $replace = "<img src='".esc_url(em_add_get_params($image_url, $image_args))."' alt='".esc_attr($this->name)."' $image_attr />";
206
+ }else{
207
+ //since we previously didn't store image ids along with the url to the image (since taxonomies don't allow normal featured images), sometimes we won't be able to do this, which is why we check there's a valid image id first
208
+ if( $this->option_ms_global ) self::ms_global_switch();
209
+ $replace = wp_get_attachment_image($this->get_image_id(), $image_size);
210
+ if( $this->option_ms_global ) self::ms_global_switch_back();
211
+ }
212
+ }
213
+ }else{
214
+ $replace = "<img src='".esc_url($this->get_image_url())."' alt='".esc_attr($this->name)."'/>";
215
+ }
216
+ }
217
+ }
218
+ break;
219
+ case '#_'. $ph .'COLOR':
220
+ $replace = $this->get_color();
221
+ break;
222
+ case '#_'. $ph .'LINK':
223
+ case '#_'. $ph .'URL':
224
+ $link = $this->get_url();
225
+ $replace = ($result == '#_'. $ph .'URL') ? $link : '<a href="'.$link.'">'.esc_html($this->name).'</a>';
226
+ break;
227
+ case '#_'. $ph .'ICALURL':
228
+ case '#_'. $ph .'ICALLINK':
229
+ $replace = $this->get_ical_url();
230
+ if( $result == '#_'. $ph .'ICALLINK' ){
231
+ $replace = '<a href="'.esc_url($replace).'">iCal</a>';
232
+ }
233
+ break;
234
+ case '#_'. $ph .'WEBCALURL':
235
+ case '#_'. $ph .'WEBCALLINK':
236
+ $replace = $this->get_ical_url();
237
+ $replace = str_replace(array('http://','https://'), 'webcal://', $replace);
238
+ if( $result == '#_'. $ph .'WEBCALLINK' ){
239
+ $replace = '<a href="'.esc_url($replace).'">Webcal</a>';
240
+ }
241
+ break;
242
+ case '#_'. $ph .'RSSURL':
243
+ case '#_'. $ph .'RSSLINK':
244
+ $replace = $this->get_rss_url();
245
+ if( $result == '#_'. $ph .'RSSLINK' ){
246
+ $replace = '<a href="'.esc_url($replace).'">RSS</a>';
247
+ }
248
+ break;
249
+ case '#_'. $ph .'SLUG':
250
+ $replace = $this->slug;
251
+ break;
252
+ case '#_'. $ph .'EVENTSPAST': //deprecated, erroneous documentation, left for compatability
253
+ case '#_'. $ph .'EVENTSNEXT': //deprecated, erroneous documentation, left for compatability
254
+ case '#_'. $ph .'EVENTSALL': //deprecated, erroneous documentation, left for compatability
255
+ case '#_'. $ph .'PASTEVENTS':
256
+ case '#_'. $ph .'NEXTEVENTS':
257
+ case '#_'. $ph .'ALLEVENTS':
258
+ //convert deprecated placeholders for compatability
259
+ $result = ($result == '#_'. $ph .'EVENTSPAST') ? '#_'. $ph .'PASTEVENTS':$result;
260
+ $result = ($result == '#_'. $ph .'EVENTSNEXT') ? '#_'. $ph .'NEXTEVENTS':$result;
261
+ $result = ($result == '#_'. $ph .'EVENTSALL') ? '#_'. $ph .'ALLEVENTS':$result;
262
+ //forget it ever happened? :/
263
+ if ($result == '#_'. $ph .'PASTEVENTS'){ $scope = 'past'; }
264
+ elseif ( $result == '#_'. $ph .'NEXTEVENTS' ){ $scope = 'future'; }
265
+ else{ $scope = 'all'; }
266
+ $args = array($this->option_name=>$this->term_id, 'scope'=>$scope, 'pagination'=>1, 'ajax'=>0);
267
+ $args['format_header'] = get_option('dbem_'. $this->option_name .'_event_list_item_header_format');
268
+ $args['format_footer'] = get_option('dbem_'. $this->option_name .'_event_list_item_footer_format');
269
+ $args['format'] = get_option('dbem_'. $this->option_name .'_event_list_item_format');
270
+ $args['no_results_msg'] = get_option('dbem_'. $this->option_name .'_no_events_message');
271
+ $args['limit'] = get_option('dbem_'. $this->option_name .'_event_list_limit');
272
+ $args['orderby'] = get_option('dbem_'. $this->option_name .'_event_list_orderby');
273
+ $args['order'] = get_option('dbem_'. $this->option_name .'_event_list_order');
274
+ $args['page'] = (!empty($_REQUEST['pno']) && is_numeric($_REQUEST['pno']) )? $_REQUEST['pno'] : 1;
275
+ $replace = EM_Events::output($args);
276
+ break;
277
+ case '#_'. $ph .'NEXTEVENT':
278
+ $events = EM_Events::get( array($this->option_name=>$this->term_id, 'scope'=>'future', 'limit'=>1, 'orderby'=>'event_start_date,event_start_time') );
279
+ $replace = get_option('dbem_'. $this->option_name .'_no_event_message');
280
+ foreach($events as $EM_Event){
281
+ $replace = $EM_Event->output(get_option('dbem_'. $this->option_name .'_event_single_format'));
282
+ }
283
+ break;
284
+ default:
285
+ $replace = $full_result;
286
+ break;
287
+ }
288
+ $replaces[$full_result] = apply_filters('em_'. $this->option_name .'_output_placeholder', $replace, $this, $full_result, $target);
289
+ }
290
+ krsort($replaces);
291
+ foreach($replaces as $full_result => $replacement){
292
+ $taxonomy_string = str_replace($full_result, $replacement , $taxonomy_string );
293
+ }
294
+ return apply_filters('em_'. $this->option_name .'_output', $taxonomy_string, $this, $format, $target);
295
+ }
296
+
297
+ public function placeholder_image( $replace, $placeholders, $key ){
298
+ if( $this->get_image_url() != ''){
299
+ $image_url = esc_url($this->get_image_url());
300
+ if( empty($placeholders[3][$key]) ){
301
+ $replace = "<img src='".esc_url($this->get_image_url())."' alt='".esc_attr($this->name)."'/>";
302
+ }else{
303
+ $image_size = explode(',', $placeholders[3][$key]);
304
+ if( self::array_is_numeric($image_size) && count($image_size) > 1 ){
305
+ if( $this->get_image_id() ){
306
+ //get a thumbnail
307
+ if( get_option('dbem_disable_thumbnails') ){
308
+ $image_attr = '';
309
+ $image_args = array();
310
+ if( empty($image_size[1]) && !empty($image_size[0]) ){
311
+ $image_attr = 'width="'.$image_size[0].'"';
312
+ $image_args['w'] = $image_size[0];
313
+ }elseif( empty($image_size[0]) && !empty($image_size[1]) ){
314
+ $image_attr = 'height="'.$image_size[1].'"';
315
+ $image_args['h'] = $image_size[1];
316
+ }elseif( !empty($image_size[0]) && !empty($image_size[1]) ){
317
+ $image_attr = 'width="'.$image_size[0].'" height="'.$image_size[1].'"';
318
+ $image_args = array('w'=>$image_size[0], 'h'=>$image_size[1]);
319
+ }
320
+ $replace = "<img src='".esc_url(em_add_get_params($image_url, $image_args))."' alt='".esc_attr($this->name)."' $image_attr />";
321
+ }else{
322
+ //since we previously didn't store image ids along with the url to the image (since taxonomies don't allow normal featured images), sometimes we won't be able to do this, which is why we check there's a valid image id first
323
+ if( $this->option_ms_global ) self::ms_global_switch();
324
+ $replace = wp_get_attachment_image($this->get_image_id(), $image_size);
325
+ if( $this->option_ms_global ) self::ms_global_switch_back();
326
+ }
327
+ }
328
+ }else{
329
+ $replace = "<img src='".esc_url($this->get_image_url())."' alt='".esc_attr($this->name)."'/>";
330
+ }
331
+ }
332
+ }
333
+ return $replace;
334
+ }
335
+
336
+ public function can_manage( $capability_owner = 'edit_event_taxonomy', $capability_admin = false, $user_to_check = false ){
337
+ global $em_capabilities_array;
338
+ //Figure out if this is multisite and require an extra bit of validation
339
+ $multisite_check = true;
340
+ $can_manage = current_user_can($capability_owner);
341
+ //if multisite and supoer admin, just return true
342
+ if( is_multisite() && em_wp_is_super_admin() ){ return true; }
343
+ if( EM_MS_GLOBAL && !is_main_site() ){
344
+ //User can't admin this bit, as they're on a sub-blog
345
+ $can_manage = false;
346
+ if(array_key_exists($capability_owner, $em_capabilities_array) ){
347
+ $this->add_error( $em_capabilities_array[$capability_owner]);
348
+ }
349
+ }
350
+ return $can_manage;
351
+ }
352
+ }
classes/em-taxonomy-terms.php ADDED
@@ -0,0 +1,323 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class EM_Taxonomy_Terms extends EM_Object implements Iterator{
3
+
4
+ protected $is_ms_global = false;
5
+ protected $meta_key = 'event-taxonomy';
6
+ protected $taxonomy = 'event-taxonomy';
7
+ protected $terms_name = 'taxonomies';
8
+ protected $term_class = 'EM_Taxonomy';
9
+
10
+ /**
11
+ * Blank instance of this class used in the static functions until PHP 5.3 brings us LSB
12
+ * @var EM_Taxonomy_Terms
13
+ */
14
+ protected static $instance;
15
+
16
+ /**
17
+ * Array of EM_Taxonomy_Term child objects for a specific event
18
+ * @var array
19
+ */
20
+ var $terms = array();
21
+ /**
22
+ * Event ID of this set of taxonomy terms
23
+ * @var int
24
+ */
25
+ var $event_id;
26
+ /**
27
+ * Post ID of this set of taxonomy terms
28
+ * @var int
29
+ */
30
+ var $post_id;
31
+
32
+ /**
33
+ * Creates an EM_Taxonomy_Terms instance, currently accepts an EM_Event object (gets all Taxonomy Terms for that event) or array of any EM_Taxonomy_Term objects, which can be manipulated in bulk with helper functions.
34
+ * @param mixed $data
35
+ * @return null
36
+ */
37
+ public function __construct( $data = false ){
38
+ global $wpdb;
39
+ if( $this->is_ms_global ) self::ms_global_switch();
40
+ if( is_object($data) && get_class($data) == "EM_Event" && !empty($data->post_id) ){ //Creates a blank taxonomies object if needed
41
+ $this->event_id = $data->event_id;
42
+ $this->post_id = $data->post_id;
43
+ if( EM_MS_GLOBAL && $this->is_ms_global && !is_main_site($data->blog_id) ){
44
+ $term_ids = $wpdb->get_col('SELECT meta_value FROM '.EM_META_TABLE." WHERE object_id='{$this->event_id}' AND meta_key='{$this->meta_key}'");
45
+ foreach($term_ids as $term_id){
46
+ $this->terms[$term_id] = new $this->term_class($term_id);
47
+ }
48
+ }else{
49
+ if( EM_MS_GLOBAL && (get_current_blog_id() !== $data->blog_id || (!$data->blog_id && !is_main_site()) ) ){
50
+ if( !$this->blog_id ) $this->blog_id = get_current_site()->blog_id;
51
+ switch_to_blog($this->blog_id);
52
+ $results = get_the_terms( $data->post_id, $this->taxonomy );
53
+ restore_current_blog();
54
+ }else{
55
+ $results = get_the_terms( $data->post_id, $this->taxonomy );
56
+ }
57
+ if( is_array($results) ){
58
+ foreach($results as $result){
59
+ $this->terms[$result->term_id] = new $this->term_class($result);
60
+ }
61
+ }
62
+ }
63
+ }elseif( is_array($data) && self::array_is_numeric($data) ){
64
+ foreach($data as $term_id){
65
+ $this->terms[$term_id] = new $this->term_class($term_id);
66
+ }
67
+ }elseif( is_array($data) ){
68
+ foreach( $data as $EM_Taxonomy_Term ){
69
+ if( get_class($EM_Taxonomy_Term) == $this->term_class){
70
+ $this->terms[] = $EM_Taxonomy_Term;
71
+ }
72
+ }
73
+ }
74
+ if( $this->is_ms_global ) self::ms_global_switch_back();
75
+ do_action('em_'.$this->terms_name, $this);
76
+ }
77
+
78
+ public function get_post(){
79
+ self::ms_global_switch();
80
+ $this->terms = array();
81
+ if(!empty($_POST['event_'.$this->terms_name]) && self::array_is_numeric($_POST['event_'.$this->terms_name])){
82
+ foreach( $_POST['event_'.$this->terms_name] as $term ){
83
+ $this->terms[$term] = new $this->term_class($term);
84
+ }
85
+ }
86
+ self::ms_global_switch_back();
87
+ do_action('em_'. $this->terms_name .'_get_post', $this);
88
+ }
89
+
90
+ public function save(){
91
+ $term_slugs = array();
92
+ foreach($this->terms as $EM_Taxonomy_Term){
93
+ /* @var $EM_Taxonomy_Term EM_Taxonomy_Term */
94
+ if( !empty($EM_Taxonomy_Term->slug) ) $term_slugs[] = $EM_Taxonomy_Term->slug; //save of taxonomy will soft-fail if slug is empty
95
+ }
96
+ if( count($term_slugs) == 0 && get_option('dbem_default_'.$this->singular) > 0 ){
97
+ $default_term = get_term_by('id',get_option('dbem_default_'.$this->singular), $this->taxonomy);
98
+ if($default_term) $term_slugs[] = $default_term->slug;
99
+ }
100
+ if( is_multisite() && $this->is_ms_global ){
101
+ //In MS Global mode, we also save taxonomy meta information for global lookups
102
+ if( EM_MS_GLOBAL && !empty($this->event_id) ){
103
+ //delete terms from event
104
+ $this->save_index();
105
+ }
106
+ if( !EM_MS_GLOBAL || is_main_site() ){
107
+ wp_set_object_terms($this->post_id, $term_slugs, $this->taxonomy);
108
+ }
109
+ }else{
110
+ wp_set_object_terms($this->post_id, $term_slugs, $this->taxonomy);
111
+ }
112
+ do_action('em_'. $this->terms_name .'_save', $this);
113
+ }
114
+
115
+ public function save_index(){
116
+ global $wpdb;
117
+ $wpdb->query('DELETE FROM '.EM_META_TABLE." WHERE object_id='{$this->event_id}' AND meta_key='{$this->meta_key}'");
118
+ foreach($this->terms as $EM_Taxonomy_Term){
119
+ $wpdb->insert(EM_META_TABLE, array('meta_value'=>$EM_Taxonomy_Term->term_id,'object_id'=>$this->event_id,'meta_key'=>$this->meta_key));
120
+ }
121
+ }
122
+
123
+ public function has( $search ){
124
+ if( is_numeric($search) ){
125
+ foreach($this->terms as $EM_Taxonomy_Term){
126
+ if($EM_Taxonomy_Term->term_id == $search) return apply_filters('em_'. $this->terms_name .'_has', true, $search, $this);
127
+ }
128
+ }else{
129
+ foreach($this->terms as $EM_Taxonomy_Term){
130
+ if($EM_Taxonomy_Term->slug == $search || $EM_Taxonomy_Term->name == $search ) return apply_filters('em_'. $this->terms_name .'_has', true, $search, $this);
131
+ }
132
+ }
133
+ return apply_filters('em_'. $this->terms_name .'_has', false, $search, $this);
134
+ }
135
+
136
+ public function get_first(){
137
+ foreach($this->terms as $EM_Taxonomy_Term){
138
+ return $EM_Taxonomy_Term;
139
+ }
140
+ return false;
141
+ }
142
+
143
+ public function get_ids(){
144
+ $ids = array();
145
+ foreach($this->terms as $EM_Taxonomy_Term){
146
+ if( !empty($EM_Taxonomy_Term->term_id) ){
147
+ $ids[] = $EM_Taxonomy_Term->term_id;
148
+ }
149
+ }
150
+ return $ids;
151
+ }
152
+
153
+ /**
154
+ * Gets the event for this object, or a blank event if none exists
155
+ * @return EM_Event
156
+ */
157
+ public function get_event(){
158
+ if( is_numeric($this->event_id) ){
159
+ return em_get_event($this->event_id);
160
+ }else{
161
+ return new EM_Event();
162
+ }
163
+ }
164
+
165
+ public static function get( $args = array() ) {
166
+ //Quick version, we can accept an array of IDs, which is easy to retrieve
167
+ self::ms_global_switch();
168
+ if( self::array_is_numeric($args) ){ //Array of numbers, assume they are taxonomy IDs to retreive
169
+ $term_args = self::get_default_search( array('include' => $args ));
170
+ $results = get_terms( $term_args );
171
+ }else{
172
+ //We assume it's either an empty array or array of search arguments to merge with defaults
173
+ $term_args = self::get_default_search($args);
174
+ $results = get_terms( $term_args );
175
+
176
+ //If we want results directly in an array, why not have a shortcut here? We don't use this in code, so if you're using it and filter the em_{taxonomy}_get hook, you may want to do this one too.
177
+ if( !empty($args['array']) ){
178
+ return apply_filters('em_'. self::$instance->terms_name .'_get_array', $results, $args);
179
+ }
180
+ }
181
+ //Make returned results EM_Taxonomy_Term child objects
182
+ $results = (is_array($results)) ? $results:array();
183
+ $terms = array();
184
+ foreach ( $results as $term ){
185
+ $terms[$term->term_id] = new self::$instance->term_class($term);
186
+ }
187
+ self::ms_global_switch_back();
188
+ return apply_filters('em_'. self::$instance->terms_name .'_get', $terms, $args);
189
+ }
190
+
191
+ public static function output( $args = array() ){
192
+ $EM_Taxonomy_Term_old = !empty($GLOBALS[self::$instance->term_class]) ? $GLOBALS[self::$instance->term_class] : false; //When looping, we can replace EM_Taxonomy_Term global with the current event in the loop
193
+ //get page number if passed on by request (still needs pagination enabled to have effect)
194
+ $page_queryvar = !empty($args['page_queryvar']) ? $args['page_queryvar'] : 'pno';
195
+ if( !array_key_exists('page',$args) && !empty($args['pagination']) && !empty($_REQUEST[$page_queryvar]) && is_numeric($_REQUEST[$page_queryvar]) ){
196
+ $page = $args['page'] = $_REQUEST[$page_queryvar];
197
+ }
198
+ //Can be either an array for the get search or an array of EM_Taxonomy_Term objects
199
+ if( is_object(current($args)) && get_class((current($args))) == self::$instance->term_class ){
200
+ $func_args = func_get_args();
201
+ $terms = $func_args[0];
202
+ $args = (!empty($func_args[1])) ? $func_args[1] : array();
203
+ $args = apply_filters('em_'. self::$instance->terms_name .'_output_args', self::get_default_search($args), $terms);
204
+ $limit = ( !empty($args['limit']) && is_numeric($args['limit']) ) ? $args['limit']:false;
205
+ $offset = ( !empty($args['offset']) && is_numeric($args['offset']) ) ? $args['offset']:0;
206
+ $page = ( !empty($args['page']) && is_numeric($args['page']) ) ? $args['page']:1;
207
+ }else{
208
+ $args = apply_filters('em_'. self::$instance->terms_name .'_output_args', self::get_default_search($args) );
209
+ $limit = ( !empty($args['limit']) && is_numeric($args['limit']) ) ? $args['limit']:false;
210
+ $offset = ( !empty($args['offset']) && is_numeric($args['offset']) ) ? $args['offset']:0;
211
+ $page = ( !empty($args['page']) && is_numeric($args['page']) ) ? $args['page']:1;
212
+ $args['limit'] = $args['offset'] = $args['page'] = false; //we count overall terms here
213
+ $terms = self::get( $args );
214
+ $args['limit'] = $limit;
215
+ $args['offset'] = $offset;
216
+ $args['page'] = $page;
217
+ }
218
+ //What format shall we output this to, or use default
219
+ $format = ( $args['format'] == '' ) ? get_option( 'dbem_'. self::$instance->terms_name .'_list_item_format' ) : $args['format'] ;
220
+
221
+ $output = "";
222
+ $terms_count = count($terms);
223
+ $terms = apply_filters('em_'. self::$instance->terms_name .'_output_'. self::$instance->terms_name, $terms);
224
+ if ( count($terms) > 0 ) {
225
+ $term_count = 0;
226
+ $terms_shown = 0;
227
+ foreach ( $terms as $EM_Taxonomy_Term ) {
228
+ $GLOBALS[self::$instance->term_class] = $EM_Taxonomy_Term;
229
+ if( ($terms_shown < $limit || empty($limit)) && ($term_count >= $offset || $offset === 0) ){
230
+ $output .= $EM_Taxonomy_Term->output($format);
231
+ $terms_shown++;
232
+ }
233
+ $term_count++;
234
+ }
235
+ //Add headers and footers to output
236
+ if( $format == get_option( 'dbem_'. self::$instance->terms_name .'_list_item_format' ) ){
237
+ //we're using the default format, so if a custom format header or footer is supplied, we can override it, if not use the default
238
+ $format_header = empty($args['format_header']) ? get_option('dbem_'. self::$instance->terms_name .'_list_item_format_header') : $args['format_header'];
239
+ $format_footer = empty($args['format_footer']) ? get_option('dbem_'. self::$instance->terms_name .'_list_item_format_footer') : $args['format_footer'];
240
+ }else{
241
+ //we're using a custom format, so if a header or footer isn't specifically supplied we assume it's blank
242
+ $format_header = !empty($args['format_header']) ? $args['format_header'] : '' ;
243
+ $format_footer = !empty($args['format_footer']) ? $args['format_footer'] : '' ;
244
+ }
245
+ $output = $format_header . $output . $format_footer;
246
+ //Pagination (if needed/requested)
247
+ if( !empty($args['pagination']) && !empty($limit) && $terms_count >= $limit ){
248
+ //Show the pagination links (unless there's less than 10 events, or the custom limit)
249
+ $output .= self::get_pagination_links($args, $terms_count);
250
+ }
251
+ } else {
252
+ $output = get_option ( 'dbem_no_'. self::$instance->terms_name .'_message' );
253
+ }
254
+ //FIXME check if reference is ok when restoring object, due to changes in php5 v 4
255
+ if( !empty($EM_Taxonomy_Term_old ) ) $GLOBALS[self::$instance->term_class] = $EM_Taxonomy_Term_old;
256
+ return apply_filters('em_'. self::$instance->terms_name .'_output', $output, $terms, $args);
257
+ }
258
+
259
+ public static function get_pagination_links($args, $count, $search_action = 'search_cats', $default_args = array()){
260
+ //get default args if we're in a search, supply to parent since we can't depend on late static binding until WP requires PHP 5.3 or later
261
+ if( empty($default_args) && (!empty($args['ajax']) || !empty($_REQUEST['action']) && $_REQUEST['action'] == $search_action) ){
262
+ $default_args = self::get_default_search();
263
+ $default_args['limit'] = get_option('dbem_'. self::$instance->terms_name .'_default_limit');
264
+ }
265
+ return parent::get_pagination_links($args, $count, $search_action, $default_args);
266
+ }
267
+
268
+ public static function get_post_search($args = array(), $filter = false, $request = array(), $accepted_args = array()){
269
+ //supply $accepted_args to parent argument since we can't depend on late static binding until WP requires PHP 5.3 or later
270
+ $accepted_args = !empty($accepted_args) ? $accepted_args : array_keys(self::get_default_search());
271
+ return apply_filters('em_'. self::$instance->terms_name .'_get_post_search', parent::get_post_search($args, $filter, $request, $accepted_args));
272
+ }
273
+
274
+ /*
275
+ * Adds custom calendar search defaults
276
+ * @param array $array_or_defaults may be the array to override defaults
277
+ * @param array $array
278
+ * @return array
279
+ * @uses EM_Object#get_default_search()
280
+ */
281
+ public static function get_default_search( $array_or_defaults = array(), $array = array() ){
282
+ $defaults = array(
283
+ //added from get_terms, so they don't get filtered out
284
+ 'orderby' => get_option('dbem_'. self::$instance->terms_name .'_default_orderby'), 'order' => get_option('dbem_'. self::$instance->terms_name .'_default_order'),
285
+ 'hide_empty' => false, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(),
286
+ 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '',
287
+ 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '',
288
+ 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core'
289
+ );
290
+ //sort out whether defaults were supplied or just the array of search values
291
+ if( empty($array) ){
292
+ $array = $array_or_defaults;
293
+ }else{
294
+ $defaults = array_merge($defaults, $array_or_defaults);
295
+ }
296
+ $return = parent::get_default_search($defaults,$array);
297
+ $return['taxonomy'] = self::$instance->taxonomy; //this shouldn't change regardless
298
+ if( is_array($return['orderby']) ) $return['orderby'] = implode(',', $return['orderby']); //clean up for WP functions
299
+ return apply_filters('em_'. self::$instance->terms_name .'_get_default_search', $return, $array, $defaults);
300
+ }
301
+
302
+ //Iterator Implementation
303
+ public function rewind(){
304
+ reset($this->terms);
305
+ }
306
+ public function current(){
307
+ $var = current($this->terms);
308
+ return $var;
309
+ }
310
+ public function key(){
311
+ $var = key($this->terms);
312
+ return $var;
313
+ }
314
+ public function next(){
315
+ $var = next($this->terms);
316
+ return $var;
317
+ }
318
+ public function valid(){
319
+ $key = key($this->terms);
320
+ $var = ($key !== NULL && $key !== FALSE);
321
+ return $var;
322
+ }
323
+ }
classes/em-ticket.php CHANGED
@@ -48,6 +48,15 @@ class EM_Ticket extends EM_Object{
48
  */
49
  var $spaces_limit = true;
50
 
 
 
 
 
 
 
 
 
 
51
  /**
52
  * Creates ticket object and retreives ticket data (default is a blank ticket object). Accepts either array of ticket data (from db) or a ticket id.
53
  * @param mixed $ticket_data
@@ -203,6 +212,8 @@ class EM_Ticket extends EM_Object{
203
  //timestamp - calculated only for purposes of not screwing up interfaces that use timestamps for outputting cut-off times such as booking settings for event
204
  $this->start_timestamp = strtotime($this->ticket_start, current_time('timestamp'));
205
  }else{
 
 
206
  $this->ticket_start = $this->start_timestamp = '';
207
  }
208
  //end of ticket cut-off
@@ -219,11 +230,13 @@ class EM_Ticket extends EM_Object{
219
  //timestamp - calculated only for purposes of not screwing up interfaces that use timestamps for outputting cut-off times such as booking settings for event
220
  $this->end_timestamp = strtotime($this->ticket_end, current_time('timestamp')); //we save these timestamps for quicker loading on construct
221
  }else{
 
 
222
  $this->ticket_end = $this->end_timestamp = '';
223
  }
224
  }
225
  $this->compat_keys();
226
- do_action('em_ticket_get_post', $this);
227
  }
228
 
229
 
@@ -371,38 +384,56 @@ class EM_Ticket extends EM_Object{
371
  return apply_filters('em_ticket_get_available_spaces', $return, $this);
372
  }
373
 
374
- function get_pending_spaces(){
375
- $spaces = 0;
376
- foreach( $this->get_bookings()->get_pending_bookings()->bookings as $EM_Booking ){ //get_bookings() is used twice so we get the confirmed (or all if confirmation disabled) bookings of this ticket's total bookings.
377
- //foreach booking, get this ticket booking info if found
378
- foreach($EM_Booking->get_tickets_bookings()->tickets_bookings as $EM_Ticket_Booking){
379
- if( $EM_Ticket_Booking->ticket_id == $this->ticket_id ){
380
- $spaces += $EM_Ticket_Booking->get_spaces();
381
- }
382
- }
 
 
 
 
383
  }
384
- return apply_filters('em_ticket_get_pending_spaces', $spaces, $this);
385
  }
386
 
387
  /**
388
  * Returns the number of booked spaces in this ticket.
 
389
  * @return int
390
  */
391
- function get_booked_spaces($force_reload=false){
392
- //get all bookings for this event
393
- $spaces = 0;
394
- if( is_object($this->bookings) && $force_reload ){
395
- //return $this->bookings;
 
 
 
 
396
  }
397
- foreach( $this->get_bookings()->get_bookings()->bookings as $EM_Booking ){ //get_bookings() is used twice so we get the confirmed (or all if confirmation disabled) bookings of this ticket's total bookings.
398
- //foreach booking, get this ticket booking info if found
399
- foreach($EM_Booking->get_tickets_bookings()->tickets_bookings as $EM_Ticket_Booking){
400
- if( $EM_Ticket_Booking->ticket_id == $this->ticket_id ){
401
- $spaces += $EM_Ticket_Booking->get_spaces();
402
- }
403
- }
 
 
 
 
 
 
 
 
 
404
  }
405
- return apply_filters('em_ticket_get_booked_spaces', $spaces, $this);
406
  }
407
 
408
  /**
48
  */
49
  var $spaces_limit = true;
50
 
51
+ /**
52
+ * An associative array containing event IDs as the keys and pending spaces as values.
53
+ * This is in array form for future-proofing since at one point tickets could be used for multiple events.
54
+ * @var array
55
+ */
56
+ protected $pending_spaces = array();
57
+ protected $booked_spaces = array();
58
+ protected $bookings_count = array();
59
+
60
  /**
61
  * Creates ticket object and retreives ticket data (default is a blank ticket object). Accepts either array of ticket data (from db) or a ticket id.
62
  * @param mixed $ticket_data
212
  //timestamp - calculated only for purposes of not screwing up interfaces that use timestamps for outputting cut-off times such as booking settings for event
213
  $this->start_timestamp = strtotime($this->ticket_start, current_time('timestamp'));
214
  }else{
215
+ unset($this->ticket_meta['recurrences']['start_days']);
216
+ unset($this->ticket_meta['recurrences']['start_time']);
217
  $this->ticket_start = $this->start_timestamp = '';
218
  }
219
  //end of ticket cut-off
230
  //timestamp - calculated only for purposes of not screwing up interfaces that use timestamps for outputting cut-off times such as booking settings for event
231
  $this->end_timestamp = strtotime($this->ticket_end, current_time('timestamp')); //we save these timestamps for quicker loading on construct
232
  }else{
233
+ unset($this->ticket_meta['recurrences']['end_days']);
234
+ unset($this->ticket_meta['recurrences']['end_time']);
235
  $this->ticket_end = $this->end_timestamp = '';
236
  }
237
  }
238
  $this->compat_keys();
239
+ do_action('em_ticket_get_post', $this, $post);
240
  }
241
 
242
 
384
  return apply_filters('em_ticket_get_available_spaces', $return, $this);
385
  }
386
 
387
+ /**
388
+ * Get total number of pending spaces for this ticket.
389
+ * @param boolean $force_refresh
390
+ * @return int
391
+ */
392
+ function get_pending_spaces( $force_refresh = false ){
393
+ global $wpdb;
394
+ if( !array_key_exists($this->event_id, $this->pending_spaces) || $force_refresh ){
395
+ $sub_sql = 'SELECT booking_id FROM '.EM_BOOKINGS_TABLE." WHERE event_id=%d AND booking_status=0";
396
+ $sql = 'SELECT SUM(ticket_booking_spaces) FROM '.EM_TICKETS_BOOKINGS_TABLE. " WHERE booking_id IN ($sub_sql) AND ticket_id=%d";
397
+ $pending_spaces = $wpdb->get_var($wpdb->prepare($sql, $this->event_id, $this->ticket_id));
398
+ $this->pending_spaces[$this->event_id] = $pending_spaces > 0 ? $pending_spaces : 0;
399
+ $this->pending_spaces[$this->event_id] = apply_filters('em_ticket_get_pending_spaces', $this->pending_spaces[$this->event_id], $this, $force_refresh);
400
  }
401
+ return $this->pending_spaces[$this->event_id];
402
  }
403
 
404
  /**
405
  * Returns the number of booked spaces in this ticket.
406
+ * @param boolean $force_refresh
407
  * @return int
408
  */
409
+ function get_booked_spaces( $force_refresh = false ){
410
+ global $wpdb;
411
+ if( !array_key_exists($this->event_id, $this->pending_spaces) || $force_refresh ){
412
+ $status_cond = !get_option('dbem_bookings_approval') ? 'booking_status IN (0,1)' : 'booking_status = 1';
413
+ $sub_sql = 'SELECT booking_id FROM '.EM_BOOKINGS_TABLE." WHERE event_id=%d AND $status_cond";
414
+ $sql = 'SELECT SUM(ticket_booking_spaces) FROM '.EM_TICKETS_BOOKINGS_TABLE. " WHERE booking_id IN ($sub_sql) AND ticket_id=%d";
415
+ $booked_spaces = $wpdb->get_var($wpdb->prepare($sql, $this->event_id, $this->ticket_id));
416
+ $this->booked_spaces[$this->event_id] = $booked_spaces > 0 ? $booked_spaces : 0;
417
+ $this->booked_spaces[$this->event_id] = apply_filters('em_ticket_get_booked_spaces', $this->booked_spaces[$this->event_id], $this, $force_refresh);
418
  }
419
+ return $this->booked_spaces[$this->event_id];
420
+ }
421
+
422
+ /**
423
+ * Returns the total number of bookings of all statuses for this ticket
424
+ * @param int $status
425
+ * @param boolean $force_refresh
426
+ * @return int
427
+ */
428
+ function get_bookings_count( $status = false, $force_refresh = false ){
429
+ global $wpdb;
430
+ if( !array_key_exists($this->event_id, $this->bookings_count) || $force_refresh ){
431
+ $sql = 'SELECT COUNT(*) FROM '.EM_TICKETS_BOOKINGS_TABLE. ' WHERE booking_id IN (SELECT booking_id FROM '.EM_BOOKINGS_TABLE.' WHERE event_id=%d) AND ticket_id=%d';
432
+ $bookings_count = $wpdb->get_var($wpdb->prepare($sql, $this->event_id, $this->ticket_id));
433
+ $this->bookings_count[$this->event_id] = $bookings_count > 0 ? $bookings_count : 0;
434
+ $this->bookings_count[$this->event_id] = apply_filters('em_ticket_get_bookings_count', $this->bookings_count[$this->event_id], $this, $force_refresh);
435
  }
436
+ return $this->bookings_count[$this->event_id];
437
  }
438
 
439
  /**
classes/em-tickets.php CHANGED
@@ -104,18 +104,31 @@ class EM_Tickets extends EM_Object implements Iterator{
104
  global $wpdb;
105
  //get all the ticket ids
106
  $result = false;
107
- $ticket_ids = array();
108
- foreach( $this->tickets as $EM_Ticket ){
109
- $ticket_ids[] = $EM_Ticket->ticket_id;
110
- }
111
- //check that tickets don't have bookings
112
- if(count($ticket_ids) > 0){
113
- $bookings = $wpdb->get_var("SELECT COUNT(*) FROM ". EM_TICKETS_BOOKINGS_TABLE." WHERE ticket_id IN (".implode(',',$ticket_ids).")");
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  if( $bookings > 0 ){
115
  $result = false;
116
  $this->add_error(__('You cannot delete tickets if there are any bookings associated with them. Please delete these bookings first.','events-manager'));
117
  }else{
118
- $result = $wpdb->query("DELETE FROM ".EM_TICKETS_TABLE." WHERE ticket_id IN (".implode(',',$ticket_ids).")");
119
  }
120
  }
121
  return ($result !== false);
@@ -128,13 +141,18 @@ class EM_Tickets extends EM_Object implements Iterator{
128
  function get_post(){
129
  //Build Event Array
130
  do_action('em_tickets_get_post_pre', $this);
 
131
  $this->tickets = array(); //clean current tickets out
132
  if( !empty($_POST['em_tickets']) && is_array($_POST['em_tickets']) ){
133
  //get all ticket data and create objects
134
  global $allowedposttags;
135
  foreach($_POST['em_tickets'] as $row => $ticket_data){
136
  if( $row > 0 ){
137
- $EM_Ticket = new EM_Ticket();
 
 
 
 
138
  $ticket_data['event_id'] = $this->event_id;
139
  $EM_Ticket->get_post($ticket_data);
140
  if( $EM_Ticket->ticket_id ){
104
  global $wpdb;
105
  //get all the ticket ids
106
  $result = false;
107
+ if( !empty($this->tickets) ){
108
+ //get ticket ids if tickets are already preloaded into the object
109
+ $ticket_ids = array();
110
+ foreach( $this->tickets as $EM_Ticket ){
111
+ $ticket_ids[] = $EM_Ticket->ticket_id;
112
+ }
113
+ //check that tickets don't have bookings
114
+ if(count($ticket_ids) > 0){
115
+ $bookings = $wpdb->get_var("SELECT COUNT(*) FROM ". EM_TICKETS_BOOKINGS_TABLE." WHERE ticket_id IN (".implode(',',$ticket_ids).")");
116
+ if( $bookings > 0 ){
117
+ $result = false;
118
+ $this->add_error(__('You cannot delete tickets if there are any bookings associated with them. Please delete these bookings first.','events-manager'));
119
+ }else{
120
+ $result = $wpdb->query("DELETE FROM ".EM_TICKETS_TABLE." WHERE ticket_id IN (".implode(',',$ticket_ids).")");
121
+ }
122
+ }
123
+ }elseif( !empty($this->event_id) ){
124
+ //if tickets aren't preloaded into object and this belongs to an event, delete via the event ID without loading any tickets
125
+ $event_id = absint($this->event_id);
126
+ $bookings = $wpdb->get_var("SELECT COUNT(*) FROM ". EM_TICKETS_BOOKINGS_TABLE." WHERE ticket_id IN (SELECT ticket_id FROM ".EM_TICKETS_TABLE." WHERE event_id='$event_id')");
127
  if( $bookings > 0 ){
128
  $result = false;
129
  $this->add_error(__('You cannot delete tickets if there are any bookings associated with them. Please delete these bookings first.','events-manager'));
130
  }else{
131
+ $result = $wpdb->query("DELETE FROM ".EM_TICKETS_TABLE." WHERE event_id='$event_id'");
132
  }
133
  }
134
  return ($result !== false);
141
  function get_post(){
142
  //Build Event Array
143
  do_action('em_tickets_get_post_pre', $this);
144
+ $current_tickets = $this->tickets; //save previous tickets so things like ticket_meta doesn't get overwritten
145
  $this->tickets = array(); //clean current tickets out
146
  if( !empty($_POST['em_tickets']) && is_array($_POST['em_tickets']) ){
147
  //get all ticket data and create objects
148
  global $allowedposttags;
149
  foreach($_POST['em_tickets'] as $row => $ticket_data){
150
  if( $row > 0 ){
151
+ if( !empty($ticket_data['ticket_id']) && !empty($current_tickets[$ticket_data['ticket_id']]) ){
152
+ $EM_Ticket = $current_tickets[$ticket_data['ticket_id']];
153
+ }else{
154
+ $EM_Ticket = new EM_Ticket();
155
+ }
156
  $ticket_data['event_id'] = $this->event_id;
157
  $EM_Ticket->get_post($ticket_data);
158
  if( $EM_Ticket->ticket_id ){
em-actions.php CHANGED
@@ -41,21 +41,19 @@ function em_init_actions() {
41
  $json_locations[$location_key]['location_balloon'] = $EM_Location->output(get_option('dbem_map_text_format'));
42
  }
43
  echo EM_Object::json_encode($json_locations);
44
- die();
45
  }
46
  if(isset($_REQUEST['query']) && $_REQUEST['query'] == 'GlobalEventsMapData') {
47
  $_REQUEST['has_location'] = true; //we're looking for locations in this context, so locations necessary
 
48
  $EM_Events = EM_Events::get( $_REQUEST );
49
  $json_locations = array();
50
  $locations = array();
51
  foreach($EM_Events as $EM_Event) {
52
- if( !empty($EM_Event->location_id) && empty($locations[$EM_Event->location_id]) ){
53
- $EM_Location = $EM_Event->get_location();
54
- $location_array = $EM_Event->get_location()->to_array();
55
- $location_array['location_balloon'] = $EM_Location->output(get_option('dbem_map_text_format'));
56
- $json_locations[] = $location_array;
57
- $locations[$EM_Event->location_id] = true;
58
- }
59
  }
60
  echo EM_Object::json_encode($json_locations);
61
  die();
@@ -307,7 +305,7 @@ function em_init_actions() {
307
  }elseif ( $_REQUEST['action'] == 'booking_add_one' && is_object($EM_Event) && is_user_logged_in() ) {
308
  //ADD/EDIT Booking
309
  em_verify_nonce('booking_add_one');
310
- if( !$EM_Event->get_bookings()->has_booking(get_current_user_id()) || get_option('dbem_bookings_double')){
311
  $EM_Booking = em_get_booking(array('person_id'=>get_current_user_id(), 'event_id'=>$EM_Event->event_id, 'booking_spaces'=>1)); //new booking
312
  $EM_Ticket = $EM_Event->get_bookings()->get_tickets()->get_first();
313
  //get first ticket in this event and book one place there. similar to getting the form values in EM_Booking::get_post_values()
@@ -649,7 +647,7 @@ function em_init_actions() {
649
  $EM_Bookings = $EM_Bookings_Table->get_bookings();
650
  $handle = fopen("php://output", "w");
651
  fputcsv($handle, $EM_Bookings_Table->get_headers(true), $delimiter);
652
- while(!empty($EM_Bookings->bookings)){
653
  foreach( $EM_Bookings->bookings as $EM_Booking ) {
654
  //Display all values
655
  /* @var $EM_Booking EM_Booking */
41
  $json_locations[$location_key]['location_balloon'] = $EM_Location->output(get_option('dbem_map_text_format'));
42
  }
43
  echo EM_Object::json_encode($json_locations);
44
+ die();
45
  }
46
  if(isset($_REQUEST['query']) && $_REQUEST['query'] == 'GlobalEventsMapData') {
47
  $_REQUEST['has_location'] = true; //we're looking for locations in this context, so locations necessary
48
+ $_REQUEST['groupby'] = 'location_id'; //grouping will generally produce much faster processing
49
  $EM_Events = EM_Events::get( $_REQUEST );
50
  $json_locations = array();
51
  $locations = array();
52
  foreach($EM_Events as $EM_Event) {
53
+ $EM_Location = $EM_Event->get_location();
54
+ $location_array = $EM_Event->get_location()->to_array();
55
+ $location_array['location_balloon'] = $EM_Location->output(get_option('dbem_map_text_format'));
56
+ $json_locations[] = $location_array;
 
 
 
57
  }
58
  echo EM_Object::json_encode($json_locations);
59
  die();
305
  }elseif ( $_REQUEST['action'] == 'booking_add_one' && is_object($EM_Event) && is_user_logged_in() ) {
306
  //ADD/EDIT Booking
307
  em_verify_nonce('booking_add_one');
308
+ if( get_option('dbem_bookings_double') || !$EM_Event->get_bookings()->has_booking(get_current_user_id()) ){
309
  $EM_Booking = em_get_booking(array('person_id'=>get_current_user_id(), 'event_id'=>$EM_Event->event_id, 'booking_spaces'=>1)); //new booking
310
  $EM_Ticket = $EM_Event->get_bookings()->get_tickets()->get_first();
311
  //get first ticket in this event and book one place there. similar to getting the form values in EM_Booking::get_post_values()
647
  $EM_Bookings = $EM_Bookings_Table->get_bookings();
648
  $handle = fopen("php://output", "w");
649
  fputcsv($handle, $EM_Bookings_Table->get_headers(true), $delimiter);
650
+ while( !empty($EM_Bookings->bookings) ){
651
  foreach( $EM_Bookings->bookings as $EM_Booking ) {
652
  //Display all values
653
  /* @var $EM_Booking EM_Booking */
em-events.php CHANGED
@@ -25,7 +25,7 @@ function em_content($page_content) {
25
  'pagination' => 1
26
  );
27
  $args['ajax'] = isset($args['ajax']) ? $args['ajax']:(!defined('EM_AJAX') || EM_AJAX );
28
- if( !post_password_required() && in_array($post->ID, array($events_page_id, $locations_page_id, $categories_page_id, $edit_bookings_page_id, $edit_events_page_id, $edit_locations_page_id, $my_bookings_page_id, $tags_page_id)) ){
29
  $content = apply_filters('em_content_pre', '', $page_content);
30
  if( empty($content) ){
31
  ob_start();
25
  'pagination' => 1
26
  );
27
  $args['ajax'] = isset($args['ajax']) ? $args['ajax']:(!defined('EM_AJAX') || EM_AJAX );
28
+ if( in_the_loop() && is_main_query() && !post_password_required() && in_array($post->ID, array($events_page_id, $locations_page_id, $categories_page_id, $edit_bookings_page_id, $edit_events_page_id, $edit_locations_page_id, $my_bookings_page_id, $tags_page_id)) ){
29
  $content = apply_filters('em_content_pre', '', $page_content);
30
  if( empty($content) ){
31
  ob_start();
em-functions.php CHANGED
@@ -302,6 +302,9 @@ function em_get_wp_users( $args = array(), $extra_users = array() ) {
302
  }
303
 
304
  function em_get_attributes($lattributes = false){
 
 
 
305
  //We also get a list of attribute names and create a ddm list (since placeholders are fixed)
306
  $formats =
307
  get_option ( 'dbem_placeholders_custom' ).
@@ -324,7 +327,6 @@ function em_get_attributes($lattributes = false){
324
  preg_match_all('/#_ATT\{([^}]+)\}(\{([^}]+)\})?/', $formats, $matches);
325
  }
326
  //Now grab all the unique attributes we can use in our event.
327
- $attributes = array('names'=>array(), 'values'=>array());
328
  foreach($matches[1] as $key => $attribute) {
329
  if( !in_array($attribute, $attributes['names']) ){
330
  $attributes['names'][] = $attribute ;
@@ -518,6 +520,22 @@ function em_new_user_notification() {
518
  return $EM_Mailer->send(get_option('dbem_bookings_email_registration_subject'), $message, $user_email);
519
  }
520
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
521
  /**
522
  * Returns an array of flags that are used in search forms.
523
  * @return array
@@ -639,7 +657,7 @@ function em_options_input_text($title, $name, $description ='', $default='') {
639
  <tr valign="top" id='<?php echo esc_attr($name);?>_row'>
640
  <th scope="row"><?php echo esc_html($title); ?></th>
641
  <td>
642
- <input name="<?php echo esc_attr($name) ?>" type="text" id="<?php echo esc_attr($name) ?>" style="width: 95%" value="<?php echo esc_attr(get_option($name, $default), ENT_QUOTES); ?>" size="45" />
643
  <?php if( $translate ): ?><span class="em-translatable dashicons dashicons-admin-site"></span><?php endif; ?>
644
  <br />
645
  <?php
@@ -686,7 +704,7 @@ function em_options_textarea($title, $name, $description ='') {
686
  <tr valign="top" id='<?php echo esc_attr($name);?>_row'>
687
  <th scope="row"><?php echo esc_html($title); ?></th>
688
  <td>
689
- <textarea name="<?php echo esc_attr($name) ?>" id="<?php echo esc_attr($name) ?>" rows="6" cols="60"><?php echo esc_attr(get_option($name), ENT_QUOTES);?></textarea>
690
  <?php if( $translate ): ?><span class="em-translatable dashicons dashicons-admin-site"></span><?php endif; ?>
691
  <br />
692
  <?php
@@ -738,14 +756,18 @@ function em_options_radio($name, $options, $title='') {
738
  <?php
739
  }
740
 
741
- function em_options_radio_binary($title, $name, $description='', $option_names = '', $trigger='') {
742
  if( empty($option_names) ) $option_names = array(0 => __('No','events-manager'), 1 => __('Yes','events-manager'));
743
  if( substr($name, 0, 7) == 'dbem_ms' ){
744
  $list_events_page = get_site_option($name);
745
  }else{
746
  $list_events_page = get_option($name);
747
  }
748
- $trigger_att = ($trigger) ? 'data-trigger="'.esc_attr($trigger).'" class="em-trigger"':'';
 
 
 
 
749
  ?>
750
  <tr valign="top" id='<?php echo $name;?>_row'>
751
  <th scope="row"><?php echo esc_html($title); ?></th>
302
  }
303
 
304
  function em_get_attributes($lattributes = false){
305
+ $attributes = array('names'=>array(), 'values'=>array());
306
+ if( !$lattributes && !get_option('dbem_attributes_enabled') ) return $attributes;
307
+ if( $lattributes && !get_option('dbem_location_attributes_enabled') ) return $attributes;
308
  //We also get a list of attribute names and create a ddm list (since placeholders are fixed)
309
  $formats =
310
  get_option ( 'dbem_placeholders_custom' ).
327
  preg_match_all('/#_ATT\{([^}]+)\}(\{([^}]+)\})?/', $formats, $matches);
328
  }
329
  //Now grab all the unique attributes we can use in our event.
 
330
  foreach($matches[1] as $key => $attribute) {
331
  if( !in_array($attribute, $attributes['names']) ){
332
  $attributes['names'][] = $attribute ;
520
  return $EM_Mailer->send(get_option('dbem_bookings_email_registration_subject'), $message, $user_email);
521
  }
522
 
523
+ /**
524
+ * Transitional function to handle WP's eventual move away from the is_super_user() function
525
+ */
526
+ function em_wp_is_super_admin( $user_id = false ){
527
+ $user = ( ! $user_id || $user_id == get_current_user_id() ) ? wp_get_current_user() : get_userdata( $user_id );
528
+
529
+ if ( ! $user || ! $user->exists() ) return false;
530
+
531
+ if ( is_multisite() ) {
532
+ if( $user->has_cap('manage_network_options') ) return true;
533
+ } else {
534
+ if ( $user->has_cap('delete_users') ) return true;
535
+ }
536
+ return false;
537
+ }
538
+
539
  /**
540
  * Returns an array of flags that are used in search forms.
541
  * @return array
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
704
  <tr valign="top" id='<?php echo esc_attr($name);?>_row'>
705
  <th scope="row"><?php echo esc_html($title); ?></th>
706
  <td>
707
+ <textarea name="<?php echo esc_attr($name) ?>" id="<?php echo esc_attr($name) ?>" rows="6"><?php echo esc_attr(get_option($name), ENT_QUOTES);?></textarea>
708
  <?php if( $translate ): ?><span class="em-translatable dashicons dashicons-admin-site"></span><?php endif; ?>
709
  <br />
710
  <?php
756
  <?php
757
  }
758
 
759
+ function em_options_radio_binary($title, $name, $description='', $option_names = '', $trigger='', $untrigger=false) {
760
  if( empty($option_names) ) $option_names = array(0 => __('No','events-manager'), 1 => __('Yes','events-manager'));
761
  if( substr($name, 0, 7) == 'dbem_ms' ){
762
  $list_events_page = get_site_option($name);
763
  }else{
764
  $list_events_page = get_option($name);
765
  }
766
+ if( $untrigger ){
767
+ $trigger_att = ($trigger) ? 'data-trigger="'.esc_attr($trigger).'" class="em-untrigger"':'';
768
+ }else{
769
+ $trigger_att = ($trigger) ? 'data-trigger="'.esc_attr($trigger).'" class="em-trigger"':'';
770
+ }
771
  ?>
772
  <tr valign="top" id='<?php echo $name;?>_row'>
773
  <th scope="row"><?php echo esc_html($title); ?></th>
em-install.php CHANGED
@@ -149,11 +149,9 @@ function em_create_events_table() {
149
  event_private bool NOT NULL DEFAULT 0,
150
  location_id bigint(20) unsigned NULL DEFAULT NULL,
151
  recurrence_id bigint(20) unsigned NULL DEFAULT NULL,
152
- event_category_id bigint(20) unsigned NULL DEFAULT NULL,
153
- event_attributes text NULL DEFAULT NULL,
154
  event_date_created datetime NULL DEFAULT NULL,
155
  event_date_modified datetime NULL DEFAULT NULL,
156
- recurrence bool NOT NULL DEFAULT 0,
157
  recurrence_interval int(4) NULL DEFAULT NULL,
158
  recurrence_freq tinytext NULL DEFAULT NULL,
159
  recurrence_byday tinytext NULL DEFAULT NULL,
@@ -179,7 +177,7 @@ function em_create_events_table() {
179
  em_migrate_events($wpdb->get_results('SELECT * FROM '.$table_name, ARRAY_A));
180
  */
181
  }else{
182
- if( get_option('dbem_version') < 4.939 ){
183
  //if updating from version 4 (4.934 is beta v5) then set all statuses to 1 since it's new
184
  $wpdb->query("ALTER TABLE $table_name CHANGE event_notes post_content longtext NULL DEFAULT NULL");
185
  $wpdb->query("ALTER TABLE $table_name CHANGE event_name event_name text NULL DEFAULT NULL");
@@ -256,11 +254,11 @@ function em_create_locations_table() {
256
  em_migrate_locations($wpdb->get_results('SELECT * FROM '.$table_name, ARRAY_A));
257
  */
258
  }else{
259
- if( get_option('dbem_version') < 4.938 ){
260
  $wpdb->query("ALTER TABLE $table_name CHANGE location_description post_content longtext NULL DEFAULT NULL");
261
  }
262
  dbDelta($sql);
263
- if( get_option('dbem_version') < 4.93 ){
264
  //if updating from version 4 (4.93 is beta v5) then set all statuses to 1 since it's new
265
  $wpdb->query("UPDATE ".$table_name." SET location_status=1");
266
  }
@@ -550,6 +548,8 @@ function em_add_options() {
550
  'dbem_location_event_list_item_format' => "<li>#_EVENTLINK - #_EVENTDATES - #_EVENTTIMES</li>",
551
  'dbem_location_event_list_item_footer_format' => "</ul>",
552
  'dbem_location_event_list_limit' => 20,
 
 
553
  'dbem_location_event_single_format' => '#_EVENTLINK - #_EVENTDATES - #_EVENTTIMES',
554
  'dbem_location_no_event_message' => __('No events in this location', 'events-manager'),
555
  //Category page options
@@ -569,6 +569,8 @@ function em_add_options() {
569
  'dbem_category_event_list_item_format' => "<li>#_EVENTLINK - #_EVENTDATES - #_EVENTTIMES</li>",
570
  'dbem_category_event_list_item_footer_format' => '</ul>',
571
  'dbem_category_event_list_limit' => 20,
 
 
572
  'dbem_category_event_single_format' => '#_EVENTLINK - #_EVENTDATES - #_EVENTTIMES',
573
  'dbem_category_no_event_message' => __('No events in this category', 'events-manager'),
574
  'dbem_category_default_color' => '#a8d144',
@@ -591,6 +593,9 @@ function em_add_options() {
591
  'dbem_tag_event_single_format' => '#_EVENTLINK - #_EVENTDATES - #_EVENTTIMES',
592
  'dbem_tag_no_event_message' => __('No events with this tag', 'events-manager'),
593
  'dbem_tag_event_list_limit' => 20,
 
 
 
594
  //RSS Stuff
595
  'dbem_rss_limit' => 0,
596
  'dbem_rss_scope' => 'future',
@@ -808,7 +813,9 @@ function em_add_options() {
808
  //optimization options
809
  'dbem_disable_thumbnails'=> false,
810
  //feedback reminder
811
- 'dbem_feedback_reminder' => time()
 
 
812
  );
813
 
814
  //do date js according to locale:
@@ -1214,7 +1221,7 @@ function em_migrate_v4(){
1214
  }
1215
  //-- CATEGORIES --
1216
  //Create the terms according to category table, use the category owner for the term ids to store this
1217
- $categories = $wpdb->get_results("SELECT * FROM ".EM_CATEGORIES_TABLE, ARRAY_A); //taking a wild-hope guess that there aren't too many categories on one site/blog
1218
  foreach( $categories as $category ){
1219
  //get all events with this category before resetting ids
1220
  $sql = "SELECT post_id FROM ".EM_EVENTS_TABLE.", ".EM_META_TABLE." WHERE event_id=object_id AND meta_key='event-category' AND meta_value='{$category['category_id']}'";
149
  event_private bool NOT NULL DEFAULT 0,
150
  location_id bigint(20) unsigned NULL DEFAULT NULL,
151
  recurrence_id bigint(20) unsigned NULL DEFAULT NULL,
 
 
152
  event_date_created datetime NULL DEFAULT NULL,
153
  event_date_modified datetime NULL DEFAULT NULL,
154
+ recurrence bool NULL DEFAULT 0,
155
  recurrence_interval int(4) NULL DEFAULT NULL,
156
  recurrence_freq tinytext NULL DEFAULT NULL,
157
  recurrence_byday tinytext NULL DEFAULT NULL,
177
  em_migrate_events($wpdb->get_results('SELECT * FROM '.$table_name, ARRAY_A));
178
  */
179
  }else{
180
+ if( get_option('dbem_version') != '' && get_option('dbem_version') < 4.939 ){
181
  //if updating from version 4 (4.934 is beta v5) then set all statuses to 1 since it's new
182
  $wpdb->query("ALTER TABLE $table_name CHANGE event_notes post_content longtext NULL DEFAULT NULL");
183
  $wpdb->query("ALTER TABLE $table_name CHANGE event_name event_name text NULL DEFAULT NULL");
254
  em_migrate_locations($wpdb->get_results('SELECT * FROM '.$table_name, ARRAY_A));
255
  */
256
  }else{
257
+ if( get_option('dbem_version') != '' && get_option('dbem_version') < 4.938 ){
258
  $wpdb->query("ALTER TABLE $table_name CHANGE location_description post_content longtext NULL DEFAULT NULL");
259
  }
260
  dbDelta($sql);
261
+ if( get_option('dbem_version') != '' && get_option('dbem_version') < 4.93 ){
262
  //if updating from version 4 (4.93 is beta v5) then set all statuses to 1 since it's new
263
  $wpdb->query("UPDATE ".$table_name." SET location_status=1");
264
  }
548
  'dbem_location_event_list_item_format' => "<li>#_EVENTLINK - #_EVENTDATES - #_EVENTTIMES</li>",
549
  'dbem_location_event_list_item_footer_format' => "</ul>",
550
  'dbem_location_event_list_limit' => 20,
551
+ 'dbem_location_event_list_orderby' => 'event_start_date,event_start_time,event_name',
552
+ 'dbem_location_event_list_order' => 'ASC',
553
  'dbem_location_event_single_format' => '#_EVENTLINK - #_EVENTDATES - #_EVENTTIMES',
554
  'dbem_location_no_event_message' => __('No events in this location', 'events-manager'),
555
  //Category page options
569
  'dbem_category_event_list_item_format' => "<li>#_EVENTLINK - #_EVENTDATES - #_EVENTTIMES</li>",
570
  'dbem_category_event_list_item_footer_format' => '</ul>',
571
  'dbem_category_event_list_limit' => 20,
572
+ 'dbem_category_event_list_orderby' => 'event_start_date,event_start_time,event_name',
573
+ 'dbem_category_event_list_order' => 'ASC',
574
  'dbem_category_event_single_format' => '#_EVENTLINK - #_EVENTDATES - #_EVENTTIMES',
575
  'dbem_category_no_event_message' => __('No events in this category', 'events-manager'),
576
  'dbem_category_default_color' => '#a8d144',
593
  'dbem_tag_event_single_format' => '#_EVENTLINK - #_EVENTDATES - #_EVENTTIMES',
594
  'dbem_tag_no_event_message' => __('No events with this tag', 'events-manager'),
595
  'dbem_tag_event_list_limit' => 20,
596
+ 'dbem_tag_event_list_orderby' => 'event_start_date,event_start_time,event_name',
597
+ 'dbem_tag_event_list_order' => 'ASC',
598
+ 'dbem_tag_default_color' => '#a8d145',
599
  //RSS Stuff
600
  'dbem_rss_limit' => 0,
601
  'dbem_rss_scope' => 'future',
813
  //optimization options
814
  'dbem_disable_thumbnails'=> false,
815
  //feedback reminder
816
+ 'dbem_feedback_reminder' => time(),
817
+ 'dbem_events_page_ajax' => 0,
818
+ 'dbem_conditional_recursions' => 1
819
  );
820
 
821
  //do date js according to locale:
1221
  }
1222
  //-- CATEGORIES --
1223
  //Create the terms according to category table, use the category owner for the term ids to store this
1224
+ $categories = $wpdb->get_results("SELECT * FROM ".$wpdb->prefix.'em_categories', ARRAY_A); //taking a wild-hope guess that there aren't too many categories on one site/blog
1225
  foreach( $categories as $category ){
1226
  //get all events with this category before resetting ids
1227
  $sql = "SELECT post_id FROM ".EM_EVENTS_TABLE.", ".EM_META_TABLE." WHERE event_id=object_id AND meta_key='event-category' AND meta_value='{$category['category_id']}'";
em-posts.php CHANGED
@@ -115,6 +115,7 @@ function wp_events_plugin_init(){
115
  )
116
  ));
117
  }
 
118
  $event_post_type = array(
119
  'public' => true,
120
  'hierarchical' => false,
@@ -126,7 +127,7 @@ function wp_events_plugin_init(){
126
  'publicly_queryable' => true,
127
  'rewrite' => array('slug' => EM_POST_TYPE_EVENT_SLUG,'with_front'=>false),
128
  'has_archive' => get_option('dbem_cp_events_has_archive', false) == true,
129
- 'supports' => apply_filters('em_cp_event_supports', array('custom-fields','title','editor','excerpt','comments','thumbnail','author')),
130
  'capability_type' => 'event',
131
  'capabilities' => array(
132
  'publish_posts' => 'publish_events',
@@ -172,7 +173,7 @@ function wp_events_plugin_init(){
172
  'has_archive' => false,
173
  'can_export' => true,
174
  'hierarchical' => false,
175
- 'supports' => apply_filters('em_cp_event_supports', array('custom-fields','title','editor','excerpt','comments','thumbnail','author')),
176
  'capability_type' => 'recurring_events',
177
  'rewrite' => array('slug' => 'events-recurring','with_front'=>false),
178
  'capabilities' => array(
@@ -306,7 +307,12 @@ function em_map_meta_cap( $caps, $cap, $user_id, $args ) {
306
  if( !empty( $args[0]) ){
307
  /* Handle event reads */
308
  if ( 'edit_event' == $cap || 'delete_event' == $cap || 'read_event' == $cap ) {
309
- $EM_Event = em_get_event($args[0],'post_id');
 
 
 
 
 
310
  $post_type = get_post_type_object( $EM_Event->post_type );
311
  /* Set an empty array for the caps. */
312
  $caps = array();
@@ -335,7 +341,12 @@ function em_map_meta_cap( $caps, $cap, $user_id, $args ) {
335
  }
336
  }
337
  if ( 'edit_recurring_event' == $cap || 'delete_recurring_event' == $cap || 'read_recurring_event' == $cap ) {
338
- $EM_Event = em_get_event($args[0],'post_id');
 
 
 
 
 
339
  $post_type = get_post_type_object( $EM_Event->post_type );
340
  /* Set an empty array for the caps. */
341
  $caps = array();
@@ -364,7 +375,12 @@ function em_map_meta_cap( $caps, $cap, $user_id, $args ) {
364
  }
365
  }
366
  if ( 'edit_location' == $cap || 'delete_location' == $cap || 'read_location' == $cap ) {
367
- $EM_Location = em_get_location($args[0],'post_id');
 
 
 
 
 
368
  $post_type = get_post_type_object( $EM_Location->post_type );
369
  /* Set an empty array for the caps. */
370
  $caps = array();
115
  )
116
  ));
117
  }
118
+ $event_post_type_supports = apply_filters('em_cp_event_supports', array('custom-fields','title','editor','excerpt','comments','thumbnail','author'));
119
  $event_post_type = array(
120
  'public' => true,
121
  'hierarchical' => false,
127
  'publicly_queryable' => true,
128
  'rewrite' => array('slug' => EM_POST_TYPE_EVENT_SLUG,'with_front'=>false),
129
  'has_archive' => get_option('dbem_cp_events_has_archive', false) == true,
130
+ 'supports' => $event_post_type_supports,
131
  'capability_type' => 'event',
132
  'capabilities' => array(
133
  'publish_posts' => 'publish_events',
173
  'has_archive' => false,
174
  'can_export' => true,
175
  'hierarchical' => false,
176
+ 'supports' => $event_post_type_supports,
177
  'capability_type' => 'recurring_events',
178
  'rewrite' => array('slug' => 'events-recurring','with_front'=>false),
179
  'capabilities' => array(
307
  if( !empty( $args[0]) ){
308
  /* Handle event reads */
309
  if ( 'edit_event' == $cap || 'delete_event' == $cap || 'read_event' == $cap ) {
310
+ $post = get_post($args[0]);
311
+ //check for revisions and deal with non-event post types
312
+ if( !empty($post->post_type) && $post->post_type == 'revision' ) $post = get_post($post->post_parent);
313
+ if( empty($post->post_type) || !in_array($post->post_type, array(EM_POST_TYPE_EVENT, 'event-recurring')) ) return $caps;
314
+ //continue with getting post type and assigning caps
315
+ $EM_Event = em_get_event($post);
316
  $post_type = get_post_type_object( $EM_Event->post_type );
317
  /* Set an empty array for the caps. */
318
  $caps = array();
341
  }
342
  }
343
  if ( 'edit_recurring_event' == $cap || 'delete_recurring_event' == $cap || 'read_recurring_event' == $cap ) {
344
+ $post = get_post($args[0]);
345
+ //check for revisions and deal with non-event post types
346
+ if( !empty($post->post_type) && $post->post_type == 'revision' ) $post = get_post($post->post_parent);
347
+ if( empty($post->post_type) || $post->post_type != 'event-recurring' ) return $caps;
348
+ //continue with getting post type and assigning caps
349
+ $EM_Event = em_get_event($post);
350
  $post_type = get_post_type_object( $EM_Event->post_type );
351
  /* Set an empty array for the caps. */
352
  $caps = array();
375
  }
376
  }
377
  if ( 'edit_location' == $cap || 'delete_location' == $cap || 'read_location' == $cap ) {
378
+ $post = get_post($args[0]);
379
+ //check for revisions and deal with non-location post types
380
+ if( !empty($post->post_type) && $post->post_type == 'revision' ) $post = get_post($post->post_parent);
381
+ if( empty($post->post_type) || $post->post_type != EM_POST_TYPE_LOCATION ) return $caps;
382
+ //continue with getting post type and assigning caps
383
+ $EM_Location = em_get_location($post);
384
  $post_type = get_post_type_object( $EM_Location->post_type );
385
  /* Set an empty array for the caps. */
386
  $caps = array();
em-pro-compatibility.php CHANGED
@@ -16,7 +16,7 @@ if( EMP_VERSION < 2.377 && (!defined('EMP_2376_FIXED') || !EMP_2376_FIXED) ){
16
  </div>
17
  <?php
18
  }
19
- if( is_super_admin() ){
20
  add_action ( 'admin_notices', 'em_empro_lt_2376_notice', 100 );
21
  add_action ( 'network_admin_notices', 'em_empro_lt_2376_notice', 100 );
22
  }
16
  </div>
17
  <?php
18
  }
19
+ if( em_wp_is_super_admin() ){
20
  add_action ( 'admin_notices', 'em_empro_lt_2376_notice', 100 );
21
  add_action ( 'network_admin_notices', 'em_empro_lt_2376_notice', 100 );
22
  }
em-shortcode.php CHANGED
@@ -28,6 +28,7 @@ add_shortcode('events_gcal', 'em_get_gcal_shortcode');
28
  * @return string
29
  */
30
  function em_get_locations_map_shortcode($args){
 
31
  $args['em_ajax'] = true;
32
  $args['query'] = 'GlobalMapData';
33
  //get dimensions with px or % added in
@@ -65,6 +66,7 @@ add_shortcode('locations-map', 'em_get_locations_map_shortcode'); //deprecate th
65
  * @return string
66
  */
67
  function em_get_events_map_shortcode($args){
 
68
  $args['em_ajax'] = true;
69
  $args['query'] = 'GlobalEventsMapData';
70
  //get dimensions with px or % added in
@@ -372,6 +374,6 @@ add_shortcode ( 'locations_search', 'em_get_location_search_form_shortcode');
372
  * @return string
373
  */
374
  function em_get_my_bookings_shortcode(){
375
- return em_my_bookings();
376
  }
377
  add_shortcode ( 'my_bookings', 'em_get_my_bookings_shortcode');
28
  * @return string
29
  */
30
  function em_get_locations_map_shortcode($args){
31
+ $args = (array) $args;
32
  $args['em_ajax'] = true;
33
  $args['query'] = 'GlobalMapData';
34
  //get dimensions with px or % added in
66
  * @return string
67
  */
68
  function em_get_events_map_shortcode($args){
69
+ $args = (array) $args;
70
  $args['em_ajax'] = true;
71
  $args['query'] = 'GlobalEventsMapData';
72
  //get dimensions with px or % added in
374
  * @return string
375
  */
376
  function em_get_my_bookings_shortcode(){
377
+ return em_get_my_bookings();
378
  }
379
  add_shortcode ( 'my_bookings', 'em_get_my_bookings_shortcode');
events-manager.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
  /*
3
  Plugin Name: Events Manager
4
- Version: 5.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
@@ -10,7 +10,7 @@ Text Domain: events-manager
10
  */
11
 
12
  /*
13
- Copyright (c) 2016, Marcus Sykes
14
 
15
  This program is free software; you can redistribute it and/or
16
  modify it under the terms of the GNU General Public License
@@ -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.7); //self expanatory
32
  define('EM_PRO_MIN_VERSION', 2.392); //self expanatory
33
  define('EM_PRO_MIN_VERSION_CRITICAL', 2.377); //self expanatory
34
  define('EM_DIR', dirname( __FILE__ )); //an absolute path to this directory
@@ -43,7 +43,7 @@ if( !defined('EM_AJAX') ){
43
  if( !defined('EM_CONDITIONAL_RECURSIONS') ) define('EM_CONDITIONAL_RECURSIONS', get_option('dbem_conditional_recursions', 1)); //allows for conditional recursios to be nested
44
 
45
  //EM_MS_GLOBAL
46
- if( get_site_option('dbem_ms_global_table') && is_multisite() ){
47
  define('EM_MS_GLOBAL', true);
48
  }else{
49
  define('EM_MS_GLOBAL',false);
@@ -65,8 +65,13 @@ function dbem_debug_mode(){
65
  //add_action('plugins_loaded', 'dbem_debug_mode');
66
 
67
  // INCLUDES
68
- include('classes/em-object.php'); //Base object, any files below may depend on this
69
- include("em-posts.php"); //set up events as posts
 
 
 
 
 
70
  //Template Tags & Template Logic
71
  include("em-actions.php");
72
  include("em-events.php");
@@ -88,8 +93,8 @@ include('classes/em-bookings.php');
88
  include("classes/em-bookings-table.php") ;
89
  include('classes/em-calendar.php');
90
  include('classes/em-category.php');
91
- include('classes/em-category-taxonomy.php');
92
  include('classes/em-categories.php');
 
93
  include('classes/em-event.php');
94
  include('classes/em-event-post.php');
95
  include('classes/em-events.php');
@@ -102,8 +107,8 @@ include('classes/em-people.php');
102
  include('classes/em-person.php');
103
  include('classes/em-permalinks.php');
104
  include('classes/em-tag.php');
105
- include('classes/em-tag-taxonomy.php');
106
  include('classes/em-tags.php');
 
107
  include('classes/em-ticket-booking.php');
108
  include('classes/em-ticket.php');
109
  include('classes/em-tickets-bookings.php');
@@ -123,7 +128,9 @@ if( is_admin() ){
123
  include('classes/em-event-posts-admin.php');
124
  include('classes/em-location-post-admin.php');
125
  include('classes/em-location-posts-admin.php');
126
- include('classes/em-categories-taxonomy.php');
 
 
127
  //bookings folder
128
  include('admin/bookings/em-cancelled.php');
129
  include('admin/bookings/em-confirmed.php');
@@ -148,7 +155,6 @@ if( EM_MS_GLOBAL ){
148
  }else{
149
  $prefix = $wpdb->prefix;
150
  }
151
- define('EM_CATEGORIES_TABLE', $prefix.'em_categories'); //TABLE NAME
152
  define('EM_EVENTS_TABLE',$prefix.'em_events'); //TABLE NAME
153
  define('EM_TICKETS_TABLE', $prefix.'em_tickets'); //TABLE NAME
154
  define('EM_TICKETS_BOOKINGS_TABLE', $prefix.'em_tickets_bookings'); //TABLE NAME
@@ -219,7 +225,7 @@ class EM_Scripts_and_Styles {
219
  if( is_page($pages) ){
220
  $script_deps['jquery'] = 'jquery';
221
  }
222
- if( (!empty($pages['events']) && is_page($pages['events']) && get_option('dbem_events_page_search_form')) || get_option('dbem_js_limit_search') === '0' || in_array($obj_id, explode(',', get_option('dbem_js_limit_search'))) ){
223
  //events page only needs datepickers
224
  $script_deps['jquery-ui-core'] = 'jquery-ui-core';
225
  $script_deps['jquery-ui-datepicker'] = 'jquery-ui-datepicker';
@@ -263,7 +269,7 @@ class EM_Scripts_and_Styles {
263
  'jquery-ui-autocomplete'=>'jquery-ui-autocomplete',
264
  'jquery-ui-dialog'=>'jquery-ui-dialog'
265
  );
266
- }
267
  $script_deps = apply_filters('em_public_script_deps', $script_deps);
268
  if( !empty($script_deps) ){ //given we depend on jQuery, there must be at least a jQuery dep for our file to be loaded
269
  wp_enqueue_script('events-manager', plugins_url('includes/js/events-manager.js',__FILE__), array_values($script_deps), EM_VERSION); //jQuery will load as dependency
@@ -293,7 +299,8 @@ class EM_Scripts_and_Styles {
293
 
294
  public static function admin_enqueue( $hook_suffix = false ){
295
  if( $hook_suffix == 'post.php' || (!empty($_GET['page']) && substr($_GET['page'],0,14) == 'events-manager') || (!empty($_GET['post_type']) && in_array($_GET['post_type'], array(EM_POST_TYPE_EVENT,EM_POST_TYPE_LOCATION,'event-recurring'))) ){
296
- wp_enqueue_script('events-manager', plugins_url('includes/js/events-manager.js',__FILE__), array('jquery', 'jquery-ui-core','jquery-ui-widget','jquery-ui-position','jquery-ui-sortable','jquery-ui-datepicker','jquery-ui-autocomplete','jquery-ui-dialog'), EM_VERSION);
 
297
  do_action('em_enqueue_admin_scripts');
298
  wp_enqueue_style('events-manager-admin', plugins_url('includes/css/events_manager_admin.css',__FILE__), array(), EM_VERSION);
299
  do_action('em_enqueue_admin_styles');
@@ -360,13 +367,13 @@ class EM_Scripts_and_Styles {
360
  //logged in messages that visitors shouldn't need to see
361
  if( is_user_logged_in() || is_page(get_option('dbem_edit_events_page')) ){
362
  if( get_option('dbem_recurrence_enabled') ){
363
- if( !empty($_REQUEST['action']) && $_REQUEST['action'] == 'edit' && !empty($_REQUEST['event_id'])){
364
- $em_localized_js['event_reschedule_warning'] = __('Are you sure you want to continue?', 'events-manager') .PHP_EOL.PHP_EOL;
365
  $em_localized_js['event_reschedule_warning'] .= __('Modifications to event times will cause all recurrences of this event to be deleted and recreated, previous bookings will be deleted.', 'events-manager');
366
- $em_localized_js['event_recurrence_overwrite'] = __('Are you sure you want to continue?', 'events-manager') .PHP_EOL.PHP_EOL;
367
- $em_localized_js['event_recurrence_overwrite'] .= __( 'Modifications to recurring events will be applied to all recurrences and will overwrite any changes made to those individual event recurrences.', 'events-manager') .PHP_EOL.PHP_EOL;
368
  $em_localized_js['event_recurrence_overwrite'] .= __( 'Bookings to individual event recurrences will be preserved if event times and ticket settings are not modified.', 'events-manager');
369
- $em_localized_js['event_recurrence_bookings'] = __('Are you sure you want to continue?', 'events-manager') .PHP_EOL.PHP_EOL;
370
  $em_localized_js['event_recurrence_bookings'] .= __('Modifications to event tickets will cause all bookings to individual recurrences of this event to be deleted.', 'events-manager');
371
  }
372
  $em_localized_js['event_detach_warning'] = __('Are you sure you want to detach this event? By doing so, this event will be independent of the recurring set of events.', 'events-manager');
@@ -586,12 +593,12 @@ if( is_multisite() ){
586
  $return = get_site_option(str_replace('pre_option_','',$filter_name));
587
  return $return;
588
  }elseif( strstr($filter_name, 'pre_update_option_') !== false ){
589
- if( is_super_admin() ){
590
  update_site_option(str_replace('pre_update_option_','',$filter_name), $value[0]);
591
  }
592
  return $value[1];
593
  }elseif( strstr($filter_name, 'add_option_') !== false ){
594
- if( is_super_admin() ){
595
  update_site_option(str_replace('add_option_','',$filter_name),$value[0]);
596
  }
597
  delete_option(str_replace('pre_option_','',$filter_name));
1
  <?php
2
  /*
3
  Plugin Name: Events Manager
4
+ Version: 5.8.0.1
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
10
  */
11
 
12
  /*
13
+ Copyright (c) 2017, Marcus Sykes
14
 
15
  This program is free software; you can redistribute it and/or
16
  modify it under the terms of the GNU General Public License
28
  */
29
 
30
  // Setting constants
31
+ define('EM_VERSION', 5.8); //self expanatory
32
  define('EM_PRO_MIN_VERSION', 2.392); //self expanatory
33
  define('EM_PRO_MIN_VERSION_CRITICAL', 2.377); //self expanatory
34
  define('EM_DIR', dirname( __FILE__ )); //an absolute path to this directory
43
  if( !defined('EM_CONDITIONAL_RECURSIONS') ) define('EM_CONDITIONAL_RECURSIONS', get_option('dbem_conditional_recursions', 1)); //allows for conditional recursios to be nested
44
 
45
  //EM_MS_GLOBAL
46
+ if( is_multisite() && get_site_option('dbem_ms_global_table') ){
47
  define('EM_MS_GLOBAL', true);
48
  }else{
49
  define('EM_MS_GLOBAL',false);
65
  //add_action('plugins_loaded', 'dbem_debug_mode');
66
 
67
  // INCLUDES
68
+ //Base classes
69
+ include('classes/em-object.php');
70
+ include('classes/em-taxonomy-term.php');
71
+ include('classes/em-taxonomy-terms.php');
72
+ include('classes/em-taxonomy-frontend.php');
73
+ //set up events as posts
74
+ include("em-posts.php");
75
  //Template Tags & Template Logic
76
  include("em-actions.php");
77
  include("em-events.php");
93
  include("classes/em-bookings-table.php") ;
94
  include('classes/em-calendar.php');
95
  include('classes/em-category.php');
 
96
  include('classes/em-categories.php');
97
+ include('classes/em-categories-frontend.php');
98
  include('classes/em-event.php');
99
  include('classes/em-event-post.php');
100
  include('classes/em-events.php');
107
  include('classes/em-person.php');
108
  include('classes/em-permalinks.php');
109
  include('classes/em-tag.php');
 
110
  include('classes/em-tags.php');
111
+ include('classes/em-tags-frontend.php');
112
  include('classes/em-ticket-booking.php');
113
  include('classes/em-ticket.php');
114
  include('classes/em-tickets-bookings.php');
128
  include('classes/em-event-posts-admin.php');
129
  include('classes/em-location-post-admin.php');
130
  include('classes/em-location-posts-admin.php');
131
+ include('classes/em-taxonomy-admin.php');
132
+ include('classes/em-categories-admin.php');
133
+ include('classes/em-tags-admin.php');
134
  //bookings folder
135
  include('admin/bookings/em-cancelled.php');
136
  include('admin/bookings/em-confirmed.php');
155
  }else{
156
  $prefix = $wpdb->prefix;
157
  }
 
158
  define('EM_EVENTS_TABLE',$prefix.'em_events'); //TABLE NAME
159
  define('EM_TICKETS_TABLE', $prefix.'em_tickets'); //TABLE NAME
160
  define('EM_TICKETS_BOOKINGS_TABLE', $prefix.'em_tickets_bookings'); //TABLE NAME
225
  if( is_page($pages) ){
226
  $script_deps['jquery'] = 'jquery';
227
  }
228
+ if( (!empty($pages['events']) && is_page($pages['events']) && ( get_option('dbem_events_page_search_form') || (EM_MS_GLOBAL && !get_site_option('dbem_ms_global_events_links', true)) )) || get_option('dbem_js_limit_search') === '0' || in_array($obj_id, explode(',', get_option('dbem_js_limit_search'))) ){
229
  //events page only needs datepickers
230
  $script_deps['jquery-ui-core'] = 'jquery-ui-core';
231
  $script_deps['jquery-ui-datepicker'] = 'jquery-ui-datepicker';
269
  'jquery-ui-autocomplete'=>'jquery-ui-autocomplete',
270
  'jquery-ui-dialog'=>'jquery-ui-dialog'
271
  );
272
+ }
273
  $script_deps = apply_filters('em_public_script_deps', $script_deps);
274
  if( !empty($script_deps) ){ //given we depend on jQuery, there must be at least a jQuery dep for our file to be loaded
275
  wp_enqueue_script('events-manager', plugins_url('includes/js/events-manager.js',__FILE__), array_values($script_deps), EM_VERSION); //jQuery will load as dependency
299
 
300
  public static function admin_enqueue( $hook_suffix = false ){
301
  if( $hook_suffix == 'post.php' || (!empty($_GET['page']) && substr($_GET['page'],0,14) == 'events-manager') || (!empty($_GET['post_type']) && in_array($_GET['post_type'], array(EM_POST_TYPE_EVENT,EM_POST_TYPE_LOCATION,'event-recurring'))) ){
302
+ wp_enqueue_style( 'wp-color-picker' );
303
+ wp_enqueue_script('events-manager', plugins_url('includes/js/events-manager.js',__FILE__), array('jquery', 'jquery-ui-core','jquery-ui-widget','jquery-ui-position','jquery-ui-sortable','jquery-ui-datepicker','jquery-ui-autocomplete','jquery-ui-dialog','wp-color-picker'), EM_VERSION);
304
  do_action('em_enqueue_admin_scripts');
305
  wp_enqueue_style('events-manager-admin', plugins_url('includes/css/events_manager_admin.css',__FILE__), array(), EM_VERSION);
306
  do_action('em_enqueue_admin_styles');
367
  //logged in messages that visitors shouldn't need to see
368
  if( is_user_logged_in() || is_page(get_option('dbem_edit_events_page')) ){
369
  if( get_option('dbem_recurrence_enabled') ){
370
+ if( !empty($_REQUEST['action']) && ($_REQUEST['action'] == 'edit' || $_REQUEST['action'] == 'event_save') && !empty($_REQUEST['event_id']) ){
371
+ $em_localized_js['event_reschedule_warning'] = __('Are you sure you want to continue?', 'events-manager') .PHP_EOL;
372
  $em_localized_js['event_reschedule_warning'] .= __('Modifications to event times will cause all recurrences of this event to be deleted and recreated, previous bookings will be deleted.', 'events-manager');
373
+ $em_localized_js['event_recurrence_overwrite'] = __('Are you sure you want to continue?', 'events-manager') .PHP_EOL;
374
+ $em_localized_js['event_recurrence_overwrite'] .= __( 'Modifications to recurring events will be applied to all recurrences and will overwrite any changes made to those individual event recurrences.', 'events-manager') .PHP_EOL;
375
  $em_localized_js['event_recurrence_overwrite'] .= __( 'Bookings to individual event recurrences will be preserved if event times and ticket settings are not modified.', 'events-manager');
376
+ $em_localized_js['event_recurrence_bookings'] = __('Are you sure you want to continue?', 'events-manager') .PHP_EOL;
377
  $em_localized_js['event_recurrence_bookings'] .= __('Modifications to event tickets will cause all bookings to individual recurrences of this event to be deleted.', 'events-manager');
378
  }
379
  $em_localized_js['event_detach_warning'] = __('Are you sure you want to detach this event? By doing so, this event will be independent of the recurring set of events.', 'events-manager');
593
  $return = get_site_option(str_replace('pre_option_','',$filter_name));
594
  return $return;
595
  }elseif( strstr($filter_name, 'pre_update_option_') !== false ){
596
+ if( em_wp_is_super_admin() ){
597
  update_site_option(str_replace('pre_update_option_','',$filter_name), $value[0]);
598
  }
599
  return $value[1];
600
  }elseif( strstr($filter_name, 'add_option_') !== false ){
601
+ if( em_wp_is_super_admin() ){
602
  update_site_option(str_replace('add_option_','',$filter_name),$value[0]);
603
  }
604
  delete_option(str_replace('pre_option_','',$filter_name));
includes/css/events_manager_admin.css CHANGED
@@ -129,6 +129,7 @@ table.events-table .category { color:#888; }
129
  #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; }
130
  #em-options-form tbody.em-subsection th { padding-left:35px; }
131
  table.em-caps-table th, table.em-caps-table td { width:auto !important; }
 
132
  .em-translatable { display:inline-block; width:20px; height:20px; font-size:20px; padding:0px 5px 0px 0px; cursor:pointer; }
133
  .em-ml-options { display:none; }
134
  .em-ml-options table { width:95%; }
129
  #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; }
130
  #em-options-form tbody.em-subsection th { padding-left:35px; }
131
  table.em-caps-table th, table.em-caps-table td { width:auto !important; }
132
+ #em-options-form textarea, #em-options-form input[type="text"] { width:95%; }
133
  .em-translatable { display:inline-block; width:20px; height:20px; font-size:20px; padding:0px 5px 0px 0px; cursor:pointer; }
134
  .em-ml-options { display:none; }
135
  .em-ml-options table { width:95%; }
includes/js/admin-settings.js CHANGED
@@ -29,11 +29,11 @@ jQuery(document).ready(function($){
29
  });
30
  var navUrl = document.location.toString();
31
  if (navUrl.match('#')) { //anchor-based navigation
32
- var nav_tab = navUrl.split('#');
33
- var current_tab = 'a#em-menu-' + nav_tab[1];
34
  $(current_tab).trigger('click');
35
- if( nav_tab.length > 2 ){
36
- section = $("#em-opt-"+nav_tab[2]);
37
  if( section.length > 0 ){
38
  section.children('h3').trigger('click');
39
  $('html, body').animate({ scrollTop: section.offset().top - 30 }); //sends user back to current section
@@ -49,13 +49,15 @@ jQuery(document).ready(function($){
49
  var docloc = document.location.toString().split('#');
50
  var newloc = docloc[0];
51
  if( docloc.length > 1 ){
52
- var nav_tab = docloc[1].split('#');
53
- newloc = newloc + "#" + nav_tab[0];
54
  if( el.attr('id') ){
55
- newloc = newloc + "#" + el.attr('id').replace('em-opt-','');
56
  }
 
57
  }
58
  document.location = newloc;
 
59
  });
60
  //Page Options
61
  $('input[name="dbem_cp_events_has_archive"]').change(function(){ //event archives
@@ -107,6 +109,11 @@ jQuery(document).ready(function($){
107
  el.val() == '1' ? $(el.attr('data-trigger')).show() : $(el.attr('data-trigger')).hide();
108
  });
109
  $('input.em-trigger:checked').trigger('change');
 
 
 
 
 
110
  //admin tools confirm
111
  $('a.admin-tools-db-cleanup').click( function( e ){
112
  if( !confirm(EM.admin_db_cleanup_warning) ){
@@ -114,4 +121,6 @@ jQuery(document).ready(function($){
114
  return false;
115
  }
116
  });
 
 
117
  });
29
  });
30
  var navUrl = document.location.toString();
31
  if (navUrl.match('#')) { //anchor-based navigation
32
+ var nav_tab = navUrl.split('#').pop().split('+');
33
+ var current_tab = 'a#em-menu-' + nav_tab[0];
34
  $(current_tab).trigger('click');
35
+ if( nav_tab.length > 1 ){
36
+ section = $("#em-opt-"+nav_tab[1]);
37
  if( section.length > 0 ){
38
  section.children('h3').trigger('click');
39
  $('html, body').animate({ scrollTop: section.offset().top - 30 }); //sends user back to current section
49
  var docloc = document.location.toString().split('#');
50
  var newloc = docloc[0];
51
  if( docloc.length > 1 ){
52
+ var nav_tab = docloc[1].split('+');
53
+ var tab_path = nav_tab[0];
54
  if( el.attr('id') ){
55
+ tab_path = tab_path + "+" + el.attr('id').replace('em-opt-','');
56
  }
57
+ newloc = newloc + "#" + tab_path;
58
  }
59
  document.location = newloc;
60
+ $(this).closest('form').append('<input type="hidden" name="tab_path" value="'+ tab_path +'" />');
61
  });
62
  //Page Options
63
  $('input[name="dbem_cp_events_has_archive"]').change(function(){ //event archives
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) ){
121
  return false;
122
  }
123
  });
124
+ //color pickers
125
+ $('#dbem_category_default_color, #dbem_tag_default_color').wpColorPicker();
126
  });
includes/js/categories-admin.js DELETED
@@ -1,69 +0,0 @@
1
- jQuery(document).ready(function($) {
2
- //color picker
3
- var f = $.farbtastic('#picker');
4
- var p = $('#picker').css('opacity', 0.25);
5
- var selected;
6
- $('.colorwell').each(function () { f.linkTo(this); $(this).css('opacity', 0.75); }).focus(function() {
7
- if (selected) { $(selected).css('opacity', 0.75).removeClass('colorwell-selected'); }
8
- f.linkTo(this);
9
- p.css('opacity', 1);
10
- $(selected = this).css('opacity', 1).addClass('colorwell-selected');
11
- });
12
- $('.colorwell').click(function() {
13
- var position = $(selected = this).position();
14
- $('#picker').css('left', (position.left + 150) );
15
- $('#picker').css('top', position.top);
16
- $('#picker').fadeIn(900,function (){
17
- $('#picker').css('display', 'inline');
18
- });
19
- }).blur(function(){
20
- $('#picker').fadeOut('fast');
21
- $('#picker').css('display', 'none');
22
- });
23
-
24
- //Event Taxonomy Image Picker
25
- var frame;
26
- // ADD IMAGE LINK
27
- $('#event-tax-image .upload-img-button').on( 'click', function( event ){
28
- event.preventDefault();
29
- // If the media frame already exists, reopen it.
30
- if ( frame ) {
31
- frame.open();
32
- return;
33
- }
34
- // Create a new media frame
35
- frame = wp.media({
36
- library: {
37
- type: 'image'
38
- },
39
- title: wp.media.view.l10n.chooseImage,
40
- multiple: false // Set to true to allow multiple files to be selected
41
- });
42
- // When an image is selected in the media frame...
43
- frame.on( 'select', function() {
44
- // Get media attachment details from the frame state
45
- var attachment = frame.state().get('selection').first().toJSON();
46
- // Send the attachment URL to our custom image input field.
47
- $( '#event-tax-image .img-container').empty().append( '<img src="'+attachment.url+'" alt="" style="max-width:100%;"/>' );
48
- // Send the attachment id to our hidden input
49
- $( '#event-tax-image .img-id' ).val( attachment.id );
50
- $( '#event-tax-image .img-url' ).val( attachment.url );
51
- // Unhide the remove image link
52
- $( '#event-tax-image .delete-img-button').show();
53
- });
54
- // Finally, open the modal on click
55
- frame.open();
56
- });
57
- // DELETE IMAGE LINK
58
- $( '#event-tax-image .delete-img-button').on( 'click', function( event ){
59
- event.preventDefault();
60
- // Clear out the preview image
61
- $( '#event-tax-image .img-container').html( '' );
62
- // Un-hide the add image link
63
- $(this).hide();
64
- // Delete the image id from the hidden input
65
- $( '#event-tax-image .img-id' ).val( '' );
66
- $( '#event-tax-image .img-url' ).val( '' );
67
- });
68
-
69
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/js/taxonomies-admin.js ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready(function($) {
2
+ //other color picker
3
+ $('.term-color').wpColorPicker();
4
+
5
+ //Event Taxonomy Image Picker
6
+ var frame;
7
+ // ADD IMAGE LINK
8
+ $('.term-image-wrap .upload-img-button').on( 'click', function( event ){
9
+ event.preventDefault();
10
+ // If the media frame already exists, reopen it.
11
+ if ( frame ) {
12
+ frame.open();
13
+ return;
14
+ }
15
+ // Create a new media frame
16
+ frame = wp.media({
17
+ library: {
18
+ type: 'image'
19
+ },
20
+ title: wp.media.view.l10n.chooseImage,
21
+ multiple: false // Set to true to allow multiple files to be selected
22
+ });
23
+ // When an image is selected in the media frame...
24
+ frame.on( 'select', function() {
25
+ // Get media attachment details from the frame state
26
+ var attachment = frame.state().get('selection').first().toJSON();
27
+ // Send the attachment URL to our custom image input field.
28
+ $( '.term-image-wrap .img-container').empty().append( '<img src="'+attachment.url+'" alt="" style="max-width:100%;"/>' );
29
+ // Send the attachment id to our hidden input
30
+ $( '.term-image-wrap .img-id' ).val( attachment.id );
31
+ $( '.term-image-wrap .img-url' ).val( attachment.url );
32
+ // Unhide the remove image link
33
+ $( '.term-image-wrap .delete-img-button').show();
34
+ });
35
+ // Finally, open the modal on click
36
+ frame.open();
37
+ });
38
+ // DELETE IMAGE LINK
39
+ $( '.term-image-wrap .delete-img-button').on( 'click', function( event ){
40
+ event.preventDefault();
41
+ // Clear out the preview image
42
+ $( '.term-image-wrap .img-container').html( '' );
43
+ // Un-hide the add image link
44
+ $(this).hide();
45
+ // Delete the image id from the hidden input
46
+ $( '.term-image-wrap .img-id' ).val( '' );
47
+ $( '.term-image-wrap .img-url' ).val( '' );
48
+ });
49
+
50
+ });
multilingual/em-ml-admin.php CHANGED
@@ -2,7 +2,9 @@
2
  class EM_ML_Admin{
3
 
4
  public static function init(){
5
- add_action('add_meta_boxes', 'EM_ML_Admin::meta_boxes',100);
 
 
6
  if( !defined('EM_SETTINGS_TABS') && count(EM_ML::$langs) > 3 ) define('EM_SETTINGS_TABS',true);
7
  }
8
 
2
  class EM_ML_Admin{
3
 
4
  public static function init(){
5
+ add_action('add_meta_boxes_'.EM_POST_TYPE_EVENT, 'EM_ML_Admin::meta_boxes',100);
6
+ add_action('add_meta_boxes_event-recurring', 'EM_ML_Admin::meta_boxes', 100, 1);
7
+ add_action('add_meta_boxes_'.EM_POST_TYPE_LOCATION, 'EM_ML_Admin::meta_boxes', 100, 1);
8
  if( !defined('EM_SETTINGS_TABS') && count(EM_ML::$langs) > 3 ) define('EM_SETTINGS_TABS',true);
9
  }
10
 
multilingual/em-ml-bookings.php CHANGED
@@ -29,15 +29,18 @@ class EM_ML_Bookings {
29
  $EM_Bookings = new EM_Bookings($event);
30
  $EM_Bookings->event_id = $event->event_id;
31
  $EM_Bookings->translated = true;
32
- //go through tickets and translate to appropriate language
33
- $event_lang = EM_ML::get_the_language($EM_Event);
34
- foreach($EM_Bookings->get_tickets()->tickets as $EM_Ticket){ /* @var $EM_Ticket EM_Ticket */
35
- if( !empty($EM_Ticket->ticket_meta['langs'][$event_lang]['ticket_name']) ){
36
- $EM_Ticket->ticket_name = $EM_Ticket->ticket_meta['langs'][$event_lang]['ticket_name'];
37
- }
38
- if( !empty($EM_Ticket->ticket_meta['langs'][$event_lang]['ticket_description']) ){
39
- $EM_Ticket->ticket_description = $EM_Ticket->ticket_meta['langs'][$event_lang]['ticket_description'];
40
- }
 
 
 
41
  }
42
  }
43
  }
29
  $EM_Bookings = new EM_Bookings($event);
30
  $EM_Bookings->event_id = $event->event_id;
31
  $EM_Bookings->translated = true;
32
+ //go through tickets and translate to appropriate language if we're not in a saving loop
33
+ global $EM_EVENT_SAVE_POST, $EM_SAVING_EVENT;
34
+ if( empty($EM_SAVING_EVENT) && empty($EM_EVENT_SAVE_POST) ){
35
+ $event_lang = EM_ML::get_the_language($EM_Event);
36
+ foreach($EM_Bookings->get_tickets()->tickets as $EM_Ticket){ /* @var $EM_Ticket EM_Ticket */
37
+ if( !empty($EM_Ticket->ticket_meta['langs'][$event_lang]['ticket_name']) ){
38
+ $EM_Ticket->ticket_name = $EM_Ticket->ticket_meta['langs'][$event_lang]['ticket_name'];
39
+ }
40
+ if( !empty($EM_Ticket->ticket_meta['langs'][$event_lang]['ticket_description']) ){
41
+ $EM_Ticket->ticket_description = $EM_Ticket->ticket_meta['langs'][$event_lang]['ticket_description'];
42
+ }
43
+ }
44
  }
45
  }
46
  }
readme.txt CHANGED
@@ -4,8 +4,8 @@ Donate link: http://wp-events-plugin.com
4
  Tags: bookings, calendar, tickets, events, buddypress, event management, google maps, maps, locations, registration
5
  Text Domain: events-manager
6
  Requires at least: 3.5
7
- Tested up to: 4.8
8
- Stable tag: 5.7.3
9
 
10
  Fully featured event registration management including recurring events, locations management, calendar, Google map integration, booking management
11
 
@@ -99,6 +99,78 @@ See our [FAQ](http://wp-events-plugin.com/documentation/faq/) page, which is upd
99
  6. Manage attendees with various booking reports
100
 
101
  == Changelog ==
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  = 5.7.3 =
103
  * fixed previous meta not getting deleted from recurrences resulting in non-saved information such as a changed featured image or location
104
  * fixed newly submitted recurrences first saved in draft or pending mode not creating new events without a reschedule
4
  Tags: bookings, calendar, tickets, events, buddypress, event management, google maps, maps, locations, registration
5
  Text Domain: events-manager
6
  Requires at least: 3.5
7
+ Tested up to: 4.9
8
+ Stable tag: 5.8.0.1
9
 
10
  Fully featured event registration management including recurring events, locations management, calendar, Google map integration, booking management
11
 
99
  6. Manage attendees with various booking reports
100
 
101
  == Changelog ==
102
+ = 5.8.0.1 =
103
+ * fixed category color picker and image uploader problems
104
+ * created base classes for EM taxonomies to make adding custom EM taxonomies even easier in the future,
105
+ * added tag image and color settings/data
106
+ * added sortable option for date columns of events and recurring events in the admin area
107
+ * fixed saving an event recreating ticket_meta and wiping out ML settings
108
+ * fixed multilingual translations of event in WPML not hiding/showing right meta boxes
109
+ * fixed saving multilingual translation of event overwriting original language ticket names
110
+ * changed admin headers to use new and accessible WP inline HTML structure
111
+ * fixed serialization problems for deprecated event/location attributes and attributes with multiple post meta entries for one post
112
+ * removed deprecated attributes editor from admin area as this is confusing and error prone when combined with other plugins manipulating custom fields
113
+ * fixed #_EVENTEXCERPT without arguments stripping HTML since 5.7
114
+ * added ability to list multiple categories/tags in conditional placeholders e.g. {has_tag_123,1234,tagname}...
115
+ * added #_EVENTPRICEMINALL and #_EVENTPRICEMAXALL to show prices of unavailable tickets as well
116
+ * fixed JS issues with MS Global mode and JS file limiting when displaying subsite single event pages on main blog
117
+ * fixed single initial abbreviation issues in Chinese calendars
118
+ * fixed duplicate events not being published to social networks via jetpack publicize (kudos @gnaag)
119
+ * fixed potential incompatibilities with other plugins using wp_query_reset() on category pages, preventing our page formats from showing
120
+ * fixed taxonomy archive pages returning zero results if taxonomy formatting is disabled and events are excluded from searches (WP Bug workaround)
121
+ * fixed inconsistent line ending causing warnings with PHP compatibility checker
122
+ * unified Tag and Category class functions into sets of parent class functionn
123
+ * fixed PHP fatal error with BP when disabling notifications
124
+ * fixed calendar day links being incorrect if another plugin adds querystring params to permalinks
125
+ * added groupby, groupby_orderby and groupby_order arguments allowing grouping in search results for events and locations
126
+ * improved validation and sanitization of orderby arguments to avoid ambiguous field SQL errors
127
+ * added optimization to optionally join event/location tables when needed for grouped searches or if EM_DISABLE_OPTIONAL_JOINS is defined and set to true
128
+ * moved condition of when argument 'bookings' = 'user' to EM_Object so it's accessible by EM_Location too
129
+ * changed default 'recurring' argument to null so that non EM_Events searches can by default avoid the recurring conditional being generated
130
+ * added grouping to AJAX response for event_map shortcode and function resulting in performance improvements
131
+ * fixed use of get_terms via deprecated two argument method
132
+ * fixed recreation of tickets for a recurring event having wrong start/end ticket dates
133
+ * fixed custom fields with a 0 value getting deleted instead of saved
134
+ * fixed/changed - shortened SQL search conditions when filtering by array or comma list of event/location ids
135
+ * fixed escaping of apostrophes on search term placeholder
136
+ * added optimized result counts (EM_Events::$num_rows and EM_Events::$num_rows_total) in EM_Events::get() to reduce number of SQL calls
137
+ * added optimized result counts (EM_Locations::$num_rows and EM_Locations::$num_rows_total) in EM_Locations::get() to reduce number of SQL calls
138
+ * changed as a result of the above two additions above we tweaked various event list areas to half number of SQL queries run per list
139
+ * added no_results_msg argument to EM_Events::output() functions for custom 'no events found'
140
+ * added location_status and event_status search arguments to event and location searches respectively
141
+ * fixed column alignment issues when using quick edit for events in wp dashboard
142
+ * tweaked excerpt filters and reduced redundant calls to our the_content filters (reducing overhead)
143
+ * changed event page the_content loading so it only shows when in_the_loop() and is_main_query() is true
144
+ * fixed issues with WordPress SEO plugin breaking the wp editor front-end
145
+ * added event list default sorting options to locations, tags and categories
146
+ * added em_wp_is_super_admin() replacing is_super_admin() in anticipation of its deprecation
147
+ * changed and optimized postmeta saving process to reduce number of rows by up to 80% per event,
148
+ * optimized events table to save NULL values when possible
149
+ * changed/removed unused event_category_id field from events table
150
+ * added em_object_can_manage filter
151
+ * fixed nested attributes such as {cond}#_ATT{name}{/cond} not being parsed properly
152
+ * fixed some installation SQL PHP errors/warnings
153
+ * fixed capability mapping problems if revisions enabled for events or locations
154
+ * added WEBCAL placeholders to events, locations, categories and tags (e.g. #_EVENTICALLINK > #_EVENTWEBCALLINK)
155
+ * removed unnecessary SQL queries for undeclared wp_options reducing number of queries made per page load
156
+ * tweaked settings page textarea elements to be wider
157
+ * added (possibly temporary) EM_FORCE_RECURRENCES_SAVE constant forcing status save for when recurrences change status,
158
+ * fixed EM_Event::set_status_events() which was not working correctly and now also runs a em_event_set_status_events filter
159
+ * fixed recurring events warning text not appearing in front-end editor if first submission produced validation errors
160
+ * performance improvements to EM_Bookings and EM_Ticket objects by preventing pre-loading of all bookings on instantiation and when querying availability
161
+ * added cache optimization for event and location loading,
162
+ * improved optimized loading of EM_Location via em_get_location() by checking globals first,
163
+ * changed bookings/tickets template for event editor to avoid loading all bookings for counting purposes
164
+ * added $include_adjustments argument to get_price_pre_taxes and get_price_post_taxes for calculation of prices without discounts/surcharges (those making use of the em_booking_get_price_post_taxes and em_booking_get_price_pre_taxes filters should check for this passed argument in their custom code)
165
+ * fixed bug where event categories saved via quick or bulk edit in multisite global mode aren't reflected in subsite event lists filtering by that category
166
+ * fixed events created before changing to multisite and enabling global mode not displaying images via placeholders
167
+ * fixed bug in filter em_ticket_get_post where function argument $post was not passed on
168
+ * improved taxonomy color retrieval so it makes use of caching
169
+ * improved/optimized deletion of multiple bookings and tickets at once if belonging to an event
170
+ * fixed my_bookings shortcode outputting content at top of content irrespective of where it is located within content
171
+ * fixed invalid recurrence ticket start/end cut-off date/times if both are not set for the recurring event
172
+ * fixed bug where ticket start/end dates cannot be unset in recurring events
173
+
174
  = 5.7.3 =
175
  * fixed previous meta not getting deleted from recurrences resulting in non-saved information such as a changed featured image or location
176
  * fixed newly submitted recurrences first saved in draft or pending mode not creating new events without a reschedule
templates/buddypress/profile.php CHANGED
@@ -11,13 +11,14 @@ if( user_can($bp->displayed_user->id,'edit_events') ){
11
  'format_header' => get_option('dbem_bp_events_list_format_header'),
12
  'format' => get_option('dbem_bp_events_list_format'),
13
  'format_footer' => get_option('dbem_bp_events_list_format_footer'),
 
14
  'owner' => $bp->displayed_user->id,
15
  'pagination'=>1
16
  );
17
  $args['limit'] = !empty($args['limit']) ? $args['limit'] : get_option('dbem_events_default_limit');
18
- if( EM_Events::count($args) > 0 ){
19
- echo EM_Events::output($args);
20
- }else{
21
  ?>
22
  <p><?php _e('No Events', 'events-manager'); ?>.
23
  <?php if( get_current_user_id() == $bp->displayed_user->id ): ?>
11
  'format_header' => get_option('dbem_bp_events_list_format_header'),
12
  'format' => get_option('dbem_bp_events_list_format'),
13
  'format_footer' => get_option('dbem_bp_events_list_format_footer'),
14
+ 'no_results_msg' => false,
15
  'owner' => $bp->displayed_user->id,
16
  'pagination'=>1
17
  );
18
  $args['limit'] = !empty($args['limit']) ? $args['limit'] : get_option('dbem_events_default_limit');
19
+ echo EM_Events::output($args);
20
+ if( EM_Events::$num_rows_found == 0 ){
21
+ //no events output on last function
22
  ?>
23
  <p><?php _e('No Events', 'events-manager'); ?>.
24
  <?php if( get_current_user_id() == $bp->displayed_user->id ): ?>
templates/forms/event/attributes.php CHANGED
@@ -46,38 +46,6 @@ $has_deprecated = false;
46
  ?>
47
  </tbody>
48
  </table>
49
- <?php if( count(array_diff(array_keys($EM_Event->event_attributes), $attributes['names'])) > 0 ): ?>
50
- <p><strong><?php _e('Deprecated Attributes', 'events-manager')?></strong></p>
51
- <p><em><?php _e("If you see any attributes under here, that means they're not used in Events Manager formats. To add them, you need to add the custom attribute again to a formatting option in the settings page. To remove any of these deprecated attributes, give it a blank value and save.",'events-manager') ?></em></p>
52
- <table class="form-table">
53
- <thead>
54
- <tr valign="top">
55
- <td><strong>Attribute Name</strong></td>
56
- <td><strong>Value</strong></td>
57
- </tr>
58
- </thead>
59
- <tbody id="mtm_body">
60
- <?php
61
- if( is_array($EM_Event->event_attributes) and count($EM_Event->event_attributes) > 0){
62
- foreach( $EM_Event->event_attributes as $name => $value){
63
- if( is_array($value) ) $value = serialize($value);
64
- if( !in_array($name, $attributes['names']) ){
65
- ?>
66
- <tr valign="top" id="em_attribute_<?php echo $count ?>">
67
- <td scope="row"><?php echo $name ?></td>
68
- <td>
69
- <input type="text" name="em_attributes[<?php echo $name ?>]" value="<?php echo esc_attr($value, ENT_QUOTES); ?>" />
70
- </td>
71
- </tr>
72
- <?php
73
- $count++;
74
- }
75
- }
76
- }
77
- ?>
78
- </tbody>
79
- </table>
80
- <?php endif; ?>
81
  <?php else : ?>
82
  <p>
83
  <?php _e('In order to use attributes, you must define some in your templates, otherwise they\'ll never show. Go to Events > Settings > General to add attribute placeholders.', 'events-manager'); ?>
46
  ?>
47
  </tbody>
48
  </table>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  <?php else : ?>
50
  <p>
51
  <?php _e('In order to use attributes, you must define some in your templates, otherwise they\'ll never show. Go to Events > Settings > General to add attribute placeholders.', 'events-manager'); ?>
templates/forms/event/bookings.php CHANGED
@@ -88,7 +88,7 @@ $reschedule_warnings = !empty($EM_Event->event_id) && $EM_Event->is_recurring()
88
  <div class="ticket_description"><?php echo wp_kses($EM_Ticket->ticket_description,$allowedposttags); ?></div>
89
  <div class="ticket-actions">
90
  <a href="#" class="ticket-actions-edit"><?php esc_html_e('Edit','events-manager'); ?></a>
91
- <?php if( count($EM_Ticket->get_bookings()->bookings) == 0 ): ?>
92
  | <a href="<?php bloginfo('wpurl'); ?>/wp-load.php" class="ticket-actions-delete"><?php esc_html_e('Delete','events-manager'); ?></a>
93
  <?php else: ?>
94
  | <a href="<?php echo esc_url(add_query_arg('ticket_id', $EM_Ticket->ticket_id, $EM_Event->get_bookings_url())); ?>"><?php esc_html_e('View Bookings','events-manager'); ?></a>
88
  <div class="ticket_description"><?php echo wp_kses($EM_Ticket->ticket_description,$allowedposttags); ?></div>
89
  <div class="ticket-actions">
90
  <a href="#" class="ticket-actions-edit"><?php esc_html_e('Edit','events-manager'); ?></a>
91
+ <?php if( $EM_Ticket->get_bookings_count() == 0 ): ?>
92
  | <a href="<?php bloginfo('wpurl'); ?>/wp-load.php" class="ticket-actions-delete"><?php esc_html_e('Delete','events-manager'); ?></a>
93
  <?php else: ?>
94
  | <a href="<?php echo esc_url(add_query_arg('ticket_id', $EM_Ticket->ticket_id, $EM_Event->get_bookings_url())); ?>"><?php esc_html_e('View Bookings','events-manager'); ?></a>
templates/forms/event/group.php CHANGED
@@ -3,7 +3,7 @@ global $EM_Event;
3
  if( !function_exists('bp_is_active') || !bp_is_active('groups') ) return false;
4
  $user_groups = array();
5
  $group_data = groups_get_user_groups(get_current_user_id());
6
- if( !is_super_admin() ){
7
  foreach( $group_data['groups'] as $group_id ){
8
  if( groups_is_user_admin(get_current_user_id(), $group_id) ){
9
  $user_groups[] = groups_get_group( array('group_id'=>$group_id));
@@ -39,7 +39,7 @@ if( count($user_groups) > 0 ){
39
  <br />
40
  <em><?php _e ( 'Select a group you admin to attach this event to it. Note that all other admins of that group can modify the booking.', 'events-manager')?></em>
41
  </p>
42
- <?php if( is_super_admin() ): ?>
43
  <p><em><?php _e ( 'As a site admin, you see all group events, users will only be able to choose groups they are admins of.', 'events-manager')?></em></p>
44
  <?php endif;
45
 
3
  if( !function_exists('bp_is_active') || !bp_is_active('groups') ) return false;
4
  $user_groups = array();
5
  $group_data = groups_get_user_groups(get_current_user_id());
6
+ if( !em_wp_is_super_admin() ){
7
  foreach( $group_data['groups'] as $group_id ){
8
  if( groups_is_user_admin(get_current_user_id(), $group_id) ){
9
  $user_groups[] = groups_get_group( array('group_id'=>$group_id));
39
  <br />
40
  <em><?php _e ( 'Select a group you admin to attach this event to it. Note that all other admins of that group can modify the booking.', 'events-manager')?></em>
41
  </p>
42
+ <?php if( em_wp_is_super_admin() ): ?>
43
  <p><em><?php _e ( 'As a site admin, you see all group events, users will only be able to choose groups they are admins of.', 'events-manager')?></em></p>
44
  <?php endif;
45
 
templates/forms/event/when-with-recurring.php CHANGED
@@ -55,7 +55,7 @@ $admin_recurring = is_admin() && $EM_Event->is_recurring();
55
  <p class="alternate-selector" id="monthly-selector" style="display:inline;">
56
  <select id="monthly-modifier" name="recurrence_byweekno">
57
  <?php
58
- $weekno_options = array ("1" => __ ( 'first', 'events-manager'), '2' => __ ( 'second', 'events-manager'), '3' => __ ( 'third', 'events-manager'), '4' => __ ( 'fourth', 'events-manager'), '-1' => __ ( 'last', 'events-manager') );
59
  em_option_items ( $weekno_options, $EM_Event->recurrence_byweekno );
60
  ?>
61
  </select>
55
  <p class="alternate-selector" id="monthly-selector" style="display:inline;">
56
  <select id="monthly-modifier" name="recurrence_byweekno">
57
  <?php
58
+ $weekno_options = array ("1" => __ ( 'first', 'events-manager'), '2' => __ ( 'second', 'events-manager'), '3' => __ ( 'third', 'events-manager'), '4' => __ ( 'fourth', 'events-manager'), '5' => __ ( 'fifth', 'events-manager'), '-1' => __ ( 'last', 'events-manager') );
59
  em_option_items ( $weekno_options, $EM_Event->recurrence_byweekno );
60
  ?>
61
  </select>
templates/forms/location/attributes.php CHANGED
@@ -46,38 +46,6 @@ $has_deprecated = false;
46
  ?>
47
  </tbody>
48
  </table>
49
- <?php if( count(array_diff(array_keys($EM_Location->location_attributes), $attributes['names'])) > 0 ): ?>
50
- <p><strong><?php _e('Deprecated Attributes', 'events-manager')?></strong></p>
51
- <p><em><?php _e("If you see any attributes under here, that means they're not used in Locations Manager formats. To add them, you need to add the custom attribute again to a formatting option in the settings page. To remove any of these deprecated attributes, give it a blank value and save.",'events-manager') ?></em></p>
52
- <table class="form-table">
53
- <thead>
54
- <tr valign="top">
55
- <td><strong>Attribute Name</strong></td>
56
- <td><strong>Value</strong></td>
57
- </tr>
58
- </thead>
59
- <tbody id="mtm_body">
60
- <?php
61
- if( is_array($EM_Location->location_attributes) and count($EM_Location->location_attributes) > 0){
62
- foreach( $EM_Location->location_attributes as $name => $value){
63
- if( is_array($value) ) $value = serialize($value);
64
- if( !in_array($name, $attributes['names']) ){
65
- ?>
66
- <tr valign="top" id="em_attribute_<?php echo $count ?>">
67
- <td scope="row"><?php echo $name ?></td>
68
- <td>
69
- <input type="text" name="em_attributes[<?php echo $name ?>]" value="<?php echo esc_attr($value, ENT_QUOTES); ?>" />
70
- </td>
71
- </tr>
72
- <?php
73
- $count++;
74
- }
75
- }
76
- }
77
- ?>
78
- </tbody>
79
- </table>
80
- <?php endif; ?>
81
  <?php else : ?>
82
  <p>
83
  <?php _e('In order to use attributes, you must define some in your templates, otherwise they\'ll never show. Go to Events > Settings > General to add attribute placeholders.', 'events-manager'); ?>
46
  ?>
47
  </tbody>
48
  </table>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  <?php else : ?>
50
  <p>
51
  <?php _e('In order to use attributes, you must define some in your templates, otherwise they\'ll never show. Go to Events > Settings > General to add attribute placeholders.', 'events-manager'); ?>
templates/templates/search/search.php CHANGED
@@ -5,7 +5,7 @@ $args = !empty($args) ? $args:array(); /* @var $args array */
5
  <!-- START General Search -->
6
  <div class="em-search-text em-search-field">
7
  <script type="text/javascript">
8
- EM.search_term_placeholder = '<?php echo esc_attr($args['search_term_label']); ?>';
9
  </script>
10
  <label>
11
  <span class="screen-reader-text"><?php echo esc_html($args['search_term_label']); ?></span>
5
  <!-- START General Search -->
6
  <div class="em-search-text em-search-field">
7
  <script type="text/javascript">
8
+ EM.search_term_placeholder = '<?php echo esc_js($args['search_term_label']); ?>';
9
  </script>
10
  <label>
11
  <span class="screen-reader-text"><?php echo esc_html($args['search_term_label']); ?></span>
widgets/em-events.php CHANGED
@@ -98,7 +98,7 @@ class EM_Widget extends WP_Widget {
98
  }
99
  //balance tags and sanitize output formats
100
  if( in_array($key, array('format', 'no_events_text', 'all_events_text')) ){
101
- if( is_multisite() && !is_super_admin() ) $new_instance[$key] = wp_kses_post($new_instance[$key]); //for multisite
102
  $new_instance[$key] = force_balance_tags($new_instance[$key]);
103
  }
104
  }
98
  }
99
  //balance tags and sanitize output formats
100
  if( in_array($key, array('format', 'no_events_text', 'all_events_text')) ){
101
+ if( is_multisite() && !em_wp_is_super_admin() ) $new_instance[$key] = wp_kses_post($new_instance[$key]); //for multisite
102
  $new_instance[$key] = force_balance_tags($new_instance[$key]);
103
  }
104
  }
widgets/em-locations.php CHANGED
@@ -70,7 +70,7 @@ class EM_Locations_Widget extends WP_Widget {
70
  }
71
  //balance tags and sanitize output formats
72
  if( in_array($key, array('format', 'no_locations_text')) ){
73
- if( is_multisite() && !is_super_admin() ) $new_instance[$key] = wp_kses_post($new_instance[$key]); //for multisite
74
  $new_instance[$key] = force_balance_tags($new_instance[$key]);
75
  }
76
  }
70
  }
71
  //balance tags and sanitize output formats
72
  if( in_array($key, array('format', 'no_locations_text')) ){
73
+ if( is_multisite() && !em_wp_is_super_admin() ) $new_instance[$key] = wp_kses_post($new_instance[$key]); //for multisite
74
  $new_instance[$key] = force_balance_tags($new_instance[$key]);
75
  }
76
  }