My Calendar - Version 3.2.8

Version Description

  • Bug fix: Extraneous screen-reader-text summary generated in event views.
  • Bug fix: Fixes to missing parameters in Schema.org microdata.
  • Bug fix: Incorrect type comparison caused custom templates not to render in single event view.
  • New feature: Default location.
Download this release

Release Info

Developer joedolson
Plugin Icon 128x128 My Calendar
Version 3.2.8
Comparing to
See all releases

Code changes from version 3.2.5 to 3.2.8

css/mc-styles.css CHANGED
@@ -765,7 +765,8 @@ select[name="event_recur"] {
765
  border: 1px solid burlywood;
766
  }
767
 
768
- tr.problem {
 
769
  background-color: #ffa;
770
  }
771
 
@@ -773,6 +774,15 @@ tr.problem .error {
773
  color: #a00;
774
  }
775
 
 
 
 
 
 
 
 
 
 
776
  .problem-icon {
777
  font-size: 64px;
778
  float: left;
765
  border: 1px solid burlywood;
766
  }
767
 
768
+ tr.problem,
769
+ tr.invalid {
770
  background-color: #ffa;
771
  }
772
 
774
  color: #a00;
775
  }
776
 
777
+ .mc_default {
778
+ padding: 2px 8px 2px 2px;
779
+ border-radius: 3px;
780
+ background: #ffa;
781
+ color: #333;
782
+ border: 1px solid #888;
783
+ float: right;
784
+ }
785
+
786
  .problem-icon {
787
  font-size: 64px;
788
  float: left;
my-calendar-api.php CHANGED
@@ -94,6 +94,8 @@ function mc_api_format_json( $data ) {
94
  * @param array $data array of event objects.
95
  */
96
  function mc_api_format_csv( $data ) {
 
 
97
  $keyed = false;
98
  // Create a stream opening it with read / write mode.
99
  $stream = fopen( 'data://text/plain,' . '', 'w+' );
94
  * @param array $data array of event objects.
95
  */
96
  function mc_api_format_csv( $data ) {
97
+ ob_clean();
98
+ ob_start();
99
  $keyed = false;
100
  // Create a stream opening it with read / write mode.
101
  $stream = fopen( 'data://text/plain,' . '', 'w+' );
my-calendar-categories.php CHANGED
@@ -587,6 +587,64 @@ function mc_category_by_name( $string ) {
587
  return $cat_id;
588
  }
589
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
590
  /**
591
  * Generate list of categories to edit.
592
  */
@@ -770,30 +828,13 @@ function mc_save_profile() {
770
  * @return string HTML fields.
771
  */
772
  function mc_category_select( $data = false, $option = true, $multiple = false, $name = false ) {
773
- global $wpdb;
774
- $mcdb = $wpdb;
775
- if ( 'true' === get_option( 'mc_remote' ) && function_exists( 'mc_remote_db' ) ) {
776
- $mcdb = mc_remote_db();
777
- }
778
  if ( ! $name ) {
779
  $name = 'event_category[]';
780
  }
781
  // Grab all the categories and list them.
782
  $list = '';
783
  $default = '';
784
- $cats = $mcdb->get_results( 'SELECT * FROM ' . my_calendar_categories_table() . ' ORDER BY category_name ASC' );
785
- if ( empty( $cats ) ) {
786
- // need to have categories. Try to create again.
787
- mc_create_category(
788
- array(
789
- 'category_name' => 'General',
790
- 'category_color' => '#ffffcc',
791
- 'category_icon' => 'event.png',
792
- )
793
- );
794
-
795
- $cats = $mcdb->get_results( $sql );
796
- }
797
  if ( ! empty( $cats ) ) {
798
  $cats = apply_filters( 'mc_category_list', $cats, $data, $option, $name );
799
  foreach ( $cats as $cat ) {
587
  return $cat_id;
588
  }
589
 
590
+ /**
591
+ * Get or create a category if no default set.
592
+ *
593
+ * @param bool $single False for all categories; true for individual category.
594
+ *
595
+ * @return int
596
+ */
597
+ function mc_no_category_default( $single = false ) {
598
+ global $wpdb;
599
+ $mcdb = $wpdb;
600
+ if ( 'true' === get_option( 'mc_remote' ) && function_exists( 'mc_remote_db' ) ) {
601
+ $mcdb = mc_remote_db();
602
+ }
603
+
604
+ $cats = $mcdb->get_results( 'SELECT * FROM ' . my_calendar_categories_table() . ' ORDER BY category_name ASC' );
605
+ $cat_id = $cats[0]->category_id;
606
+ if ( empty( $cats ) ) {
607
+ // need to have categories. Try to create again.
608
+ $cat_id = mc_create_category(
609
+ array(
610
+ 'category_name' => 'General',
611
+ 'category_color' => '#ffffcc',
612
+ 'category_icon' => 'event.png',
613
+ )
614
+ );
615
+
616
+ $cats = $mcdb->get_results( 'SELECT * FROM ' . my_calendar_categories_table() . ' ORDER BY category_name ASC' );
617
+ }
618
+ if ( $single ) {
619
+ return $cat_id;
620
+ }
621
+
622
+ return $cats;
623
+ }
624
+
625
+ /**
626
+ * Fetch category object by ID or name.
627
+ *
628
+ * @param int|string $category Category name/id.
629
+ *
630
+ * @return object
631
+ */
632
+ function mc_get_category( $category ) {
633
+ global $wpdb;
634
+ $mcdb = $wpdb;
635
+ if ( 'true' === get_option( 'mc_remote' ) && function_exists( 'mc_remote_db' ) ) {
636
+ $mcdb = mc_remote_db();
637
+ }
638
+ if ( is_int( $category ) ) {
639
+ $sql = 'SELECT * FROM ' . my_calendar_categories_table() . ' WHERE category_id = %d';
640
+ $cat = $mcdb->get_row( $mcdb->prepare( $sql, $category ) );
641
+ } else {
642
+ $cat = mc_category_by_name( $category );
643
+ }
644
+
645
+ return $cat;
646
+ }
647
+
648
  /**
649
  * Generate list of categories to edit.
650
  */
828
  * @return string HTML fields.
829
  */
830
  function mc_category_select( $data = false, $option = true, $multiple = false, $name = false ) {
 
 
 
 
 
831
  if ( ! $name ) {
832
  $name = 'event_category[]';
833
  }
834
  // Grab all the categories and list them.
835
  $list = '';
836
  $default = '';
837
+ $cats = mc_no_category_default();
 
 
 
 
 
 
 
 
 
 
 
 
838
  if ( ! empty( $cats ) ) {
839
  $cats = apply_filters( 'mc_category_list', $cats, $data, $option, $name );
840
  foreach ( $cats as $cat ) {
my-calendar-event-manager.php CHANGED
@@ -914,12 +914,16 @@ function mc_delete_event( $event_id ) {
914
  $instance = false;
915
  $post_id = mc_get_data( 'event_post', $event_id );
916
  if ( empty( $_POST['event_instance'] ) ) {
917
- $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . my_calendar_event_table() . ' WHERE occur_event_id =%d', $event_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
 
 
918
  $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . my_calendar_table() . ' WHERE event_id=%d', $event_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
919
  $result = $wpdb->get_results( $wpdb->prepare( 'SELECT event_id FROM ' . my_calendar_table() . ' WHERE event_id=%d', $event_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
 
 
920
  } else {
921
  $event_in = absint( $_POST['event_instance'] );
922
- $result = $wpdb->get_results( $wpdb->prepare( 'DELETE FROM ' . my_calendar_event_table() . ' WHERE occur_id = %d', $event_in ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
923
  $instance = true;
924
  }
925
  if ( empty( $result ) || empty( $result[0]->event_id ) ) {
@@ -1661,8 +1665,9 @@ function mc_form_fields( $data, $mode, $event_id ) {
1661
  foreach ( $locs as $loc ) {
1662
  if ( is_object( $loc ) ) {
1663
  $loc_name = strip_tags( stripslashes( $loc->location_label ), mc_strip_tags() );
1664
- $selected = '';
1665
  if ( is_object( $data ) ) {
 
1666
  if ( property_exists( $data, 'event_location' ) ) {
1667
  $event_location = $data->event_location;
1668
  } else {
@@ -1798,9 +1803,12 @@ function mc_get_users( $group = 'authors' ) {
1798
  * @return string select options.
1799
  */
1800
  function mc_selected_users( $selected = '', $group = 'authors' ) {
 
 
 
 
1801
  $selected = explode( ',', $selected );
1802
  $users = mc_get_users( $group );
1803
- $options = '';
1804
  foreach ( $users as $u ) {
1805
  if ( in_array( $u->ID, $selected, true ) ) {
1806
  $checked = ' selected="selected"';
@@ -2027,7 +2035,6 @@ function mc_list_events() {
2027
  $limit = ( strpos( $limit, 'AND' ) === 0 ) ? $limit : 'AND ' . $limit;
2028
  $events = $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT SQL_CALC_FOUND_ROWS events.event_id FROM ' . my_calendar_table() . ' AS events JOIN ' . my_calendar_categories_table() . " AS categories WHERE events.event_category = categories.category_id $limit ORDER BY categories.category_name $sortbydirection " . 'LIMIT %d, %d', $query_limit, $items_per_page ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
2029
  }
2030
-
2031
  $found_rows = $wpdb->get_col( 'SELECT FOUND_ROWS();' );
2032
  $items = $found_rows[0];
2033
  $counts = get_option( 'mc_count_cache' );
@@ -2174,12 +2181,15 @@ function mc_list_events() {
2174
  $categories = $wpdb->get_results( 'SELECT * FROM ' . my_calendar_categories_table() ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
2175
 
2176
  foreach ( array_keys( $events ) as $key ) {
2177
- $e =& $events[ $key ];
2178
- $event = mc_get_first_event( $e->event_id );
 
2179
  if ( ! is_object( $event ) ) {
2180
- continue;
 
2181
  }
2182
  $class = ( 'alternate' === $class ) ? 'even' : 'alternate';
 
2183
  $pending = ( 0 === (int) $event->event_approved ) ? 'pending' : '';
2184
  $trashed = ( 2 === (int) $event->event_approved ) ? 'trashed' : '';
2185
  $author = ( 0 !== (int) $event->event_author ) ? get_userdata( $event->event_author ) : 'Public Submitter';
@@ -2193,13 +2203,18 @@ function mc_list_events() {
2193
  $spam_label = '';
2194
  }
2195
 
2196
- $trash = ( '' !== $trashed ) ? ' - ' . __( 'Trash', 'my-calendar' ) : '';
2197
- $draft = ( '' !== $pending ) ? ' - ' . __( 'Draft', 'my-calendar' ) : $trash;
2198
- $check = mc_test_occurrence_overlap( $event, true );
2199
- $problem = ( '' !== $check ) ? 'problem' : '';
2200
- $edit_url = admin_url( "admin.php?page=my-calendar&mode=edit&event_id=$event->event_id" );
2201
- $copy_url = admin_url( "admin.php?page=my-calendar&mode=copy&event_id=$event->event_id" );
2202
- $view_url = mc_get_details_link( $event );
 
 
 
 
 
2203
  $group_url = admin_url( "admin.php?page=my-calendar-groups&mode=edit&event_id=$event->event_id&group_id=$event->event_group_id" );
2204
  $delete_url = admin_url( "admin.php?page=my-calendar-manage&mode=delete&event_id=$event->event_id" );
2205
  $can_edit = mc_can_edit_event( $event );
@@ -2233,6 +2248,7 @@ function mc_list_events() {
2233
  }
2234
  }
2235
  echo $draft;
 
2236
  ?>
2237
  </strong>
2238
 
@@ -2332,8 +2348,15 @@ function mc_list_events() {
2332
  // Events *must* have a category.
2333
  mc_update_event( 'event_category', 1, $event->event_id, '%d' );
2334
  }
2335
- $cat = mc_get_category_detail( $event->event_category, false );
2336
- $color = strip_tags( $cat->category_color );
 
 
 
 
 
 
 
2337
  $color = ( 0 !== strpos( $color, '#' ) ) ? '#' . $color : $color;
2338
  $categories = mc_get_categories( $event );
2339
  $cats = array();
@@ -2541,7 +2564,7 @@ function mc_check_data( $action, $post, $i ) {
2541
  $event_longitude = '';
2542
  $event_latitude = '';
2543
 
2544
- if ( get_magic_quotes_gpc() ) {
2545
  $post = array_map( 'stripslashes_deep', $post );
2546
  }
2547
  if ( ! wp_verify_nonce( $post['event_nonce_name'], 'event_nonce' ) ) {
@@ -2630,6 +2653,11 @@ function mc_check_data( $action, $post, $i ) {
2630
  $primary = $cats;
2631
  $cats = array( $cats );
2632
  }
 
 
 
 
 
2633
  }
2634
  $event_author = ( isset( $post['event_author'] ) && is_numeric( $post['event_author'] ) ) ? $post['event_author'] : 0;
2635
  $event_link = ! empty( $post['event_link'] ) ? trim( $post['event_link'] ) : '';
@@ -3045,7 +3073,7 @@ function mc_instance_list( $args ) {
3045
 
3046
  global $wpdb;
3047
  $output = '';
3048
- if ( true === $instance ) {
3049
  $sql = 'SELECT * FROM ' . my_calendar_event_table() . ' WHERE occur_id=%d ORDER BY occur_begin ASC';
3050
  } else {
3051
  $sql = 'SELECT * FROM ' . my_calendar_event_table() . ' WHERE occur_event_id=%d ORDER BY occur_begin ASC';
@@ -3499,6 +3527,7 @@ function mc_can_edit_category( $category, $user ) {
3499
  * @return boolean
3500
  */
3501
  function mc_can_edit_event( $event = false ) {
 
3502
  if ( ! $event ) {
3503
 
3504
  return false;
@@ -3519,8 +3548,11 @@ function mc_can_edit_event( $event = false ) {
3519
  $event_id = $event->event_id;
3520
  $event_author = $event->event_author;
3521
  } elseif ( is_int( $event ) ) {
3522
- $event_id = $event;
3523
- $event = mc_get_first_event( $event );
 
 
 
3524
  $event_author = $event->event_author;
3525
  } else {
3526
  // What is the case where the event is neither an object, int, or falsey? Hmm.
914
  $instance = false;
915
  $post_id = mc_get_data( 'event_post', $event_id );
916
  if ( empty( $_POST['event_instance'] ) ) {
917
+ // Delete from instance table.
918
+ $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . my_calendar_event_table() . ' WHERE occur_event_id=%d', $event_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
919
+ // Delete from event table.
920
  $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . my_calendar_table() . ' WHERE event_id=%d', $event_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
921
  $result = $wpdb->get_results( $wpdb->prepare( 'SELECT event_id FROM ' . my_calendar_table() . ' WHERE event_id=%d', $event_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
922
+ // Delete category relationship records.
923
+ $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . my_calendar_category_relationships_table() . ' WHERE event_id=%d', $event_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
924
  } else {
925
  $event_in = absint( $_POST['event_instance'] );
926
+ $result = $wpdb->get_results( $wpdb->prepare( 'DELETE FROM ' . my_calendar_event_table() . ' WHERE occur_id=%d', $event_in ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
927
  $instance = true;
928
  }
929
  if ( empty( $result ) || empty( $result[0]->event_id ) ) {
1665
  foreach ( $locs as $loc ) {
1666
  if ( is_object( $loc ) ) {
1667
  $loc_name = strip_tags( stripslashes( $loc->location_label ), mc_strip_tags() );
1668
+ $selected = ( is_numeric( get_option( 'mc_default_location' ) ) && (int) get_option( 'mc_default_location' ) === (int) $loc->location_id ) ? ' selected="selected"' : '';
1669
  if ( is_object( $data ) ) {
1670
+ $selected = '';
1671
  if ( property_exists( $data, 'event_location' ) ) {
1672
  $event_location = $data->event_location;
1673
  } else {
1803
  * @return string select options.
1804
  */
1805
  function mc_selected_users( $selected = '', $group = 'authors' ) {
1806
+ $options = apply_filters( 'mc_custom_user_select', '', $selected, $group );
1807
+ if ( '' !== $options ) {
1808
+ return $options;
1809
+ }
1810
  $selected = explode( ',', $selected );
1811
  $users = mc_get_users( $group );
 
1812
  foreach ( $users as $u ) {
1813
  if ( in_array( $u->ID, $selected, true ) ) {
1814
  $checked = ' selected="selected"';
2035
  $limit = ( strpos( $limit, 'AND' ) === 0 ) ? $limit : 'AND ' . $limit;
2036
  $events = $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT SQL_CALC_FOUND_ROWS events.event_id FROM ' . my_calendar_table() . ' AS events JOIN ' . my_calendar_categories_table() . " AS categories WHERE events.event_category = categories.category_id $limit ORDER BY categories.category_name $sortbydirection " . 'LIMIT %d, %d', $query_limit, $items_per_page ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
2037
  }
 
2038
  $found_rows = $wpdb->get_col( 'SELECT FOUND_ROWS();' );
2039
  $items = $found_rows[0];
2040
  $counts = get_option( 'mc_count_cache' );
2181
  $categories = $wpdb->get_results( 'SELECT * FROM ' . my_calendar_categories_table() ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
2182
 
2183
  foreach ( array_keys( $events ) as $key ) {
2184
+ $e =& $events[ $key ];
2185
+ $event = mc_get_first_event( $e->event_id );
2186
+ $invalid = false;
2187
  if ( ! is_object( $event ) ) {
2188
+ $event = $wpdb->get_row( $wpdb->prepare( 'SELECT * FROM ' . my_calendar_table() . ' WHERE event_id = %d', $e->event_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
2189
+ $invalid = true;
2190
  }
2191
  $class = ( 'alternate' === $class ) ? 'even' : 'alternate';
2192
+ $class = ( $invalid ) ? $class . ' invalid' : $class;
2193
  $pending = ( 0 === (int) $event->event_approved ) ? 'pending' : '';
2194
  $trashed = ( 2 === (int) $event->event_approved ) ? 'trashed' : '';
2195
  $author = ( 0 !== (int) $event->event_author ) ? get_userdata( $event->event_author ) : 'Public Submitter';
2203
  $spam_label = '';
2204
  }
2205
 
2206
+ $trash = ( '' !== $trashed ) ? ' - ' . __( 'Trash', 'my-calendar' ) : '';
2207
+ $draft = ( '' !== $pending ) ? ' - ' . __( 'Draft', 'my-calendar' ) : $trash;
2208
+ $invalid = ( $invalid ) ? ' - ' . __( 'Invalid Event', 'my-calendar' ) : $trash;
2209
+ $check = mc_test_occurrence_overlap( $event, true );
2210
+ $problem = ( '' !== $check ) ? 'problem' : '';
2211
+ $edit_url = admin_url( "admin.php?page=my-calendar&amp;mode=edit&amp;event_id=$event->event_id" );
2212
+ $copy_url = admin_url( "admin.php?page=my-calendar&amp;mode=copy&amp;event_id=$event->event_id" );
2213
+ if ( ! $invalid ) {
2214
+ $view_url = mc_get_details_link( $event );
2215
+ } else {
2216
+ $view_url = '';
2217
+ }
2218
  $group_url = admin_url( "admin.php?page=my-calendar-groups&amp;mode=edit&amp;event_id=$event->event_id&amp;group_id=$event->event_group_id" );
2219
  $delete_url = admin_url( "admin.php?page=my-calendar-manage&amp;mode=delete&amp;event_id=$event->event_id" );
2220
  $can_edit = mc_can_edit_event( $event );
2248
  }
2249
  }
2250
  echo $draft;
2251
+ echo $invalid;
2252
  ?>
2253
  </strong>
2254
 
2348
  // Events *must* have a category.
2349
  mc_update_event( 'event_category', 1, $event->event_id, '%d' );
2350
  }
2351
+ $cat = mc_get_category_detail( $event->event_category, false );
2352
+ if ( ! is_object( $cat ) ) {
2353
+ $cat = (object) array(
2354
+ 'category_color' => '',
2355
+ 'category_id' => '',
2356
+ 'category_name' => '',
2357
+ );
2358
+ }
2359
+ $color = $cat->category_color;
2360
  $color = ( 0 !== strpos( $color, '#' ) ) ? '#' . $color : $color;
2361
  $categories = mc_get_categories( $event );
2362
  $cats = array();
2564
  $event_longitude = '';
2565
  $event_latitude = '';
2566
 
2567
+ if ( version_compare( PHP_VERSION, '7.4', '<' ) && get_magic_quotes_gpc() ) {
2568
  $post = array_map( 'stripslashes_deep', $post );
2569
  }
2570
  if ( ! wp_verify_nonce( $post['event_nonce_name'], 'event_nonce' ) ) {
2653
  $primary = $cats;
2654
  $cats = array( $cats );
2655
  }
2656
+ } else {
2657
+ $default = get_option( 'mc_default_category' );
2658
+ $default = ( ! $default ) ? mc_no_category_default( true ) : $default;
2659
+ $cats = array( $default );
2660
+ $primary = $default;
2661
  }
2662
  $event_author = ( isset( $post['event_author'] ) && is_numeric( $post['event_author'] ) ) ? $post['event_author'] : 0;
2663
  $event_link = ! empty( $post['event_link'] ) ? trim( $post['event_link'] ) : '';
3073
 
3074
  global $wpdb;
3075
  $output = '';
3076
+ if ( true === $instance || '1' === $instance ) {
3077
  $sql = 'SELECT * FROM ' . my_calendar_event_table() . ' WHERE occur_id=%d ORDER BY occur_begin ASC';
3078
  } else {
3079
  $sql = 'SELECT * FROM ' . my_calendar_event_table() . ' WHERE occur_event_id=%d ORDER BY occur_begin ASC';
3527
  * @return boolean
3528
  */
3529
  function mc_can_edit_event( $event = false ) {
3530
+ global $wpdb;
3531
  if ( ! $event ) {
3532
 
3533
  return false;
3548
  $event_id = $event->event_id;
3549
  $event_author = $event->event_author;
3550
  } elseif ( is_int( $event ) ) {
3551
+ $event_id = $event;
3552
+ $event = mc_get_first_event( $event );
3553
+ if ( ! is_object( $event ) ) {
3554
+ $event = $wpdb->get_row( $wpdb->prepare( 'SELECT * FROM ' . my_calendar_table() . ' WHERE event_id=%d LIMIT 1', $event_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
3555
+ }
3556
  $event_author = $event->event_author;
3557
  } else {
3558
  // What is the case where the event is neither an object, int, or falsey? Hmm.
my-calendar-group-manager.php CHANGED
@@ -1150,11 +1150,15 @@ function mc_list_groups() {
1150
  <td><?php echo ( is_object( $author ) ) ? $author->display_name : $author; ?></td>
1151
  <?php
1152
  $this_category = $event->event_category;
 
1153
  foreach ( $categories as $key => $value ) {
1154
  if ( $value->category_id === $this_category ) {
1155
  $this_cat = $categories[ $key ];
1156
  }
1157
  }
 
 
 
1158
  $background = strip_tags( ( strpos( $this_cat->category_color, '#' ) !== 0 ) ? '#' : '' ) . $this_cat->category_color;
1159
  ?>
1160
  <td>
1150
  <td><?php echo ( is_object( $author ) ) ? $author->display_name : $author; ?></td>
1151
  <?php
1152
  $this_category = $event->event_category;
1153
+ $this_cat = false;
1154
  foreach ( $categories as $key => $value ) {
1155
  if ( $value->category_id === $this_category ) {
1156
  $this_cat = $categories[ $key ];
1157
  }
1158
  }
1159
+ if ( ! $this_cat ) {
1160
+ $this_cat = mc_get_category( $event->event_category );
1161
+ }
1162
  $background = strip_tags( ( strpos( $this_cat->category_color, '#' ) !== 0 ) ? '#' : '' ) . $this_cat->category_color;
1163
  ?>
1164
  <td>
my-calendar-location-manager.php CHANGED
@@ -46,11 +46,33 @@ function my_calendar_manage_locations() {
46
  </div>
47
  </div>
48
  </div>
49
- <?php mc_show_sidebar(); ?>
 
 
 
50
  </div>
51
  <?php
52
  }
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  /**
55
  * Mass delete locations.
56
  *
@@ -232,7 +254,18 @@ function mc_manage_locations() {
232
  <input type="checkbox" value="<?php echo $location->location_id; ?>" name="mass_edit[]" id="mc<?php echo $location->location_id; ?>"/>
233
  <label for="mc<?php echo $location->location_id; ?>"><?php echo $location->location_id; ?></label>
234
  </th>
235
- <td><?php echo mc_hcard( $location, 'true', 'false', 'location' ); ?></td>
 
 
 
 
 
 
 
 
 
 
 
236
  <td><?php echo esc_html( $location->location_city ); ?></td>
237
  <td><?php echo esc_html( $location->location_state ); ?></td>
238
  <td>
46
  </div>
47
  </div>
48
  </div>
49
+ <?php
50
+ $default = array( __( 'Default Location', 'my-calendar' ) => mc_default_location() );
51
+ mc_show_sidebar( '', $default );
52
+ ?>
53
  </div>
54
  <?php
55
  }
56
 
57
+ /**
58
+ * Fetch default location data.
59
+ *
60
+ * @return string
61
+ */
62
+ function mc_default_location() {
63
+ $default = get_option( 'mc_default_location' );
64
+ if ( $default ) {
65
+ $location = mc_get_location( $default );
66
+ $output = mc_hcard( $location, 'true', false, 'location' );
67
+ $output .= '<p><a href="' . admin_url( "admin.php?page=my-calendar-locations&amp;mode=edit&amp;location_id=$default" ) . '">' . __( 'Edit Default Location', 'my-calendar' ) . '</a></p>';
68
+ }
69
+ if ( ! $output ) {
70
+ $output = '<p>' . __( 'No default location selected.', 'my-calendar' ) . '</p>';
71
+ }
72
+
73
+ return $output;
74
+ }
75
+
76
  /**
77
  * Mass delete locations.
78
  *
254
  <input type="checkbox" value="<?php echo $location->location_id; ?>" name="mass_edit[]" id="mc<?php echo $location->location_id; ?>"/>
255
  <label for="mc<?php echo $location->location_id; ?>"><?php echo $location->location_id; ?></label>
256
  </th>
257
+ <?php
258
+ $default = '';
259
+ if ( (int) get_option( 'mc_default_location' ) === (int) $location->location_id ) {
260
+ $default = '<span class="mc_default"><span class="dashicons dashicons-location" aria-hidden="true"></span>' . __( 'Default Location', 'my-calendar' ) . '</span>';
261
+ }
262
+ ?>
263
+ <td>
264
+ <?php
265
+ echo $default;
266
+ echo mc_hcard( $location, 'true', 'false', 'location' );
267
+ ?>
268
+ </td>
269
  <td><?php echo esc_html( $location->location_city ); ?></td>
270
  <td><?php echo esc_html( $location->location_state ); ?></td>
271
  <td>
my-calendar-locations.php CHANGED
@@ -298,6 +298,9 @@ function my_calendar_add_locations() {
298
  );
299
 
300
  $results = mc_insert_location( $add );
 
 
 
301
  do_action( 'mc_save_location', $results, $add, $_POST );
302
  if ( $results ) {
303
  mc_show_notice( __( 'Location added successfully', 'my-calendar' ) );
@@ -309,6 +312,10 @@ function my_calendar_add_locations() {
309
  do_action( 'mc_delete_location', $results, (int) $_GET['location_id'] );
310
  if ( $results ) {
311
  mc_show_notice( __( 'Location deleted successfully', 'my-calendar' ) );
 
 
 
 
312
  } else {
313
  mc_show_error( __( 'Location could not be deleted', 'my-calendar' ) );
314
  }
@@ -334,7 +341,10 @@ function my_calendar_add_locations() {
334
  'location_access' => isset( $_POST['location_access'] ) ? serialize( $_POST['location_access'] ) : '',
335
  );
336
 
337
- $where = array( 'location_id' => (int) $_POST['location_id'] );
 
 
 
338
  $results = mc_modify_location( $update, $where );
339
 
340
  do_action( 'mc_modify_location', $where, $update, $_POST );
@@ -580,8 +590,14 @@ function mc_locations_fields( $has_data, $data, $context = 'location' ) {
580
  if ( current_user_can( 'mc_edit_locations' ) && 'event' === $context ) {
581
  $return .= '<p class="checkboxes"><input type="checkbox" value="on" name="mc_copy_location" id="mc_copy_location" /> <label for="mc_copy_location">' . __( 'Copy this location into the locations table', 'my-calendar' ) . '</label></p>';
582
  }
 
 
 
 
 
 
583
  $return .= '
584
- <p class="checkbox">
585
  <label for="e_label">' . __( 'Name of Location (e.g. <em>Joe\'s Bar and Grill</em>)', 'my-calendar' ) . '</label>';
586
  $cur_label = ( ! empty( $data ) ) ? ( stripslashes( $data->{$context . '_label'} ) ) : '';
587
  if ( mc_controlled_field( 'label' ) ) {
@@ -988,7 +1004,7 @@ function mc_get_locations( $args ) {
988
  // Prevent invalid order columns.
989
  $orderby = 'location_label';
990
  }
991
- $results = $wpdb->get_results( $wpdb->prepare( 'SELECT location_id,location_label FROM ' . my_calendar_locations_table() . ' WHERE %s = %s ORDER BY ' . $orderby . ' ' . $order, $where, $is ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
992
 
993
  return apply_filters( 'mc_filter_results', $results, $args );
994
  }
298
  );
299
 
300
  $results = mc_insert_location( $add );
301
+ if ( isset( $_POST['mc_default_location'] ) && $results ) {
302
+ update_option( 'mc_default_location', (int) $results );
303
+ }
304
  do_action( 'mc_save_location', $results, $add, $_POST );
305
  if ( $results ) {
306
  mc_show_notice( __( 'Location added successfully', 'my-calendar' ) );
312
  do_action( 'mc_delete_location', $results, (int) $_GET['location_id'] );
313
  if ( $results ) {
314
  mc_show_notice( __( 'Location deleted successfully', 'my-calendar' ) );
315
+ $default_location = get_option( 'mc_default_location', false );
316
+ if ( (int) $default_location === (int) $_GET['location_id'] ) {
317
+ delete_option( 'mc_default_location' );
318
+ }
319
  } else {
320
  mc_show_error( __( 'Location could not be deleted', 'my-calendar' ) );
321
  }
341
  'location_access' => isset( $_POST['location_access'] ) ? serialize( $_POST['location_access'] ) : '',
342
  );
343
 
344
+ $where = array( 'location_id' => (int) $_POST['location_id'] );
345
+ if ( isset( $_POST['mc_default_location'] ) ) {
346
+ update_option( 'mc_default_location', (int) $_POST['location_id'] );
347
+ }
348
  $results = mc_modify_location( $update, $where );
349
 
350
  do_action( 'mc_modify_location', $where, $update, $_POST );
590
  if ( current_user_can( 'mc_edit_locations' ) && 'event' === $context ) {
591
  $return .= '<p class="checkboxes"><input type="checkbox" value="on" name="mc_copy_location" id="mc_copy_location" /> <label for="mc_copy_location">' . __( 'Copy this location into the locations table', 'my-calendar' ) . '</label></p>';
592
  }
593
+ if ( current_user_can( 'mc_edit_settings' ) && isset( $_GET['page'] ) && 'my-calendar-locations' === $_GET['page'] ) {
594
+ $checked = ( isset( $_GET['location_id'] ) && (int) get_option( 'mc_default_location' ) === (int) $_GET['location_id'] ) ? 'checked="checked"' : '';
595
+ $return .= '<p class="checkbox">';
596
+ $return .= '<input type="checkbox" name="mc_default_location" id="mc_default_location"' . $checked . ' /> <label for="mc_default_location">' . __( 'Default Location', 'my-calendar' ) . '</label>';
597
+ $return .= '</p>';
598
+ }
599
  $return .= '
600
+ <p>
601
  <label for="e_label">' . __( 'Name of Location (e.g. <em>Joe\'s Bar and Grill</em>)', 'my-calendar' ) . '</label>';
602
  $cur_label = ( ! empty( $data ) ) ? ( stripslashes( $data->{$context . '_label'} ) ) : '';
603
  if ( mc_controlled_field( 'label' ) ) {
1004
  // Prevent invalid order columns.
1005
  $orderby = 'location_label';
1006
  }
1007
+ $results = $wpdb->get_results( $wpdb->prepare( 'SELECT location_id,location_label FROM ' . my_calendar_locations_table() . ' WHERE ' . esc_sql( $where ) . ' = %s ORDER BY ' . esc_sql( $orderby ) . ' ' . esc_sql( $order ), $is ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
1008
 
1009
  return apply_filters( 'mc_filter_results', $results, $args );
1010
  }
my-calendar-output.php CHANGED
@@ -97,7 +97,8 @@ function mc_time_html( $e, $type ) {
97
  }
98
  $time_content .= apply_filters( 'mcs_end_time_block', '', $e );
99
  // Generate date/time meta data.
100
- $meta = ( 0 === (int) $e->event_hide_end ) ? "<meta itemprop='endDate' content='" . $start . 'T' . $e->event_endtime . "'/>" : '';
 
101
  $meta .= '<meta itemprop="duration" content="' . mc_duration( $e ) . '"/>';
102
 
103
  $time = "
@@ -266,6 +267,7 @@ function my_calendar_draw_event( $event, $type = 'calendar', $process_date, $tim
266
  $tickets = '';
267
  $data = mc_create_tags( $event, $id );
268
  $details = '';
 
269
  if ( mc_show_details( $time, $type ) ) {
270
  $details = apply_filters( 'mc_custom_template', false, $data, $event, $type, $process_date, $time, $template );
271
  $template = apply_filters( 'mc_use_custom_template', $template, $data, $event, $type, $process_date, $time );
@@ -318,7 +320,12 @@ function my_calendar_draw_event( $event, $type = 'calendar', $process_date, $tim
318
  $event_title = str_replace( ': ', '', $event_title );
319
  }
320
  $event_title = ( '' === $event_title ) ? $data['title'] : strip_tags( $event_title, mc_strip_tags() );
321
- $no_link = apply_filters( 'mc_disable_link', false, $data );
 
 
 
 
 
322
 
323
  if ( ( ( strpos( $event_title, 'href' ) === false ) && 'mini' !== $type && 'list' !== $type ) && ! $no_link ) {
324
  if ( 'true' === $open_uri ) {
@@ -334,15 +341,18 @@ function my_calendar_draw_event( $event, $type = 'calendar', $process_date, $tim
334
  $balance = '';
335
  }
336
 
337
- $group_class = ( 1 === (int) $event->event_span ) ? ' multidate group' . $event->event_group_id : '';
338
- $hlevel = apply_filters( 'mc_heading_level_table', 'h3', $type, $time, $template );
339
- $inner_heading = apply_filters( 'mc_heading_inner_title', $wrap . $image . trim( $event_title ) . $balance, $event_title, $event );
340
- $header .= ( 'single' !== $type && 'list' !== $type ) ? " <$hlevel class='event-title summary$group_class' id='mc_$event->occur_id-title-$id'>$inner_heading</$hlevel>\n" : '';
341
- $event_title = ( 'single' === $type ) ? apply_filters( 'mc_single_event_title', $event_title, $event ) : $event_title;
342
- $title = ( 'single' === $type && ! is_singular( 'mc-events' ) ) ? " <h2 class='event-title summary'>$image $event_title</h2>\n" : ' <span class="summary screen-reader-text">' . $event_title . '</span>';
343
- $title = apply_filters( 'mc_event_title', $title, $event, $event_title, $image );
344
- $header .= ( false === stripos( $title, 'summary' ) ) ? ' <span class="summary screen-reader-text">' . strip_tags( $title ) . '</span>' : $title;
345
-
 
 
 
346
  $close_button = mc_close_button( "$uid-$type-details-$id" );
347
 
348
  if ( mc_show_details( $time, $type ) ) {
@@ -1888,7 +1898,7 @@ function my_calendar( $args ) {
1888
  }
1889
  $events = ( isset( $event_array[ $date_is ] ) ) ? $event_array[ $date_is ] : array();
1890
  $week_header = date_i18n( $week_format, $start );
1891
- $thisday_heading = ( 'week' === $params['time'] ) ? "<small>$week_header</small>" : mc_date( 'j', $start, false );
1892
 
1893
  // Generate event classes & attributes.
1894
  $events_class = mc_events_class( $events, $date_is );
97
  }
98
  $time_content .= apply_filters( 'mcs_end_time_block', '', $e );
99
  // Generate date/time meta data.
100
+ $meta = "<meta itemprop='startDate' content='" . $start . 'T' . $e->event_time . "' />";
101
+ $meta .= ( 0 === (int) $e->event_hide_end ) ? "<meta itemprop='endDate' content='" . $end . 'T' . $e->event_endtime . "'/>" : '';
102
  $meta .= '<meta itemprop="duration" content="' . mc_duration( $e ) . '"/>';
103
 
104
  $time = "
267
  $tickets = '';
268
  $data = mc_create_tags( $event, $id );
269
  $details = '';
270
+
271
  if ( mc_show_details( $time, $type ) ) {
272
  $details = apply_filters( 'mc_custom_template', false, $data, $event, $type, $process_date, $time, $template );
273
  $template = apply_filters( 'mc_use_custom_template', $template, $data, $event, $type, $process_date, $time );
320
  $event_title = str_replace( ': ', '', $event_title );
321
  }
322
  $event_title = ( '' === $event_title ) ? $data['title'] : strip_tags( $event_title, mc_strip_tags() );
323
+ if ( 'single' === $type ) {
324
+ $event_title = apply_filters( 'mc_single_event_title', $event_title, $event );
325
+ } else {
326
+ $event_title = apply_filters( 'mc_event_title', $event_title, $event, $data['title'], $image );
327
+ }
328
+ $no_link = apply_filters( 'mc_disable_link', false, $data );
329
 
330
  if ( ( ( strpos( $event_title, 'href' ) === false ) && 'mini' !== $type && 'list' !== $type ) && ! $no_link ) {
331
  if ( 'true' === $open_uri ) {
341
  $balance = '';
342
  }
343
 
344
+ $group_class = ( 1 === (int) $event->event_span ) ? ' multidate group' . $event->event_group_id : '';
345
+ $hlevel = apply_filters( 'mc_heading_level_table', 'h3', $type, $time, $template );
346
+ // Set up .summary - required once per page for structured data. Should only be added in cases where heading & anchor are removed.
347
+ if ( 'single' === $type ) {
348
+ $title = ( ! is_singular( 'mc-events' ) ) ? " <h2 class='event-title summary'>$image $event_title</h2>\n" : ' <span class="summary screen-reader-text">' . strip_tags( $event_title ) . '</span>';
349
+ } elseif ( 'list' !== $type ) {
350
+ $inner_heading = apply_filters( 'mc_heading_inner_title', $wrap . $image . trim( $event_title ) . $balance, $event_title, $event );
351
+ $title = " <$hlevel class='event-title summary$group_class' id='mc_$event->occur_id-title-$id'>$inner_heading</$hlevel>\n";
352
+ } else {
353
+ $title = '';
354
+ }
355
+ $header .= ( false === stripos( $title, 'summary' ) ) ? ' <span class="summary screen-reader-text">' . strip_tags( $event_title ) . '</span>' : $title;
356
  $close_button = mc_close_button( "$uid-$type-details-$id" );
357
 
358
  if ( mc_show_details( $time, $type ) ) {
1898
  }
1899
  $events = ( isset( $event_array[ $date_is ] ) ) ? $event_array[ $date_is ] : array();
1900
  $week_header = date_i18n( $week_format, $start );
1901
+ $thisday_heading = ( 'week' === $params['time'] ) ? "<small>$week_header</small>" : mc_date( apply_filters( 'mc_grid_date', 'j', $params ), $start, false );
1902
 
1903
  // Generate event classes & attributes.
1904
  $events_class = mc_events_class( $events, $date_is );
my-calendar.php CHANGED
@@ -17,7 +17,7 @@
17
  * License: GPL-2.0+
18
  * License URI: http://www.gnu.org/license/gpl-2.0.txt
19
  * Domain Path: lang
20
- * Version: 3.2.5
21
  */
22
 
23
  /*
@@ -42,7 +42,7 @@ if ( ! defined( 'ABSPATH' ) ) {
42
  }
43
 
44
  global $mc_version, $wpdb;
45
- $mc_version = '3.2.5';
46
 
47
  define( 'MC_DEBUG', false );
48
 
17
  * License: GPL-2.0+
18
  * License URI: http://www.gnu.org/license/gpl-2.0.txt
19
  * Domain Path: lang
20
+ * Version: 3.2.8
21
  */
22
 
23
  /*
42
  }
43
 
44
  global $mc_version, $wpdb;
45
+ $mc_version = '3.2.8';
46
 
47
  define( 'MC_DEBUG', false );
48
 
readme.txt CHANGED
@@ -3,9 +3,9 @@ Contributors: joedolson
3
  Donate link: http://www.joedolson.com/donate/
4
  Tags: calendar, dates, times, event, events, scheduling, schedule, event manager, event calendar, class, concert, venue, location, box office, tickets, registration
5
  Requires at least: 4.4
6
- Tested up to: 5.4
7
  Requires PHP: 5.6
8
- Stable tag: 3.2.5
9
  Text domain: my-calendar
10
  License: GPLv2 or later
11
 
@@ -83,6 +83,27 @@ Translating my plug-ins is always appreciated. Visit <a href="https://translate.
83
 
84
  == Changelog ==
85
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  = 3.2.5 =
87
 
88
  * Bug fix: CSV exported text fields contained newline characters.
3
  Donate link: http://www.joedolson.com/donate/
4
  Tags: calendar, dates, times, event, events, scheduling, schedule, event manager, event calendar, class, concert, venue, location, box office, tickets, registration
5
  Requires at least: 4.4
6
+ Tested up to: 5.5
7
  Requires PHP: 5.6
8
+ Stable tag: 3.2.8
9
  Text domain: my-calendar
10
  License: GPLv2 or later
11
 
83
 
84
  == Changelog ==
85
 
86
+ = 3.2.8 =
87
+
88
+ * Bug fix: Extraneous screen-reader-text summary generated in event views.
89
+ * Bug fix: Fixes to missing parameters in Schema.org microdata.
90
+ * Bug fix: Incorrect type comparison caused custom templates not to render in single event view.
91
+ * New feature: Default location.
92
+
93
+ = 3.2.7 =
94
+
95
+ * Bug fix: Prevent events from being created without categories.
96
+ * Bug fix: Ensure category relationships are deleted when related events are deleted.
97
+ * Add handling for seeing & managing events that are invalid.
98
+ * Add styles for invalid rows.
99
+
100
+ = 3.2.6 =
101
+
102
+ * Added filter to change date format on calendar grid.
103
+ * New filter for modifying user selection output.
104
+ * Bug fix: only check for get_magic_quotes_gpc() if below PHP 7.4
105
+ * Bug fix: invalid query in mc_get_locations() if arguments passed as array.
106
+
107
  = 3.2.5 =
108
 
109
  * Bug fix: CSV exported text fields contained newline characters.