My Calendar - Version 3.3.23

Version Description

  • Bug fix: Don't set to default location if location already set.
  • Bug fix: Add stopImmediatePropgation to click handlers to prevent other script's scroll effects
  • Change: Remove .mcajax class as unneeded.
Download this release

Release Info

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

Code changes from version 3.3.16 to 3.3.23

Files changed (57) hide show
  1. changelog.txt +156 -0
  2. css/reset.css +17 -1
  3. includes/class-gamajo-template-loader.php +313 -0
  4. includes/class-geolocation.php +5 -5
  5. includes/class-mc-template-loader.php +50 -0
  6. includes/conditionals.php +4 -2
  7. includes/date-utilities.php +42 -20
  8. includes/db.php +15 -13
  9. includes/deprecated.php +0 -54
  10. includes/event-utilities.php +3 -1
  11. includes/general-utilities.php +46 -29
  12. includes/kses.php +3 -2
  13. includes/privacy.php +6 -4
  14. includes/screen-options.php +22 -14
  15. includes/urls.php +11 -10
  16. includes/widgets/class-my-calendar-filters.php +3 -3
  17. includes/widgets/class-my-calendar-mini-widget.php +3 -3
  18. includes/widgets/class-my-calendar-simple-search.php +3 -3
  19. includes/widgets/class-my-calendar-today-widget.php +3 -3
  20. includes/widgets/class-my-calendar-upcoming-widget.php +5 -3
  21. js/mcjs.js +8 -2
  22. js/mcjs.min.js +1 -1
  23. mc-templates/event-grid.php +13 -0
  24. mc-templates/event-list.php +13 -0
  25. mc-templates/event-mini.php +13 -0
  26. mc-templates/event-single.php +13 -0
  27. mc-templates/event-upcoming.php +13 -0
  28. my-calendar-ajax.php +1 -1
  29. my-calendar-api.php +72 -32
  30. my-calendar-behaviors.php +1 -1
  31. my-calendar-call-template.php +0 -4
  32. my-calendar-categories.php +118 -28
  33. my-calendar-core.php +358 -91
  34. my-calendar-event-editor.php +600 -175
  35. my-calendar-event-manager.php +97 -35
  36. my-calendar-events.php +193 -33
  37. my-calendar-generator.php +37 -5
  38. my-calendar-group-manager.php +132 -74
  39. my-calendar-help.php +16 -2
  40. my-calendar-iframe.php +1 -1
  41. my-calendar-install.php +30 -14
  42. my-calendar-limits.php +37 -17
  43. my-calendar-location-manager.php +53 -8
  44. my-calendar-locations.php +232 -62
  45. my-calendar-navigation.php +229 -50
  46. my-calendar-output.php +1113 -334
  47. my-calendar-print.php +55 -18
  48. my-calendar-search.php +116 -10
  49. my-calendar-settings.php +90 -44
  50. my-calendar-shortcodes.php +85 -14
  51. my-calendar-styles.php +18 -20
  52. my-calendar-templates.php +569 -80
  53. my-calendar-templating.php +2 -1
  54. my-calendar-upgrade-db.php +6 -9
  55. my-calendar-widgets.php +239 -17
  56. my-calendar.php +20 -11
  57. readme.txt +70 -169
changelog.txt CHANGED
@@ -1,3 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  = 3.1.18 =
2
 
3
  * Add filters to 'Add to Google Calendar' link: mc_gcal_location & mc_gcal_description
1
+ = 3.2.19 =
2
+
3
+ * Resolve svn problem causing missing files.
4
+
5
+ = 3.2.18 =
6
+
7
+ * Security: Fixes reflected XSS flaw in admin. Props to @erwinr and WPScan.
8
+
9
+ = 3.2.17 =
10
+
11
+ * Bug fix: Add parameter for required fields handling to ignore during imports.
12
+ * Add filter handling calendar URLs when using Polylang or WPML.
13
+
14
+ = 3.2.16 =
15
+
16
+ * Bug fix: Check for undefined objects in localization, not for undefined object props.
17
+ * Change: Set parameter for location autocomplete switchover to 50 instead of 25 locations.
18
+ * Change: Tweak directory removal process slightly.
19
+
20
+ = 3.2.15 =
21
+
22
+ * Bug fix: Hide event details section if no fields are visible for section.
23
+ * Bug fix: Update localization to correct usage of l10n parameter.
24
+ * Bug fix: Location AJAX query executed function that only existed in Pro.
25
+
26
+ = 3.2.14 =
27
+
28
+ * Bug fixes: Misc. type casting issues.
29
+ * Add filters `mc_filter_events` to filter results of main event queries.
30
+ * Add $args array to `mc_searched_events` filter parameters.
31
+ * Avoid running My Calendar core functionality through My Calendar's own hooks.
32
+ * When using REST API, variables are not submitted in a POST query.
33
+ * [Performance] Move custom location query into object creation to reduce DB calls.
34
+ * Use try/catch in mc-ajax.js to handle case where href does not contain a full URL.
35
+ * Autocomplete support for locations in admin.
36
+ * Reset select elements in My Calendar nav to inline.
37
+ * Minor refactoring in settings pages.
38
+
39
+ = 3.2.13 =
40
+
41
+ * Bug fix: Using embed targets is more complicated than expected; disable by default. Enable with 'mc_use_embed_targets' filter.
42
+ * Bug fix: Strip embed from parameters when building links (when embed target enabled.)
43
+
44
+ = 3.2.12 =
45
+
46
+ * Bug fix: Don't use embed target link when AJAX disabled.
47
+ * Improvement: Add AJAX navigation into browser history & address bar.
48
+
49
+ = 3.2.11 =
50
+
51
+ * Bug fix: switching to week view display broken.
52
+ * Bug fix: links to template help pointed to old location for help.
53
+ * Bug fix: AJAX nav pulled height from first rendered calendar, not current navigating calendar.
54
+ * Change: filter to pass custom notices for front end submissions and editing.
55
+ * Remove fallback function for is_ssl()
56
+ * Improve conflicting event errors when the conflicting event is still unpublished.
57
+ * Add custom template to pass a calendar that's embeddable via iframe.
58
+ * Bug fix: Multisite environments need to use navigation on current site, not from remote site.
59
+
60
+ = 3.2.10 =
61
+
62
+ * Change: Fallback text should have a stylable wrapper.
63
+ * Bug fix: Missing translatable string.
64
+ * Bug fix: When multiple categories selected, events in more than one category would appear multiple times.
65
+ * Bug fix: Missing space in MySQL filters in event manager.
66
+ * Bug fix: PHP Notice thrown in location manager.
67
+ * Bug fix: Add note to open events link field if no URI configured.
68
+ * Layout fix: Ensure there's always a space between date & time.
69
+
70
+ = 3.2.9 =
71
+
72
+ * Bug fix: Additional of required fields testing erased error messages generated prior to required fields testing.
73
+ * Bug fix: If an individual occurrence title is edited, event permalinks show the single change on all events.
74
+ * Bug fix: Prev/next event links don't include unique event IDs.
75
+ * Bug fix: Remove irrelevant arguments from prev/next event link generation.
76
+ * Bug fix: Ignore templates if no data passed.
77
+
78
+ = 3.2.8 =
79
+
80
+ * Bug fix: Extraneous screen-reader-text summary generated in event views.
81
+ * Bug fix: Fixes to missing parameters in Schema.org microdata.
82
+ * Bug fix: Incorrect type comparison caused custom templates not to render in single event view.
83
+ * New feature: Default location.
84
+
85
+ = 3.2.7 =
86
+
87
+ * Bug fix: Prevent events from being created without categories.
88
+ * Bug fix: Ensure category relationships are deleted when related events are deleted.
89
+ * Add handling for seeing & managing events that are invalid.
90
+ * Add styles for invalid rows.
91
+
92
+ = 3.2.6 =
93
+
94
+ * Added filter to change date format on calendar grid.
95
+ * New filter for modifying user selection output.
96
+ * Bug fix: only check for get_magic_quotes_gpc() if below PHP 7.4
97
+ * Bug fix: invalid query in mc_get_locations() if arguments passed as array.
98
+
99
+ = 3.2.5 =
100
+
101
+ * Bug fix: CSV exported text fields contained newline characters.
102
+
103
+ = 3.2.4 =
104
+
105
+ * Bug fix: Permissions issue caused by variable type mismatch.
106
+
107
+ = 3.2.3 =
108
+
109
+ * Bug fix: 3.2.2 created multiple post types with the same slug, triggering 404 errors.
110
+ * Bug fix: Templates could return the name of the template if template empty/missing.
111
+
112
+ = 3.2.2 =
113
+
114
+ * Bug fix: Curly brace offset access deprecated
115
+ * Bug fix: Make next/prev post link arguments optional.
116
+ * Bug fix: Template queries could return an empty template.
117
+ * Change: Remove trashed events from default events list.
118
+
119
+ = 3.2.1 =
120
+
121
+ * PHP Notice: undefined variable.
122
+ * Bug fix: screen options not saving.
123
+ * Bug fix: Accidental auto-assigning of first category to events when editing.
124
+
125
+ = 3.2.0 =
126
+
127
+ * Auto-toggle admin time format if display time format set to European format.
128
+ * Show API endpoint when API enabled.
129
+ * Add alternate alias for API endpoint.
130
+ * Add style variables with category colors to style output.
131
+ * Add color output icon with CSS variables in style editor.
132
+ * Add new default stylesheet: Twentytwenty.css
133
+ * Move permalink setting to general settings panel.
134
+ * Change event timestamps to use a real UTC timestamp for reference.
135
+ * Switch from using date() to gmdate().
136
+ * Update Pickadate to 3.6.4. Resolves some bugs, but introduces an accessibility issue.
137
+ * Customizations to Pickadate 3.6.4 to repair accessibility
138
+ * don't move focus to picker
139
+ * add 'close' button to time picker.
140
+ * Switch Pickadate to classic theme (modified).
141
+ * Improvements to output code layout.
142
+ * Eliminate empty HTML wrappers in content.
143
+ * New filter: mc_get_users. Use custom arguments to get users.
144
+ * New filters: mc_header_navigation, mc_footer_navigation
145
+ * New template tags: {userstart}, {userend} - date/time in local users timezone.
146
+ * Bug fix: Misc. ARIA/id relationships broken.
147
+ * Bug fix: remote locations sometimes pulled from local database.
148
+ * Bug fix: Long-standing issues in user input settings.
149
+ * Bug fix: Don't duplicate .summary values.
150
+ * Bug fix: Only render one close button in mini calendar.
151
+ * Collapse 'View Calendar' and 'Add Event' adminbar menus into a single menu.
152
+ * Remove upgrade path from 2.2.10.
153
+ * Remove .mc-event-visible from style output. Unused since 2011.
154
+ * Remove numerous deprecated functions.
155
+ * Conformance with latest WordPress PHPCS ruleset.
156
+
157
  = 3.1.18 =
158
 
159
  * Add filters to 'Add to Google Calendar' link: mc_gcal_location & mc_gcal_description
css/reset.css CHANGED
@@ -156,7 +156,8 @@ div.site-content, table {
156
  color: inherit;
157
  }
158
 
159
- .category-icon,
 
160
  .category-color-sample.svg * {
161
  max-width: 18px;
162
  min-width: 16px;
@@ -290,6 +291,21 @@ button.mc-toggle:hover, button.mc-toggle:focus {
290
  padding: .5em;
291
  }
292
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
  @-webkit-keyframes animation-rotate {
294
  100% {
295
  -webkit-transform: rotate(360deg);
156
  color: inherit;
157
  }
158
 
159
+ img.category-icon,
160
+ svg.category-icon,
161
  .category-color-sample.svg * {
162
  max-width: 18px;
163
  min-width: 16px;
291
  padding: .5em;
292
  }
293
 
294
+ ol.mc-search-results {
295
+ list-style-type: none;
296
+ margin: 0 auto;
297
+ padding: 0;
298
+ }
299
+
300
+ ol.mc-search-results li {
301
+ margin: 0 0 1em;
302
+ }
303
+
304
+ .mc-search-results .mc_search_term {
305
+ background: #ffa;
306
+ color: #111;
307
+ }
308
+
309
  @-webkit-keyframes animation-rotate {
310
  100% {
311
  -webkit-transform: rotate(360deg);
includes/class-gamajo-template-loader.php ADDED
@@ -0,0 +1,313 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template Loader for Plugins.
4
+ *
5
+ * @package Gamajo_Template_Loader
6
+ * @author Gary Jones
7
+ * @link http://github.com/GaryJones/Gamajo-Template-Loader
8
+ * @copyright 2013 Gary Jones
9
+ * @license GPL-2.0-or-later
10
+ * @version 1.3.1
11
+ */
12
+
13
+ if ( ! class_exists( 'Gamajo_Template_Loader' ) ) {
14
+
15
+ /**
16
+ * Template loader.
17
+ *
18
+ * Originally based on functions in Easy Digital Downloads (thanks Pippin!).
19
+ *
20
+ * When using in a plugin, create a new class that extends this one and just overrides the properties.
21
+ *
22
+ * @package Gamajo_Template_Loader
23
+ * @author Gary Jones
24
+ */
25
+ class Gamajo_Template_Loader {
26
+ /**
27
+ * Prefix for filter names.
28
+ *
29
+ * @since 1.0.0
30
+ *
31
+ * @var string
32
+ */
33
+ protected $filter_prefix = 'your_plugin';
34
+
35
+ /**
36
+ * Directory name where custom templates for this plugin should be found in the theme.
37
+ *
38
+ * For example: 'your-plugin-templates'.
39
+ *
40
+ * @since 1.0.0
41
+ *
42
+ * @var string
43
+ */
44
+ protected $theme_template_directory = 'plugin-templates';
45
+
46
+ /**
47
+ * Reference to the root directory path of this plugin.
48
+ *
49
+ * Can either be a defined constant, or a relative reference from where the subclass lives.
50
+ *
51
+ * e.g. YOUR_PLUGIN_TEMPLATE or plugin_dir_path( dirname( __FILE__ ) ); etc.
52
+ *
53
+ * @since 1.0.0
54
+ *
55
+ * @var string
56
+ */
57
+ protected $plugin_directory = 'YOUR_PLUGIN_DIR';
58
+
59
+ /**
60
+ * Directory name where templates are found in this plugin.
61
+ *
62
+ * Can either be a defined constant, or a relative reference from where the subclass lives.
63
+ *
64
+ * e.g. 'templates' or 'includes/templates', etc.
65
+ *
66
+ * @since 1.1.0
67
+ *
68
+ * @var string
69
+ */
70
+ protected $plugin_template_directory = 'templates';
71
+
72
+ /**
73
+ * Internal use only: Store located template paths.
74
+ *
75
+ * @var array<string>
76
+ */
77
+ private $template_path_cache = array();
78
+
79
+ /**
80
+ * Internal use only: Store variable names used for template data.
81
+ *
82
+ * Means unset_template_data() can remove all custom references from $wp_query.
83
+ *
84
+ * Initialized to contain the default 'data'.
85
+ *
86
+ * @var array<string>
87
+ */
88
+ private $template_data_var_names = array( 'data' );
89
+
90
+ /**
91
+ * Clean up template data.
92
+ *
93
+ * @since 1.2.0
94
+ */
95
+ public function __destruct() {
96
+ $this->unset_template_data();
97
+ }
98
+
99
+ /**
100
+ * Retrieve a template part.
101
+ *
102
+ * @since 1.0.0
103
+ *
104
+ * @param string $slug Template slug.
105
+ * @param string $name Optional. Template variation name. Default null.
106
+ * @param bool $load Optional. Whether to load template. Default true.
107
+ * @return string
108
+ */
109
+ public function get_template_part( $slug, $name = null, $load = true ) {
110
+ // Execute code for this part.
111
+ do_action( 'get_template_part_' . $slug, $slug, $name );
112
+ do_action( $this->filter_prefix . '_get_template_part_' . $slug, $slug, $name );
113
+
114
+ // Get files names of templates, for given slug and name.
115
+ $templates = $this->get_template_file_names( $slug, $name );
116
+
117
+ // Return the part that is found.
118
+ return $this->locate_template( $templates, $load, false );
119
+ }
120
+
121
+ /**
122
+ * Make custom data available to template.
123
+ *
124
+ * Data is available to the template as properties under the `$data` variable.
125
+ * i.e. A value provided here under `$data['foo']` is available as `$data->foo`.
126
+ *
127
+ * When an input key has a hyphen, you can use `$data->{foo-bar}` in the template.
128
+ *
129
+ * @since 1.2.0
130
+ *
131
+ * @param mixed $data Custom data for the template.
132
+ * @param string $var_name Optional. Variable under which the custom data is available in the template.
133
+ * Default is 'data'.
134
+ * @return Gamajo_Template_Loader
135
+ */
136
+ public function set_template_data( $data, $var_name = 'data' ) {
137
+ global $wp_query;
138
+
139
+ $wp_query->query_vars[ $var_name ] = (object) $data;
140
+
141
+ // Add $var_name to custom variable store if not default value.
142
+ if ( 'data' !== $var_name ) {
143
+ $this->template_data_var_names[] = $var_name;
144
+ }
145
+
146
+ return $this;
147
+ }
148
+
149
+ /**
150
+ * Remove access to custom data in template.
151
+ *
152
+ * Good to use once the final template part has been requested.
153
+ *
154
+ * @since 1.2.0
155
+ *
156
+ * @return Gamajo_Template_Loader
157
+ */
158
+ public function unset_template_data() {
159
+ global $wp_query;
160
+
161
+ // Remove any duplicates from the custom variable store.
162
+ $custom_var_names = array_unique( $this->template_data_var_names );
163
+
164
+ // Remove each custom data reference from $wp_query.
165
+ foreach ( $custom_var_names as $var ) {
166
+ if ( isset( $wp_query->query_vars[ $var ] ) ) {
167
+ unset( $wp_query->query_vars[ $var ] );
168
+ }
169
+ }
170
+
171
+ return $this;
172
+ }
173
+
174
+ /**
175
+ * Given a slug and optional name, create the file names of templates.
176
+ *
177
+ * @since 1.0.0
178
+ *
179
+ * @param string $slug Template slug.
180
+ * @param string $name Template variation name.
181
+ * @return array<string>
182
+ */
183
+ protected function get_template_file_names( $slug, $name = '' ) {
184
+ $templates = array();
185
+ if ( ! empty( $name ) ) {
186
+ $templates[] = $slug . '-' . $name . '.php';
187
+ }
188
+ $templates[] = $slug . '.php';
189
+
190
+ /**
191
+ * Allow template choices to be filtered.
192
+ *
193
+ * The resulting array should be in the order of most specific first, to least specific last.
194
+ * e.g. 0 => recipe-instructions.php, 1 => recipe.php
195
+ *
196
+ * @since 1.0.0
197
+ *
198
+ * @param array $templates Names of template files that should be looked for, for given slug and name.
199
+ * @param string $slug Template slug.
200
+ * @param string $name Template variation name.
201
+ */
202
+ return apply_filters( $this->filter_prefix . '_get_template_part', $templates, $slug, $name );
203
+ }
204
+
205
+ /**
206
+ * Retrieve the name of the highest priority template file that exists.
207
+ *
208
+ * Searches in the STYLESHEETPATH before TEMPLATEPATH so that themes which
209
+ * inherit from a parent theme can just overload one file. If the template is
210
+ * not found in either of those, it looks in the theme-compat folder last.
211
+ *
212
+ * @since 1.0.0
213
+ *
214
+ * @param string|array<string> $template_names Template file(s) to search for, in order.
215
+ * @param bool $load If true the template file will be loaded if it is found.
216
+ * @param bool $require_once Whether to require_once or require. Default true.
217
+ * Has no effect if $load is false.
218
+ * @return string The template filename if one is located.
219
+ */
220
+ public function locate_template( $template_names, $load = false, $require_once = true ) {
221
+
222
+ // Use $template_names as a cache key - either first element of array or the variable itself if it's a string.
223
+ $cache_key = is_array( $template_names ) ? $template_names[0] : $template_names;
224
+
225
+ // If the key is in the cache array, we've already located this file.
226
+ if ( isset( $this->template_path_cache[ $cache_key ] ) ) {
227
+ $located = $this->template_path_cache[ $cache_key ];
228
+ } else {
229
+
230
+ // No file found yet.
231
+ $located = false;
232
+
233
+ // Remove empty entries.
234
+ $template_names = array_filter( (array) $template_names );
235
+ $template_paths = $this->get_template_paths();
236
+
237
+ // Try to find a template file.
238
+ foreach ( $template_names as $template_name ) {
239
+ // Trim off any slashes from the template name.
240
+ $template_name = ltrim( $template_name, '/' );
241
+
242
+ // Try locating this template file by looping through the template paths.
243
+ foreach ( $template_paths as $template_path ) {
244
+ if ( file_exists( $template_path . $template_name ) ) {
245
+ $located = $template_path . $template_name;
246
+ // Store the template path in the cache.
247
+ $this->template_path_cache[ $cache_key ] = $located;
248
+ break 2;
249
+ }
250
+ }
251
+ }
252
+ }
253
+
254
+ if ( $load && $located ) {
255
+ load_template( $located, $require_once );
256
+ }
257
+
258
+ return $located;
259
+ }
260
+
261
+ /**
262
+ * Return a list of paths to check for template locations.
263
+ *
264
+ * Default is to check in a child theme (if relevant) before a parent theme, so that themes which inherit from a
265
+ * parent theme can just overload one file. If the template is not found in either of those, it looks in the
266
+ * theme-compat folder last.
267
+ *
268
+ * @since 1.0.0
269
+ *
270
+ * @return mixed|void
271
+ */
272
+ protected function get_template_paths() {
273
+ $theme_directory = trailingslashit( $this->theme_template_directory );
274
+
275
+ $file_paths = array(
276
+ 10 => trailingslashit( get_template_directory() ) . $theme_directory,
277
+ 100 => $this->get_templates_dir(),
278
+ );
279
+
280
+ // Only add this conditionally, so non-child themes don't redundantly check active theme twice.
281
+ if ( get_stylesheet_directory() !== get_template_directory() ) {
282
+ $file_paths[1] = trailingslashit( get_stylesheet_directory() ) . $theme_directory;
283
+ }
284
+
285
+ /**
286
+ * Allow ordered list of template paths to be amended.
287
+ *
288
+ * @since 1.0.0
289
+ *
290
+ * @param array<string> $file_paths Default is directory in child theme at index 1, parent theme at 10, and plugin at 100.
291
+ */
292
+ $file_paths = apply_filters( $this->filter_prefix . '_template_paths', $file_paths );
293
+
294
+ // Sort the file paths based on priority.
295
+ ksort( $file_paths, SORT_NUMERIC );
296
+
297
+ return array_map( 'trailingslashit', $file_paths );
298
+ }
299
+
300
+ /**
301
+ * Return the path to the templates directory in this plugin.
302
+ *
303
+ * May be overridden in subclass.
304
+ *
305
+ * @since 1.0.0
306
+ *
307
+ * @return string
308
+ */
309
+ protected function get_templates_dir() {
310
+ return trailingslashit( $this->plugin_directory ) . $this->plugin_template_directory;
311
+ }
312
+ }
313
+ }
includes/class-geolocation.php CHANGED
@@ -30,7 +30,7 @@ class Geolocation {
30
  *
31
  * @param array $parameters Query array.
32
  *
33
- * @return object
34
  */
35
  protected static function call( $parameters = array() ) {
36
 
@@ -45,12 +45,12 @@ class Geolocation {
45
  }
46
  $api_key = ( '' !== get_option( 'mc_gmap_api_key', '' ) ) ? get_option( 'mc_gmap_api_key' ) : false;
47
  if ( ! $api_key ) {
48
- return '';
49
  }
50
  $url = add_query_arg( 'key', sanitize_text_field( $api_key ), $url );
51
  $response = wp_remote_get( $url );
52
  if ( is_wp_error( $response ) ) {
53
- return false;
54
  }
55
  $data = $response['body'];
56
 
@@ -94,7 +94,7 @@ class Geolocation {
94
  'sensor' => 'false',
95
  )
96
  );
97
- if ( ! $address_suggestions ) {
98
  return $addresses;
99
  }
100
 
@@ -175,7 +175,7 @@ class Geolocation {
175
  'sensor' => 'false',
176
  )
177
  );
178
- if ( ! $results ) {
179
  return array();
180
  }
181
  // return coordinates latitude/longitude.
30
  *
31
  * @param array $parameters Query array.
32
  *
33
+ * @return array
34
  */
35
  protected static function call( $parameters = array() ) {
36
 
45
  }
46
  $api_key = ( '' !== get_option( 'mc_gmap_api_key', '' ) ) ? get_option( 'mc_gmap_api_key' ) : false;
47
  if ( ! $api_key ) {
48
+ return array();
49
  }
50
  $url = add_query_arg( 'key', sanitize_text_field( $api_key ), $url );
51
  $response = wp_remote_get( $url );
52
  if ( is_wp_error( $response ) ) {
53
+ return array();
54
  }
55
  $data = $response['body'];
56
 
94
  'sensor' => 'false',
95
  )
96
  );
97
+ if ( empty( $address_suggestions ) ) {
98
  return $addresses;
99
  }
100
 
175
  'sensor' => 'false',
176
  )
177
  );
178
+ if ( empty( $results ) ) {
179
  return array();
180
  }
181
  // return coordinates latitude/longitude.
includes/class-mc-template-loader.php ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template Loader for My Calendar
4
+ *
5
+ * @category Templates
6
+ * @package My Calendar
7
+ * @author Joe Dolson
8
+ * @license GPLv2 or later
9
+ * @link https://www.joedolson.com/my-calendar/
10
+ */
11
+
12
+ /**
13
+ * Template loader for My Calendar.
14
+ *
15
+ * Only need to specify class properties here.
16
+ */
17
+ class Mc_Template_Loader extends Gamajo_Template_Loader {
18
+
19
+ /**
20
+ * Prefix for filter names.
21
+ *
22
+ * @since 1.0.0
23
+ * @var string $filter_prefix Filter prefix.
24
+ */
25
+ protected $filter_prefix = 'mc';
26
+
27
+ /**
28
+ * Directory name where custom templates for this plugin should be found in the theme.
29
+ *
30
+ * @since 1.0.0
31
+ * @var string $theme_template_directory Theme template directory slug.
32
+ */
33
+ protected $theme_template_directory = 'mc-templates';
34
+
35
+ /**
36
+ * Reference to the root directory path of this plugin.
37
+ *
38
+ * @since 1.0.0
39
+ * @var string $plugin_directory Path to root plugin directory.
40
+ */
41
+ protected $plugin_directory = MC_DIRECTORY;
42
+
43
+ /**
44
+ * Directory name where templates are found in this plugin.
45
+ *
46
+ * @since 1.0.0
47
+ * @var string $plugin_template_directory Plugin template directory slug.
48
+ */
49
+ protected $plugin_template_directory = 'mc-templates';
50
+ }
includes/conditionals.php CHANGED
@@ -157,11 +157,13 @@ function mc_is_core_template( $key ) {
157
  case 'details':
158
  case 'list':
159
  case 'mini':
160
- return true;
161
  break;
162
  default:
163
- return false;
164
  }
 
 
165
  }
166
 
167
  /**
157
  case 'details':
158
  case 'list':
159
  case 'mini':
160
+ $return = true;
161
  break;
162
  default:
163
+ $return = false;
164
  }
165
+
166
+ return $return;
167
  }
168
 
169
  /**
includes/date-utilities.php CHANGED
@@ -16,7 +16,7 @@ if ( ! defined( 'ABSPATH' ) ) {
16
  /**
17
  * Generate classes for a given date
18
  *
19
- * @param string $current timestamp.
20
  *
21
  * @return string classes
22
  */
@@ -41,11 +41,11 @@ function mc_dateclass( $current ) {
41
  * @param int $mth Number of months to add.
42
  * @param int $yr number of years to add.
43
  *
44
- * @return timestamp
45
  */
46
  function my_calendar_add_date( $givendate, $day = 0, $mth = 0, $yr = 0 ) {
47
  $cd = strtotime( $givendate );
48
- $newdate = mktime( mc_date( 'H', $cd, false ), mc_date( 'i', $cd, false ), mc_date( 's', $cd, false ), mc_date( 'm', $cd, false ) + $mth, mc_date( 'd', $cd, false ) + $day, mc_date( 'Y', $cd, false ) + $yr );
49
 
50
  return $newdate;
51
  }
@@ -176,10 +176,10 @@ function mc_timediff_cmp( $a, $b ) {
176
  /**
177
  * Compare two dates for diff with high precision
178
  *
179
- * @param int $start timestamp.
180
- * @param mixed int/string $end timestamp or 'now'.
181
  *
182
- * @return absolute time diff
183
  */
184
  function mc_date_diff_precise( $start, $end = 'NOW' ) {
185
  if ( 'NOW' === $end ) {
@@ -250,7 +250,7 @@ function mc_recur_date( $ts ) {
250
  $ts = is_numeric( $ts ) ? $ts : strtotime( $ts );
251
  $weekday = mc_date( 'l', $ts );
252
  $month = mc_date( 'M', $ts );
253
- $ord = 0;
254
 
255
  while ( mc_date( 'M', ( $ts = strtotime( '-1 week', $ts ) ) ) === $month ) { // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
256
  $ord++;
@@ -265,7 +265,7 @@ function mc_recur_date( $ts ) {
265
  /**
266
  * Get the first day value of the current week.
267
  *
268
- * @param mixed int/boolean $timestamp timestamp + offset or false if now.
269
  *
270
  * @return array day and month
271
  */
@@ -417,7 +417,7 @@ function mc_private_event( $event, $type = true ) {
417
  *
418
  * @param string $string Date information.
419
  *
420
- * @return string de-internationalized change
421
  */
422
  function mc_strtotime( $string ) {
423
  $months = array(
@@ -477,13 +477,13 @@ function mc_strtotime( $string ) {
477
  }
478
 
479
  /**
480
- * Wrapper for mc_date()
481
  *
482
- * @param string $format Format to use.
483
- * @param int $timestamp Timestamp.
484
- * @param bool $offset false to not add offset; if already a true timestamp.
485
  *
486
- * @return string Formatted date.
487
  */
488
  function mc_date( $format, $timestamp = false, $offset = true ) {
489
  if ( ! $timestamp ) {
@@ -678,17 +678,39 @@ function mc_get_from_to( $show_months, $params, $date ) {
678
  * @return int
679
  */
680
  function mc_date_relation( $event ) {
681
- $ts = $event->ts_occur_begin;
682
- $end = $event->ts_occur_end;
683
- $now = time();
 
684
  if ( $ts < $now && $end > $now ) {
685
- do_action( 'mc_event_happening', true, $event );
 
 
 
 
 
 
 
686
  $date_relation = 1;
687
  } elseif ( $now < $ts ) {
688
- do_action( 'mc_event_future', true, $event );
 
 
 
 
 
 
 
689
  $date_relation = 2;
690
  } elseif ( $now > $ts ) {
691
- do_action( 'mc_event_over', true, $event );
 
 
 
 
 
 
 
692
  $date_relation = 0;
693
  }
694
 
16
  /**
17
  * Generate classes for a given date
18
  *
19
+ * @param int $current timestamp.
20
  *
21
  * @return string classes
22
  */
41
  * @param int $mth Number of months to add.
42
  * @param int $yr number of years to add.
43
  *
44
+ * @return int
45
  */
46
  function my_calendar_add_date( $givendate, $day = 0, $mth = 0, $yr = 0 ) {
47
  $cd = strtotime( $givendate );
48
+ $newdate = mktime( mc_date( 'H', $cd, false ), mc_date( 'i', $cd, false ), mc_date( 's', $cd, false ), (int) mc_date( 'm', $cd, false ) + $mth, (int) mc_date( 'd', $cd, false ) + $day, (int) mc_date( 'Y', $cd, false ) + $yr );
49
 
50
  return $newdate;
51
  }
176
  /**
177
  * Compare two dates for diff with high precision
178
  *
179
+ * @param int $start timestamp.
180
+ * @param int|string $end timestamp or 'now'.
181
  *
182
+ * @return int absolute value of time diff
183
  */
184
  function mc_date_diff_precise( $start, $end = 'NOW' ) {
185
  if ( 'NOW' === $end ) {
250
  $ts = is_numeric( $ts ) ? $ts : strtotime( $ts );
251
  $weekday = mc_date( 'l', $ts );
252
  $month = mc_date( 'M', $ts );
253
+ $ord = 1;
254
 
255
  while ( mc_date( 'M', ( $ts = strtotime( '-1 week', $ts ) ) ) === $month ) { // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
256
  $ord++;
265
  /**
266
  * Get the first day value of the current week.
267
  *
268
+ * @param int|boolean $timestamp timestamp + offset or false if now.
269
  *
270
  * @return array day and month
271
  */
417
  *
418
  * @param string $string Date information.
419
  *
420
+ * @return int de-internationalized change
421
  */
422
  function mc_strtotime( $string ) {
423
  $months = array(
477
  }
478
 
479
  /**
480
+ * Wrapper for date()
481
  *
482
+ * @param string $format Format to use.
483
+ * @param int|false $timestamp Timestamp or false if now.
484
+ * @param bool $offset false to not add offset; if already a true timestamp.
485
  *
486
+ * @return string|int Formatted date or timestamp if no format provided.
487
  */
488
  function mc_date( $format, $timestamp = false, $offset = true ) {
489
  if ( ! $timestamp ) {
678
  * @return int
679
  */
680
  function mc_date_relation( $event ) {
681
+ $ts = $event->ts_occur_begin;
682
+ $end = $event->ts_occur_end;
683
+ $date_relation = 2;
684
+ $now = time();
685
  if ( $ts < $now && $end > $now ) {
686
+ /**
687
+ * Execute action while an event is happening.
688
+ *
689
+ * @hook mc_event_happening
690
+ *
691
+ * @param {object} $object Event object.
692
+ */
693
+ do_action( 'mc_event_happening', $event );
694
  $date_relation = 1;
695
  } elseif ( $now < $ts ) {
696
+ /**
697
+ * Execute action before an event will occur.
698
+ *
699
+ * @hook mc_event_future
700
+ *
701
+ * @param {object} $object Event object.
702
+ */
703
+ do_action( 'mc_event_future', $event );
704
  $date_relation = 2;
705
  } elseif ( $now > $ts ) {
706
+ /**
707
+ * Execute action after an event has occurred.
708
+ *
709
+ * @hook mc_event_over
710
+ *
711
+ * @param {object} $object Event object.
712
+ */
713
+ do_action( 'mc_event_over', $event );
714
  $date_relation = 0;
715
  }
716
 
includes/db.php CHANGED
@@ -16,7 +16,7 @@ if ( ! defined( 'ABSPATH' ) ) {
16
  /**
17
  * My Calendar main table
18
  *
19
- * @param mixed int/boolean $site Site ID in multisite.
20
  *
21
  * @return string table name
22
  */
@@ -27,7 +27,7 @@ function my_calendar_table( $site = false ) {
27
  /**
28
  * My Calendar event table
29
  *
30
- * @param mixed int/boolean $site Site ID in multisite.
31
  *
32
  * @return string table name
33
  */
@@ -38,7 +38,7 @@ function my_calendar_event_table( $site = false ) {
38
  /**
39
  * My Calendar category table
40
  *
41
- * @param mixed int/boolean $site Site ID in multisite.
42
  *
43
  * @return string table name
44
  */
@@ -49,7 +49,7 @@ function my_calendar_categories_table( $site = false ) {
49
  /**
50
  * My Calendar category relationships table
51
  *
52
- * @param mixed int/boolean $site Site ID in multisite.
53
  *
54
  * @return string table name
55
  */
@@ -60,7 +60,7 @@ function my_calendar_category_relationships_table( $site = false ) {
60
  /**
61
  * My Calendar location relationships table
62
  *
63
- * @param mixed int/boolean $site Site ID in multisite.
64
  *
65
  * @return string table name
66
  */
@@ -71,7 +71,7 @@ function my_calendar_location_relationships_table( $site = false ) {
71
  /**
72
  * My Calendar locations table
73
  *
74
- * @param mixed int/boolean $site Site ID in multisite.
75
  *
76
  * @return string table name
77
  */
@@ -85,9 +85,9 @@ function my_calendar_locations_table( $site = false ) {
85
  * @since 2.5.0
86
  *
87
  * @param string $table table name.
88
- * @param mixed int/string $site 'global' to get global database; site ID to get that site's database; false for defaults according to settings.
89
  *
90
- * @return prefixed string table name
91
  */
92
  function my_calendar_select_table( $table = 'my_calendar_events', $site = false ) {
93
  global $wpdb;
@@ -109,18 +109,20 @@ function my_calendar_select_table( $table = 'my_calendar_events', $site = false
109
 
110
  switch ( $option ) {
111
  case 0:
112
- return $local;
113
  break;
114
  case 1:
115
- return $global;
116
  break;
117
  case 2:
118
- return ( 1 === $choice ) ? $global : $local;
119
  break;
120
  default:
121
- return $local;
122
  }
123
  } else {
124
- return $local;
125
  }
 
 
126
  }
16
  /**
17
  * My Calendar main table
18
  *
19
+ * @param int|boolean $site Site ID in multisite.
20
  *
21
  * @return string table name
22
  */
27
  /**
28
  * My Calendar event table
29
  *
30
+ * @param int|boolean $site Site ID in multisite.
31
  *
32
  * @return string table name
33
  */
38
  /**
39
  * My Calendar category table
40
  *
41
+ * @param int|boolean $site Site ID in multisite.
42
  *
43
  * @return string table name
44
  */
49
  /**
50
  * My Calendar category relationships table
51
  *
52
+ * @param int|boolean $site Site ID in multisite.
53
  *
54
  * @return string table name
55
  */
60
  /**
61
  * My Calendar location relationships table
62
  *
63
+ * @param int|boolean $site Site ID in multisite.
64
  *
65
  * @return string table name
66
  */
71
  /**
72
  * My Calendar locations table
73
  *
74
+ * @param int|boolean $site Site ID in multisite.
75
  *
76
  * @return string table name
77
  */
85
  * @since 2.5.0
86
  *
87
  * @param string $table table name.
88
+ * @param int|string|false $site 'global' to get global database; site ID to get that site's database; false for defaults according to settings.
89
  *
90
+ * @return string properly prefixed table name
91
  */
92
  function my_calendar_select_table( $table = 'my_calendar_events', $site = false ) {
93
  global $wpdb;
109
 
110
  switch ( $option ) {
111
  case 0:
112
+ $return = $local;
113
  break;
114
  case 1:
115
+ $return = $global;
116
  break;
117
  case 2:
118
+ $return = ( 1 === $choice ) ? $global : $local;
119
  break;
120
  default:
121
+ $return = $local;
122
  }
123
  } else {
124
+ $return = $local;
125
  }
126
+
127
+ return $return;
128
  }
includes/deprecated.php CHANGED
@@ -9,57 +9,3 @@
9
  * @license GPLv2 or later
10
  * @link https://www.joedolson.com/my-calendar/
11
  */
12
-
13
- // Define the table constants used in My Calendar in case anybody is still using them.
14
- // These were eliminated some time ago.
15
- if ( is_multisite() && get_site_option( 'mc_multisite_show' ) === '1' ) {
16
- define( 'MY_CALENDAR_TABLE', $wpdb->base_prefix . 'my_calendar' );
17
- define( 'MY_CALENDAR_EVENTS_TABLE', $wpdb->base_prefix . 'my_calendar_events' );
18
- define( 'MY_CALENDAR_CATEGORIES_TABLE', $wpdb->base_prefix . 'my_calendar_categories' );
19
- define( 'MY_CALENDAR_LOCATIONS_TABLE', $wpdb->base_prefix . 'my_calendar_locations' );
20
- } else {
21
- define( 'MY_CALENDAR_TABLE', $wpdb->prefix . 'my_calendar' );
22
- define( 'MY_CALENDAR_EVENTS_TABLE', $wpdb->prefix . 'my_calendar_events' );
23
- define( 'MY_CALENDAR_CATEGORIES_TABLE', $wpdb->prefix . 'my_calendar_categories' );
24
- define( 'MY_CALENDAR_LOCATIONS_TABLE', $wpdb->prefix . 'my_calendar_locations' );
25
- }
26
-
27
- if ( is_multisite() ) {
28
- // Define the tables used in My Calendar.
29
- define( 'MY_CALENDAR_GLOBAL_TABLE', $wpdb->base_prefix . 'my_calendar' );
30
- define( 'MY_CALENDAR_GLOBAL_EVENT_TABLE', $wpdb->base_prefix . 'my_calendar_events' );
31
- define( 'MY_CALENDAR_GLOBAL_CATEGORIES_TABLE', $wpdb->base_prefix . 'my_calendar_categories' );
32
- define( 'MY_CALENDAR_GLOBAL_LOCATIONS_TABLE', $wpdb->base_prefix . 'my_calendar_locations' );
33
- }
34
-
35
- /**
36
- * Old support box function
37
- *
38
- * @see mc_show_sidebar()
39
- * @deprecated
40
- */
41
- function jd_show_support_box() {
42
- $purchase_url = 'https://www.joedolson.com/awesome/my-calendar-pro/';
43
- $check_url = 'https://www.joedolson.com/login/';
44
- $add = array(
45
- // Translators: Purchase URL, account URL.
46
- 'My Calendar Pro out of date!' => '<p>' . __( 'The version of My Calendar Pro (or My Calendar Submissions) you have installed is very out of date!', 'my-calendar' ) . '</p><p>' . __( 'The latest version of My Calendar Pro is the only version recommended for compatibility with My Calendar. Please <a href="%1$s">purchase an upgrade</a> or <a href="%2$s">login to check your license status</a>!', 'my-calendar' ) . '</p>',
47
- );
48
- mc_show_sidebar( '', $add, true );
49
- }
50
-
51
- /**
52
- * Old name of template drawing function. Deprecated 6/14/2018. Removed in Pro 3/31/2019.
53
- *
54
- * @see mc_draw_template()
55
- *
56
- * @param array $array Associative Array of information.
57
- * @param string $template String containing tags.
58
- * @param string $type Type of display.
59
- *
60
- * @return string
61
- */
62
- function jd_draw_template( $array, $template, $type = 'list' ) {
63
-
64
- return mc_draw_template( $array, $template, $type );
65
- }
9
  * @license GPLv2 or later
10
  * @link https://www.joedolson.com/my-calendar/
11
  */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/event-utilities.php CHANGED
@@ -49,7 +49,7 @@ function mc_test_occurrence_overlap( $data, $return = false ) {
49
  $start_end = ( $data->event_begin === $data->event_end ) ? true : false;
50
  // Only run test when an event is set up to recur & starts/ends on different days.
51
  if ( ! $single_recur && ! $start_end ) {
52
- $check = mc_increment_event( $data->event_id, array(), 'test' );
53
  if ( my_calendar_date_xcomp( $check['occur_begin'], $data->event_end . ' ' . $data->event_endtime ) ) {
54
  $warning = "<div class='error'><span class='problem-icon dashicons dashicons-performance' aria-hidden='true'></span> <p><strong>" . __( 'Event hidden from public view.', 'my-calendar' ) . '</strong> ' . __( 'This event ends after the next occurrence begins. Events must end <strong>before</strong> the next occurrence begins.', 'my-calendar' ) . '</p><p>';
55
  $enddate = date_i18n( mc_date_format(), strtotime( $data->event_end ) );
@@ -70,6 +70,8 @@ function mc_test_occurrence_overlap( $data, $return = false ) {
70
  } else {
71
  echo wp_kses_post( $warning );
72
  }
 
 
73
  }
74
 
75
  /**
49
  $start_end = ( $data->event_begin === $data->event_end ) ? true : false;
50
  // Only run test when an event is set up to recur & starts/ends on different days.
51
  if ( ! $single_recur && ! $start_end ) {
52
+ $check = mc_increment_event( $data->event_id, array(), true );
53
  if ( my_calendar_date_xcomp( $check['occur_begin'], $data->event_end . ' ' . $data->event_endtime ) ) {
54
  $warning = "<div class='error'><span class='problem-icon dashicons dashicons-performance' aria-hidden='true'></span> <p><strong>" . __( 'Event hidden from public view.', 'my-calendar' ) . '</strong> ' . __( 'This event ends after the next occurrence begins. Events must end <strong>before</strong> the next occurrence begins.', 'my-calendar' ) . '</p><p>';
55
  $enddate = date_i18n( mc_date_format(), strtotime( $data->event_end ) );
70
  } else {
71
  echo wp_kses_post( $warning );
72
  }
73
+
74
+ return $warning;
75
  }
76
 
77
  /**
includes/general-utilities.php CHANGED
@@ -39,6 +39,8 @@ function mc_switch_sites() {
39
  *
40
  * @param string $prev Previous status.
41
  * @param string $new New status.
 
 
42
  */
43
  function mc_tweet_approval( $prev, $new ) {
44
  if ( function_exists( 'wpt_post_to_twitter' ) && isset( $_POST['mc_twitter'] ) && trim( $_POST['mc_twitter'] ) !== '' ) {
@@ -54,7 +56,7 @@ function mc_tweet_approval( $prev, $new ) {
54
  *
55
  * @param array $events Array of events.
56
  *
57
- * @return new array
58
  */
59
  function mc_flatten_array( $events ) {
60
  $new_array = array();
@@ -72,6 +74,8 @@ function mc_flatten_array( $events ) {
72
  add_action( 'admin_menu', 'mc_add_outer_box' );
73
  /**
74
  * Add meta boxes
 
 
75
  */
76
  function mc_add_outer_box() {
77
  add_meta_box( 'mcs_add_event', __( 'My Calendar Event', 'my-calendar' ), 'mc_add_inner_box', 'mc-events', 'side', 'high' );
@@ -79,6 +83,8 @@ function mc_add_outer_box() {
79
 
80
  /**
81
  * Add inner metabox
 
 
82
  */
83
  function mc_add_inner_box() {
84
  global $post;
@@ -105,7 +111,7 @@ function mc_add_inner_box() {
105
  /**
106
  * Pass group of allowed tags to strip_tags
107
  *
108
- * @return string of allowed tags parseable by strip_tags.
109
  */
110
  function mc_strip_tags() {
111
 
@@ -115,7 +121,7 @@ function mc_strip_tags() {
115
  /**
116
  * Pass group of allowed tags to strip_tags
117
  *
118
- * @return string of allowed tags parseable by strip_tags.
119
  */
120
  function mc_admin_strip_tags() {
121
 
@@ -125,12 +131,12 @@ function mc_admin_strip_tags() {
125
  /**
126
  * Old function for checking value of an option field
127
  *
128
- * @param string $field Name of the field.
129
- * @param mixed string/int/boolean $value Current value.
130
- * @param string $array if this setting is an array, the array key.
131
- * @param boolean $return whether to return or echo.
132
  *
133
- * @return checked=checked
134
  */
135
  function mc_is_checked( $field, $value, $array = '', $return = false ) {
136
  if ( ! is_array( get_option( $field ) ) ) {
@@ -151,14 +157,16 @@ function mc_is_checked( $field, $value, $array = '', $return = false ) {
151
  }
152
  }
153
  }
 
 
154
  }
155
 
156
  /**
157
  * Old function for checking value of an option field in a select
158
  *
159
- * @param string $field Name of the field.
160
- * @param mixed string/int/boolean $value Current value.
161
- * @param string $array if this setting is an array, the array key.
162
  *
163
  * @return string selected=selected
164
  */
@@ -182,9 +190,9 @@ function mc_is_selected( $field, $value, $array = '' ) {
182
  *
183
  * @deprecated 3.3.0
184
  *
185
- * @param string $field Name of the field.
186
- * @param mixed string/int/boolean $value Current value.
187
- * @param string $type checkbox, radio, option.
188
  *
189
  * @return string
190
  */
@@ -213,11 +221,13 @@ function mc_option_selected( $field, $value, $type = 'checkbox' ) {
213
  /**
214
  * Check selection
215
  *
216
- * @param string $field Name of field.
217
- * @param mixed string/int/boolean $value Type of value.
218
- * @param string $type Type of input.
219
  *
220
  * @see mc_option_selected()
 
 
221
  */
222
  function jd_option_selected( $field, $value, $type = 'checkbox' ) {
223
 
@@ -230,7 +240,7 @@ if ( ! function_exists( 'exif_imagetype' ) ) {
230
  *
231
  * @param string $filename Name of file.
232
  *
233
- * @return string type of file.
234
  */
235
  function exif_imagetype( $filename ) {
236
  if ( ! is_dir( $filename ) && ( list( $width, $height, $type, $attr ) = getimagesize( $filename ) ) !== false ) { // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.NonVariableAssignmentFound
@@ -381,7 +391,7 @@ function mc_html_type() {
381
  *
382
  * @param string $url URL.
383
  *
384
- * @return URL, if valid.
385
  */
386
  function _mc_is_url( $url ) {
387
 
@@ -397,7 +407,7 @@ function _mc_is_url( $url ) {
397
  */
398
  function mc_external_link( $link ) {
399
  if ( ! _mc_is_url( $link ) ) {
400
- return 'class="error-link"';
401
  }
402
 
403
  $url = parse_url( $link );
@@ -450,7 +460,7 @@ function reverse_array( $array, $boolean, $order ) {
450
  * @param string $body Text for email body.
451
  * @param string $email target email (if sending via email).
452
  */
453
- function mc_debug( $subject, $body, $email = false ) {
454
  if ( defined( 'MC_DEBUG' ) && true === MC_DEBUG ) {
455
  if ( ! $email ) {
456
  $email = get_option( 'admin_email' );
@@ -458,6 +468,14 @@ function mc_debug( $subject, $body, $email = false ) {
458
  if ( defined( 'MC_DEBUG_METHOD' ) && 'email' === MC_DEBUG_METHOD ) {
459
  wp_mail( get_option( 'admin_email' ), $subject, print_r( $body ) );
460
  } else {
 
 
 
 
 
 
 
 
461
  do_action( 'mc_debug', $subject, $body );
462
  }
463
  }
@@ -533,28 +551,27 @@ function mc_show_notice( $message, $echo = true, $code = false ) {
533
  $message = "<div class='updated'><p>$message</p></div>";
534
  if ( $echo ) {
535
  echo wp_kses_post( $message );
536
- } else {
537
- return $message;
538
  }
 
539
  }
540
 
541
  /**
542
  * Display an error message.
543
  *
544
- * @param string $message Error message.
545
- * @param boolean $echo Echo or return. Default true (echo).
 
546
  *
547
  * @return string
548
  */
549
- function mc_show_error( $message, $echo = true ) {
550
  if ( trim( $message ) === '' ) {
551
  return '';
552
  }
553
- $message = strip_tags( $message, mc_admin_strip_tags() );
554
  $message = "<div class='error'><p>$message</p></div>";
555
  if ( $echo ) {
556
  echo $message;
557
- } else {
558
- return $message;
559
  }
 
560
  }
39
  *
40
  * @param string $prev Previous status.
41
  * @param string $new New status.
42
+ *
43
+ * @return void
44
  */
45
  function mc_tweet_approval( $prev, $new ) {
46
  if ( function_exists( 'wpt_post_to_twitter' ) && isset( $_POST['mc_twitter'] ) && trim( $_POST['mc_twitter'] ) !== '' ) {
56
  *
57
  * @param array $events Array of events.
58
  *
59
+ * @return array<int, mixed>
60
  */
61
  function mc_flatten_array( $events ) {
62
  $new_array = array();
74
  add_action( 'admin_menu', 'mc_add_outer_box' );
75
  /**
76
  * Add meta boxes
77
+ *
78
+ * @return void
79
  */
80
  function mc_add_outer_box() {
81
  add_meta_box( 'mcs_add_event', __( 'My Calendar Event', 'my-calendar' ), 'mc_add_inner_box', 'mc-events', 'side', 'high' );
83
 
84
  /**
85
  * Add inner metabox
86
+ *
87
+ * @return void
88
  */
89
  function mc_add_inner_box() {
90
  global $post;
111
  /**
112
  * Pass group of allowed tags to strip_tags
113
  *
114
+ * @return string Allowed tags parseable by strip_tags.
115
  */
116
  function mc_strip_tags() {
117
 
121
  /**
122
  * Pass group of allowed tags to strip_tags
123
  *
124
+ * @return string Allowed tags parseable by strip_tags in admin.
125
  */
126
  function mc_admin_strip_tags() {
127
 
131
  /**
132
  * Old function for checking value of an option field
133
  *
134
+ * @param string $field Name of the field.
135
+ * @param string|int|boolean $value Current value.
136
+ * @param string $array if this setting is an array, the array key.
137
+ * @param boolean $return whether to return or echo.
138
  *
139
+ * @return string checked=checked
140
  */
141
  function mc_is_checked( $field, $value, $array = '', $return = false ) {
142
  if ( ! is_array( get_option( $field ) ) ) {
157
  }
158
  }
159
  }
160
+
161
+ return '';
162
  }
163
 
164
  /**
165
  * Old function for checking value of an option field in a select
166
  *
167
+ * @param string $field Name of the field.
168
+ * @param string|int|boolean $value Current value.
169
+ * @param string $array if this setting is an array, the array key.
170
  *
171
  * @return string selected=selected
172
  */
190
  *
191
  * @deprecated 3.3.0
192
  *
193
+ * @param string $field Name of the field.
194
+ * @param string|int|boolean $value Current value.
195
+ * @param string $type checkbox, radio, option.
196
  *
197
  * @return string
198
  */
221
  /**
222
  * Check selection
223
  *
224
+ * @param string $field Name of field.
225
+ * @param string|int|boolean $value Current value.
226
+ * @param string $type Type of input.
227
  *
228
  * @see mc_option_selected()
229
+ *
230
+ * @return string
231
  */
232
  function jd_option_selected( $field, $value, $type = 'checkbox' ) {
233
 
240
  *
241
  * @param string $filename Name of file.
242
  *
243
+ * @return string|bool type of file.
244
  */
245
  function exif_imagetype( $filename ) {
246
  if ( ! is_dir( $filename ) && ( list( $width, $height, $type, $attr ) = getimagesize( $filename ) ) !== false ) { // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.NonVariableAssignmentFound
391
  *
392
  * @param string $url URL.
393
  *
394
+ * @return int|false URL, if valid.
395
  */
396
  function _mc_is_url( $url ) {
397
 
407
  */
408
  function mc_external_link( $link ) {
409
  if ( ! _mc_is_url( $link ) ) {
410
+ return true; // If this is not a valid URL, consider it to be external.
411
  }
412
 
413
  $url = parse_url( $link );
460
  * @param string $body Text for email body.
461
  * @param string $email target email (if sending via email).
462
  */
463
+ function mc_debug( $subject, $body, $email = '' ) {
464
  if ( defined( 'MC_DEBUG' ) && true === MC_DEBUG ) {
465
  if ( ! $email ) {
466
  $email = get_option( 'admin_email' );
468
  if ( defined( 'MC_DEBUG_METHOD' ) && 'email' === MC_DEBUG_METHOD ) {
469
  wp_mail( get_option( 'admin_email' ), $subject, print_r( $body ) );
470
  } else {
471
+ /**
472
+ * Execute a custom debug action during an mc_debug call. Runs if MC_DEBUG_METHOD is not 'email'.
473
+ *
474
+ * @hook mc_debug
475
+ *
476
+ * @param {string} $subject Subject line of email debugging message.
477
+ * @param {string} $body Body of email debugging message.
478
+ */
479
  do_action( 'mc_debug', $subject, $body );
480
  }
481
  }
551
  $message = "<div class='updated'><p>$message</p></div>";
552
  if ( $echo ) {
553
  echo wp_kses_post( $message );
 
 
554
  }
555
+ return $message;
556
  }
557
 
558
  /**
559
  * Display an error message.
560
  *
561
+ * @param string $message Error message.
562
+ * @param boolean $echo Echo or return. Default true (echo).
563
+ * @param boolean|string $code Message code.
564
  *
565
  * @return string
566
  */
567
+ function mc_show_error( $message, $echo = true, $code = false ) {
568
  if ( trim( $message ) === '' ) {
569
  return '';
570
  }
571
+ $message = strip_tags( apply_filters( 'mc_filter_error', $message, $code ), mc_admin_strip_tags() );
572
  $message = "<div class='error'><p>$message</p></div>";
573
  if ( $echo ) {
574
  echo $message;
 
 
575
  }
576
+ return $message;
577
  }
includes/kses.php CHANGED
@@ -18,7 +18,7 @@ if ( ! defined( 'ABSPATH' ) ) {
18
  *
19
  * @param string $string Any string.
20
  *
21
- * @return Value passed or cleaned string
22
  */
23
  function mc_kses_post( $string ) {
24
  if ( ! is_string( $string ) ) {
@@ -36,7 +36,7 @@ add_filter( 'wp_kses_allowed_html', 'mc_allowed_tags', 10, 2 );
36
  * @param array $tags Original allowed tags.
37
  * @param string $context Custom context for My Calendar to avoid running elsewhere.
38
  *
39
- * @return return array tags
40
  */
41
  function mc_allowed_tags( $tags, $context ) {
42
  if ( 'mycalendar' === $context ) {
@@ -388,6 +388,7 @@ function mc_kses_elements() {
388
  ),
389
  'time' => array(
390
  'data-label' => array(),
 
391
  ),
392
  );
393
 
18
  *
19
  * @param string $string Any string.
20
  *
21
+ * @return string Value passed or cleaned string
22
  */
23
  function mc_kses_post( $string ) {
24
  if ( ! is_string( $string ) ) {
36
  * @param array $tags Original allowed tags.
37
  * @param string $context Custom context for My Calendar to avoid running elsewhere.
38
  *
39
+ * @return array tags
40
  */
41
  function mc_allowed_tags( $tags, $context ) {
42
  if ( 'mycalendar' === $context ) {
388
  ),
389
  'time' => array(
390
  'data-label' => array(),
391
+ 'class' => array(),
392
  ),
393
  );
394
 
includes/privacy.php CHANGED
@@ -19,7 +19,7 @@ add_filter( 'wp_privacy_personal_data_exporters', 'my_calendar_exporter', 10 );
19
  *
20
  * @param array $exporters All registered exporters.
21
  *
22
- * @return array
23
  */
24
  function my_calendar_exporter( $exporters ) {
25
  $exporters['my-calendar-exporter'] = array(
@@ -36,7 +36,7 @@ function my_calendar_exporter( $exporters ) {
36
  * @param string $email_address Email address to get data for.
37
  * @param int $page Page of data to remove.
38
  *
39
- * @return array
40
  */
41
  function my_calendar_privacy_export( $email_address, $page = 1 ) {
42
  global $wpdb;
@@ -117,7 +117,7 @@ add_filter( 'wp_privacy_personal_data_erasers', 'my_calendar_eraser', 10 );
117
  *
118
  * @param array $erasers All registered erasers.
119
  *
120
- * @return array
121
  */
122
  function my_calendar_eraser( $erasers ) {
123
  $erasers['my-calendar-eraser'] = array(
@@ -134,7 +134,7 @@ function my_calendar_eraser( $erasers ) {
134
  * @param string $email_address Email address to get data for.
135
  * @param int $page Page of data to remove.
136
  *
137
- * @return array
138
  */
139
  function my_calendar_privacy_eraser( $email_address, $page = 1 ) {
140
  global $wpdb;
@@ -146,6 +146,8 @@ function my_calendar_privacy_eraser( $email_address, $page = 1 ) {
146
  'done' => true,
147
  );
148
  }
 
 
149
 
150
  // Need to get all events with this email address as host, author, or meta data.
151
  $posts = $wpdb->get_results( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_submitter_details' AND 'meta_value' LIKE %s", '%' . esc_sql( $email_address ) . '%s' ) );
19
  *
20
  * @param array $exporters All registered exporters.
21
  *
22
+ * @return array<string, mixed>
23
  */
24
  function my_calendar_exporter( $exporters ) {
25
  $exporters['my-calendar-exporter'] = array(
36
  * @param string $email_address Email address to get data for.
37
  * @param int $page Page of data to remove.
38
  *
39
+ * @return array<string, mixed>
40
  */
41
  function my_calendar_privacy_export( $email_address, $page = 1 ) {
42
  global $wpdb;
117
  *
118
  * @param array $erasers All registered erasers.
119
  *
120
+ * @return array<string, mixed>
121
  */
122
  function my_calendar_eraser( $erasers ) {
123
  $erasers['my-calendar-eraser'] = array(
134
  * @param string $email_address Email address to get data for.
135
  * @param int $page Page of data to remove.
136
  *
137
+ * @return array<string, mixed>
138
  */
139
  function my_calendar_privacy_eraser( $email_address, $page = 1 ) {
140
  global $wpdb;
146
  'done' => true,
147
  );
148
  }
149
+ $deletions = array();
150
+ $updates = array();
151
 
152
  // Need to get all events with this email address as host, author, or meta data.
153
  $posts = $wpdb->get_results( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_submitter_details' AND 'meta_value' LIKE %s", '%' . esc_sql( $email_address ) . '%s' ) );
includes/screen-options.php CHANGED
@@ -14,7 +14,9 @@ if ( ! defined( 'ABSPATH' ) ) {
14
  }
15
 
16
  /**
17
- * Implement show on page selection fields
 
 
18
  */
19
  function mc_event_editing() {
20
  $args = array(
@@ -28,7 +30,7 @@ function mc_event_editing() {
28
  /**
29
  * Get event input default values.
30
  *
31
- * @return array
32
  */
33
  function mc_input_defaults() {
34
  return apply_filters(
@@ -53,13 +55,13 @@ add_filter( 'screen_settings', 'mc_show_event_editing', 10, 2 );
53
  * Show event editing options for user
54
  *
55
  * @param string $status string.
56
- * @param array $args array Arguments.
57
  *
58
  * @return string
59
  */
60
- function mc_show_event_editing( $status, $args ) {
61
  $return = $status;
62
- if ( 'toplevel_page_my-calendar' === $args->base ) {
63
  $input_options = get_user_meta( get_current_user_id(), 'mc_show_on_page', true );
64
  $settings_options = get_option( 'mc_input_options' );
65
  if ( ! is_array( $input_options ) ) {
@@ -88,11 +90,11 @@ function mc_show_event_editing( $status, $args ) {
88
  $enabled = ( isset( $input_options[ $key ] ) ) ? $input_options[ $key ] : false;
89
  $checked = ( 'on' === $enabled ) ? "checked='checked'" : '';
90
  $allowed = ( isset( $settings_options[ $key ] ) && 'on' === $settings_options[ $key ] ) ? true : false;
91
- if ( ! ( current_user_can( 'manage_options' ) && 'true' === get_option( 'mc_input_options_administrators' ) ) && ! $allowed ) {
 
 
92
  // don't display options if this user can't use them.
93
  $output .= "<input type='hidden' name='mc_show_on_page[$key]' value='off' />";
94
- } else {
95
- $output .= "<label for='mci_$key'><input type='checkbox' id='mci_$key' name='mc_show_on_page[$key]' value='on' $checked /> $value</label>";
96
  }
97
  }
98
  $button = get_submit_button( __( 'Apply', 'my-calendar' ), 'button', 'screen-options-apply', false );
@@ -116,11 +118,11 @@ add_filter( 'set-screen-option', 'mc_set_event_editing', 11, 3 );
116
  /**
117
  * Save settings for screen options
118
  *
119
- * @param string $status string.
120
- * @param string $option option name.
121
- * @param string $value rows to use.
122
  *
123
- * @return value
124
  */
125
  function mc_set_event_editing( $status, $option, $value ) {
126
  if ( 'mc_show_on_page' === $option ) {
@@ -140,7 +142,9 @@ function mc_set_event_editing( $status, $option, $value ) {
140
  }
141
 
142
  /**
143
- * Add the screen option for num per page
 
 
144
  */
145
  function mc_add_screen_option() {
146
  $items_per_page = apply_filters( 'mc_num_per_page_default', 50 );
@@ -155,6 +159,8 @@ function mc_add_screen_option() {
155
 
156
  /**
157
  * Add help tab on events.
 
 
158
  */
159
  function mc_add_help_tab() {
160
  $screen = get_current_screen();
@@ -178,7 +184,9 @@ function mc_add_help_tab() {
178
  }
179
 
180
  /**
181
- * Add help tab on events.
 
 
182
  */
183
  function mc_location_help_tab() {
184
  $screen = get_current_screen();
14
  }
15
 
16
  /**
17
+ * Implement show on page selection fields.
18
+ *
19
+ * @return void
20
  */
21
  function mc_event_editing() {
22
  $args = array(
30
  /**
31
  * Get event input default values.
32
  *
33
+ * @return array<string, string>
34
  */
35
  function mc_input_defaults() {
36
  return apply_filters(
55
  * Show event editing options for user
56
  *
57
  * @param string $status string.
58
+ * @param object $screen Object - screen object.
59
  *
60
  * @return string
61
  */
62
+ function mc_show_event_editing( $status, $screen ) {
63
  $return = $status;
64
+ if ( 'toplevel_page_my-calendar' === $screen->base ) {
65
  $input_options = get_user_meta( get_current_user_id(), 'mc_show_on_page', true );
66
  $settings_options = get_option( 'mc_input_options' );
67
  if ( ! is_array( $input_options ) ) {
90
  $enabled = ( isset( $input_options[ $key ] ) ) ? $input_options[ $key ] : false;
91
  $checked = ( 'on' === $enabled ) ? "checked='checked'" : '';
92
  $allowed = ( isset( $settings_options[ $key ] ) && 'on' === $settings_options[ $key ] ) ? true : false;
93
+ if ( current_user_can( 'manage_options' ) || $allowed ) {
94
+ $output .= "<label for='mci_$key'><input type='checkbox' id='mci_$key' name='mc_show_on_page[$key]' value='on' $checked /> $value</label>";
95
+ } else {
96
  // don't display options if this user can't use them.
97
  $output .= "<input type='hidden' name='mc_show_on_page[$key]' value='off' />";
 
 
98
  }
99
  }
100
  $button = get_submit_button( __( 'Apply', 'my-calendar' ), 'button', 'screen-options-apply', false );
118
  /**
119
  * Save settings for screen options
120
  *
121
+ * @param string $status string.
122
+ * @param string $option option name.
123
+ * @param array|string $value rows to use.
124
  *
125
+ * @return array<string>|string
126
  */
127
  function mc_set_event_editing( $status, $option, $value ) {
128
  if ( 'mc_show_on_page' === $option ) {
142
  }
143
 
144
  /**
145
+ * Add the screen option for num per page.
146
+ *
147
+ * @return void
148
  */
149
  function mc_add_screen_option() {
150
  $items_per_page = apply_filters( 'mc_num_per_page_default', 50 );
159
 
160
  /**
161
  * Add help tab on events.
162
+ *
163
+ * @return void
164
  */
165
  function mc_add_help_tab() {
166
  $screen = get_current_screen();
184
  }
185
 
186
  /**
187
+ * Add help tab on locations.
188
+ *
189
+ * @return void
190
  */
191
  function mc_location_help_tab() {
192
  $screen = get_current_screen();
includes/urls.php CHANGED
@@ -16,9 +16,9 @@ if ( ! defined( 'ABSPATH' ) ) {
16
  /**
17
  * Build a URL for My Calendar views.
18
  *
19
- * @param array $add keys and values to add to URL.
20
- * @param array $subtract keys to subtract from URL.
21
- * @param string $root Root URL, optional.
22
  *
23
  * @return string URL.
24
  */
@@ -51,7 +51,7 @@ function mc_build_url( $add, $subtract, $root = '' ) {
51
  }
52
  }
53
 
54
- $variables = $_GET;
55
  $subtract = array_merge( (array) $subtract, array( 'from', 'to', 'my-calendar-api', 's', 'embed' ) );
56
  foreach ( $subtract as $value ) {
57
  unset( $variables[ $value ] );
@@ -89,11 +89,11 @@ function mc_url_in_loop( $url ) {
89
  /**
90
  * Build the URL for use in the mini calendar
91
  *
92
- * @param string $start link date.
93
- * @param int $category current category.
94
- * @param array $events array of event objects.
95
- * @param array $args calendar view parameters.
96
- * @param string $date view date.
97
  *
98
  * @return string URL
99
  */
@@ -107,7 +107,7 @@ function mc_build_mini_url( $start, $category, $events, $args, $date ) {
107
  'dy' => mc_date( 'j', $start, false ),
108
  'time' => 'day',
109
  );
110
- if ( ! ( '' === $category ) ) {
111
  $target['mcat'] = $category;
112
  }
113
  $day_url = mc_build_url( $target, array( 'month', 'dy', 'yr', 'ltype', 'loc', 'mcat', 'cid', 'mc_id' ), apply_filters( 'mc_modify_day_uri', mc_get_uri( reset( $events ), $args ) ) );
@@ -141,6 +141,7 @@ function mc_build_mini_url( $start, $category, $events, $args, $date ) {
141
  */
142
  function mc_translate_url( $url ) {
143
  $is_default = true;
 
144
  // Polylang support.
145
  if ( function_exists( 'pll_home_url' ) ) {
146
  $home_url = pll_home_url();
16
  /**
17
  * Build a URL for My Calendar views.
18
  *
19
+ * @param array<string> $add keys and values to add to URL.
20
+ * @param array<string> $subtract keys to subtract from URL.
21
+ * @param string $root Root URL, optional.
22
  *
23
  * @return string URL.
24
  */
51
  }
52
  }
53
 
54
+ $variables = map_deep( $_GET, 'sanitize_text_field' );
55
  $subtract = array_merge( (array) $subtract, array( 'from', 'to', 'my-calendar-api', 's', 'embed' ) );
56
  foreach ( $subtract as $value ) {
57
  unset( $variables[ $value ] );
89
  /**
90
  * Build the URL for use in the mini calendar
91
  *
92
+ * @param int $start date timestamp.
93
+ * @param int $category current category.
94
+ * @param array $events array of event objects.
95
+ * @param array $args calendar view parameters.
96
+ * @param array $date view date.
97
  *
98
  * @return string URL
99
  */
107
  'dy' => mc_date( 'j', $start, false ),
108
  'time' => 'day',
109
  );
110
+ if ( $category ) {
111
  $target['mcat'] = $category;
112
  }
113
  $day_url = mc_build_url( $target, array( 'month', 'dy', 'yr', 'ltype', 'loc', 'mcat', 'cid', 'mc_id' ), apply_filters( 'mc_modify_day_uri', mc_get_uri( reset( $events ), $args ) ) );
141
  */
142
  function mc_translate_url( $url ) {
143
  $is_default = true;
144
+ $home_url = home_url();
145
  // Polylang support.
146
  if ( function_exists( 'pll_home_url' ) ) {
147
  $home_url = pll_home_url();
includes/widgets/class-my-calendar-filters.php CHANGED
@@ -114,10 +114,10 @@ class My_Calendar_Filters extends WP_Widget {
114
  /**
115
  * Update the My Calendar Event Filters Widget settings.
116
  *
117
- * @param object $new Widget settings new data.
118
- * @param object $instance Widget settings instance.
119
  *
120
- * @return $instance Updated instance.
121
  */
122
  function update( $new, $instance ) {
123
  $instance['title'] = esc_html( $new['title'] );
114
  /**
115
  * Update the My Calendar Event Filters Widget settings.
116
  *
117
+ * @param array $new Widget settings new data.
118
+ * @param array $instance Widget settings instance.
119
  *
120
+ * @return array $instance Updated instance.
121
  */
122
  function update( $new, $instance ) {
123
  $instance['title'] = esc_html( $new['title'] );
includes/widgets/class-my-calendar-mini-widget.php CHANGED
@@ -235,10 +235,10 @@ class My_Calendar_Mini_Widget extends WP_Widget {
235
  /**
236
  * Update the My Calendar Mini Widget settings.
237
  *
238
- * @param object $new Widget settings new data.
239
- * @param object $instance Widget settings instance.
240
  *
241
- * @return $instance Updated instance.
242
  */
243
  function update( $new, $instance ) {
244
  $instance['my_calendar_mini_title'] = mc_kses_post( $new['my_calendar_mini_title'] );
235
  /**
236
  * Update the My Calendar Mini Widget settings.
237
  *
238
+ * @param array $new Widget settings new data.
239
+ * @param array $instance Widget settings instance.
240
  *
241
+ * @return array $instance Updated instance.
242
  */
243
  function update( $new, $instance ) {
244
  $instance['my_calendar_mini_title'] = mc_kses_post( $new['my_calendar_mini_title'] );
includes/widgets/class-my-calendar-simple-search.php CHANGED
@@ -82,10 +82,10 @@ class My_Calendar_Simple_Search extends WP_Widget {
82
  /**
83
  * Update the My Calendar Search Widget settings.
84
  *
85
- * @param object $new Widget settings new data.
86
- * @param object $instance Widget settings instance.
87
  *
88
- * @return $instance Updated instance.
89
  */
90
  function update( $new, $instance ) {
91
  $instance['title'] = mc_kses_post( $new['title'] );
82
  /**
83
  * Update the My Calendar Search Widget settings.
84
  *
85
+ * @param array $new Widget settings new data.
86
+ * @param array $instance Widget settings instance.
87
  *
88
+ * @return array $instance Updated instance.
89
  */
90
  function update( $new, $instance ) {
91
  $instance['title'] = mc_kses_post( $new['title'] );
includes/widgets/class-my-calendar-today-widget.php CHANGED
@@ -181,10 +181,10 @@ class My_Calendar_Today_Widget extends WP_Widget {
181
  /**
182
  * Update the My Calendar Today's Events Widget settings.
183
  *
184
- * @param object $new Widget settings new data.
185
- * @param object $instance Widget settings instance.
186
  *
187
- * @return $instance Updated instance.
188
  */
189
  function update( $new, $instance ) {
190
  $instance = array_map( 'mc_kses_post', array_merge( $instance, $new ) );
181
  /**
182
  * Update the My Calendar Today's Events Widget settings.
183
  *
184
+ * @param array $new Widget settings new data.
185
+ * @param array $instance Widget settings instance.
186
  *
187
+ * @return array $instance Updated instance.
188
  */
189
  function update( $new, $instance ) {
190
  $instance = array_map( 'mc_kses_post', array_merge( $instance, $new ) );
includes/widgets/class-my-calendar-upcoming-widget.php CHANGED
@@ -117,6 +117,8 @@ class My_Calendar_Upcoming_Widget extends WP_Widget {
117
  * Edit the upcoming events widget.
118
  *
119
  * @param array $instance Current widget settings.
 
 
120
  */
121
  function form( $instance ) {
122
  $defaults = mc_widget_defaults();
@@ -305,10 +307,10 @@ class My_Calendar_Upcoming_Widget extends WP_Widget {
305
  /**
306
  * Update the My Calendar Upcoming Widget settings.
307
  *
308
- * @param object $new Widget settings new data.
309
- * @param object $instance Widget settings instance.
310
  *
311
- * @return $instance Updated instance.
312
  */
313
  function update( $new, $instance ) {
314
  $instance = array_map( 'mc_kses_post', array_merge( $instance, $new ) );
117
  * Edit the upcoming events widget.
118
  *
119
  * @param array $instance Current widget settings.
120
+ *
121
+ * @return void
122
  */
123
  function form( $instance ) {
124
  $defaults = mc_widget_defaults();
307
  /**
308
  * Update the My Calendar Upcoming Widget settings.
309
  *
310
+ * @param array $new Widget settings new data.
311
+ * @param array $instance Widget settings instance.
312
  *
313
+ * @return array Updated instance.
314
  */
315
  function update( $new, $instance ) {
316
  $instance = array_map( 'mc_kses_post', array_merge( $instance, $new ) );
js/mcjs.js CHANGED
@@ -15,11 +15,13 @@
15
  $( '.mini .has-events' ).children( '.trigger' ).removeClass( 'active-toggle' );
16
  $( '.mini .has-events' ).children().not( '.trigger, .mc-date, .event-date' ).not( current_date ).hide();
17
  $( this ).addClass( 'active-toggle' );
 
18
  } );
19
  $( document ).on( "click", ".calendar-events .close", function (e) {
20
  e.preventDefault();
21
  $(this).closest( '.mini .has-events' ).children( '.trigger' ).removeClass( 'active-toggle' );
22
  $(this).closest( 'div.calendar-events' ).toggle();
 
23
  } );
24
  });
25
  }
@@ -41,6 +43,8 @@
41
  } else {
42
  $(this).attr('aria-expanded', 'false');
43
  }
 
 
44
  });
45
  });
46
  }
@@ -68,6 +72,7 @@
68
  lastFocus.attr( 'data-action', 'shiftback' );
69
 
70
  $('.calendar-event').children().not('.event-title,.screen-reader-text').not( current_date ).hide();
 
71
  return false;
72
  });
73
 
@@ -78,6 +83,7 @@
78
  $(this).closest( '.mc-main' ).removeClass( 'grid-open' );
79
  $(this).closest('.mc-event').find('.event-title a').trigger( 'focus' );
80
  $(this).closest('div.details').toggle();
 
81
  });
82
 
83
  $(document).on( 'keydown', function(e) {
@@ -107,7 +113,7 @@
107
 
108
  if ( 'true' === my_calendar.ajax ) {
109
  $(function () {
110
- $(document).on('click', ".my-calendar-header a.mcajax, .my-calendar-footer a.mcajax, .my-calendar-header input[type=submit], .my-calendar-footer input[type=submit]", function (e) {
111
  e.preventDefault();
112
  var calendar = $( this ).closest( '.mc-main' );
113
  var ref = calendar.attr('id');
@@ -186,7 +192,7 @@
186
  /* $('#' + ref + ' .mc-content' ).html('<div class=\"mc-loading\"></div><div class=\"loading\" style=\"height:' + height + 'px\"><span class="screen-reader-text">Loading...</span></div>');
187
  $( '#' + ref + ' .mc-content' ).load(link + ' #' + ref + ' .mc-content > *', function ( response, status, xhr ) { */
188
  $('#' + ref ).html('<div class=\"mc-loading\"></div><div class=\"loading\" style=\"height:' + height + 'px\"><span class="screen-reader-text">Loading...</span></div>');
189
- $( '#' + ref ).load(link + ' #' + ref + ' > *', function ( response, status, xhr ) {
190
 
191
  if ( status == 'error' ) {
192
  $( '#' + ref ).html( xhr.status + " " + xhr.statusText );
15
  $( '.mini .has-events' ).children( '.trigger' ).removeClass( 'active-toggle' );
16
  $( '.mini .has-events' ).children().not( '.trigger, .mc-date, .event-date' ).not( current_date ).hide();
17
  $( this ).addClass( 'active-toggle' );
18
+ e.stopImmediatePropagation();
19
  } );
20
  $( document ).on( "click", ".calendar-events .close", function (e) {
21
  e.preventDefault();
22
  $(this).closest( '.mini .has-events' ).children( '.trigger' ).removeClass( 'active-toggle' );
23
  $(this).closest( 'div.calendar-events' ).toggle();
24
+ e.stopImmediatePropagation();
25
  } );
26
  });
27
  }
43
  } else {
44
  $(this).attr('aria-expanded', 'false');
45
  }
46
+ e.stopImmediatePropagation();
47
+ return false;
48
  });
49
  });
50
  }
72
  lastFocus.attr( 'data-action', 'shiftback' );
73
 
74
  $('.calendar-event').children().not('.event-title,.screen-reader-text').not( current_date ).hide();
75
+ e.stopImmediatePropagation();
76
  return false;
77
  });
78
 
83
  $(this).closest( '.mc-main' ).removeClass( 'grid-open' );
84
  $(this).closest('.mc-event').find('.event-title a').trigger( 'focus' );
85
  $(this).closest('div.details').toggle();
86
+ e.stopImmediatePropagation();
87
  });
88
 
89
  $(document).on( 'keydown', function(e) {
113
 
114
  if ( 'true' === my_calendar.ajax ) {
115
  $(function () {
116
+ $(document).on('click', ".my-calendar-header a, .my-calendar-footer a, .my-calendar-header input[type=submit], .my-calendar-footer input[type=submit]", function (e) {
117
  e.preventDefault();
118
  var calendar = $( this ).closest( '.mc-main' );
119
  var ref = calendar.attr('id');
192
  /* $('#' + ref + ' .mc-content' ).html('<div class=\"mc-loading\"></div><div class=\"loading\" style=\"height:' + height + 'px\"><span class="screen-reader-text">Loading...</span></div>');
193
  $( '#' + ref + ' .mc-content' ).load(link + ' #' + ref + ' .mc-content > *', function ( response, status, xhr ) { */
194
  $('#' + ref ).html('<div class=\"mc-loading\"></div><div class=\"loading\" style=\"height:' + height + 'px\"><span class="screen-reader-text">Loading...</span></div>');
195
+ $( '#' + ref ).load( link + ' #' + ref + ' > *', function ( response, status, xhr ) {
196
 
197
  if ( status == 'error' ) {
198
  $( '#' + ref ).html( xhr.status + " " + xhr.statusText );
js/mcjs.min.js CHANGED
@@ -1 +1 @@
1
- !function(e){"use strict";function t(){e(".mc-user-time").each(function(){var t=e(this).text(),a=e(this).attr("data-label"),n='<span class="mc-local-time-time">'+new Date(t).toLocaleTimeString().replace(":00 "," ")+"</span>",s='<span class="mc-local-time-date">'+new Date(t).toLocaleDateString()+"</span>";e(this).html('<span class="mc-local-time-label">'+a+"</span> "+s+'<span class="sep">, </span>'+n).attr("data-time",t)})}e(function(){t(),e(".mc-main").removeClass("mcjs")}),"true"===my_calendar.mini&&e(function(){e(".mini .has-events").children().not(".trigger, .mc-date, .event-date").hide(),e(document).on("click",".mini .has-events .trigger",function(t){t.preventDefault();var a=e(this).parent().children();a.not(".trigger").toggle().attr("tabindex","-1").trigger("focus"),e(".mini .has-events").children(".trigger").removeClass("active-toggle"),e(".mini .has-events").children().not(".trigger, .mc-date, .event-date").not(a).hide(),e(this).addClass("active-toggle")}),e(document).on("click",".calendar-events .close",function(t){t.preventDefault(),e(this).closest(".mini .has-events").children(".trigger").removeClass("active-toggle"),e(this).closest("div.calendar-events").toggle()})}),"true"===my_calendar.list&&e(function(){e("li.mc-events").children().not(".event-date").hide(),e("li.current-day").children().show(),e("li.current-day .event-date .mc-text-button").attr("aria-expanded",!0),e(document).on("click",".event-date button",function(t){t.preventDefault();var a=e(this).closest(".mc-events").find(".mc-event:first");e(this).closest(".mc-events").find(".mc-event").toggle(),a.attr("tabindex","-1").trigger("focus"),e(this).closest(".mc-events").find(".mc-event").is(":visible")?e(this).attr("aria-expanded","true"):e(this).attr("aria-expanded","false")})}),"true"===my_calendar.grid&&e(function(){e(".calendar-event").children().not(".event-title,.screen-reader-text").hide();var t=document.createElement("div");t.classList.add("my-calendar-mask"),document.querySelector("body").insertAdjacentElement("beforeend",t),e(document).on("click",".calendar-event .event-title .open",function(a){a.preventDefault();var n=e(this).parents(".mc-event").children();t.classList.add("mc-mask-active"),e(this).closest(".mc-main").toggleClass("grid-open"),e(this).parents(".mc-event").children().not(".event-title").toggle().attr("tabindex","-1"),e(this).parents(".mc-event").trigger("focus");var s=n.find("a, object, :input, iframe, [tabindex]"),i=s.last();s.first();return i.attr("data-action","shiftback"),e(".calendar-event").children().not(".event-title,.screen-reader-text").not(n).hide(),!1}),e(document).on("click",".calendar-event .close",function(a){t.classList.remove("mc-mask-active"),a.preventDefault(),e(this).closest(".mc-main").removeClass("grid-open"),e(this).closest(".mc-event").find(".event-title a").trigger("focus"),e(this).closest("div.details").toggle()}),e(document).on("keydown",function(a){27==(a.keyCode?a.keyCode:a.which)&&(t.classList.remove("mc-mask-active"),e(".mc-main ").removeClass("grid-open"),e(".calendar-event div.details").hide())}),e(document).on("keydown",".details a, .details object, .details :input, .details iframe, .details [tabindex]",function(t){var a=t.keyCode?t.keyCode:t.which,n=e(":focus").attr("data-action");t.shiftKey||9!=a||"shiftback"!=n||(t.preventDefault(),e(".mc-toggle.close").trigger("focus")),t.shiftKey&&9==a&&"shiftforward"==n&&(t.preventDefault(),e("[data-action=shiftback]").trigger("focus"))})}),"true"===my_calendar.ajax&&e(function(){e(document).on("click",".my-calendar-header a.mcajax, .my-calendar-footer a.mcajax, .my-calendar-header input[type=submit], .my-calendar-footer input[type=submit]",function(a){a.preventDefault();var n=e(this).closest(".mc-main"),s=n.attr("id"),i="",c="",r="",d="",l="",o="",m="";if("INPUT"===this.nodeName){var h=e(this).parents("form");if(h.hasClass("mc-date-switcher"))i=h.find("select[name=month]").val(),c=h.find("select[name=dy]").val(),r=h.find("select[name=yr]").val();if(h.hasClass("mc-categories-switcher"))d=h.find("select[name=mcat]").val();if(h.hasClass("mc-locations-switcher"))l=h.find("select[name=loc]").val();if(h.hasClass("mc-access-switcher"))o=h.find("select[name=access]").val();m=n.find("#mcs").val();var v=e(this).attr("data-href")}else v=e(this).attr("href");let f;try{(f=new URL(v)).searchParams.delete("embed"),"INPUT"===this.nodeName&&(""!==i&&(f.searchParams.delete("month"),f.searchParams.delete("dy"),f.searchParams.delete("yr"),f.searchParams.append("month",parseInt(i)),void 0!==c&&f.searchParams.append("dy",parseInt(c)),f.searchParams.append("yr",parseInt(r))),""!==d&&(f.searchParams.delete("mcat"),f.searchParams.append("mcat",d)),""!==l&&(f.searchParams.delete("loc"),f.searchParams.delete("ltype"),f.searchParams.append("ltype","name"),f.searchParams.append("loc",l)),""!==o&&(f.searchParams.delete("access"),f.searchParams.append("access",parseInt(o))),f.searchParams.delete("mcs"),""!==m&&void 0!==m&&f.searchParams.append("mcs",encodeURIComponent(m)),v=f.toString()),window.history.pushState({},"",f)}catch(e){f=!1}var u=n.height();e("#"+s).html('<div class="mc-loading"></div><div class="loading" style="height:'+u+'px"><span class="screen-reader-text">Loading...</span></div>'),e("#"+s).load(v+" #"+s+" > *",function(a,n,i){"error"==n&&e("#"+s).html(i.status+" "+i.statusText),"undefined"!=typeof my_calendar&&"true"==my_calendar.list&&(e("li.mc-events").children().not(".event-date").hide(),e("li.current-day").children().show()),"undefined"!=typeof my_calendar&&"true"==my_calendar.grid&&e(".calendar-event").children().not(".event-title").hide(),"undefined"!=typeof my_calendar&&"true"==my_calendar.mini&&e(".mini .has-events").children().not(".trigger, .mc-date, .event-date").hide(),e("#"+s).attr("tabindex","-1").trigger("focus"),t()})})}),e(".mc-main a[target=_blank]").append(' <span class="dashicons dashicons-external" aria-hidden="true"></span><span class="screen-reader-text"> '+my_calendar.newWindow+"</span>")}(jQuery);
1
+ !function(e){"use strict";function t(){e(".mc-user-time").each((function(){var t=e(this).text(),a=e(this).attr("data-label"),n='<span class="mc-local-time-time">'+new Date(t).toLocaleTimeString().replace(":00 "," ")+"</span>",s='<span class="mc-local-time-date">'+new Date(t).toLocaleDateString()+"</span>";e(this).html('<span class="mc-local-time-label">'+a+"</span> "+s+'<span class="sep">, </span>'+n).attr("data-time",t)}))}e((function(){t(),e(".mc-main").removeClass("mcjs")})),"true"===my_calendar.mini&&e((function(){e(".mini .has-events").children().not(".trigger, .mc-date, .event-date").hide(),e(document).on("click",".mini .has-events .trigger",(function(t){t.preventDefault();var a=e(this).parent().children();a.not(".trigger").toggle().attr("tabindex","-1").trigger("focus"),e(".mini .has-events").children(".trigger").removeClass("active-toggle"),e(".mini .has-events").children().not(".trigger, .mc-date, .event-date").not(a).hide(),e(this).addClass("active-toggle"),t.stopImmediatePropagation()})),e(document).on("click",".calendar-events .close",(function(t){t.preventDefault(),e(this).closest(".mini .has-events").children(".trigger").removeClass("active-toggle"),e(this).closest("div.calendar-events").toggle(),t.stopImmediatePropagation()}))})),"true"===my_calendar.list&&e((function(){e("li.mc-events").children().not(".event-date").hide(),e("li.current-day").children().show(),e("li.current-day .event-date .mc-text-button").attr("aria-expanded",!0),e(document).on("click",".event-date button",(function(t){t.preventDefault();var a=e(this).closest(".mc-events").find(".mc-event:first");return e(this).closest(".mc-events").find(".mc-event").toggle(),a.attr("tabindex","-1").trigger("focus"),e(this).closest(".mc-events").find(".mc-event").is(":visible")?e(this).attr("aria-expanded","true"):e(this).attr("aria-expanded","false"),t.stopImmediatePropagation(),!1}))})),"true"===my_calendar.grid&&e((function(){e(".calendar-event").children().not(".event-title,.screen-reader-text").hide();var t=document.createElement("div");t.classList.add("my-calendar-mask"),document.querySelector("body").insertAdjacentElement("beforeend",t),e(document).on("click",".calendar-event .event-title .open",(function(a){a.preventDefault();var n=e(this).parents(".mc-event").children();t.classList.add("mc-mask-active"),e(this).closest(".mc-main").toggleClass("grid-open"),e(this).parents(".mc-event").children().not(".event-title").toggle().attr("tabindex","-1"),e(this).parents(".mc-event").trigger("focus");var s=n.find("a, object, :input, iframe, [tabindex]"),i=s.last();s.first();return i.attr("data-action","shiftback"),e(".calendar-event").children().not(".event-title,.screen-reader-text").not(n).hide(),a.stopImmediatePropagation(),!1})),e(document).on("click",".calendar-event .close",(function(a){t.classList.remove("mc-mask-active"),a.preventDefault(),e(this).closest(".mc-main").removeClass("grid-open"),e(this).closest(".mc-event").find(".event-title a").trigger("focus"),e(this).closest("div.details").toggle(),a.stopImmediatePropagation()})),e(document).on("keydown",(function(a){27==(a.keyCode?a.keyCode:a.which)&&(t.classList.remove("mc-mask-active"),e(".mc-main ").removeClass("grid-open"),e(".calendar-event div.details").hide())})),e(document).on("keydown",".details a, .details object, .details :input, .details iframe, .details [tabindex]",(function(t){var a=t.keyCode?t.keyCode:t.which,n=e(":focus").attr("data-action");t.shiftKey||9!=a||"shiftback"!=n||(t.preventDefault(),e(".mc-toggle.close").trigger("focus")),t.shiftKey&&9==a&&"shiftforward"==n&&(t.preventDefault(),e("[data-action=shiftback]").trigger("focus"))}))})),"true"===my_calendar.ajax&&e((function(){e(document).on("click",".my-calendar-header a, .my-calendar-footer a, .my-calendar-header input[type=submit], .my-calendar-footer input[type=submit]",(function(a){a.preventDefault();var n=e(this).closest(".mc-main"),s=n.attr("id"),i="",r="",c="",d="",l="",o="",m="";if("INPUT"===this.nodeName){var h=e(this).parents("form");if(h.hasClass("mc-date-switcher"))i=h.find("select[name=month]").val(),r=h.find("select[name=dy]").val(),c=h.find("select[name=yr]").val();if(h.hasClass("mc-categories-switcher"))d=h.find("select[name=mcat]").val();if(h.hasClass("mc-locations-switcher"))l=h.find("select[name=loc]").val();if(h.hasClass("mc-access-switcher"))o=h.find("select[name=access]").val();m=n.find("#mcs").val();var v=e(this).attr("data-href")}else v=e(this).attr("href");let p;try{p=new URL(v),p.searchParams.delete("embed"),"INPUT"===this.nodeName&&(""!==i&&(p.searchParams.delete("month"),p.searchParams.delete("dy"),p.searchParams.delete("yr"),p.searchParams.append("month",parseInt(i)),void 0!==r&&p.searchParams.append("dy",parseInt(r)),p.searchParams.append("yr",parseInt(c))),""!==d&&(p.searchParams.delete("mcat"),p.searchParams.append("mcat",d)),""!==l&&(p.searchParams.delete("loc"),p.searchParams.delete("ltype"),p.searchParams.append("ltype","name"),p.searchParams.append("loc",l)),""!==o&&(p.searchParams.delete("access"),p.searchParams.append("access",parseInt(o))),p.searchParams.delete("mcs"),""!==m&&void 0!==m&&p.searchParams.append("mcs",encodeURIComponent(m)),v=p.toString()),window.history.pushState({},"",p)}catch(e){p=!1}var u=n.height();e("#"+s).html('<div class="mc-loading"></div><div class="loading" style="height:'+u+'px"><span class="screen-reader-text">Loading...</span></div>'),e("#"+s).load(v+" #"+s+" > *",(function(a,n,i){"error"==n&&e("#"+s).html(i.status+" "+i.statusText),"undefined"!=typeof my_calendar&&"true"==my_calendar.list&&(e("li.mc-events").children().not(".event-date").hide(),e("li.current-day").children().show()),"undefined"!=typeof my_calendar&&"true"==my_calendar.grid&&e(".calendar-event").children().not(".event-title").hide(),"undefined"!=typeof my_calendar&&"true"==my_calendar.mini&&e(".mini .has-events").children().not(".trigger, .mc-date, .event-date").hide(),e("#"+s).attr("tabindex","-1").trigger("focus"),t()}))}))})),e(".mc-main a[target=_blank]").append(' <span class="dashicons dashicons-external" aria-hidden="true"></span><span class="screen-reader-text"> '+my_calendar.newWindow+"</span>")}(jQuery);
mc-templates/event-grid.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template: Single Event, Grid view.
4
+ *
5
+ * @category Templates
6
+ * @package My Calendar
7
+ * @author Joe Dolson
8
+ * @license GPLv2 or later
9
+ * @link https://www.joedolson.com/my-calendar/
10
+ */
11
+
12
+ ?>
13
+ <div class="event">Grid Event</div>
mc-templates/event-list.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template: Single Event, List view.
4
+ *
5
+ * @category Templates
6
+ * @package My Calendar
7
+ * @author Joe Dolson
8
+ * @license GPLv2 or later
9
+ * @link https://www.joedolson.com/my-calendar/
10
+ */
11
+
12
+ ?>
13
+ <div class="event">List Event</div>
mc-templates/event-mini.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template: Single Event, Mini view.
4
+ *
5
+ * @category Templates
6
+ * @package My Calendar
7
+ * @author Joe Dolson
8
+ * @license GPLv2 or later
9
+ * @link https://www.joedolson.com/my-calendar/
10
+ */
11
+
12
+ ?>
13
+ <div class="event">Mini Event</div>
mc-templates/event-single.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template: Single Event, Single view.
4
+ *
5
+ * @category Templates
6
+ * @package My Calendar
7
+ * @author Joe Dolson
8
+ * @license GPLv2 or later
9
+ * @link https://www.joedolson.com/my-calendar/
10
+ */
11
+
12
+ ?>
13
+ <div class="event">Single Event</div>
mc-templates/event-upcoming.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Template: Single Event, Upcoming events view.
4
+ *
5
+ * @category Templates
6
+ * @package My Calendar
7
+ * @author Joe Dolson
8
+ * @license GPLv2 or later
9
+ * @link https://www.joedolson.com/my-calendar/
10
+ */
11
+
12
+ ?>
13
+ <div class="event">Upcoming Event</div>
my-calendar-ajax.php CHANGED
@@ -348,7 +348,7 @@ function mc_core_autocomplete_search_locations() {
348
  }
349
  $query = sanitize_text_field( $_REQUEST['data'] );
350
 
351
- $locations = mc_core_search_locations( $query, array( 'location_id', 'location_label' ) );
352
  $response = array();
353
  foreach ( $locations as $location ) {
354
  $response[] = array(
348
  }
349
  $query = sanitize_text_field( $_REQUEST['data'] );
350
 
351
+ $locations = mc_core_search_locations( $query );
352
  $response = array();
353
  foreach ( $locations as $location ) {
354
  $response[] = array(
my-calendar-api.php CHANGED
@@ -20,11 +20,13 @@ function my_calendar_api() {
20
  if ( isset( $_REQUEST['my-calendar-api'] ) || isset( $_REQUEST['mc-api'] ) ) {
21
  if ( 'true' === get_option( 'mc_api_enabled' ) ) {
22
  /**
23
- * Filter to test access to the event API.
24
  *
25
- * @param bool true Allows all by default.
26
  *
27
- * @return bool
 
 
28
  */
29
  $api_key = apply_filters( 'mc_api_key', true );
30
  if ( $api_key ) {
@@ -33,11 +35,13 @@ function my_calendar_api() {
33
  $from = ( isset( $_REQUEST['from'] ) ) ? $_REQUEST['from'] : current_time( 'Y-m-d' );
34
  $range = '+ 7 days';
35
  /**
36
- * Default date for API 'to' parameter.
 
 
37
  *
38
- * @param string $time time string convertable using strtotime.
39
  *
40
- * @return string
41
  */
42
  $adjust = apply_filters( 'mc_api_auto_date', $range );
43
  $to = ( isset( $_REQUEST['to'] ) ) ? $_REQUEST['to'] : mc_date( 'Y-m-d', strtotime( $adjust ) );
@@ -62,11 +66,14 @@ function my_calendar_api() {
62
  /**
63
  * Filter arguments submitted to the API.
64
  *
65
- * @param array $args Keys: ['from', 'to', 'category', 'ltype', 'lvalue', 'author', 'host', 'search'].
 
 
 
66
  *
67
  * @return array
68
  */
69
- $args = apply_filters( 'mc_filter_api_args', $args, $_REQUEST );
70
  $data = my_calendar_events( $args );
71
  $output = mc_format_api( $data, $format );
72
  echo wp_kses_post( $output );
@@ -209,11 +216,11 @@ function my_calendar_send_vcal( $event_id ) {
209
  /**
210
  * Generate iCal formatted event for one event
211
  *
212
- * @param integer $event_id Event ID.
213
  *
214
  * @return string text for iCal
215
  */
216
- function mc_generate_vcal( $event_id = false ) {
217
  global $mc_version;
218
  $output = '';
219
  $mc_id = ( isset( $_GET['vcal'] ) ) ? (int) str_replace( 'mc_', '', $_GET['vcal'] ) : $event_id;
@@ -222,13 +229,15 @@ function mc_generate_vcal( $event_id = false ) {
222
  // need to modify date values to match real values using date above.
223
  $array = mc_create_tags( $event );
224
  /**
225
- * Add an alarm to an event.
226
  *
227
- * @param array $array Empty array.
228
- * @param int $event_id Event ID.
229
- * @param int $post_id Post ID.
230
  *
231
- * @return array
 
 
 
 
232
  */
233
  $alarm = apply_filters( 'mc_event_has_alarm', array(), $event_id, $array['post'] );
234
  $alert = '';
@@ -260,9 +269,12 @@ END:VCALENDAR";
260
  /**
261
  * Filter template for a single iCal event download.
262
  *
263
- * @param string $template iCal template.
 
 
 
264
  *
265
- * @return string
266
  */
267
  $template = apply_filters( 'mc_single_ical_template', $template, $array );
268
  $output = mc_draw_template( $array, $template );
@@ -325,19 +337,23 @@ function my_calendar_ical() {
325
  /**
326
  * Filter iCal download 'from' date.
327
  *
328
- * @param string $from Date string.
329
- * @param string $p Date span.
330
  *
331
- * @return string
 
 
 
332
  */
333
  $from = apply_filters( 'mc_ical_download_from', $from, $p );
334
  /**
335
  * Filter iCal download 'to' date.
336
  *
337
- * @param string $from Date string.
338
- * @param string $p Date span.
 
 
339
  *
340
- * @return string
341
  */
342
  $to = apply_filters( 'mc_ical_download_to', $to, $p );
343
  $site = ( ! isset( $_GET['site'] ) ) ? get_current_blog_id() : intval( $_GET['site'] );
@@ -355,14 +371,16 @@ function my_calendar_ical() {
355
  );
356
 
357
  /**
358
- * Filter calendar arguments for iCal downloads.
359
  *
360
- * @param array $args Array of calendar query args.
361
- * @param array $get GET data.
362
  *
363
- * @return array
 
 
 
364
  */
365
- $args = apply_filters( 'mc_ical_attributes', $args, $_GET );
366
  // Load search result from $_SESSION array.
367
  if ( isset( $_GET['searched'] ) && $_GET['searched'] && isset( $_SESSION['MC_SEARCH_RESULT'] ) ) {
368
  $data = mc_get_searched_events();
@@ -384,9 +402,11 @@ function mc_api_format_ical( $data, $context ) {
384
  /**
385
  * Filter iCal template for multi-event output.
386
  *
387
- * @param string $templates['template'] Template string.
 
 
388
  *
389
- * @return string
390
  */
391
  $template = apply_filters( 'mc_filter_ical_template', $templates['template'] );
392
  $events = mc_flatten_array( $data );
@@ -404,6 +424,17 @@ function mc_api_format_ical( $data, $context ) {
404
  $processed[] = $event->event_id;
405
  }
406
  $array = mc_create_tags( $event, $context );
 
 
 
 
 
 
 
 
 
 
 
407
  $alarm = apply_filters( 'mc_event_has_alarm', array(), $event->event_id, $array['post'] );
408
  $alert = '';
409
  if ( ! empty( $alarm ) ) {
@@ -445,7 +476,16 @@ function mc_ical_template() {
445
  $tz_id = ( $tz_id ) ? $tz_id : $etc;
446
  // Translators: Blogname.
447
  $events_from = sprintf( __( 'Events from %s', 'my-calendar' ), get_bloginfo( 'blogname' ) );
448
- $ttl = apply_filters( 'ical_x_published_ttl', 'PT24H' );
 
 
 
 
 
 
 
 
 
449
  // establish template.
450
  $template = "
451
  BEGIN:VEVENT
@@ -535,7 +575,7 @@ function mc_generate_rrule( $event ) {
535
  $until = 'UNTIL=' . mc_date( 'Ymd\THis', strtotime( $repeat ), false ) . 'Z';
536
  }
537
 
538
- $rrule = ( '' !== $rrule ) ? PHP_EOL . "RRULE:$rrule;$by;$interval;$until" . PHP_EOL : '';
539
  $rrule = str_replace( array( ';;;', ';;' ), ';', $rrule );
540
 
541
  return $rrule;
20
  if ( isset( $_REQUEST['my-calendar-api'] ) || isset( $_REQUEST['mc-api'] ) ) {
21
  if ( 'true' === get_option( 'mc_api_enabled' ) ) {
22
  /**
23
+ * Filter to test access to the event API. Default 'true'.
24
  *
25
+ * @hook mc_api_key
26
  *
27
+ * @param {bool} $api_key Return true to allow access. Use filter to control or limit access.
28
+ *
29
+ * @return {bool}
30
  */
31
  $api_key = apply_filters( 'mc_api_key', true );
32
  if ( $api_key ) {
35
  $from = ( isset( $_REQUEST['from'] ) ) ? $_REQUEST['from'] : current_time( 'Y-m-d' );
36
  $range = '+ 7 days';
37
  /**
38
+ * Default date for API 'to' parameter. Default '+ 7 days'.
39
+ *
40
+ * @hook mc_api_auto_date
41
  *
42
+ * @param {string} $time A time string convertable using strtotime.
43
  *
44
+ * @return {string}
45
  */
46
  $adjust = apply_filters( 'mc_api_auto_date', $range );
47
  $to = ( isset( $_REQUEST['to'] ) ) ? $_REQUEST['to'] : mc_date( 'Y-m-d', strtotime( $adjust ) );
66
  /**
67
  * Filter arguments submitted to the API.
68
  *
69
+ * @hook mc_filter_api_args
70
+ *
71
+ * @param {array} $args Keys: ['from', 'to', 'category', 'ltype', 'lvalue', 'author', 'host', 'search'].
72
+ * @param {array} $request $_REQUEST parameters, sanitized.
73
  *
74
  * @return array
75
  */
76
+ $args = apply_filters( 'mc_filter_api_args', $args, map_deep( $_REQUEST, 'sanitize_text_field' ) );
77
  $data = my_calendar_events( $args );
78
  $output = mc_format_api( $data, $format );
79
  echo wp_kses_post( $output );
216
  /**
217
  * Generate iCal formatted event for one event
218
  *
219
+ * @param int $event_id Event ID or false to get active event.
220
  *
221
  * @return string text for iCal
222
  */
223
+ function mc_generate_vcal( $event_id ) {
224
  global $mc_version;
225
  $output = '';
226
  $mc_id = ( isset( $_GET['vcal'] ) ) ? (int) str_replace( 'mc_', '', $_GET['vcal'] ) : $event_id;
229
  // need to modify date values to match real values using date above.
230
  $array = mc_create_tags( $event );
231
  /**
232
+ * Filter information used to set an alarm on an event in .ics files.
233
  *
234
+ * @hook mc_event_has_alarm
 
 
235
  *
236
+ * @param {array} Alarm information passable to `mc_generate_alert_ical()`
237
+ * @param {int} $event_id Event ID.
238
+ * @param {int} $post Post ID.
239
+ *
240
+ * @return {array}
241
  */
242
  $alarm = apply_filters( 'mc_event_has_alarm', array(), $event_id, $array['post'] );
243
  $alert = '';
269
  /**
270
  * Filter template for a single iCal event download.
271
  *
272
+ * @hook mc_single_ical_template
273
+ *
274
+ * @param {string} $template iCal template.
275
+ * @param {array} $array Event data.
276
  *
277
+ * @return {string}
278
  */
279
  $template = apply_filters( 'mc_single_ical_template', $template, $array );
280
  $output = mc_draw_template( $array, $template );
337
  /**
338
  * Filter iCal download 'from' date.
339
  *
340
+ * @hook mc_ical_download_from
 
341
  *
342
+ * @param {string} $from Date string.
343
+ * @param {string} $p Date span.
344
+ *
345
+ * @return {string}
346
  */
347
  $from = apply_filters( 'mc_ical_download_from', $from, $p );
348
  /**
349
  * Filter iCal download 'to' date.
350
  *
351
+ * @hook mc_ical_download_to
352
+ *
353
+ * @param {string} $from Date string.
354
+ * @param {string} $p Date span.
355
  *
356
+ * @return {string}
357
  */
358
  $to = apply_filters( 'mc_ical_download_to', $to, $p );
359
  $site = ( ! isset( $_GET['site'] ) ) ? get_current_blog_id() : intval( $_GET['site'] );
371
  );
372
 
373
  /**
374
+ * Filter calendar arguments for iCal output.
375
  *
376
+ * @hook mc_ical_attributes
 
377
  *
378
+ * @param {array} $args Array of calendar query args.
379
+ * @param {array} $get GET parameters, sanitized.
380
+ *
381
+ * @return {array}
382
  */
383
+ $args = apply_filters( 'mc_ical_attributes', $args, map_deep( $_GET, 'sanitize_text_field' ) );
384
  // Load search result from $_SESSION array.
385
  if ( isset( $_GET['searched'] ) && $_GET['searched'] && isset( $_SESSION['MC_SEARCH_RESULT'] ) ) {
386
  $data = mc_get_searched_events();
402
  /**
403
  * Filter iCal template for multi-event output.
404
  *
405
+ * @hook mc_filter_ical_template
406
+ *
407
+ * @param {string} $template Template string.
408
  *
409
+ * @return {string}
410
  */
411
  $template = apply_filters( 'mc_filter_ical_template', $templates['template'] );
412
  $events = mc_flatten_array( $data );
424
  $processed[] = $event->event_id;
425
  }
426
  $array = mc_create_tags( $event, $context );
427
+ /**
428
+ * Filter information used to set an alarm on an event in .ics files.
429
+ *
430
+ * @hook mc_event_has_alarm
431
+ *
432
+ * @param {array} Alarm information passable to `mc_generate_alert_ical()`
433
+ * @param {int} $event_id Event ID.
434
+ * @param {int} $post Post ID.
435
+ *
436
+ * @return {array}
437
+ */
438
  $alarm = apply_filters( 'mc_event_has_alarm', array(), $event->event_id, $array['post'] );
439
  $alert = '';
440
  if ( ! empty( $alarm ) ) {
476
  $tz_id = ( $tz_id ) ? $tz_id : $etc;
477
  // Translators: Blogname.
478
  $events_from = sprintf( __( 'Events from %s', 'my-calendar' ), get_bloginfo( 'blogname' ) );
479
+ /**
480
+ * Filter the suggested frequency for iCal subscription sources to be rechecked. Default 'PT24H'.
481
+ *
482
+ * @hook ical_x_published_ttl
483
+ *
484
+ * @param {string} $ttl Time string for iCal templates.
485
+ *
486
+ * @return {string}
487
+ */
488
+ $ttl = apply_filters( 'ical_x_published_ttl', 'PT24H' );
489
  // establish template.
490
  $template = "
491
  BEGIN:VEVENT
575
  $until = 'UNTIL=' . mc_date( 'Ymd\THis', strtotime( $repeat ), false ) . 'Z';
576
  }
577
 
578
+ $rrule = ( '' !== $rrule ) ? PHP_EOL . "RRULE:$rrule;$by;$interval;$until" : '';
579
  $rrule = str_replace( array( ';;;', ';;' ), ';', $rrule );
580
 
581
  return $rrule;
my-calendar-behaviors.php CHANGED
@@ -20,7 +20,7 @@ function my_calendar_behaviors_save() {
20
  if ( isset( $_POST['mc-js-save'] ) ) {
21
  $nonce = $_REQUEST['_wpnonce'];
22
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
23
- die( 'Security check failed' );
24
  }
25
 
26
  $use_custom_js = ( isset( $_POST['mc_use_custom_js'] ) ) ? 1 : 0;
20
  if ( isset( $_POST['mc-js-save'] ) ) {
21
  $nonce = $_REQUEST['_wpnonce'];
22
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
23
+ wp_die( 'My Calendar: Security check failed' );
24
  }
25
 
26
  $use_custom_js = ( isset( $_POST['mc_use_custom_js'] ) ) ? 1 : 0;
my-calendar-call-template.php CHANGED
@@ -47,8 +47,4 @@ function mc_embed_template() {
47
  require_once( MC_TEMPLATES . 'my-calendar-template.php' );
48
  exit;
49
  }
50
-
51
- // You've gone too far. Error case.
52
- header( 'HTTP/1.0 404 Not Found' );
53
- exit;
54
  }
47
  require_once( MC_TEMPLATES . 'my-calendar-template.php' );
48
  exit;
49
  }
 
 
 
 
50
  }
my-calendar-categories.php CHANGED
@@ -17,10 +17,10 @@ if ( ! defined( 'ABSPATH' ) ) {
17
  * Update a single field of a category
18
  *
19
  * @param string $field field name.
20
- * @param mixed $data Data to change.
21
  * @param int $category Category ID.
22
  *
23
- * @return result
24
  */
25
  function mc_update_category( $field, $data, $category ) {
26
  global $wpdb;
@@ -104,6 +104,15 @@ function mc_get_private_categories() {
104
  $categories[] = $result->category_id;
105
  }
106
 
 
 
 
 
 
 
 
 
 
107
  return apply_filters( 'mc_private_categories', $categories );
108
  }
109
 
@@ -123,7 +132,7 @@ function my_calendar_manage_categories() {
123
  if ( ! empty( $_POST ) ) {
124
  $nonce = $_REQUEST['_wpnonce'];
125
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
126
- die( 'Security check failed' );
127
  }
128
  }
129
 
@@ -344,10 +353,29 @@ function mc_create_category( $category ) {
344
  'category_term' => $term,
345
  );
346
 
347
- $add = array_map( 'mc_kses_post', $add );
 
 
 
 
 
 
 
 
 
 
348
  $add = apply_filters( 'mc_pre_add_category', $add, $category );
349
  $results = $wpdb->insert( my_calendar_categories_table(), $add, $formats );
350
  $cat_id = $wpdb->insert_id;
 
 
 
 
 
 
 
 
 
351
  do_action( 'mc_post_add_category', $add, $cat_id, $category );
352
 
353
  return $cat_id;
@@ -370,10 +398,10 @@ function mc_get_category_count( $category_id ) {
370
  /**
371
  * Form to edit a category
372
  *
373
- * @param string $view Edit or create.
374
- * @param id $cat_id Category ID.
375
  */
376
- function mc_edit_category_form( $view = 'edit', $cat_id = '' ) {
377
  global $wpdb;
378
  $dir = plugin_dir_path( __FILE__ );
379
  $url = plugin_dir_url( __FILE__ );
@@ -479,6 +507,16 @@ function mc_edit_category_form( $view = 'edit', $cat_id = '' ) {
479
  </li>
480
  </ul>
481
  <?php
 
 
 
 
 
 
 
 
 
 
482
  echo apply_filters( 'mc_category_fields', '', $cur_cat );
483
  if ( 'add' === $view ) {
484
  $save_text = __( 'Add Category', 'my-calendar' );
@@ -490,7 +528,17 @@ function mc_edit_category_form( $view = 'edit', $cat_id = '' ) {
490
  <p>
491
  <input type="submit" name="save" class="button-primary" value="<?php echo esc_attr( $save_text ); ?> "/>
492
  </p>
493
- <?php do_action( 'mc_post_category_form', $cur_cat, $view ); ?>
 
 
 
 
 
 
 
 
 
 
494
  </form>
495
  </div>
496
  </div>
@@ -551,7 +599,7 @@ function mc_edit_category_form( $view = 'edit', $cat_id = '' ) {
551
  /**
552
  * Update category settings.
553
  *
554
- * @return Update message.
555
  */
556
  function mc_category_settings_update() {
557
  $message = '';
@@ -608,6 +656,8 @@ function mc_category_settings() {
608
 
609
  return $settings;
610
  }
 
 
611
  }
612
 
613
  /**
@@ -869,7 +919,7 @@ function mc_profile() {
869
  $selected = ( empty( $permissions ) || in_array( 'all', $permissions, true ) || user_can( $user_edit, 'manage_options' ) ) ? ' checked="checked"' : '';
870
  ?>
871
  <h3><?php esc_html_e( 'My Calendar Editor Permissions', 'my-calendar' ); ?></h3>
872
- <table class="form-table">
873
  <tr>
874
  <th scope="row">
875
  <label for="mc_user_permissions"><?php esc_html_e( 'Allowed Categories', 'my-calendar' ); ?></label>
@@ -881,7 +931,19 @@ function mc_profile() {
881
  </ul>
882
  </td>
883
  </tr>
884
- <?php echo apply_filters( 'mc_user_fields', '', $user_edit ); ?>
 
 
 
 
 
 
 
 
 
 
 
 
885
  </table>
886
  <?php
887
  }
@@ -906,19 +968,26 @@ function mc_save_profile() {
906
  delete_user_meta( $edit_id, 'mc_user_permissions' );
907
  }
908
  }
909
-
910
- apply_filters( 'mc_save_user', $edit_id, $_POST );
 
 
 
 
 
 
 
911
  }
912
 
913
 
914
  /**
915
  * Generate fields to select event categories.
916
  *
917
- * @param object $data object with event_category value.
918
- * @param boolean $option Type of form.
919
- * @param boolean $multiple Allow multiple categories to be entered.
920
- * @param mixed boolean|string $name Field name for input.
921
- * @param mixed boolean|string $id ID for label/input.
922
  *
923
  * @return string HTML fields.
924
  */
@@ -934,7 +1003,17 @@ function mc_category_select( $data = false, $option = true, $multiple = false, $
934
  $default = '';
935
  $cats = mc_no_category_default();
936
  if ( ! empty( $cats ) ) {
937
- $cats = apply_filters( 'mc_category_list', $cats, $data, $option, $name );
 
 
 
 
 
 
 
 
 
 
938
  foreach ( $cats as $cat ) {
939
  $selected = '';
940
  // if category is private, don't show if user is not logged in.
@@ -1083,8 +1162,8 @@ function mc_admin_category_list( $event ) {
1083
  /**
1084
  * Get all categories for given event
1085
  *
1086
- * @param object $event Event object.
1087
- * @param boolean $ids Return objects or ids.
1088
  *
1089
  * @return array of values
1090
  */
@@ -1178,7 +1257,7 @@ function mc_get_img( $file, $is_custom = false ) {
1178
  $url = plugin_dir_url( __FILE__ );
1179
  $self = plugin_dir_path( __FILE__ );
1180
  if ( $is_custom ) {
1181
- $path = $parent . 'my-calendar-custom/';
1182
  $link = $parent_url . 'my-calendar-custom/';
1183
  } else {
1184
  $path = $self . 'images/icons/';
@@ -1258,7 +1337,7 @@ function mc_category_icon( $event, $type = 'html' ) {
1258
  $image = mc_generate_category_icon( $event );
1259
  }
1260
  } else {
1261
- $image = '<img src="' . esc_url( $src ) . '" alt="' . $cat_name . '" class="category-icon" style="background:' . $color . '" />';
1262
  }
1263
  } else {
1264
  $image = $path . $event->category_icon;
@@ -1266,6 +1345,17 @@ function mc_category_icon( $event, $type = 'html' ) {
1266
  }
1267
  }
1268
 
 
 
 
 
 
 
 
 
 
 
 
1269
  return apply_filters( 'mc_category_icon', $image, $event, $type );
1270
  }
1271
 
@@ -1291,13 +1381,13 @@ function mc_generate_category_icon( $source ) {
1291
  if ( 'default' === $apply ) {
1292
  $color = '';
1293
  }
 
1294
  // Is this an event context or a category context.
1295
  if ( property_exists( $source, 'occur_id' ) ) {
1296
- $cat_name = __( 'Category', 'my-calendar' ) . ': ' . esc_attr( $source->category_name );
1297
  $occur_id = $source->occur_id;
1298
  $context = 'event';
1299
  } else {
1300
- $cat_name = esc_attr( $source->category_name );
1301
  $occur_id = $source->category_id;
1302
  $context = 'category';
1303
  }
@@ -1308,7 +1398,7 @@ function mc_generate_category_icon( $source ) {
1308
  $image = ( $wp_filesystem->exists( $src ) ) ? $wp_filesystem->get_contents( $src ) : false;
1309
  if ( 0 === stripos( $image, '<svg' ) ) {
1310
  $image = str_replace( '<svg ', '<svg style="fill:' . $color . '" focusable="false" role="img" aria-labelledby="' . $label_id . '" class="category-icon" ', $image );
1311
- $image = str_replace( '<path ', "<title id='" . $label_id . "'>$cat_name</title><path ", $image );
1312
 
1313
  update_option( 'mc_category_icon_' . $context . '_' . $source->category_id, $image );
1314
  } else {
@@ -1328,8 +1418,8 @@ add_filter(
1328
  /**
1329
  * Generate category classes for a given date
1330
  *
1331
- * @param object $object Usually an event, can be category.
1332
- * @param string $prefix Prefix to append to category; varies on context.
1333
  *
1334
  * @return string classes
1335
  */
17
  * Update a single field of a category
18
  *
19
  * @param string $field field name.
20
+ * @param int $data Data to change.
21
  * @param int $category Category ID.
22
  *
23
+ * @return int|bool Row ID or false.
24
  */
25
  function mc_update_category( $field, $data, $category ) {
26
  global $wpdb;
104
  $categories[] = $result->category_id;
105
  }
106
 
107
+ /**
108
+ * Filter which categories are considered private.
109
+ *
110
+ * @hook mc_private_categories
111
+ *
112
+ * @param {array} $categories Array of category objects.
113
+ *
114
+ * @return {array}
115
+ */
116
  return apply_filters( 'mc_private_categories', $categories );
117
  }
118
 
132
  if ( ! empty( $_POST ) ) {
133
  $nonce = $_REQUEST['_wpnonce'];
134
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
135
+ die( 'My Calendar: Security check failed' );
136
  }
137
  }
138
 
353
  'category_term' => $term,
354
  );
355
 
356
+ $add = array_map( 'mc_kses_post', $add );
357
+ /**
358
+ * Filter data before inserting a new category.
359
+ *
360
+ * @hook mc_pre_add_category
361
+ *
362
+ * @param {array} $add Data to be inserted.
363
+ * @param {array} $category Category data passed to insert function.
364
+ *
365
+ * @return {array}
366
+ */
367
  $add = apply_filters( 'mc_pre_add_category', $add, $category );
368
  $results = $wpdb->insert( my_calendar_categories_table(), $add, $formats );
369
  $cat_id = $wpdb->insert_id;
370
+ /**
371
+ * Execute action after inserting a new category into the My Calendar database.
372
+ *
373
+ * @hook mc_post_add_category
374
+ *
375
+ * @param {array} $add Category data array used for DB insert.
376
+ * @param {int} $cat_id ID of new category.
377
+ * @param {string} $category Original array sent to function.
378
+ */
379
  do_action( 'mc_post_add_category', $add, $cat_id, $category );
380
 
381
  return $cat_id;
398
  /**
399
  * Form to edit a category
400
  *
401
+ * @param string $view Edit or create.
402
+ * @param int|bool $cat_id Category ID.
403
  */
404
+ function mc_edit_category_form( $view = 'edit', $cat_id = false ) {
405
  global $wpdb;
406
  $dir = plugin_dir_path( __FILE__ );
407
  $url = plugin_dir_url( __FILE__ );
507
  </li>
508
  </ul>
509
  <?php
510
+ /**
511
+ * Insert custom fields for categories.
512
+ *
513
+ * @hook mc_category_fields
514
+ *
515
+ * @param {string} $output Field HTML output.
516
+ * @param {object} $cur_cat Current category object.
517
+ *
518
+ * @return {string}
519
+ */
520
  echo apply_filters( 'mc_category_fields', '', $cur_cat );
521
  if ( 'add' === $view ) {
522
  $save_text = __( 'Add Category', 'my-calendar' );
528
  <p>
529
  <input type="submit" name="save" class="button-primary" value="<?php echo esc_attr( $save_text ); ?> "/>
530
  </p>
531
+ <?php
532
+ /**
533
+ * Execute action after category editor form prints to screen.
534
+ *
535
+ * @hook mc_post_category_form
536
+ *
537
+ * @param {object} $cur_cat Current category object.
538
+ * @param {string} $view Type of view ('add' or 'edit').
539
+ */
540
+ do_action( 'mc_post_category_form', $cur_cat, $view );
541
+ ?>
542
  </form>
543
  </div>
544
  </div>
599
  /**
600
  * Update category settings.
601
  *
602
+ * @return string Update message.
603
  */
604
  function mc_category_settings_update() {
605
  $message = '';
656
 
657
  return $settings;
658
  }
659
+
660
+ return '';
661
  }
662
 
663
  /**
919
  $selected = ( empty( $permissions ) || in_array( 'all', $permissions, true ) || user_can( $user_edit, 'manage_options' ) ) ? ' checked="checked"' : '';
920
  ?>
921
  <h3><?php esc_html_e( 'My Calendar Editor Permissions', 'my-calendar' ); ?></h3>
922
+ <table class="form-table" role="presentation">
923
  <tr>
924
  <th scope="row">
925
  <label for="mc_user_permissions"><?php esc_html_e( 'Allowed Categories', 'my-calendar' ); ?></label>
931
  </ul>
932
  </td>
933
  </tr>
934
+ <?php
935
+ /**
936
+ * Add custom fields to the My Calendar section of the user profile.
937
+ *
938
+ * @hook mc_user_fields
939
+ *
940
+ * @param {string} $output HTML for fields.
941
+ * @param {int} $user_edit User ID being edited.
942
+ *
943
+ * @return {string}
944
+ */
945
+ echo apply_filters( 'mc_user_fields', '', $user_edit );
946
+ ?>
947
  </table>
948
  <?php
949
  }
968
  delete_user_meta( $edit_id, 'mc_user_permissions' );
969
  }
970
  }
971
+ /**
972
+ * Execute action when saving My Calendar data in a user profile.
973
+ *
974
+ * @hook mc_save_user
975
+ *
976
+ * @param {object} $int Edited user ID.
977
+ * @param {array} $_POST POST data.
978
+ */
979
+ do_action( 'mc_save_user', $edit_id, $_POST );
980
  }
981
 
982
 
983
  /**
984
  * Generate fields to select event categories.
985
  *
986
+ * @param object|false|int|null|array $data object with event_category value, empty value, or a category ID.
987
+ * @param boolean $option Type of form.
988
+ * @param boolean $multiple Allow multiple categories to be entered.
989
+ * @param boolean|string $name Field name for input.
990
+ * @param boolean|string $id ID for label/input.
991
  *
992
  * @return string HTML fields.
993
  */
1003
  $default = '';
1004
  $cats = mc_no_category_default();
1005
  if ( ! empty( $cats ) ) {
1006
+ /**
1007
+ * Filter the categories available in a category selection interface.
1008
+ *
1009
+ * @hook mc_category_list
1010
+ *
1011
+ * @param {array} $cats Array of categories.
1012
+ * @param {object} $data An object with selected category data.
1013
+ *
1014
+ * @return {array}
1015
+ */
1016
+ $cats = apply_filters( 'mc_category_list', $cats, $data );
1017
  foreach ( $cats as $cat ) {
1018
  $selected = '';
1019
  // if category is private, don't show if user is not logged in.
1162
  /**
1163
  * Get all categories for given event
1164
  *
1165
+ * @param object|int $event Event object or event ID.
1166
+ * @param boolean|string $ids Return objects, ids, or html output.
1167
  *
1168
  * @return array of values
1169
  */
1257
  $url = plugin_dir_url( __FILE__ );
1258
  $self = plugin_dir_path( __FILE__ );
1259
  if ( $is_custom ) {
1260
+ $path = $parent_path . 'my-calendar-custom/';
1261
  $link = $parent_url . 'my-calendar-custom/';
1262
  } else {
1263
  $path = $self . 'images/icons/';
1337
  $image = mc_generate_category_icon( $event );
1338
  }
1339
  } else {
1340
+ $image = '<img src="' . esc_url( $src ) . '" alt="' . esc_attr( $cat_name ) . '" class="category-icon" style="background:' . esc_attr( $color ) . '" />';
1341
  }
1342
  } else {
1343
  $image = $path . $event->category_icon;
1345
  }
1346
  }
1347
 
1348
+ /**
1349
+ * Filter the HTML output for a category icon.
1350
+ *
1351
+ * @hook mc_category_icon
1352
+ *
1353
+ * @param {string} $image Image HTML.
1354
+ * @param {object} $event Event object.
1355
+ * @param {string} $type Type of output - HTML or URL only.
1356
+ *
1357
+ * @return {string}
1358
+ */
1359
  return apply_filters( 'mc_category_icon', $image, $event, $type );
1360
  }
1361
 
1381
  if ( 'default' === $apply ) {
1382
  $color = '';
1383
  }
1384
+ $cat_name = $source->category_name;
1385
  // Is this an event context or a category context.
1386
  if ( property_exists( $source, 'occur_id' ) ) {
1387
+ $cat_name = __( 'Category', 'my-calendar' ) . ': ' . $cat_name;
1388
  $occur_id = $source->occur_id;
1389
  $context = 'event';
1390
  } else {
 
1391
  $occur_id = $source->category_id;
1392
  $context = 'category';
1393
  }
1398
  $image = ( $wp_filesystem->exists( $src ) ) ? $wp_filesystem->get_contents( $src ) : false;
1399
  if ( 0 === stripos( $image, '<svg' ) ) {
1400
  $image = str_replace( '<svg ', '<svg style="fill:' . $color . '" focusable="false" role="img" aria-labelledby="' . $label_id . '" class="category-icon" ', $image );
1401
+ $image = str_replace( '<path ', "<title id='" . $label_id . "'>" . esc_html( $cat_name ) . '</title><path ', $image );
1402
 
1403
  update_option( 'mc_category_icon_' . $context . '_' . $source->category_id, $image );
1404
  } else {
1418
  /**
1419
  * Generate category classes for a given date
1420
  *
1421
+ * @param object|array $object Usually an event, can be category.
1422
+ * @param string $prefix Prefix to append to category; varies on context.
1423
  *
1424
  * @return string classes
1425
  */
my-calendar-core.php CHANGED
@@ -70,6 +70,16 @@ function mc_custom_dirs( $type = 'path' ) {
70
  $dirs[] = ( 'path' === $type ) ? get_stylesheet_directory() . '/css/' : get_stylesheet_directory_uri() . '/css/';
71
  $dirs[] = ( 'path' === $type ) ? get_stylesheet_directory() . '/' : get_stylesheet_directory_uri() . '/';
72
 
 
 
 
 
 
 
 
 
 
 
73
  $directories = apply_filters( 'mc_custom_dirs', $dirs, $type );
74
 
75
  return ( is_array( $directories ) && ! empty( $directories ) ) ? $directories : $dirs;
@@ -83,7 +93,17 @@ function mc_custom_dirs( $type = 'path' ) {
83
  * @return boolean
84
  */
85
  function mc_file_exists( $file ) {
86
- $file = sanitize_file_name( $file );
 
 
 
 
 
 
 
 
 
 
87
  $return = apply_filters( 'mc_file_exists', false, $file );
88
  if ( $return ) {
89
  return true;
@@ -91,7 +111,6 @@ function mc_file_exists( $file ) {
91
  foreach ( mc_custom_dirs() as $dir ) {
92
  if ( file_exists( $dir . $file ) ) {
93
  return true;
94
- break;
95
  }
96
  }
97
 
@@ -123,6 +142,16 @@ function mc_get_file( $file, $type = 'path' ) {
123
  break;
124
  }
125
  }
 
 
 
 
 
 
 
 
 
 
126
  $path = apply_filters( 'mc_get_file', $path, $file );
127
 
128
  return $path;
@@ -153,8 +182,17 @@ add_action( 'wp_enqueue_scripts', 'mc_register_styles' );
153
  */
154
  function mc_register_styles() {
155
  global $wp_query;
156
- $version = get_option( 'mc_version' );
157
- $this_post = $wp_query->get_queried_object();
 
 
 
 
 
 
 
 
 
158
  $stylesheet = apply_filters( 'mc_registered_stylesheet', mc_get_style_path( get_option( 'mc_css_file' ), 'url' ) );
159
  wp_register_style( 'my-calendar-reset', plugins_url( 'css/reset.css', __FILE__ ), array( 'dashicons' ), $version );
160
  wp_register_style( 'my-calendar-style', $stylesheet, array( 'my-calendar-reset' ), $version );
@@ -170,6 +208,16 @@ function mc_register_styles() {
170
  wp_enqueue_style( 'my-calendar-admin-style' );
171
  }
172
 
 
 
 
 
 
 
 
 
 
 
173
  $default = apply_filters( 'mc_display_css_on_archives', true, $wp_query );
174
  $id = ( is_object( $this_post ) && isset( $this_post->ID ) ) ? $this_post->ID : false;
175
  $js_array = ( '' !== trim( get_option( 'mc_show_js', '' ) ) ) ? explode( ',', get_option( 'mc_show_js' ) ) : array();
@@ -329,16 +377,15 @@ function mc_generate_category_styles() {
329
  * Deal with events posted by a user when that user is deleted
330
  *
331
  * @param int $id user ID of deleted user.
 
332
  */
333
- function mc_deal_with_deleted_user( $id ) {
334
  global $wpdb;
335
- $new = $wpdb->get_var( 'SELECT MIN(ID) FROM ' . $wpdb->users, 0, 0 );
336
- $new_author = apply_filters( 'mc_deleted_author', $new );
337
  // This may not work quite right in multi-site. Need to explore further when I have time.
338
- $wpdb->get_results( $wpdb->prepare( 'UPDATE ' . my_calendar_table() . ' SET event_author=%d WHERE event_author=%d', $new_author, $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
339
 
340
- $new_host = apply_filters( 'mc_deleted_host', $new );
341
- $wpdb->get_results( $wpdb->prepare( 'UPDATE ' . my_calendar_table() . ' SET event_host=%d WHERE event_host=%d', $new_host, $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
342
  }
343
 
344
  /**
@@ -410,8 +457,17 @@ function mc_plugin_update_message() {
410
  function mc_footer_js() {
411
  global $wp_query;
412
  $version = get_option( 'mc_version' );
 
413
  $mcjs = "<script>(function ($) { 'use strict'; $(function () { $( '.mc-main' ).removeClass( 'mcjs' ); });}(jQuery));</script>";
414
-
 
 
 
 
 
 
 
 
415
  if ( mc_is_mobile() && apply_filters( 'mc_disable_mobile_js', false ) ) {
416
 
417
  return;
@@ -427,6 +483,7 @@ function mc_footer_js() {
427
  $id = false;
428
  }
429
  if ( '1' === get_option( 'mc_use_custom_js' ) ) {
 
430
  $top = '';
431
  $bottom = '';
432
  $inner = '';
@@ -466,7 +523,6 @@ function mc_footer_js() {
466
  (function( $ ) { \'use strict\';' . $inner . '}(jQuery));
467
  </script>';
468
  }
469
- $inner = apply_filters( 'mc_filter_javascript_footer', $inner );
470
  echo ( '' !== $inner ) ? $script . $mcjs : '';
471
  } else {
472
  $enqueue_mcjs = false;
@@ -476,6 +532,15 @@ function mc_footer_js() {
476
  $ajax = 'false';
477
  if ( ! $id || ( is_array( $pages ) && in_array( $id, $pages, true ) ) || '' === $show_js ) {
478
  if ( '1' !== get_option( 'mc_calendar_javascript' ) && 'true' !== get_option( 'mc_open_uri' ) ) {
 
 
 
 
 
 
 
 
 
479
  $url = apply_filters( 'mc_grid_js', '' );
480
  $enqueue_mcjs = true;
481
  if ( $url ) {
@@ -485,6 +550,15 @@ function mc_footer_js() {
485
  }
486
  }
487
  if ( '1' !== get_option( 'mc_list_javascript' ) ) {
 
 
 
 
 
 
 
 
 
488
  $url = apply_filters( 'mc_list_js', '' );
489
  $enqueue_mcjs = true;
490
  if ( $url ) {
@@ -494,6 +568,15 @@ function mc_footer_js() {
494
  }
495
  }
496
  if ( '1' !== get_option( 'mc_mini_javascript' ) && 'true' !== get_option( 'mc_open_day_uri' ) ) {
 
 
 
 
 
 
 
 
 
497
  $url = apply_filters( 'mc_mini_js', '' );
498
  $enqueue_mcjs = true;
499
 
@@ -504,6 +587,15 @@ function mc_footer_js() {
504
  }
505
  }
506
  if ( '1' !== get_option( 'mc_ajax_javascript' ) ) {
 
 
 
 
 
 
 
 
 
507
  $url = apply_filters( 'mc_ajax_js', '' );
508
  $enqueue_mcjs = true;
509
  if ( $url ) {
@@ -520,7 +612,7 @@ function mc_footer_js() {
520
  'list' => $list,
521
  'mini' => $mini,
522
  'ajax' => $ajax,
523
- 'newWindow' => __( 'Opens in new tab', 'my-calendar' ),
524
  );
525
  wp_localize_script( 'mc.mcjs', 'my_calendar', $args );
526
  }
@@ -534,10 +626,14 @@ function mc_footer_js() {
534
  */
535
  function mc_admin_styles() {
536
  global $current_screen;
537
- $version = get_option( 'mc_version' );
538
- $id = $current_screen->id;
539
- $is_mc_page = isset( $_GET['post'] ) && get_option( 'mc_uri_id' ) === $_GET['post'];
540
-
 
 
 
 
541
  if ( false !== strpos( $id, 'my-calendar' ) || $is_mc_page ) {
542
  // Toggle CSS & Scripts based on current mode.
543
  $mode = get_option( 'mc_default_admin_view' );
@@ -555,62 +651,32 @@ function mc_admin_styles() {
555
  wp_register_style( 'my-calendar-admin-style', plugins_url( 'css/admin.css', __FILE__ ), array( 'my-calendar-reset' ), $version );
556
  wp_enqueue_style( 'my-calendar-admin-style' );
557
  if ( '1' !== get_option( 'mc_calendar_javascript' ) && 'true' !== get_option( 'mc_open_uri' ) ) {
558
- $url = apply_filters( 'mc_grid_js', plugins_url( 'js/mc-grid.js', __FILE__ ) );
559
  $enqueue_mcjs = true;
560
- wp_enqueue_script( 'mc.grid', $url, array( 'jquery' ), $version );
561
- wp_localize_script(
562
- 'mc.grid',
563
- 'mcgrid',
564
- array(
565
- 'grid' => 'true',
566
- )
567
- );
568
  }
569
  if ( '1' !== get_option( 'mc_list_javascript' ) ) {
570
- $url = apply_filters( 'mc_list_js', plugins_url( 'js/mc-list.js', __FILE__ ) );
571
  $enqueue_mcjs = true;
572
- wp_enqueue_script( 'mc.list', $url, array( 'jquery' ), $version );
573
- wp_localize_script(
574
- 'mc.list',
575
- 'mclist',
576
- array(
577
- 'list' => 'true',
578
- )
579
- );
580
  }
581
  if ( '1' !== get_option( 'mc_mini_javascript' ) && 'true' !== get_option( 'mc_open_day_uri' ) ) {
582
- $url = apply_filters( 'mc_mini_js', plugins_url( 'js/mc-mini.js', __FILE__ ) );
583
  $enqueue_mcjs = true;
584
- wp_enqueue_script( 'mc.mini', $url, array( 'jquery' ), $version );
585
- wp_localize_script(
586
- 'mc.mini',
587
- 'mcmini',
588
- array(
589
- 'mini' => 'true',
590
- )
591
- );
592
  }
593
  if ( '1' !== get_option( 'mc_ajax_javascript' ) ) {
594
- $url = apply_filters( 'mc_ajax_js', plugins_url( 'js/mc-ajax.js', __FILE__ ) );
595
  $enqueue_mcjs = true;
596
- wp_enqueue_script( 'mc.ajax', $url, array( 'jquery' ), $version );
597
- wp_localize_script(
598
- 'mc.ajax',
599
- 'mcAjax',
600
- array(
601
- 'ajax' => 'true',
602
- )
603
- );
604
  }
605
  if ( $enqueue_mcjs ) {
606
- wp_enqueue_script( 'mc.mcjs', plugins_url( 'js/mcjs.js', __FILE__ ), array( 'jquery' ), $version );
607
- wp_localize_script(
608
- 'mc.mcjs',
609
- 'my_calendar',
610
- array(
611
- 'newWindow' => __( 'Opens in new tab', 'my-calendar' ),
612
- )
 
613
  );
 
614
  }
615
  }
616
  wp_enqueue_style( 'mc-styles', plugins_url( 'css/mc-styles.css', __FILE__ ), array(), $version );
@@ -704,6 +770,15 @@ function mc_get_current_url() {
704
  if ( $wp_rewrite->using_permalinks() ) {
705
  $current_url = trailingslashit( $current_url );
706
  }
 
 
 
 
 
 
 
 
 
707
  $current_url = apply_filters( 'mc_get_current_url', $current_url );
708
 
709
  return esc_url( $current_url );
@@ -740,9 +815,9 @@ function mc_if_needs_permissions() {
740
  /**
741
  * Grant capabilities to standard site roles
742
  *
743
- * @param mixed string/boolean $add Add capabilities to this role.
744
- * @param mixed string/boolean $manage Manage capabilities to this role.
745
- * @param mixed string/boolean $approve Approve capabilities to this role.
746
  */
747
  function mc_add_roles( $add = false, $manage = false, $approve = false ) {
748
  $role = get_role( 'administrator' );
@@ -1029,6 +1104,15 @@ function mc_admin_bar() {
1029
  $mc_id = get_option( 'mc_uri_id' );
1030
  if ( mc_get_uri( 'boolean' ) ) {
1031
  if ( is_page( $mc_id ) && current_user_can( 'mc_add_events' ) ) {
 
 
 
 
 
 
 
 
 
1032
  $url = apply_filters( 'mc_add_events_url', admin_url( 'admin.php?page=my-calendar' ) );
1033
  $args = array(
1034
  'id' => 'mc-my-calendar',
@@ -1036,6 +1120,15 @@ function mc_admin_bar() {
1036
  'href' => $url,
1037
  );
1038
  } else {
 
 
 
 
 
 
 
 
 
1039
  $url = esc_url( apply_filters( 'mc_adminbar_uri', mc_get_uri() ) );
1040
  $args = array(
1041
  'id' => 'mc-my-calendar',
@@ -1055,14 +1148,25 @@ function mc_admin_bar() {
1055
  }
1056
  if ( current_user_can( 'mc_add_events' ) && 'true' !== get_option( 'mc_remote' ) ) {
1057
  if ( ! is_page( $mc_id ) ) {
1058
- $url = apply_filters( 'mc_add_events_url', admin_url( 'admin.php?page=my-calendar' ) );
1059
- $args = array(
1060
- 'id' => 'mc-add-event',
1061
- 'title' => __( 'Add Event', 'my-calendar' ),
1062
- 'href' => $url,
1063
- 'parent' => 'mc-my-calendar',
1064
- );
1065
- $wp_admin_bar->add_node( $args );
 
 
 
 
 
 
 
 
 
 
 
1066
  }
1067
  }
1068
  if ( isset( $_GET['mc_id'] ) && mc_can_edit_event( $_GET['mc_id'] ) ) {
@@ -1147,20 +1251,59 @@ add_filter( 'display_post_states', 'mc_admin_state', 10, 2 );
1147
  * @param object $event Event object.
1148
  */
1149
  function my_calendar_send_email( $event ) {
1150
- $details = mc_create_tags( $event );
1151
- $headers = array();
1152
- // shift to boolean.
1153
  $send_email_option = ( 'true' === get_option( 'mc_event_mail' ) ) ? true : false;
1154
- $send_email = apply_filters( 'mc_send_notification', $send_email_option, $details );
 
 
 
 
 
 
 
 
 
 
1155
  if ( true === $send_email ) {
1156
  add_filter( 'wp_mail_content_type', 'mc_html_type' );
1157
  }
1158
  if ( 'true' === get_option( 'mc_event_mail' ) ) {
1159
- $to = apply_filters( 'mc_event_mail_to', get_option( 'mc_event_mail_to' ), $details );
1160
- $from = ( '' === get_option( 'mc_event_mail_from', '' ) ) ? get_bloginfo( 'admin_email' ) : get_option( 'mc_event_mail_from' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1161
  $from = apply_filters( 'mc_event_mail_from', $from, $details );
1162
  $headers[] = 'From: ' . __( 'Event Notifications', 'my-calendar' ) . " <$from>";
1163
- $bcc = apply_filters( 'mc_event_mail_bcc', get_option( 'mc_event_mail_bcc' ), $details );
 
 
 
 
 
 
 
 
 
 
1164
  if ( $bcc ) {
1165
  $bcc = explode( PHP_EOL, $bcc );
1166
  foreach ( $bcc as $b ) {
@@ -1170,8 +1313,38 @@ function my_calendar_send_email( $event ) {
1170
  }
1171
  }
1172
  }
 
 
 
 
 
 
 
 
 
 
1173
  $headers = apply_filters( 'mc_customize_email_headers', $headers, $event );
 
 
 
 
 
 
 
 
 
 
1174
  $subject = apply_filters( 'mc_event_mail_subject', get_option( 'mc_event_mail_subject' ), $details );
 
 
 
 
 
 
 
 
 
 
1175
  $body = apply_filters( 'mc_event_mail_body', get_option( 'mc_event_mail_message' ), $details );
1176
  $subject = mc_draw_template( $details, $subject );
1177
  $message = mc_draw_template( $details, $body );
@@ -1189,11 +1362,31 @@ function my_calendar_send_email( $event ) {
1189
  * @param string $description Event description.
1190
  * @param array $post Posted details.
1191
  *
1192
- * @return boolean true if spam
1193
  */
1194
  function mc_spam( $event_url = '', $description = '', $post = array() ) {
1195
  global $akismet_api_host, $akismet_api_port;
 
 
 
 
 
 
 
 
 
 
1196
  if ( current_user_can( 'mc_add_events' ) || apply_filters( 'mc_disable_spam_checking', false, $post ) ) { // is a privileged user.
 
 
 
 
 
 
 
 
 
 
1197
  return apply_filters( 'mc_custom_spam_status', 0, $post );
1198
  }
1199
  $akismet = false;
@@ -1473,6 +1666,15 @@ function mc_scripts() {
1473
  )
1474
  );
1475
  }
 
 
 
 
 
 
 
 
 
1476
  if ( mc_count_locations() > apply_filters( 'mc_convert_locations_select_to_autocomplete', 90 ) ) {
1477
  wp_enqueue_script( 'accessible-autocomplete', plugins_url( '/js/accessible-autocomplete.min.js', __FILE__ ), array(), $version );
1478
  wp_enqueue_script( 'mc-autocomplete', plugins_url( '/js/autocomplete.js', __FILE__ ), array( 'jquery', 'accessible-autocomplete' ), $version, true );
@@ -1718,7 +1920,7 @@ $plugins_string
1718
  if ( isset( $_POST['mc_support'] ) ) {
1719
  $nonce = $_REQUEST['_wpnonce'];
1720
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
1721
- wp_die( 'Security check failed' );
1722
  }
1723
  $request = ( ! empty( $_POST['support_request'] ) ) ? stripslashes( $_POST['support_request'] ) : false;
1724
  $subject = 'My Calendar Pro support request.';
@@ -1729,7 +1931,7 @@ $plugins_string
1729
  $sitename = substr( $sitename, 4 );
1730
  }
1731
  $from_email = 'wordpress@' . $sitename;
1732
- $from = "From: $current_user->username <$from_email>\r\nReply-to: $current_user->username <$current_user->user_email>\r\n";
1733
 
1734
  if ( ! $request ) {
1735
  echo wp_kses_post( '<div class="message error"><p>' . __( 'Please describe your problem in detail. I\'m not psychic.', 'my-calendar' ) . '</p></div>' );
@@ -1829,7 +2031,7 @@ function mc_previous_post_link( $output, $format ) {
1829
  if ( empty( $event ) ) {
1830
  return '';
1831
  }
1832
- remove_filter( 'the_title', 'mc_the_title', 10, 2 );
1833
  $title = apply_filters( 'the_title', $event['title'], $event['post'] );
1834
  add_filter( 'the_title', 'mc_the_title', 10, 2 );
1835
  $link = add_query_arg( 'mc_id', $event['dateid'], $event['details_link'] );
@@ -1860,7 +2062,7 @@ function mc_next_post_link( $output, $format ) {
1860
  if ( empty( $event ) ) {
1861
  return '';
1862
  }
1863
- remove_filter( 'the_title', 'mc_the_title', 10, 2 );
1864
  $title = apply_filters( 'the_title', $event['title'], $event['post'] );
1865
  add_filter( 'the_title', 'mc_the_title', 10, 2 );
1866
  $link = add_query_arg( 'mc_id', $event['dateid'], $event['details_link'] );
@@ -1883,6 +2085,7 @@ function mc_next_post_link( $output, $format ) {
1883
  function mc_the_title( $title, $post_id = null ) {
1884
  if ( is_singular( 'mc-events' ) && in_the_loop() ) {
1885
  if ( $post_id ) {
 
1886
  $event_id = ( isset( $_GET['mc_id'] ) && is_numeric( $_GET['mc_id'] ) ) ? $_GET['mc_id'] : false;
1887
  if ( ! $event_id ) {
1888
  $parent_id = get_post_meta( $post_id, '_mc_event_id', true );
@@ -1944,18 +2147,44 @@ function mc_post_type() {
1944
  $arguments = array(
1945
  'public' => apply_filters( 'mc_event_posts_public', true ),
1946
  'publicly_queryable' => true,
1947
- // WARNING: Allowing the post type to be searchable will not provide a true event search, especially with respect to recurring events.
1948
- // It will not search recurring events by date, only the post content from each event. Enable only if requirements for search are limited to post content.
1949
- // Details: https://github.com/joedolson/my-calendar/issues/23.
 
 
 
 
 
 
 
 
1950
  'exclude_from_search' => apply_filters( 'mc_event_exclude_from_search', true ),
1951
  'show_ui' => true,
 
 
 
 
 
 
 
 
 
1952
  'show_in_menu' => apply_filters( 'mc_show_custom_posts_in_menu', false ),
1953
  'menu_icon' => null,
1954
  'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields' ),
1955
  );
1956
 
1957
- $loc_arguments = $arguments;
1958
- $loc_arguments['supports'] = array( 'title', 'custom-fields', 'thumbnail' );
 
 
 
 
 
 
 
 
 
1959
  $loc_arguments['exclude_from_search'] = apply_filters( 'mc_location_exclude_from_search', true );
1960
 
1961
  $types = array(
@@ -2011,6 +2240,15 @@ function mc_posttypes() {
2011
  'query_var' => true,
2012
  'rewrite' => array(
2013
  'with_front' => false,
 
 
 
 
 
 
 
 
 
2014
  'slug' => apply_filters( 'mc_event_slug', $key ),
2015
  ),
2016
  'hierarchical' => false,
@@ -2055,6 +2293,15 @@ function mc_close_comments( $posts ) {
2055
  }
2056
 
2057
  if ( 'mc-events' === get_post_type( $posts[0]->ID ) ) {
 
 
 
 
 
 
 
 
 
2058
  if ( apply_filters( 'mc_autoclose_comments', true ) && 'closed' !== $posts[0]->comment_status ) {
2059
  $posts[0]->comment_status = 'closed';
2060
  $posts[0]->ping_status = 'closed';
@@ -2072,7 +2319,7 @@ add_filter( 'default_content', 'mc_posttypes_defaults', 10, 2 );
2072
  * @param string $post_content unused.
2073
  * @param object $post WP Post object.
2074
  *
2075
- * @return $post_content;
2076
  */
2077
  function mc_posttypes_defaults( $post_content, $post ) {
2078
  if ( $post->post_type ) {
@@ -2095,6 +2342,16 @@ function mc_taxonomies() {
2095
  if ( is_array( $enabled ) ) {
2096
  foreach ( $enabled as $key ) {
2097
  $value = $types[ $key ];
 
 
 
 
 
 
 
 
 
 
2098
  register_taxonomy(
2099
  'mc-event-category',
2100
  // Internal name = machine-readable taxonomy name.
@@ -2103,7 +2360,7 @@ function mc_taxonomies() {
2103
  'hierarchical' => true,
2104
  'label' => __( 'Event Categories', 'my-calendar' ),
2105
  'query_var' => true,
2106
- 'rewrite' => array( 'slug' => apply_filters( 'mc_event_category_slug', 'mc-event-category' ) ),
2107
  )
2108
  );
2109
  }
@@ -2188,7 +2445,17 @@ function mc_setup_cors_access() {
2188
  if ( $cache ) {
2189
  $allowed = $cache;
2190
  } else {
2191
- $sites = ( function_exists( 'get_sites' ) ) ? get_sites() : array();
 
 
 
 
 
 
 
 
 
 
2192
  $allowed = apply_filters( 'mc_setup_allowed_sites', array(), $origin );
2193
  if ( ! empty( $sites ) ) {
2194
  foreach ( $sites as $site ) {
70
  $dirs[] = ( 'path' === $type ) ? get_stylesheet_directory() . '/css/' : get_stylesheet_directory_uri() . '/css/';
71
  $dirs[] = ( 'path' === $type ) ? get_stylesheet_directory() . '/' : get_stylesheet_directory_uri() . '/';
72
 
73
+ /**
74
+ * Filter My Calendar's array of directories to check for custom files. Use to define where your custom templates and styles will be found.
75
+ *
76
+ * @hook mc_custom_dirs
77
+ *
78
+ * @param {array} $dirs Array of directory paths to check.
79
+ * @param {string} $type Checking paths or URLs.
80
+ *
81
+ * @return {array}
82
+ */
83
  $directories = apply_filters( 'mc_custom_dirs', $dirs, $type );
84
 
85
  return ( is_array( $directories ) && ! empty( $directories ) ) ? $directories : $dirs;
93
  * @return boolean
94
  */
95
  function mc_file_exists( $file ) {
96
+ $file = sanitize_file_name( $file );
97
+ /**
98
+ * Filter test for whether a file exists. Return true to confirm file exists.
99
+ *
100
+ * @hook mc_file_exists
101
+ *
102
+ * @param {bool} $path File path.
103
+ * @param {string} $file File name.
104
+ *
105
+ * @return {bool}
106
+ */
107
  $return = apply_filters( 'mc_file_exists', false, $file );
108
  if ( $return ) {
109
  return true;
111
  foreach ( mc_custom_dirs() as $dir ) {
112
  if ( file_exists( $dir . $file ) ) {
113
  return true;
 
114
  }
115
  }
116
 
142
  break;
143
  }
144
  }
145
+ /**
146
+ * Filter file paths for My Calendar files.
147
+ *
148
+ * @hook mc_get_file
149
+ *
150
+ * @param {string} $path File path.
151
+ * @param {string} $file File name.
152
+ *
153
+ * @return {string}
154
+ */
155
  $path = apply_filters( 'mc_get_file', $path, $file );
156
 
157
  return $path;
182
  */
183
  function mc_register_styles() {
184
  global $wp_query;
185
+ $version = get_option( 'mc_version' );
186
+ $this_post = $wp_query->get_queried_object();
187
+ /**
188
+ * Filter url to get My Calendar stylesheet.
189
+ *
190
+ * @hook mc_registered_stylesheet
191
+ *
192
+ * @param {string} $stylesheet URL to locate My Calendar's stylesheet.
193
+ *
194
+ * @return {string}
195
+ */
196
  $stylesheet = apply_filters( 'mc_registered_stylesheet', mc_get_style_path( get_option( 'mc_css_file' ), 'url' ) );
197
  wp_register_style( 'my-calendar-reset', plugins_url( 'css/reset.css', __FILE__ ), array( 'dashicons' ), $version );
198
  wp_register_style( 'my-calendar-style', $stylesheet, array( 'my-calendar-reset' ), $version );
208
  wp_enqueue_style( 'my-calendar-admin-style' );
209
  }
210
 
211
+ /**
212
+ * Filter whether My Calendar styles should be displayed on archive pages. Default 'true'.
213
+ *
214
+ * @hook mc_display_css_on_archives
215
+ *
216
+ * @param {bool} $default 'true' to display.
217
+ * @param {WP_Query} $wp_query WP Query.
218
+ *
219
+ * @return {bool}
220
+ */
221
  $default = apply_filters( 'mc_display_css_on_archives', true, $wp_query );
222
  $id = ( is_object( $this_post ) && isset( $this_post->ID ) ) ? $this_post->ID : false;
223
  $js_array = ( '' !== trim( get_option( 'mc_show_js', '' ) ) ) ? explode( ',', get_option( 'mc_show_js' ) ) : array();
377
  * Deal with events posted by a user when that user is deleted
378
  *
379
  * @param int $id user ID of deleted user.
380
+ * @param int $reassign User ID chosen for reassignment of content.
381
  */
382
+ function mc_deal_with_deleted_user( $id, $reassign ) {
383
  global $wpdb;
384
+ $new = ( ! $reassign ) ? $wpdb->get_var( 'SELECT MIN(ID) FROM ' . $wpdb->users, 0, 0 ) : $reassign;
 
385
  // This may not work quite right in multi-site. Need to explore further when I have time.
386
+ $wpdb->get_results( $wpdb->prepare( 'UPDATE ' . my_calendar_table() . ' SET event_author=%d WHERE event_author=%d', $reassign, $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
387
 
388
+ $wpdb->get_results( $wpdb->prepare( 'UPDATE ' . my_calendar_table() . ' SET event_host=%d WHERE event_host=%d', $reassign, $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
 
389
  }
390
 
391
  /**
457
  function mc_footer_js() {
458
  global $wp_query;
459
  $version = get_option( 'mc_version' );
460
+ $script = '';
461
  $mcjs = "<script>(function ($) { 'use strict'; $(function () { $( '.mc-main' ).removeClass( 'mcjs' ); });}(jQuery));</script>";
462
+ /**
463
+ * Disable scripting on mobile devices.
464
+ *
465
+ * @hook mc_disable_mobile_js
466
+ *
467
+ * @param {bool} $disable Return true to disable JS on detected mobile devices.
468
+ *
469
+ * @return {bool}
470
+ */
471
  if ( mc_is_mobile() && apply_filters( 'mc_disable_mobile_js', false ) ) {
472
 
473
  return;
483
  $id = false;
484
  }
485
  if ( '1' === get_option( 'mc_use_custom_js' ) ) {
486
+ // Due for removal.
487
  $top = '';
488
  $bottom = '';
489
  $inner = '';
523
  (function( $ ) { \'use strict\';' . $inner . '}(jQuery));
524
  </script>';
525
  }
 
526
  echo ( '' !== $inner ) ? $script . $mcjs : '';
527
  } else {
528
  $enqueue_mcjs = false;
532
  $ajax = 'false';
533
  if ( ! $id || ( is_array( $pages ) && in_array( $id, $pages, true ) ) || '' === $show_js ) {
534
  if ( '1' !== get_option( 'mc_calendar_javascript' ) && 'true' !== get_option( 'mc_open_uri' ) ) {
535
+ /**
536
+ * Filter to replace scripts used on front-end for grid behavior. Default empty string.
537
+ *
538
+ * @hook mc_grid_js
539
+ *
540
+ * @param {string} $url URL to JS to operate My Calendar grid view.
541
+ *
542
+ * @return {string}
543
+ */
544
  $url = apply_filters( 'mc_grid_js', '' );
545
  $enqueue_mcjs = true;
546
  if ( $url ) {
550
  }
551
  }
552
  if ( '1' !== get_option( 'mc_list_javascript' ) ) {
553
+ /**
554
+ * Filter to replace scripts used on front-end for list behavior. Default empty string.
555
+ *
556
+ * @hook mc_list_js
557
+ *
558
+ * @param {string} $url URL to JS to operate My Calendar list view.
559
+ *
560
+ * @return {string}
561
+ */
562
  $url = apply_filters( 'mc_list_js', '' );
563
  $enqueue_mcjs = true;
564
  if ( $url ) {
568
  }
569
  }
570
  if ( '1' !== get_option( 'mc_mini_javascript' ) && 'true' !== get_option( 'mc_open_day_uri' ) ) {
571
+ /**
572
+ * Filter to replace scripts used on front-end for mini calendar behavior. Default empty string.
573
+ *
574
+ * @hook mc_mini_js
575
+ *
576
+ * @param {string} $url URL to JS to operate My Calendar mini calendar.
577
+ *
578
+ * @return {string}
579
+ */
580
  $url = apply_filters( 'mc_mini_js', '' );
581
  $enqueue_mcjs = true;
582
 
587
  }
588
  }
589
  if ( '1' !== get_option( 'mc_ajax_javascript' ) ) {
590
+ /**
591
+ * Filter to replace scripts used on front-end for AJAX behavior. Default empty string.
592
+ *
593
+ * @hook mc_ajax_js
594
+ *
595
+ * @param {string} $url URL to JS to operate My Calendar AJAX.
596
+ *
597
+ * @return {string}
598
+ */
599
  $url = apply_filters( 'mc_ajax_js', '' );
600
  $enqueue_mcjs = true;
601
  if ( $url ) {
612
  'list' => $list,
613
  'mini' => $mini,
614
  'ajax' => $ajax,
615
+ 'newWindow' => __( 'New tab', 'my-calendar' ),
616
  );
617
  wp_localize_script( 'mc.mcjs', 'my_calendar', $args );
618
  }
626
  */
627
  function mc_admin_styles() {
628
  global $current_screen;
629
+ $version = get_option( 'mc_version' );
630
+ $id = $current_screen->id;
631
+ $is_mc_page = isset( $_GET['post'] ) && get_option( 'mc_uri_id' ) === $_GET['post'];
632
+ $enqueue_mcjs = false;
633
+ $grid = 'false';
634
+ $list = 'false';
635
+ $mini = 'false';
636
+ $ajax = 'false';
637
  if ( false !== strpos( $id, 'my-calendar' ) || $is_mc_page ) {
638
  // Toggle CSS & Scripts based on current mode.
639
  $mode = get_option( 'mc_default_admin_view' );
651
  wp_register_style( 'my-calendar-admin-style', plugins_url( 'css/admin.css', __FILE__ ), array( 'my-calendar-reset' ), $version );
652
  wp_enqueue_style( 'my-calendar-admin-style' );
653
  if ( '1' !== get_option( 'mc_calendar_javascript' ) && 'true' !== get_option( 'mc_open_uri' ) ) {
 
654
  $enqueue_mcjs = true;
655
+ $grid = 'true';
 
 
 
 
 
 
 
656
  }
657
  if ( '1' !== get_option( 'mc_list_javascript' ) ) {
 
658
  $enqueue_mcjs = true;
659
+ $list = 'true';
 
 
 
 
 
 
 
660
  }
661
  if ( '1' !== get_option( 'mc_mini_javascript' ) && 'true' !== get_option( 'mc_open_day_uri' ) ) {
 
662
  $enqueue_mcjs = true;
663
+ $mini = 'true';
 
 
 
 
 
 
 
664
  }
665
  if ( '1' !== get_option( 'mc_ajax_javascript' ) ) {
 
666
  $enqueue_mcjs = true;
667
+ $ajax = 'true';
 
 
 
 
 
 
 
668
  }
669
  if ( $enqueue_mcjs ) {
670
+ $url = ( true === SCRIPT_DEBUG ) ? plugins_url( 'js/mcjs.js', __FILE__ ) : plugins_url( 'js/mcjs.min.js', __FILE__ );
671
+ wp_enqueue_script( 'mc.mcjs', $url, array( 'jquery' ), $version );
672
+ $args = array(
673
+ 'grid' => $grid,
674
+ 'list' => $list,
675
+ 'mini' => $mini,
676
+ 'ajax' => $ajax,
677
+ 'newWindow' => __( 'New tab', 'my-calendar' ),
678
  );
679
+ wp_localize_script( 'mc.mcjs', 'my_calendar', $args );
680
  }
681
  }
682
  wp_enqueue_style( 'mc-styles', plugins_url( 'css/mc-styles.css', __FILE__ ), array(), $version );
770
  if ( $wp_rewrite->using_permalinks() ) {
771
  $current_url = trailingslashit( $current_url );
772
  }
773
+ /**
774
+ * Filter the URL returned for the current URL.
775
+ *
776
+ * @hook mc_get_current_url
777
+ *
778
+ * @param {string} $current_url Current URL according to wp_rewrite.
779
+ *
780
+ * @return {string}
781
+ */
782
  $current_url = apply_filters( 'mc_get_current_url', $current_url );
783
 
784
  return esc_url( $current_url );
815
  /**
816
  * Grant capabilities to standard site roles
817
  *
818
+ * @param string|boolean $add Add capabilities to this role.
819
+ * @param string|boolean $manage Manage capabilities to this role.
820
+ * @param string|boolean $approve Approve capabilities to this role.
821
  */
822
  function mc_add_roles( $add = false, $manage = false, $approve = false ) {
823
  $role = get_role( 'administrator' );
1104
  $mc_id = get_option( 'mc_uri_id' );
1105
  if ( mc_get_uri( 'boolean' ) ) {
1106
  if ( is_page( $mc_id ) && current_user_can( 'mc_add_events' ) ) {
1107
+ /**
1108
+ * Filter URL displayed for 'Add Event' link in adminbar. Return empty value to disable.
1109
+ *
1110
+ * @hook mc_add_events_url
1111
+ *
1112
+ * @param {string} $url Admin URL for adding events.
1113
+ *
1114
+ * @return {string}
1115
+ */
1116
  $url = apply_filters( 'mc_add_events_url', admin_url( 'admin.php?page=my-calendar' ) );
1117
  $args = array(
1118
  'id' => 'mc-my-calendar',
1120
  'href' => $url,
1121
  );
1122
  } else {
1123
+ /**
1124
+ * Filter URL displayed for 'My Calendar' link in adminbar.
1125
+ *
1126
+ * @hook mc_adminbar_uri
1127
+ *
1128
+ * @param {string} $url Front-end URL for viewing events.
1129
+ *
1130
+ * @return {string}
1131
+ */
1132
  $url = esc_url( apply_filters( 'mc_adminbar_uri', mc_get_uri() ) );
1133
  $args = array(
1134
  'id' => 'mc-my-calendar',
1148
  }
1149
  if ( current_user_can( 'mc_add_events' ) && 'true' !== get_option( 'mc_remote' ) ) {
1150
  if ( ! is_page( $mc_id ) ) {
1151
+ /**
1152
+ * Filter URL displayed for 'Add Event' link in adminbar. Return empty value to disable.
1153
+ *
1154
+ * @hook mc_add_events_url
1155
+ *
1156
+ * @param {string} $url Admin URL for adding events.
1157
+ *
1158
+ * @return {string}
1159
+ */
1160
+ $url = apply_filters( 'mc_add_events_url', admin_url( 'admin.php?page=my-calendar' ) );
1161
+ if ( $url ) {
1162
+ $args = array(
1163
+ 'id' => 'mc-add-event',
1164
+ 'title' => __( 'Add Event', 'my-calendar' ),
1165
+ 'href' => $url,
1166
+ 'parent' => 'mc-my-calendar',
1167
+ );
1168
+ $wp_admin_bar->add_node( $args );
1169
+ }
1170
  }
1171
  }
1172
  if ( isset( $_GET['mc_id'] ) && mc_can_edit_event( $_GET['mc_id'] ) ) {
1251
  * @param object $event Event object.
1252
  */
1253
  function my_calendar_send_email( $event ) {
1254
+ $details = mc_create_tags( $event );
1255
+ $headers = array();
 
1256
  $send_email_option = ( 'true' === get_option( 'mc_event_mail' ) ) ? true : false;
1257
+ /**
1258
+ * Filter whether email notifications should be sent.
1259
+ *
1260
+ * @hook mc_send_notification
1261
+ *
1262
+ * @param {bool} $send_email Boolean equivalent of value of event email setting.
1263
+ * @param {array} $details Event details for notifications.
1264
+ *
1265
+ * @return {bool}
1266
+ */
1267
+ $send_email = apply_filters( 'mc_send_notification', $send_email_option, $details );
1268
  if ( true === $send_email ) {
1269
  add_filter( 'wp_mail_content_type', 'mc_html_type' );
1270
  }
1271
  if ( 'true' === get_option( 'mc_event_mail' ) ) {
1272
+ /**
1273
+ * Filter event notification email to header.
1274
+ *
1275
+ * @hook mc_event_mail_to
1276
+ *
1277
+ * @param {string} $to Email to field string.
1278
+ * @param {array} $details Array of details passed to email function.
1279
+ *
1280
+ * @return {string}
1281
+ */
1282
+ $to = apply_filters( 'mc_event_mail_to', get_option( 'mc_event_mail_to' ), $details );
1283
+ $from = ( '' === get_option( 'mc_event_mail_from', '' ) ) ? get_bloginfo( 'admin_email' ) : get_option( 'mc_event_mail_from' );
1284
+ /**
1285
+ * Filter event notification email from header.
1286
+ *
1287
+ * @hook mc_event_mail_from
1288
+ *
1289
+ * @param {string} $from Email string for email header from value.
1290
+ * @param {array} $details Array of details passed to email function.
1291
+ *
1292
+ * @return {string}
1293
+ */
1294
  $from = apply_filters( 'mc_event_mail_from', $from, $details );
1295
  $headers[] = 'From: ' . __( 'Event Notifications', 'my-calendar' ) . " <$from>";
1296
+ /**
1297
+ * Filter event notification email bcc headers.
1298
+ *
1299
+ * @hook mc_event_mail_bcc
1300
+ *
1301
+ * @param {string} $bcc Comma separated list of emails for BCC.
1302
+ * @param {array} $details Array of details passed to email function.
1303
+ *
1304
+ * @return {string}
1305
+ */
1306
+ $bcc = apply_filters( 'mc_event_mail_bcc', get_option( 'mc_event_mail_bcc' ), $details );
1307
  if ( $bcc ) {
1308
  $bcc = explode( PHP_EOL, $bcc );
1309
  foreach ( $bcc as $b ) {
1313
  }
1314
  }
1315
  }
1316
+ /**
1317
+ * Filter event notification email headers.
1318
+ *
1319
+ * @hook mc_customize_email_headers
1320
+ *
1321
+ * @param {array} $headers Email headers.
1322
+ * @param {object} $event Event object.
1323
+ *
1324
+ * @return {string}
1325
+ */
1326
  $headers = apply_filters( 'mc_customize_email_headers', $headers, $event );
1327
+ /**
1328
+ * Filter event notification email subject.
1329
+ *
1330
+ * @hook mc_event_mail_subject
1331
+ *
1332
+ * @param {string} $subject Email subject.
1333
+ * @param {array} $details Array of details passed to email function.
1334
+ *
1335
+ * @return {string}
1336
+ */
1337
  $subject = apply_filters( 'mc_event_mail_subject', get_option( 'mc_event_mail_subject' ), $details );
1338
+ /**
1339
+ * Filter event notification email body.
1340
+ *
1341
+ * @hook mc_event_mail_body
1342
+ *
1343
+ * @param {string} $body Email body.
1344
+ * @param {array} $details Array of details passed to email function.
1345
+ *
1346
+ * @return {string}
1347
+ */
1348
  $body = apply_filters( 'mc_event_mail_body', get_option( 'mc_event_mail_message' ), $details );
1349
  $subject = mc_draw_template( $details, $subject );
1350
  $message = mc_draw_template( $details, $body );
1362
  * @param string $description Event description.
1363
  * @param array $post Posted details.
1364
  *
1365
+ * @return int 1 if spam, 0 if not.
1366
  */
1367
  function mc_spam( $event_url = '', $description = '', $post = array() ) {
1368
  global $akismet_api_host, $akismet_api_port;
1369
+ /**
1370
+ * Disable automatic spam checking (turned on when Akismet is active.)
1371
+ *
1372
+ * @hook mc_disable_spam_checking
1373
+ *
1374
+ * @param {bool} $disabled True to disable spam checking. Default false.
1375
+ * @param {array} $post Posted event details for checking.
1376
+ *
1377
+ * @return {bool}
1378
+ */
1379
  if ( current_user_can( 'mc_add_events' ) || apply_filters( 'mc_disable_spam_checking', false, $post ) ) { // is a privileged user.
1380
+ /**
1381
+ * Test spam status before Akismet runs. Returns immediately and shortcircuits tests.
1382
+ *
1383
+ * @hook mc_custom_spam_status
1384
+ *
1385
+ * @param {int} $status Numeric status. 0 for valid, 1 for spam.
1386
+ * @param {array} $post Submitted data from POST.
1387
+ *
1388
+ * @return {int}
1389
+ */
1390
  return apply_filters( 'mc_custom_spam_status', 0, $post );
1391
  }
1392
  $akismet = false;
1666
  )
1667
  );
1668
  }
1669
+ /**
1670
+ * Filter the number of locations required to trigger a switch between a select input and an autocomplete.
1671
+ *
1672
+ * @hook mc_convert_locations_select_to_autocomplete
1673
+ *
1674
+ * @param {int} $count Number of locations that will remain a select. Default 90.
1675
+ *
1676
+ * @return {int}
1677
+ */
1678
  if ( mc_count_locations() > apply_filters( 'mc_convert_locations_select_to_autocomplete', 90 ) ) {
1679
  wp_enqueue_script( 'accessible-autocomplete', plugins_url( '/js/accessible-autocomplete.min.js', __FILE__ ), array(), $version );
1680
  wp_enqueue_script( 'mc-autocomplete', plugins_url( '/js/autocomplete.js', __FILE__ ), array( 'jquery', 'accessible-autocomplete' ), $version, true );
1920
  if ( isset( $_POST['mc_support'] ) ) {
1921
  $nonce = $_REQUEST['_wpnonce'];
1922
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
1923
+ wp_die( 'My Calendar: Security check failed' );
1924
  }
1925
  $request = ( ! empty( $_POST['support_request'] ) ) ? stripslashes( $_POST['support_request'] ) : false;
1926
  $subject = 'My Calendar Pro support request.';
1931
  $sitename = substr( $sitename, 4 );
1932
  }
1933
  $from_email = 'wordpress@' . $sitename;
1934
+ $from = "From: $current_user->display_name <$from_email>\r\nReply-to: $current_user->display_name <$current_user->user_email>\r\n";
1935
 
1936
  if ( ! $request ) {
1937
  echo wp_kses_post( '<div class="message error"><p>' . __( 'Please describe your problem in detail. I\'m not psychic.', 'my-calendar' ) . '</p></div>' );
2031
  if ( empty( $event ) ) {
2032
  return '';
2033
  }
2034
+ remove_filter( 'the_title', 'mc_the_title', 10 );
2035
  $title = apply_filters( 'the_title', $event['title'], $event['post'] );
2036
  add_filter( 'the_title', 'mc_the_title', 10, 2 );
2037
  $link = add_query_arg( 'mc_id', $event['dateid'], $event['details_link'] );
2062
  if ( empty( $event ) ) {
2063
  return '';
2064
  }
2065
+ remove_filter( 'the_title', 'mc_the_title', 10 );
2066
  $title = apply_filters( 'the_title', $event['title'], $event['post'] );
2067
  add_filter( 'the_title', 'mc_the_title', 10, 2 );
2068
  $link = add_query_arg( 'mc_id', $event['dateid'], $event['details_link'] );
2085
  function mc_the_title( $title, $post_id = null ) {
2086
  if ( is_singular( 'mc-events' ) && in_the_loop() ) {
2087
  if ( $post_id ) {
2088
+ $event = false;
2089
  $event_id = ( isset( $_GET['mc_id'] ) && is_numeric( $_GET['mc_id'] ) ) ? $_GET['mc_id'] : false;
2090
  if ( ! $event_id ) {
2091
  $parent_id = get_post_meta( $post_id, '_mc_event_id', true );
2147
  $arguments = array(
2148
  'public' => apply_filters( 'mc_event_posts_public', true ),
2149
  'publicly_queryable' => true,
2150
+ /**
2151
+ * Should My Calendar post types be excluded from search. Default false.
2152
+ * Allowing the event post type to be searchable will not provide a true event search, especially with respect to recurring events.
2153
+ * It will not search recurring events by date, only the post content from each event. See https://github.com/joedolson/my-calendar/issues/23.
2154
+ *
2155
+ * @hook mc_event_exclude_from_search
2156
+ *
2157
+ * @param {bool} $show True to exclude from search.
2158
+ *
2159
+ * @return {bool}
2160
+ */
2161
  'exclude_from_search' => apply_filters( 'mc_event_exclude_from_search', true ),
2162
  'show_ui' => true,
2163
+ /**
2164
+ * Should My Calendar post types be shown in admin menus. Default false.
2165
+ *
2166
+ * @hook mc_show_custom_posts_in_menu
2167
+ *
2168
+ * @param {bool} $show True to show in menus.
2169
+ *
2170
+ * @return {bool}
2171
+ */
2172
  'show_in_menu' => apply_filters( 'mc_show_custom_posts_in_menu', false ),
2173
  'menu_icon' => null,
2174
  'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields' ),
2175
  );
2176
 
2177
+ $loc_arguments = $arguments;
2178
+ $loc_arguments['supports'] = array( 'title', 'custom-fields', 'thumbnail' );
2179
+ /**
2180
+ * Should location posts be excluded from search? Default true.
2181
+ *
2182
+ * @hook mc_location_exclude_from_search
2183
+ *
2184
+ * @param {bool} $exclude True to exclude.
2185
+ *
2186
+ * @return {bool}
2187
+ */
2188
  $loc_arguments['exclude_from_search'] = apply_filters( 'mc_location_exclude_from_search', true );
2189
 
2190
  $types = array(
2240
  'query_var' => true,
2241
  'rewrite' => array(
2242
  'with_front' => false,
2243
+ /**
2244
+ * Filter default calendar post type slugs. Default 'mc-events' for events and 'mc-locations' for locations.
2245
+ *
2246
+ * @hook mc_event_slug
2247
+ *
2248
+ * @param {string} $key Slug.
2249
+ *
2250
+ * @return {string}
2251
+ */
2252
  'slug' => apply_filters( 'mc_event_slug', $key ),
2253
  ),
2254
  'hierarchical' => false,
2293
  }
2294
 
2295
  if ( 'mc-events' === get_post_type( $posts[0]->ID ) ) {
2296
+ /**
2297
+ * Filter whether event posts should automatically close comments. Default 'true'.
2298
+ *
2299
+ * @hook mc_autoclose_comments
2300
+ *
2301
+ * @param {bool} $close 'true' to close comments.
2302
+ *
2303
+ * @return {bool}
2304
+ */
2305
  if ( apply_filters( 'mc_autoclose_comments', true ) && 'closed' !== $posts[0]->comment_status ) {
2306
  $posts[0]->comment_status = 'closed';
2307
  $posts[0]->ping_status = 'closed';
2319
  * @param string $post_content unused.
2320
  * @param object $post WP Post object.
2321
  *
2322
+ * @return string $post_content;
2323
  */
2324
  function mc_posttypes_defaults( $post_content, $post ) {
2325
  if ( $post->post_type ) {
2342
  if ( is_array( $enabled ) ) {
2343
  foreach ( $enabled as $key ) {
2344
  $value = $types[ $key ];
2345
+ /**
2346
+ * Filter event category taxonomy slug. Default 'mc-event-category'.
2347
+ *
2348
+ * @hook mc_event_category_slug
2349
+ *
2350
+ * @param {string} $slug Default slug.
2351
+ *
2352
+ * @return {string}
2353
+ */
2354
+ $slug = apply_filters( 'mc_event_category_slug', 'mc-event-category' );
2355
  register_taxonomy(
2356
  'mc-event-category',
2357
  // Internal name = machine-readable taxonomy name.
2360
  'hierarchical' => true,
2361
  'label' => __( 'Event Categories', 'my-calendar' ),
2362
  'query_var' => true,
2363
+ 'rewrite' => array( 'slug' => $slug ),
2364
  )
2365
  );
2366
  }
2445
  if ( $cache ) {
2446
  $allowed = $cache;
2447
  } else {
2448
+ $sites = ( function_exists( 'get_sites' ) ) ? get_sites() : array();
2449
+ /**
2450
+ * Filter what sites are allowed CORS access.
2451
+ *
2452
+ * @hook mc_setup_allowed_sites
2453
+ *
2454
+ * @param {array} $allowed URLs permitted access. Default empty array.
2455
+ * @param {string} $origin HTTP origin passed.
2456
+ *
2457
+ * @return {array}
2458
+ */
2459
  $allowed = apply_filters( 'mc_setup_allowed_sites', array(), $origin );
2460
  if ( ! empty( $sites ) ) {
2461
  foreach ( $sites as $site ) {
my-calendar-event-editor.php CHANGED
@@ -21,10 +21,11 @@ if ( ! defined( 'ABSPATH' ) ) {
21
  * @param int $event_id My Calendar event ID.
22
  * @param bool|int $result Results of DB query.
23
  *
24
- * @return int post ID
25
  */
26
  function mc_event_post( $action, $data, $event_id, $result = false ) {
27
  // if the event save was successful.
 
28
  if ( 'add' === $action || 'copy' === $action ) {
29
  $post_id = mc_create_event_post( $data, $event_id );
30
  } elseif ( 'edit' === $action ) {
@@ -62,7 +63,17 @@ function mc_event_post( $action, $data, $event_id, $result = false ) {
62
  $terms[] = (int) $term;
63
  }
64
 
65
- $title = $data['event_title'];
 
 
 
 
 
 
 
 
 
 
66
  $template = apply_filters( 'mc_post_template', 'details', $terms );
67
  $data['shortcode'] = "[my_calendar_event event='$event_id' template='$template' list='']";
68
  $description = $data['event_desc'];
@@ -99,6 +110,16 @@ function mc_event_post( $action, $data, $event_id, $result = false ) {
99
  $access_terms = implode( ',', array_values( $access ) );
100
  mc_update_event( 'event_access', $access_terms, $event_id, '%s' );
101
  mc_add_post_meta_data( $post_id, $_POST, $data, $event_id );
 
 
 
 
 
 
 
 
 
 
102
  do_action( 'mc_update_event_post', $post_id, $_POST, $data, $event_id );
103
  if ( mc_switch_sites() ) {
104
  restore_current_blog();
@@ -189,8 +210,18 @@ function mc_create_event_post( $data, $event_id ) {
189
  }
190
  $terms[] = (int) $term;
191
  }
192
- $title = $data['event_title'];
193
- $template = apply_filters( 'mc_post_template', 'details', $term );
 
 
 
 
 
 
 
 
 
 
194
  $data['shortcode'] = "[my_calendar_event event='$event_id' template='$template' list='']";
195
  $description = isset( $data['event_desc'] ) ? $data['event_desc'] : '';
196
  $excerpt = isset( $data['event_short'] ) ? $data['event_short'] : '';
@@ -227,6 +258,16 @@ function mc_create_event_post( $data, $event_id ) {
227
  mc_update_event( 'event_post', $post_id, $event_id );
228
  mc_update_event( 'event_location', $location_id, $event_id );
229
  mc_add_post_meta_data( $post_id, $_POST, $data, $event_id );
 
 
 
 
 
 
 
 
 
 
230
  do_action( 'mc_update_event_post', $post_id, $_POST, $data, $event_id );
231
  wp_publish_post( $post_id );
232
  }
@@ -262,6 +303,14 @@ function mc_event_delete_posts( $deleted ) {
262
  * @param int $post_id Post ID.
263
  */
264
  function mc_event_delete_post( $event_id, $post_id ) {
 
 
 
 
 
 
 
 
265
  do_action( 'mc_deleted_post', $event_id, $post_id );
266
  wp_delete_post( $post_id, true );
267
  }
@@ -269,12 +318,12 @@ function mc_event_delete_post( $event_id, $post_id ) {
269
  /**
270
  * Update a single field in an event.
271
  *
272
- * @param string $field database column.
273
- * @param mixed $data value to be saved.
274
- * @param mixed string/integer $event could be integer or string.
275
- * @param string $type signifier representing data type of $data (e.g. %d or %s).
276
  *
277
- * @return database result
278
  */
279
  function mc_update_event( $field, $data, $event, $type = '%d' ) {
280
  global $wpdb;
@@ -310,7 +359,7 @@ function my_calendar_edit() {
310
  if ( isset( $_POST['event_action'] ) ) {
311
  $nonce = $_REQUEST['_wpnonce'];
312
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
313
- die( 'Security check failed' );
314
  }
315
 
316
  global $mc_output;
@@ -382,11 +431,11 @@ function my_calendar_edit() {
382
  /**
383
  * Save an event to the database
384
  *
385
- * @param string $action Type of action.
386
- * @param array $output Checked event data.
387
- * @param int $event_id Event ID.
388
  *
389
- * @return string message
390
  */
391
  function my_calendar_save( $action, $output, $event_id = false ) {
392
  global $wpdb;
@@ -399,6 +448,15 @@ function my_calendar_save( $action, $output, $event_id = false ) {
399
  $cats = $add['event_categories'];
400
 
401
  unset( $add['event_categories'] );
 
 
 
 
 
 
 
 
 
402
  $add = apply_filters( 'mc_before_save_insert', $add );
403
  $result = $wpdb->insert( my_calendar_table(), $add, $formats );
404
  $event_id = $wpdb->insert_id;
@@ -411,13 +469,31 @@ function my_calendar_save( $action, $output, $event_id = false ) {
411
  $data = $add;
412
  $event_error = '';
413
  $event_link = '';
 
414
  mc_event_post( $action, $data, $event_id, $result );
 
 
 
 
 
 
 
 
 
 
415
  do_action( 'mc_save_event', $action, $data, $event_id, $result );
416
 
417
  if ( 'true' === get_option( 'mc_event_mail' ) ) {
418
  // insert_id is last occurrence inserted in the db.
419
  $event = mc_get_first_event( $event_id );
420
  if ( 1 === (int) $event->event_flagged ) {
 
 
 
 
 
 
 
421
  do_action( 'mc_notify_event_spam', $event );
422
  } else {
423
  my_calendar_send_email( $event );
@@ -476,6 +552,16 @@ function my_calendar_save( $action, $output, $event_id = false ) {
476
  unset( $update['event_categories'] );
477
  mc_update_category_relationships( $cats, $event_id );
478
 
 
 
 
 
 
 
 
 
 
 
479
  $update = apply_filters( 'mc_before_save_update', $update, $event_id );
480
  $endtime = mc_date( 'H:i:00', mc_strtotime( $update['event_endtime'] ), false );
481
  $prev_eb = ( isset( $_POST['prev_event_begin'] ) ) ? $_POST['prev_event_begin'] : '';
@@ -550,19 +636,39 @@ function my_calendar_save( $action, $output, $event_id = false ) {
550
  }
551
  $data = $update;
552
  mc_event_post( $action, $data, $event_id, $result );
 
 
 
 
 
 
 
 
 
 
553
  do_action( 'mc_save_event', $action, $data, $event_id, $result );
554
  if ( false === $result ) {
555
  $message = mc_show_error( __( 'Your event was not updated.', 'my-calendar' ) . " $url", false );
556
  } else {
557
  // do an action using the $action and processed event data.
558
- $event_approved = ( current_user_can( 'mc_approve_events' ) ) ? 1 : 0;
559
  // check for event_approved provides support for older versions of My Calendar Pro.
560
- if ( isset( $post['event_approved'] ) && $post['event_approved'] !== $event_approved ) {
561
- $event_approved = absint( $post['event_approved'] );
562
  }
563
  if ( isset( $_POST['prev_event_status'] ) ) {
564
- // Don't execute transition actions if prev status not known.
565
- do_action( 'mc_transition_event', (int) $_POST['prev_event_status'], $event_approved, $action, $data, $event_id );
 
 
 
 
 
 
 
 
 
 
566
  }
567
  $message = mc_show_notice( __( 'Event updated successfully', 'my-calendar' ) . ". $url", false, 'event-updated' );
568
  }
@@ -578,6 +684,15 @@ function my_calendar_save( $action, $output, $event_id = false ) {
578
  );
579
  mc_update_count_cache();
580
 
 
 
 
 
 
 
 
 
 
581
  return apply_filters( 'mc_event_saved_message', $saved_response );
582
  }
583
 
@@ -614,8 +729,25 @@ function mc_delete_event( $event_id ) {
614
  if ( empty( $result ) || empty( $result[0]->event_id ) ) {
615
  // Do an action using the event_id.
616
  if ( $instance ) {
 
 
 
 
 
 
 
 
 
617
  do_action( 'mc_delete_event_instance', $event_id, $post_id, $event_in );
618
  } else {
 
 
 
 
 
 
 
 
619
  do_action( 'mc_delete_event', $event_id, $post_id );
620
  }
621
  $message = mc_show_notice( __( 'Event deleted successfully', 'my-calendar' ), false, 'event-deleted' );
@@ -631,9 +763,9 @@ function mc_delete_event( $event_id ) {
631
  /**
632
  * Get form data for an event ID
633
  *
634
- * @param mixed int/boolean $event_id My Calendar event ID or false if submission had errors.
635
  *
636
- * @return mixed array/object submitted or saved data
637
  */
638
  function mc_form_data( $event_id = false ) {
639
  global $wpdb, $submission;
@@ -659,10 +791,10 @@ function mc_form_data( $event_id = false ) {
659
  /**
660
  * The event edit form for the manage events admin page
661
  *
662
- * @param string $mode add, edit, or copy.
663
- * @param mixed int/boolean $event_id My Calendar event ID (false for new events).
664
  *
665
- * @return string HTML form
666
  */
667
  function mc_edit_event_form( $mode = 'add', $event_id = false ) {
668
  global $submission;
@@ -679,6 +811,17 @@ function mc_edit_event_form( $mode = 'add', $event_id = false ) {
679
  $data = $submission;
680
  }
681
 
 
 
 
 
 
 
 
 
 
 
 
682
  apply_filters( 'mc_event_notices', '', $data, $event_id );
683
 
684
  if ( is_object( $data ) && 1 !== (int) $data->event_approved && 'edit' === $mode ) {
@@ -697,7 +840,7 @@ function mc_edit_event_form( $mode = 'add', $event_id = false ) {
697
  *
698
  * @param string $field Name of field group.
699
  *
700
- * @return string.
701
  */
702
  function mc_show_edit_block( $field ) {
703
  $admin = ( 'true' === get_option( 'mc_input_options_administrators' ) && current_user_can( 'manage_options' ) ) ? true : false;
@@ -779,8 +922,6 @@ function mc_edit_block_is_visible( $field ) {
779
  return true;
780
  }
781
  }
782
-
783
- return false;
784
  }
785
 
786
  /**
@@ -821,7 +962,17 @@ function mc_datepicker_html( $args ) {
821
  $value = isset( $args['value'] ) ? esc_attr( $args['value'] ) : '';
822
  $required = isset( $args['required'] ) ? 'required' : '';
823
  $output = "<duet-date-picker first-day-of-week='$firstday' identifier='$id' name='$name' value='$value' $required></duet-date-picker><input type='date' id='$id' name='$name' value='$value' $required class='duet-fallback' />";
824
- $output = apply_filters( 'mc_datepicker_html', $output, $args );
 
 
 
 
 
 
 
 
 
 
825
 
826
  return $output;
827
  }
@@ -829,12 +980,12 @@ function mc_datepicker_html( $args ) {
829
  /**
830
  * Show a block of enabled fields.
831
  *
832
- * @param string $field name of field group.
833
- * @param boolean $has_data Whether fields have data.
834
- * @param mixed array/object $data Current data.
835
- * @param boolean $echo whether to return or echo.
836
- * @param string $default Default string value.
837
- * @param int $group_id If in group editing, group ID.
838
  *
839
  * @return string.
840
  */
@@ -880,7 +1031,18 @@ function mc_show_block( $field, $has_data, $data, $echo = true, $default = '', $
880
  if ( $show_block ) {
881
  global $current_screen;
882
  // Because wp_editor cannot return a value, event_desc fields cannot be filtered if its enabled.
883
- $value = ( $has_data ) ? stripslashes( $data->event_desc ) : '';
 
 
 
 
 
 
 
 
 
 
 
884
  $custom_editor = apply_filters( 'mc_custom_content_editor', false, $value, $data );
885
  if ( false !== $custom_editor ) {
886
  $return = $custom_editor;
@@ -925,6 +1087,7 @@ function mc_show_block( $field, $has_data, $data, $echo = true, $default = '', $
925
  if ( $show_block ) {
926
  $button_text = __( 'Select Featured Image', 'my-calendar' );
927
  $remove = '';
 
928
  if ( '' !== $image ) {
929
  $alt = ( $image_id ) ? get_post_meta( $image_id, '_wp_attachment_image_alt', true ) : '';
930
  $button_text = __( 'Change Featured Image', 'my-calendar' );
@@ -1090,13 +1253,38 @@ function mc_show_block( $field, $has_data, $data, $echo = true, $default = '', $
1090
  break;
1091
  case 'event_access':
1092
  if ( $show_block ) {
1093
- $label = __( 'Accessibility', 'my-calendar' );
1094
- $return = $pre . '<h2>' . $label . '</h2><div class="inside">' . mc_event_accessibility( '', $data, $label ) . apply_filters( 'mc_event_access_fields', '', $has_data, $data ) . '</div>' . $post;
 
 
 
 
 
 
 
 
 
 
 
 
1095
  }
1096
  break;
1097
  case 'event_open':
1098
  if ( $show_block ) {
1099
- $return = $pre . '<h2>' . __( 'Registration Settings', 'my-calendar' ) . '</h2><div class="inside"><fieldset><legend class="screen-reader-text">' . __( 'Event Registration', 'my-calendar' ) . '</legend>' . apply_filters( 'mc_event_registration', '', $has_data, $data, 'admin' ) . '</fieldset></div>' . $post;
 
 
 
 
 
 
 
 
 
 
 
 
 
1100
  } else {
1101
  $tickets = ( $has_data ) ? esc_url( $data->event_tickets ) : '';
1102
  $registration = ( $has_data ) ? esc_attr( $data->event_registration ) : '';
@@ -1130,12 +1318,24 @@ function mc_show_block( $field, $has_data, $data, $echo = true, $default = '', $
1130
  default:
1131
  return;
1132
  }
 
 
 
 
 
 
 
 
 
 
 
 
1133
  $return = apply_filters( 'mc_show_block', $return, $data, $field, $has_data );
1134
  if ( true === $echo ) {
1135
  echo $return;
1136
- } else {
1137
- return $return;
1138
  }
 
 
1139
  }
1140
 
1141
  /**
@@ -1184,22 +1384,24 @@ function mc_additional_dates( $data ) {
1184
  /**
1185
  * Display all enabled form fields.
1186
  *
1187
- * @param mixed array/object $data Passed data.
1188
- * @param string $mode Copy/edit/add.
1189
- * @param int $event_id Event ID.
1190
  */
1191
  function mc_form_fields( $data, $mode, $event_id ) {
1192
  global $wpdb, $user_ID;
1193
- $has_data = ( empty( $data ) ) ? false : true;
1194
  if ( $data ) {
1195
  // This was previously only shown if $data was an object. Don't know why.
1196
  $test = mc_test_occurrence_overlap( $data );
1197
  }
1198
  $instance = ( isset( $_GET['date'] ) ) ? (int) $_GET['date'] : false;
1199
  if ( $instance ) {
1200
- $ins = mc_get_instance_data( $instance );
1201
- $event_id = $ins->occur_event_id;
1202
- $data = mc_get_first_event( $event_id );
 
 
1203
  }
1204
  ?>
1205
  <div class="postbox-container jcd-wide">
@@ -1216,6 +1418,16 @@ function mc_form_fields( $data, $mode, $event_id ) {
1216
  $query_args['date'] = $instance;
1217
  }
1218
  }
 
 
 
 
 
 
 
 
 
 
1219
  echo apply_filters( 'mc_before_event_form', '', $event_id );
1220
  $action = add_query_arg( $query_args, admin_url( 'admin.php?page=my-calendar' ) );
1221
  $group_id = ( ! empty( $data->event_group_id ) && 'copy' !== $mode ) ? $data->event_group_id : mc_group_id();
@@ -1267,6 +1479,7 @@ function mc_form_fields( $data, $mode, $event_id ) {
1267
  $deleted = get_post_meta( $post_id, '_mc_deleted_instances', true );
1268
  $custom = get_post_meta( $post_id, '_mc_custom_instances', true );
1269
  if ( $deleted || $custom ) {
 
1270
  if ( $deleted ) {
1271
  $notice = __( 'Some dates in this event have been deleted.', 'my-calendar' );
1272
  }
@@ -1297,7 +1510,7 @@ function mc_form_fields( $data, $mode, $event_id ) {
1297
  <legend class="screen-reader-text"><?php esc_html_e( 'Event', 'my-calendar' ); ?></legend>
1298
  <p>
1299
  <label for="e_title"><?php esc_html_e( 'Event Title', 'my-calendar' ); ?></label><br/>
1300
- <input type="text" id="e_title" name="event_title" size="50" maxlength="255" value="<?php echo ( $has_data ) ? apply_filters( 'mc_manage_event_title', stripslashes( esc_attr( $data->event_title ) ), $data ) : ''; ?>" />
1301
  </p>
1302
  <?php
1303
  if ( is_object( $data ) && 1 === (int) $data->event_flagged ) {
@@ -1311,6 +1524,17 @@ function mc_form_fields( $data, $mode, $event_id ) {
1311
  </div>
1312
  <?php
1313
  }
 
 
 
 
 
 
 
 
 
 
 
1314
  apply_filters( 'mc_insert_custom_fields', '', $has_data, $data );
1315
 
1316
  if ( function_exists( 'wpt_post_to_twitter' ) && current_user_can( 'wpt_can_tweet' ) ) {
@@ -1355,6 +1579,18 @@ function mc_form_fields( $data, $mode, $event_id ) {
1355
  <legend class="screen-reader-text"><?php esc_html_e( 'Event Date and Time', 'my-calendar' ); ?></legend>
1356
  <div id="e_schedule">
1357
  <?php
 
 
 
 
 
 
 
 
 
 
 
 
1358
  echo apply_filters( 'mc_datetime_inputs', '', $has_data, $data, 'admin' );
1359
  if ( 'edit' !== $mode ) {
1360
  $span_checked = '';
@@ -1431,6 +1667,18 @@ function mc_form_fields( $data, $mode, $event_id ) {
1431
  </div>
1432
  <?php
1433
  }
 
 
 
 
 
 
 
 
 
 
 
 
1434
  $custom_fields = apply_filters( 'mc_event_details', '', $has_data, $data, 'admin' );
1435
  if ( '' !== $custom_fields ) {
1436
  ?>
@@ -1438,7 +1686,7 @@ function mc_form_fields( $data, $mode, $event_id ) {
1438
  <div class="postbox">
1439
  <h2><?php esc_html_e( 'Event Custom Fields', 'my-calendar' ); ?></h2>
1440
  <div class="inside">
1441
- <?php echo apply_filters( 'mc_event_details', '', $has_data, $data, 'admin' ); ?>
1442
  </div>
1443
  </div>
1444
  </div>
@@ -1549,6 +1797,15 @@ function mc_event_location_dropdown_block( $data ) {
1549
  $fields = '';
1550
  $autocomplete = false;
1551
  $count = mc_count_locations();
 
 
 
 
 
 
 
 
 
1552
  if ( $count > apply_filters( 'mc_convert_locations_select_to_autocomplete', 90 ) ) {
1553
  $autocomplete = true;
1554
  }
@@ -1569,7 +1826,11 @@ function mc_event_location_dropdown_block( $data ) {
1569
  foreach ( $locs as $loc ) {
1570
  if ( is_object( $loc ) ) {
1571
  $base_loc = strip_tags( stripslashes( $loc->location_label ), mc_strip_tags() );
1572
- $selected = ( is_numeric( get_option( 'mc_default_location' ) ) && (int) get_option( 'mc_default_location' ) === (int) $loc->location_id ) ? ' selected="selected"' : '';
 
 
 
 
1573
  if ( (int) $loc->location_id === (int) $event_location ) {
1574
  $location_link = ( current_user_can( 'mc_edit_locations' ) ) ? add_query_arg(
1575
  array(
@@ -1579,7 +1840,7 @@ function mc_event_location_dropdown_block( $data ) {
1579
  admin_url( 'admin.php?page=my-calendar-locations' )
1580
  ) : false;
1581
  // Translators: name of currently selected location.
1582
- $loc_name = ( $location_link ) ? '<a href="' . esc_url( $location_link ) . '" target="blank">' . sprintf( __( 'Edit %s', 'my-calendar' ), $base_loc ) . ' (' . __( 'Opens in new tab', 'my-calendar' ) . ')</a>' : $base_loc;
1583
  // Translators: Link to edit current location, e.g. 'Edit %s'.
1584
  $current_location = "<div id='mc-current-location'><span class='dashicons dashicons-location' aria-hidden='true'></span>" . sprintf( __( 'Current location: %s', 'my-calendar' ), $loc_name ) . '</div>';
1585
  $current_location .= "<input type='hidden' name='preset_location' value='$event_location' />";
@@ -1615,6 +1876,15 @@ function mc_event_location_dropdown_block( $data ) {
1615
  * @return array
1616
  */
1617
  function mc_event_access() {
 
 
 
 
 
 
 
 
 
1618
  $event_access = apply_filters(
1619
  'mc_event_access_choices',
1620
  array(
@@ -1639,9 +1909,9 @@ function mc_event_access() {
1639
  /**
1640
  * Form to select accessibility features.
1641
  *
1642
- * @param string $form Form HTML.
1643
- * @param mixed array/object $data Event data.
1644
- * @param string $label Primary label for fields.
1645
  */
1646
  function mc_event_accessibility( $form, $data, $label ) {
1647
  $note_value = '';
@@ -1651,7 +1921,7 @@ function mc_event_accessibility( $form, $data, $label ) {
1651
  <fieldset class='accessibility'>
1652
  <legend class='$class'>$label</legend>
1653
  <ul class='accessibility-features checkboxes'>";
1654
- $access = apply_filters( 'mc_event_accessibility', mc_event_access() );
1655
  if ( ! empty( $data ) ) {
1656
  if ( property_exists( $data, 'event_post' ) ) {
1657
  $events_access = get_post_meta( $data->event_post, '_mc_event_access', true );
@@ -1691,7 +1961,18 @@ function mc_event_accessibility( $form, $data, $label ) {
1691
  */
1692
  function mc_check_data( $action, $post, $i, $ignore_required = false ) {
1693
  global $wpdb, $submission;
1694
- $user = wp_get_current_user();
 
 
 
 
 
 
 
 
 
 
 
1695
  $post = apply_filters( 'mc_pre_checkdata', $post, $action, $i );
1696
  $submit = array();
1697
  $errors = '';
@@ -1732,6 +2013,12 @@ function mc_check_data( $action, $post, $i, $ignore_required = false ) {
1732
  $event_longitude = '';
1733
  $event_latitude = '';
1734
  $event_location = '';
 
 
 
 
 
 
1735
 
1736
  if ( version_compare( PHP_VERSION, '7.4', '<' ) && get_magic_quotes_gpc() ) { //phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.get_magic_quotes_gpcDeprecated
1737
  $post = array_map( 'stripslashes_deep', $post );
@@ -1757,7 +2044,9 @@ function mc_check_data( $action, $post, $i, $ignore_required = false ) {
1757
  // ...AND there's no reason to allow it, since weekday events will NEVER happen on the weekend.
1758
  $begin = trim( $post['event_begin'][ $i ] );
1759
  $end = ( ! empty( $post['event_end'] ) ) ? trim( $post['event_end'][ $i ] ) : $post['event_begin'][ $i ];
1760
- if ( 'E' === $recur && '0' === ( mc_date( 'w', mc_strtotime( $begin ), false ) || '6' === mc_date( 'w', mc_strtotime( $begin ), false ) ) ) {
 
 
1761
  if ( 0 === (int) mc_date( 'w', mc_strtotime( $begin ), false ) ) {
1762
  $newbegin = my_calendar_add_date( $begin, 1 );
1763
  if ( ! empty( $post['event_end'][ $i ] ) ) {
@@ -1783,6 +2072,15 @@ function mc_check_data( $action, $post, $i, $ignore_required = false ) {
1783
  $begin = mc_date( 'Y-m-d', mc_strtotime( $begin ), false );// regardless of entry format, convert.
1784
  $time = ! empty( $post['event_time'][ $i ] ) ? trim( $post['event_time'][ $i ] ) : '';
1785
  if ( '' !== $time ) {
 
 
 
 
 
 
 
 
 
1786
  $default_modifier = apply_filters( 'mc_default_event_length', '1 hour' );
1787
  $endtime = ! empty( $post['event_endtime'][ $i ] ) ? trim( $post['event_endtime'][ $i ] ) : mc_date( 'H:i:s', mc_strtotime( $time . ' +' . $default_modifier ), false );
1788
  if ( empty( $post['event_endtime'][ $i ] ) && mc_date( 'H', mc_strtotime( $endtime ), false ) === '00' ) {
@@ -1838,6 +2136,17 @@ function mc_check_data( $action, $post, $i, $ignore_required = false ) {
1838
  $cats = array( $default );
1839
  $primary = $default;
1840
  }
 
 
 
 
 
 
 
 
 
 
 
1841
  $primary = apply_filters( 'mc_set_primary_category', $primary, $cats, $post );
1842
  $event_author = ( isset( $post['event_author'] ) && is_numeric( $post['event_author'] ) ) ? $post['event_author'] : 0;
1843
  $event_link = ! empty( $post['event_link'] ) ? trim( $post['event_link'] ) : '';
@@ -1925,6 +2234,15 @@ function mc_check_data( $action, $post, $i, $ignore_required = false ) {
1925
  );
1926
  $loc_id = mc_insert_location( $add_loc );
1927
  $event_location = $loc_id;
 
 
 
 
 
 
 
 
 
1928
  do_action( 'mc_save_location', $loc_id, $add_loc, $add_loc );
1929
  }
1930
  }
@@ -1973,6 +2291,16 @@ function mc_check_data( $action, $post, $i, $ignore_required = false ) {
1973
  }
1974
  if ( isset( $post['mcs_check_conflicts'] ) ) {
1975
  $conflicts = mcs_check_conflicts( $begin, $time, $end, $endtime, $event_label );
 
 
 
 
 
 
 
 
 
 
1976
  $conflicts = apply_filters( 'mcs_check_conflicts', $conflicts, $post );
1977
  if ( $conflicts ) {
1978
  $conflict_id = $conflicts[0]->occur_id;
@@ -1984,7 +2312,7 @@ function mc_check_data( $action, $post, $i, $ignore_required = false ) {
1984
  } else {
1985
  if ( mc_can_edit_event( $conflict_ev->event_id ) ) {
1986
  $referer = urlencode( mc_get_current_url() );
1987
- $link = admin_url( "admin.php?page=my-calendar&amp;mode=edit&amp;event_id=$event->event_id&amp;ref=$referer" );
1988
  // Translators: Link to edit event draft.
1989
  $error = sprintf( __( 'That event conflicts with a <a href="%s">previously submitted draft</a>.', 'my-calendar' ), $link );
1990
  } else {
@@ -2061,6 +2389,17 @@ function mc_check_data( $action, $post, $i, $ignore_required = false ) {
2061
  // Array: removed before DB insertion.
2062
  'event_categories' => $cats,
2063
  );
 
 
 
 
 
 
 
 
 
 
 
2064
  $errors = ( $ignore_required ) ? $errors : apply_filters( 'mc_fields_required', $errors, $submit );
2065
 
2066
  if ( '' === $errors ) {
@@ -2167,8 +2506,8 @@ function mc_update_instance( $event_instance, $event_id, $update = array() ) {
2167
  if ( ! empty( $update ) ) {
2168
  $event = mc_get_event( $event_instance );
2169
  $formats = array( '%d', '%s', '%s', '%d' );
2170
- $begin = ( ! empty( $update ) ) ? $update['event_begin'] . ' ' . $update['event_time'] : $event->occur_begin;
2171
- $end = ( ! empty( $update ) ) ? $update['event_end'] . ' ' . $update['event_endtime'] : $event->occur_end;
2172
  $data = array(
2173
  'occur_event_id' => $event_id,
2174
  'occur_begin' => $begin,
@@ -2197,7 +2536,7 @@ function mc_update_instance( $event_instance, $event_id, $update = array() ) {
2197
  * @param mixed $value required value for field.
2198
  * @param string $format type of data format.
2199
  *
2200
- * @return mixed boolean/int $result Success condition
2201
  */
2202
  function mc_update_data( $event_id, $field, $value, $format = '%d' ) {
2203
  global $wpdb;
@@ -2264,12 +2603,30 @@ function mc_standard_datetime_input( $form, $has_data, $data, $instance, $contex
2264
  'name' => 'event_end[]',
2265
  );
2266
  $picker_end = mc_datepicker_html( $args );
2267
- $max = apply_filters( 'mc_time_max', '00:00' );
2268
- $min = apply_filters( 'mc_time_min', '00:00' );
2269
- $attrs = ( '00:00' !== $min || '00:00' !== $max ) ? ' max="' . $max . '" min="' . $min . '"' : '';
2270
- $append = '';
2271
- $range = '';
2272
- $aria = '';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2273
  if ( '00:00' !== $max || '00:00' !== $min ) {
2274
  // Translators: starting time, ending time.
2275
  $range = '<p id="mc_time_range_allowed">' . sprintf( __( 'Times must be between %1$s and %2$s', 'my-calendar' ), mc_date( mc_time_format(), strtotime( $min ) ), mc_date( mc_time_format(), strtotime( $max ) ) ) . '</p>';
@@ -2440,6 +2797,18 @@ function mc_standard_event_registration( $form, $has_data, $data, $context = 'ad
2440
  <label for='event_registration'>" . __( 'Registration Information', 'my-calendar' ) . "</label> <textarea name='event_registration'id='event_registration'cols='40'rows='4'/>$registration</textarea>
2441
  </p>";
2442
 
 
 
 
 
 
 
 
 
 
 
 
 
2443
  return apply_filters( 'mc_event_registration_form', $form, $has_data, $data, 'admin' );
2444
  }
2445
 
@@ -2491,6 +2860,15 @@ function mc_controls( $mode, $has_data, $event, $position = 'header' ) {
2491
  }
2492
  }
2493
  $controls['delete'] = "<span class='dashicons dashicons-no' aria-hidden='true'></span><a href='" . admin_url( "admin.php?page=my-calendar-manage&amp;mode=delete&amp;event_id=$event_id$args" ) . "' class='delete'>" . __( 'Delete', 'my-calendar' ) . '</a>';
 
 
 
 
 
 
 
 
 
2494
  if ( 'true' === apply_filters( 'mc_use_permalinks', get_option( 'mc_use_permalinks' ) ) ) {
2495
  $post_id = $event->event_post;
2496
  $post_link = ( $post_id ) ? get_edit_post_link( $post_id ) : false;
@@ -2582,7 +2960,7 @@ function mc_grouped_events( $id, $template = '' ) {
2582
  $output = '';
2583
 
2584
  $results = mc_get_grouped_events( $id );
2585
- if ( is_array( $results ) && ! empty( $results ) ) {
2586
  foreach ( $results as $result ) {
2587
  $first = mc_get_first_event( $result->event_id );
2588
  if ( ! is_object( $first ) ) {
@@ -2598,7 +2976,7 @@ function mc_grouped_events( $id, $template = '' ) {
2598
  'end' => $close,
2599
  );
2600
 
2601
- $current_output = ( '' === $template ) ? $current . $begin . $end : mc_draw_template( $array, $template );
2602
  $output .= "<li>$current_output</li>";
2603
  }
2604
  } else {
@@ -2685,11 +3063,13 @@ function mc_reuse_id_format( $format, $begin, $instances ) {
2685
  * @param boolean $test true if testing.
2686
  * @param array $instances When rebuilding, an array of all prior event dates & ids.
2687
  *
2688
- * @return null by default; data array if testing
2689
  */
2690
  function mc_increment_event( $id, $post = array(), $test = false, $instances = array() ) {
2691
- global $wpdb;
2692
- $event = mc_get_event_core( $id, true );
 
 
2693
  $data = array();
2694
  $return = array();
2695
  if ( empty( $post ) ) {
@@ -2708,7 +3088,6 @@ function mc_increment_event( $id, $post = array(), $test = false, $instances = a
2708
  $end_diff = strtotime( gmdate( 'Y-m-d H:i:s', strtotime( $orig_end ) ) ) - strtotime( gmdate( 'Y-m-d', strtotime( $orig_end ) ) );
2709
 
2710
  $group_id = $event->event_group_id;
2711
- $format = array( '%d', '%s', '%s', '%d' );
2712
  $recurs = str_split( $event->event_recur, 1 );
2713
  $recur = $recurs[0];
2714
  // Can't use 2nd value directly if it's two digits.
@@ -2741,7 +3120,7 @@ function mc_increment_event( $id, $post = array(), $test = false, $instances = a
2741
  'occur_end' => mc_date( 'Y-m-d H:i:s', $end, false ),
2742
  'occur_group_id' => $group_id,
2743
  );
2744
- if ( 'test' === $test && $i > 0 ) {
2745
  return $data;
2746
  }
2747
  if ( $post_until ) {
@@ -2752,14 +3131,7 @@ function mc_increment_event( $id, $post = array(), $test = false, $instances = a
2752
  }
2753
  }
2754
  $return[] = $data;
2755
- if ( ! $test ) {
2756
- $insert = apply_filters( 'mc_insert_recurring', false, $data, $format, $id, 'daily' );
2757
- if ( ! $insert ) {
2758
- $data = apply_filters( 'mc_instance_data', $data, $begin, $instances );
2759
- $format = apply_filters( 'mc_instance_format', $format, $begin, $instances );
2760
- $wpdb->insert( my_calendar_event_table(), $data, $format );
2761
- }
2762
- }
2763
  }
2764
  break;
2765
  // Weekdays only.
@@ -2775,7 +3147,7 @@ function mc_increment_event( $id, $post = array(), $test = false, $instances = a
2775
  'occur_end' => mc_date( 'Y-m-d H:i:s', $end, false ),
2776
  'occur_group_id' => $group_id,
2777
  );
2778
- if ( 'test' === $test && $i > 0 ) {
2779
  return $data;
2780
  }
2781
  if ( $post_until ) {
@@ -2786,14 +3158,7 @@ function mc_increment_event( $id, $post = array(), $test = false, $instances = a
2786
  }
2787
  }
2788
  $return[] = $data;
2789
- if ( ! $test ) {
2790
- $insert = apply_filters( 'mc_insert_recurring', false, $data, $format, $id, 'daily' );
2791
- if ( ! $insert ) {
2792
- $data = apply_filters( 'mc_instance_data', $data, $begin, $instances );
2793
- $format = apply_filters( 'mc_instance_format', $format, $begin, $instances );
2794
- $wpdb->insert( my_calendar_event_table(), $data, $format );
2795
- }
2796
- }
2797
  }
2798
  break;
2799
  // Weekly.
@@ -2807,7 +3172,7 @@ function mc_increment_event( $id, $post = array(), $test = false, $instances = a
2807
  'occur_end' => mc_date( 'Y-m-d H:i:s', $end, false ),
2808
  'occur_group_id' => $group_id,
2809
  );
2810
- if ( 'test' === $test && $i > 0 ) {
2811
  return $data;
2812
  }
2813
  if ( $post_until ) {
@@ -2818,14 +3183,7 @@ function mc_increment_event( $id, $post = array(), $test = false, $instances = a
2818
  }
2819
  }
2820
  $return[] = $data;
2821
- if ( ! $test ) {
2822
- $insert = apply_filters( 'mc_insert_recurring', false, $data, $format, $id, 'weekly' );
2823
- if ( ! $insert ) {
2824
- $data = apply_filters( 'mc_instance_data', $data, $begin, $instances );
2825
- $format = apply_filters( 'mc_instance_format', $format, $begin, $instances );
2826
- $wpdb->insert( my_calendar_event_table(), $data, $format );
2827
- }
2828
- }
2829
  }
2830
  break;
2831
  // Biweekly.
@@ -2839,7 +3197,7 @@ function mc_increment_event( $id, $post = array(), $test = false, $instances = a
2839
  'occur_end' => mc_date( 'Y-m-d H:i:s', $end, false ),
2840
  'occur_group_id' => $group_id,
2841
  );
2842
- if ( 'test' === $test && $i > 0 ) {
2843
  return $data;
2844
  }
2845
  if ( $post_until ) {
@@ -2850,14 +3208,7 @@ function mc_increment_event( $id, $post = array(), $test = false, $instances = a
2850
  }
2851
  }
2852
  $return[] = $data;
2853
- if ( ! $test ) {
2854
- $insert = apply_filters( 'mc_insert_recurring', false, $data, $format, $id, 'biweekly' );
2855
- if ( ! $insert ) {
2856
- $data = apply_filters( 'mc_instance_data', $data, $begin, $instances );
2857
- $format = apply_filters( 'mc_instance_format', $format, $begin, $instances );
2858
- $wpdb->insert( my_calendar_event_table(), $data, $format );
2859
- }
2860
- }
2861
  }
2862
  break;
2863
  // Monthly by date.
@@ -2871,7 +3222,7 @@ function mc_increment_event( $id, $post = array(), $test = false, $instances = a
2871
  'occur_end' => mc_date( 'Y-m-d H:i:s', $end, false ),
2872
  'occur_group_id' => $group_id,
2873
  );
2874
- if ( 'test' === $test && $i > 0 ) {
2875
  return $data;
2876
  }
2877
  if ( $post_until ) {
@@ -2882,77 +3233,71 @@ function mc_increment_event( $id, $post = array(), $test = false, $instances = a
2882
  }
2883
  }
2884
  $return[] = $data;
2885
- if ( ! $test ) {
2886
- $insert = apply_filters( 'mc_insert_recurring', false, $data, $format, $id, 'monthly' );
2887
- if ( ! $insert ) {
2888
- $data = apply_filters( 'mc_instance_data', $data, $begin, $instances );
2889
- $format = apply_filters( 'mc_instance_format', $format, $begin, $instances );
2890
- $wpdb->insert( my_calendar_event_table(), $data, $format );
2891
- }
2892
- }
2893
  }
2894
  break;
2895
  // Monthly by day.
2896
  case 'U':
2897
- // Important to keep track of which date variables are strings and which are timestamps.
2898
  $week_of_event = mc_week_of_month( mc_date( 'd', strtotime( $event->event_begin ), false ) );
2899
  $newbegin = my_calendar_add_date( $orig_begin, 28, 0, 0 );
2900
  $newend = my_calendar_add_date( $orig_end, 28, 0, 0 );
2901
- $fifth_week = $event->event_fifth_week;
2902
  $data = array(
2903
  'occur_event_id' => $id,
2904
  'occur_begin' => mc_date( 'Y-m-d H:i:s', strtotime( $orig_begin ), false ),
2905
  'occur_end' => mc_date( 'Y-m-d H:i:s', strtotime( $orig_end ), false ),
2906
  'occur_group_id' => $group_id,
2907
  );
 
2908
 
2909
- if ( ! $test ) {
2910
- $insert = apply_filters( 'mc_insert_recurring', false, $data, $format, $id, 'month-by-day' );
2911
- if ( ! $insert ) {
2912
- $data = apply_filters( 'mc_instance_data', $data, strtotime( $orig_begin ), $instances );
2913
- $format = apply_filters( 'mc_instance_format', $format, strtotime( $orig_begin ), $instances );
2914
- $wpdb->insert( my_calendar_event_table(), $data, $format );
2915
- }
2916
- }
2917
  $numforward = ( $numforward - 1 );
2918
  for ( $i = 0; $i <= $numforward; $i ++ ) {
2919
- $next_week_diff = ( mc_date( 'm', $newbegin, false ) === mc_date( 'm', my_calendar_add_date( mc_date( 'Y-m-d', $newbegin, false ), 7, 0, 0 ) ) ) ? false : true;
2920
- $move_event = ( ( 1 === (int) $fifth_week ) && ( ( mc_week_of_month( mc_date( 'd', $newbegin ), false ) + 1 ) === (int) $week_of_event ) && true === $next_week_diff ) ? true : false;
2921
- if ( mc_week_of_month( mc_date( 'd', $newbegin, false ) ) === $week_of_event || true === $move_event ) {
 
 
 
2922
  } else {
 
 
 
2923
  $newbegin = my_calendar_add_date( mc_date( 'Y-m-d H:i:s', $newbegin, false ), 7, 0, 0 );
2924
  $newend = my_calendar_add_date( mc_date( 'Y-m-d H:i:s', $newend, false ), 7, 0, 0 );
2925
- $move_event = ( 1 === (int) $fifth_week && mc_week_of_month( mc_date( 'd', $newbegin ), false ) + 1 === (int) $week_of_event ) ? true : false;
2926
- if ( mc_week_of_month( mc_date( 'd', $newbegin, false ) ) === $week_of_event || true === $move_event ) {
 
 
2927
  } else {
2928
- $newbegin = my_calendar_add_date( mc_date( 'Y-m-d H:i:s', $newbegin, false ), 14, 0, 0 );
2929
- $newend = my_calendar_add_date( mc_date( 'Y-m-d H:i:s', $newend, false ), 14, 0, 0 );
 
 
2930
  }
2931
  }
2932
- $data = array(
2933
- 'occur_event_id' => $id,
2934
- 'occur_begin' => mc_date( 'Y-m-d H:i:s', $newbegin, false ),
2935
- 'occur_end' => mc_date( 'Y-m-d H:i:s', $newend, false ),
2936
- 'occur_group_id' => $group_id,
2937
- );
2938
- if ( 'test' === $test && $i > 0 ) {
2939
- return $data;
2940
- }
2941
- if ( $post_until ) {
2942
- if ( $newbegin <= strtotime( $post_until ) ) {
2943
- $numforward ++;
2944
- } else {
2945
- continue;
2946
  }
2947
- }
2948
- $return[] = $data;
2949
- if ( ! $test ) {
2950
- $insert = apply_filters( 'mc_insert_recurring', false, $data, $format, $id, 'month-by-day' );
2951
- if ( ! $insert ) {
2952
- $data = apply_filters( 'mc_instance_data', $data, $newbegin, $instances );
2953
- $format = apply_filters( 'mc_instance_format', $format, $newbegin, $instances );
2954
- $wpdb->insert( my_calendar_event_table(), $data, $format );
2955
  }
 
 
 
 
 
2956
  }
2957
  $newbegin = my_calendar_add_date( mc_date( 'Y-m-d H:i:s', $newbegin, false ), 28, 0, 0 );
2958
  $newend = my_calendar_add_date( mc_date( 'Y-m-d H:i:s', $newend, false ), 28, 0, 0 );
@@ -2969,7 +3314,7 @@ function mc_increment_event( $id, $post = array(), $test = false, $instances = a
2969
  'occur_end' => mc_date( 'Y-m-d H:i:s', $end, false ),
2970
  'occur_group_id' => $group_id,
2971
  );
2972
- if ( 'test' === $test && $i > 0 ) {
2973
  return $data;
2974
  }
2975
  if ( $post_until ) {
@@ -2980,14 +3325,7 @@ function mc_increment_event( $id, $post = array(), $test = false, $instances = a
2980
  }
2981
  }
2982
  $return[] = $data;
2983
- if ( ! $test ) {
2984
- $insert = apply_filters( 'mc_insert_recurring', false, $data, $format, $id, 'annual' );
2985
- if ( ! $insert ) {
2986
- $data = apply_filters( 'mc_instance_data', $data, $begin, $instances );
2987
- $format = apply_filters( 'mc_instance_format', $format, $begin, $instances );
2988
- $wpdb->insert( my_calendar_event_table(), $data, $format );
2989
- }
2990
- }
2991
  }
2992
  break;
2993
  }
@@ -3000,14 +3338,7 @@ function mc_increment_event( $id, $post = array(), $test = false, $instances = a
3000
  'occur_end' => mc_date( 'Y-m-d H:i:s', $end, false ),
3001
  'occur_group_id' => $group_id,
3002
  );
3003
- if ( ! $test ) {
3004
- $insert = apply_filters( 'mc_insert_recurring', false, $data, $format, $id, 'single' );
3005
- if ( ! $insert ) {
3006
- $data = apply_filters( 'mc_instance_data', $data, $begin, $instances );
3007
- $format = apply_filters( 'mc_instance_format', $format, $begin, $instances );
3008
- $wpdb->insert( my_calendar_event_table(), $data, $format );
3009
- }
3010
- }
3011
  }
3012
 
3013
  if ( true === $test ) {
@@ -3017,6 +3348,87 @@ function mc_increment_event( $id, $post = array(), $test = false, $instances = a
3017
  return $data;
3018
  }
3019
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3020
  /**
3021
  * Execute a refresh of the My Calendar primary URL cache if caching plug-in installed.
3022
  *
@@ -3026,7 +3438,20 @@ function mc_increment_event( $id, $post = array(), $test = false, $instances = a
3026
  * @param int $result Result of calendar save query.
3027
  */
3028
  function mc_refresh_cache( $action, $data, $event_id, $result ) {
3029
- $mc_uri_id = ( get_option( 'mc_uri_id' ) ) ? get_option( 'mc_uri_id' ) : false;
 
 
 
 
 
 
 
 
 
 
 
 
 
3030
  $to_refresh = apply_filters( 'mc_cached_pages_to_refresh', array( $mc_uri_id ), $action, $data, $event_id, $result );
3031
 
3032
  foreach ( $to_refresh as $calendar ) {
21
  * @param int $event_id My Calendar event ID.
22
  * @param bool|int $result Results of DB query.
23
  *
24
+ * @return int|false post ID or false if post ID not available or created.
25
  */
26
  function mc_event_post( $action, $data, $event_id, $result = false ) {
27
  // if the event save was successful.
28
+ $post_id = false;
29
  if ( 'add' === $action || 'copy' === $action ) {
30
  $post_id = mc_create_event_post( $data, $event_id );
31
  } elseif ( 'edit' === $action ) {
63
  $terms[] = (int) $term;
64
  }
65
 
66
+ $title = $data['event_title'];
67
+ /**
68
+ * Filter post template when event post is generated.
69
+ *
70
+ * @hook mc_post_template
71
+ *
72
+ * @param {string} $template Template keyword or structure.
73
+ * @param {array} $terms Terms attached to this event.
74
+ *
75
+ * @return {string}
76
+ */
77
  $template = apply_filters( 'mc_post_template', 'details', $terms );
78
  $data['shortcode'] = "[my_calendar_event event='$event_id' template='$template' list='']";
79
  $description = $data['event_desc'];
110
  $access_terms = implode( ',', array_values( $access ) );
111
  mc_update_event( 'event_access', $access_terms, $event_id, '%s' );
112
  mc_add_post_meta_data( $post_id, $_POST, $data, $event_id );
113
+ /**
114
+ * Execute action when an event's post is updated.
115
+ *
116
+ * @hook mc_update_event_post
117
+ *
118
+ * @param {int} $post_id Post ID.
119
+ * @param {array} $_POST POST data.
120
+ * @param {array} $data Submitted event data.
121
+ * @param {int} $event_id Event ID.
122
+ */
123
  do_action( 'mc_update_event_post', $post_id, $_POST, $data, $event_id );
124
  if ( mc_switch_sites() ) {
125
  restore_current_blog();
210
  }
211
  $terms[] = (int) $term;
212
  }
213
+ $title = $data['event_title'];
214
+ /**
215
+ * Filter post template when event post is generated.
216
+ *
217
+ * @hook mc_post_template
218
+ *
219
+ * @param {string} $template Template keyword or structure.
220
+ * @param {array} $terms Terms attached to this event.
221
+ *
222
+ * @return {string}
223
+ */
224
+ $template = apply_filters( 'mc_post_template', 'details', $terms );
225
  $data['shortcode'] = "[my_calendar_event event='$event_id' template='$template' list='']";
226
  $description = isset( $data['event_desc'] ) ? $data['event_desc'] : '';
227
  $excerpt = isset( $data['event_short'] ) ? $data['event_short'] : '';
258
  mc_update_event( 'event_post', $post_id, $event_id );
259
  mc_update_event( 'event_location', $location_id, $event_id );
260
  mc_add_post_meta_data( $post_id, $_POST, $data, $event_id );
261
+ /**
262
+ * Execute action when an event's post is updated.
263
+ *
264
+ * @hook mc_update_event_post
265
+ *
266
+ * @param {int} $post_id Post ID.
267
+ * @param {array} $_POST POST data.
268
+ * @param {array} $data Submitted event data.
269
+ * @param {int} $event_id Event ID.
270
+ */
271
  do_action( 'mc_update_event_post', $post_id, $_POST, $data, $event_id );
272
  wp_publish_post( $post_id );
273
  }
303
  * @param int $post_id Post ID.
304
  */
305
  function mc_event_delete_post( $event_id, $post_id ) {
306
+ /**
307
+ * Execute action when an event's post is deleted.
308
+ *
309
+ * @hook mc_deleted_post
310
+ *
311
+ * @param {int} $event_id Event ID.
312
+ * @param {int} $post_id Post ID.
313
+ */
314
  do_action( 'mc_deleted_post', $event_id, $post_id );
315
  wp_delete_post( $post_id, true );
316
  }
318
  /**
319
  * Update a single field in an event.
320
  *
321
+ * @param string $field database column.
322
+ * @param mixed $data value to be saved.
323
+ * @param string|integer $event could be integer or string.
324
+ * @param string $type signifier representing data type of $data (e.g. %d or %s).
325
  *
326
+ * @return int Number of rows affected.
327
  */
328
  function mc_update_event( $field, $data, $event, $type = '%d' ) {
329
  global $wpdb;
359
  if ( isset( $_POST['event_action'] ) ) {
360
  $nonce = $_REQUEST['_wpnonce'];
361
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
362
+ wp_die( 'My Calendar: Security check failed' );
363
  }
364
 
365
  global $mc_output;
431
  /**
432
  * Save an event to the database
433
  *
434
+ * @param string $action Type of action.
435
+ * @param array $output Checked event data.
436
+ * @param int}boolean $event_id Event ID or false for new events.
437
  *
438
+ * @return array Array with event_id and message keys.
439
  */
440
  function my_calendar_save( $action, $output, $event_id = false ) {
441
  global $wpdb;
448
  $cats = $add['event_categories'];
449
 
450
  unset( $add['event_categories'] );
451
+ /**
452
+ * Filter updated event data prior to saving.
453
+ *
454
+ * @hook mc_before_save_insert
455
+ *
456
+ * @param {array} $add Newly added event data.
457
+ *
458
+ * @return {array}
459
+ */
460
  $add = apply_filters( 'mc_before_save_insert', $add );
461
  $result = $wpdb->insert( my_calendar_table(), $add, $formats );
462
  $event_id = $wpdb->insert_id;
469
  $data = $add;
470
  $event_error = '';
471
  $event_link = '';
472
+ $edit_link = '';
473
  mc_event_post( $action, $data, $event_id, $result );
474
+ /**
475
+ * Run action when an event is saved.
476
+ *
477
+ * @hook mc_save_event
478
+ *
479
+ * @param {string} $action Current action: edit, copy, add.
480
+ * @param {array} $data Data updated.
481
+ * @param {int} $event_id Event ID.
482
+ * @param {int|false} $result Result of the DB update query.
483
+ */
484
  do_action( 'mc_save_event', $action, $data, $event_id, $result );
485
 
486
  if ( 'true' === get_option( 'mc_event_mail' ) ) {
487
  // insert_id is last occurrence inserted in the db.
488
  $event = mc_get_first_event( $event_id );
489
  if ( 1 === (int) $event->event_flagged ) {
490
+ /**
491
+ * Action executed when an event is marked as spam and eemail notifications are enabled.
492
+ *
493
+ * @hook mc_notify_event_spam
494
+ *
495
+ * @param {object} $event Event object.
496
+ */
497
  do_action( 'mc_notify_event_spam', $event );
498
  } else {
499
  my_calendar_send_email( $event );
552
  unset( $update['event_categories'] );
553
  mc_update_category_relationships( $cats, $event_id );
554
 
555
+ /**
556
+ * Filter updated event data prior to saving.
557
+ *
558
+ * @hook mc_before_save_update
559
+ *
560
+ * @param {array} $update Updated event data.
561
+ * @param {int} $event_id Event ID.
562
+ *
563
+ * @return {array}
564
+ */
565
  $update = apply_filters( 'mc_before_save_update', $update, $event_id );
566
  $endtime = mc_date( 'H:i:00', mc_strtotime( $update['event_endtime'] ), false );
567
  $prev_eb = ( isset( $_POST['prev_event_begin'] ) ) ? $_POST['prev_event_begin'] : '';
636
  }
637
  $data = $update;
638
  mc_event_post( $action, $data, $event_id, $result );
639
+ /**
640
+ * Run action when an event is saved.
641
+ *
642
+ * @hook mc_save_event
643
+ *
644
+ * @param {string} $action Current action: edit, copy, add.
645
+ * @param {array} $data Data updated.
646
+ * @param {int} $event_id Event ID.
647
+ * @param {int|false} $result Result of the DB update query.
648
+ */
649
  do_action( 'mc_save_event', $action, $data, $event_id, $result );
650
  if ( false === $result ) {
651
  $message = mc_show_error( __( 'Your event was not updated.', 'my-calendar' ) . " $url", false );
652
  } else {
653
  // do an action using the $action and processed event data.
654
+ $new_event_status = ( current_user_can( 'mc_approve_events' ) ) ? 1 : 0;
655
  // check for event_approved provides support for older versions of My Calendar Pro.
656
+ if ( isset( $_POST['event_approved'] ) && $_POST['event_approved'] !== $new_event_status ) {
657
+ $new_event_status = absint( $_POST['event_approved'] );
658
  }
659
  if ( isset( $_POST['prev_event_status'] ) ) {
660
+ /**
661
+ * Execute an action when an event changes status.
662
+ *
663
+ * @hook mc_transition_event
664
+ *
665
+ * @param {int} $prev_event_status Previous status.
666
+ * @param {int} $new_event_status New status.
667
+ * @param {string} $action Action being performed.
668
+ * @param {array} $data Submitted event data.
669
+ * @param {int} $event_id Event ID.
670
+ */
671
+ do_action( 'mc_transition_event', (int) $_POST['prev_event_status'], $new_event_status, $action, $data, $event_id );
672
  }
673
  $message = mc_show_notice( __( 'Event updated successfully', 'my-calendar' ) . ". $url", false, 'event-updated' );
674
  }
684
  );
685
  mc_update_count_cache();
686
 
687
+ /**
688
+ * Filter message returned to user after saving an event.
689
+ *
690
+ * @hook mc_event_saved_message
691
+ *
692
+ * @param {array} $saved_response Array with event ID and message text.
693
+ *
694
+ * @return {array}
695
+ */
696
  return apply_filters( 'mc_event_saved_message', $saved_response );
697
  }
698
 
729
  if ( empty( $result ) || empty( $result[0]->event_id ) ) {
730
  // Do an action using the event_id.
731
  if ( $instance ) {
732
+ /**
733
+ * Action run when a single date of an event is deleted.
734
+ *
735
+ * @hook mc_delete_event_instance
736
+ *
737
+ * @param {int} $event_id Event ID.
738
+ * @param {int} $post_id Post ID.
739
+ * @param {int} $event_in Event instance ID.
740
+ */
741
  do_action( 'mc_delete_event_instance', $event_id, $post_id, $event_in );
742
  } else {
743
+ /**
744
+ * Action run when an event is deleted.
745
+ *
746
+ * @hook mc_delete_event
747
+ *
748
+ * @param {int} $event_id Event ID.
749
+ * @param {int} $post_id Event Post ID.
750
+ */
751
  do_action( 'mc_delete_event', $event_id, $post_id );
752
  }
753
  $message = mc_show_notice( __( 'Event deleted successfully', 'my-calendar' ), false, 'event-deleted' );
763
  /**
764
  * Get form data for an event ID
765
  *
766
+ * @param int|false $event_id My Calendar event ID or false if submission had errors.
767
  *
768
+ * @return array|object|string submitted or saved data. Error message string if event not found.
769
  */
770
  function mc_form_data( $event_id = false ) {
771
  global $wpdb, $submission;
791
  /**
792
  * The event edit form for the manage events admin page
793
  *
794
+ * @param string $mode add, edit, or copy.
795
+ * @param int|false $event_id My Calendar event ID (false for new events).
796
  *
797
+ * @return void
798
  */
799
  function mc_edit_event_form( $mode = 'add', $event_id = false ) {
800
  global $submission;
811
  $data = $submission;
812
  }
813
 
814
+ /**
815
+ * Filter notices. This is really handled and used as an action, but is still labeled a filter for backwards compatibility.
816
+ *
817
+ * @hook mc_event_notices
818
+ *
819
+ * @param {string} $output Output (unused).
820
+ * @param {object} $data Event object.
821
+ * @param {int} $event_id Event ID.
822
+ *
823
+ * @return void
824
+ */
825
  apply_filters( 'mc_event_notices', '', $data, $event_id );
826
 
827
  if ( is_object( $data ) && 1 !== (int) $data->event_approved && 'edit' === $mode ) {
840
  *
841
  * @param string $field Name of field group.
842
  *
843
+ * @return boolean
844
  */
845
  function mc_show_edit_block( $field ) {
846
  $admin = ( 'true' === get_option( 'mc_input_options_administrators' ) && current_user_can( 'manage_options' ) ) ? true : false;
922
  return true;
923
  }
924
  }
 
 
925
  }
926
 
927
  /**
962
  $value = isset( $args['value'] ) ? esc_attr( $args['value'] ) : '';
963
  $required = isset( $args['required'] ) ? 'required' : '';
964
  $output = "<duet-date-picker first-day-of-week='$firstday' identifier='$id' name='$name' value='$value' $required></duet-date-picker><input type='date' id='$id' name='$name' value='$value' $required class='duet-fallback' />";
965
+ /**
966
+ * Filter the My Calendar datepicker output.
967
+ *
968
+ * @hook mc_datepicker_html
969
+ *
970
+ * @param {string} $output Default datepicker output.
971
+ * @param {array} $args Datepicker setup arguments.
972
+ *
973
+ * @return {string}
974
+ */
975
+ $output = apply_filters( 'mc_datepicker_html', $output, $args );
976
 
977
  return $output;
978
  }
980
  /**
981
  * Show a block of enabled fields.
982
  *
983
+ * @param string $field name of field group.
984
+ * @param boolean $has_data Whether fields have data.
985
+ * @param object $data Current data.
986
+ * @param boolean $echo whether to return or echo.
987
+ * @param string $default Default string value.
988
+ * @param int|false $group_id If in group editing, group ID.
989
  *
990
  * @return string.
991
  */
1031
  if ( $show_block ) {
1032
  global $current_screen;
1033
  // Because wp_editor cannot return a value, event_desc fields cannot be filtered if its enabled.
1034
+ $value = ( $has_data ) ? stripslashes( $data->event_desc ) : '';
1035
+ /**
1036
+ * Filter the editor to use a custom content editor field.
1037
+ *
1038
+ * @hook mc_custom_content_editor
1039
+ *
1040
+ * @param {string|bool} $custom_editor Bool to use default editor, otherwise the HTML output for your editor.
1041
+ * @param {string} $value Event description.
1042
+ * @param {object} $data Event object.
1043
+ *
1044
+ * @return {string|bool}
1045
+ */
1046
  $custom_editor = apply_filters( 'mc_custom_content_editor', false, $value, $data );
1047
  if ( false !== $custom_editor ) {
1048
  $return = $custom_editor;
1087
  if ( $show_block ) {
1088
  $button_text = __( 'Select Featured Image', 'my-calendar' );
1089
  $remove = '';
1090
+ $alt = '';
1091
  if ( '' !== $image ) {
1092
  $alt = ( $image_id ) ? get_post_meta( $image_id, '_wp_attachment_image_alt', true ) : '';
1093
  $button_text = __( 'Change Featured Image', 'my-calendar' );
1253
  break;
1254
  case 'event_access':
1255
  if ( $show_block ) {
1256
+ /**
1257
+ * Custom fields after event accessibility selections.
1258
+ *
1259
+ * @hook mc_event_access_fields
1260
+ *
1261
+ * @param {string} $output HTML output.
1262
+ * @param {bool} $has_data Has Data.
1263
+ * @param {object} $data Event object.
1264
+ *
1265
+ * @return {string}
1266
+ */
1267
+ $mc_event_accessibility_fields = apply_filters( 'mc_event_access_fields', '', $has_data, $data );
1268
+ $label = __( 'Accessibility', 'my-calendar' );
1269
+ $return = $pre . '<h2>' . $label . '</h2><div class="inside">' . mc_event_accessibility( '', $data, $label ) . $mc_event_accessibility_fields . '</div>' . $post;
1270
  }
1271
  break;
1272
  case 'event_open':
1273
  if ( $show_block ) {
1274
+ /**
1275
+ * Filter event registration fields.
1276
+ *
1277
+ * @hook mc_event_registration
1278
+ *
1279
+ * @param {string} $output HTML output. Default empty.
1280
+ * @param {bool} $has_data Whether this event has data.
1281
+ * @param {object} $data Event data object.
1282
+ * @param {string} $context Indicates this is running in the admin.
1283
+ *
1284
+ * @return {string}
1285
+ */
1286
+ $event_registration_output = apply_filters( 'mc_event_registration', '', $has_data, $data, 'admin' );
1287
+ $return = $pre . '<h2>' . __( 'Registration Settings', 'my-calendar' ) . '</h2><div class="inside"><fieldset><legend class="screen-reader-text">' . __( 'Event Registration', 'my-calendar' ) . '</legend>' . $event_registration_output . '</fieldset></div>' . $post;
1288
  } else {
1289
  $tickets = ( $has_data ) ? esc_url( $data->event_tickets ) : '';
1290
  $registration = ( $has_data ) ? esc_attr( $data->event_registration ) : '';
1318
  default:
1319
  return;
1320
  }
1321
+ /**
1322
+ * Filter the content of an editing block.
1323
+ *
1324
+ * @hook mc_show_block
1325
+ *
1326
+ * @param {string} $return HTML output of editing fields.
1327
+ * @param {object} $data Event object.
1328
+ * @param {string} $field Field hook.
1329
+ * @param {bool} $has_data If has data.
1330
+ *
1331
+ * @return {string}
1332
+ */
1333
  $return = apply_filters( 'mc_show_block', $return, $data, $field, $has_data );
1334
  if ( true === $echo ) {
1335
  echo $return;
 
 
1336
  }
1337
+
1338
+ return $return;
1339
  }
1340
 
1341
  /**
1384
  /**
1385
  * Display all enabled form fields.
1386
  *
1387
+ * @param object|null $data Passed data; null for new events.
1388
+ * @param string $mode Copy/edit/add.
1389
+ * @param int $event_id Event ID.
1390
  */
1391
  function mc_form_fields( $data, $mode, $event_id ) {
1392
  global $wpdb, $user_ID;
1393
+ $has_data = ( is_object( $data ) ) ? true : false;
1394
  if ( $data ) {
1395
  // This was previously only shown if $data was an object. Don't know why.
1396
  $test = mc_test_occurrence_overlap( $data );
1397
  }
1398
  $instance = ( isset( $_GET['date'] ) ) ? (int) $_GET['date'] : false;
1399
  if ( $instance ) {
1400
+ $ins = mc_get_instance_data( $instance );
1401
+ if ( property_exists( $ins, 'occur_event_id' ) ) {
1402
+ $event_id = $ins->occur_event_id;
1403
+ $data = mc_get_first_event( $event_id );
1404
+ }
1405
  }
1406
  ?>
1407
  <div class="postbox-container jcd-wide">
1418
  $query_args['date'] = $instance;
1419
  }
1420
  }
1421
+ /**
1422
+ * Insert content before the event form. Not in the form.
1423
+ *
1424
+ * @hook mc_before_event_form
1425
+ *
1426
+ * @param {string} $output HTML output. Default empty string.
1427
+ * @param {int} $event_id Event ID.
1428
+ *
1429
+ * @return {string}
1430
+ */
1431
  echo apply_filters( 'mc_before_event_form', '', $event_id );
1432
  $action = add_query_arg( $query_args, admin_url( 'admin.php?page=my-calendar' ) );
1433
  $group_id = ( ! empty( $data->event_group_id ) && 'copy' !== $mode ) ? $data->event_group_id : mc_group_id();
1479
  $deleted = get_post_meta( $post_id, '_mc_deleted_instances', true );
1480
  $custom = get_post_meta( $post_id, '_mc_custom_instances', true );
1481
  if ( $deleted || $custom ) {
1482
+ $notice = '';
1483
  if ( $deleted ) {
1484
  $notice = __( 'Some dates in this event have been deleted.', 'my-calendar' );
1485
  }
1510
  <legend class="screen-reader-text"><?php esc_html_e( 'Event', 'my-calendar' ); ?></legend>
1511
  <p>
1512
  <label for="e_title"><?php esc_html_e( 'Event Title', 'my-calendar' ); ?></label><br/>
1513
+ <input type="text" id="e_title" name="event_title" size="50" maxlength="255" value="<?php echo ( $has_data ) ? stripslashes( esc_attr( $data->event_title ) ) : ''; ?>" />
1514
  </p>
1515
  <?php
1516
  if ( is_object( $data ) && 1 === (int) $data->event_flagged ) {
1524
  </div>
1525
  <?php
1526
  }
1527
+ /**
1528
+ * Container for custom fields inserted after the event title field.
1529
+ *
1530
+ * @hook mc_insert_custom_fields
1531
+ *
1532
+ * @param {string} $output Output HTML.
1533
+ * @param {bool} $has_data Has data.
1534
+ * @param {object} $data Event object.
1535
+ *
1536
+ * @return {string}
1537
+ */
1538
  apply_filters( 'mc_insert_custom_fields', '', $has_data, $data );
1539
 
1540
  if ( function_exists( 'wpt_post_to_twitter' ) && current_user_can( 'wpt_can_tweet' ) ) {
1579
  <legend class="screen-reader-text"><?php esc_html_e( 'Event Date and Time', 'my-calendar' ); ?></legend>
1580
  <div id="e_schedule">
1581
  <?php
1582
+ /**
1583
+ * Filter My Calendar default date/time inputs.
1584
+ *
1585
+ * @hook mc_datetime_inputs
1586
+ *
1587
+ * @param {string} $output HTML output.
1588
+ * @param {bool} $has_data If event has data.
1589
+ * @param {object} $data Event object.
1590
+ * @param {string} $context Admin context.
1591
+ *
1592
+ * @return {string}
1593
+ */
1594
  echo apply_filters( 'mc_datetime_inputs', '', $has_data, $data, 'admin' );
1595
  if ( 'edit' !== $mode ) {
1596
  $span_checked = '';
1667
  </div>
1668
  <?php
1669
  }
1670
+ /**
1671
+ * Render custom fields in the admin.
1672
+ *
1673
+ * @hook mc_event_details
1674
+ *
1675
+ * @param {string} $output HTML output. Default empty string.
1676
+ * @param {bool} $has_data If event has data.
1677
+ * @param {object} $data Event object.
1678
+ * @param {string} $context Admin context.
1679
+ *
1680
+ * @return {string}
1681
+ */
1682
  $custom_fields = apply_filters( 'mc_event_details', '', $has_data, $data, 'admin' );
1683
  if ( '' !== $custom_fields ) {
1684
  ?>
1686
  <div class="postbox">
1687
  <h2><?php esc_html_e( 'Event Custom Fields', 'my-calendar' ); ?></h2>
1688
  <div class="inside">
1689
+ <?php echo $custom_fields; ?>
1690
  </div>
1691
  </div>
1692
  </div>
1797
  $fields = '';
1798
  $autocomplete = false;
1799
  $count = mc_count_locations();
1800
+ /**
1801
+ * Filter the number of locations required to trigger a switch between a select input and an autocomplete.
1802
+ *
1803
+ * @hook mc_convert_locations_select_to_autocomplete
1804
+ *
1805
+ * @param {int} $count Number of locations that will remain a select. Default 90.
1806
+ *
1807
+ * @return {int}
1808
+ */
1809
  if ( $count > apply_filters( 'mc_convert_locations_select_to_autocomplete', 90 ) ) {
1810
  $autocomplete = true;
1811
  }
1826
  foreach ( $locs as $loc ) {
1827
  if ( is_object( $loc ) ) {
1828
  $base_loc = strip_tags( stripslashes( $loc->location_label ), mc_strip_tags() );
1829
+ if ( ! $event_location ) {
1830
+ $selected = ( is_numeric( get_option( 'mc_default_location' ) ) && (int) get_option( 'mc_default_location' ) === (int) $loc->location_id ) ? ' selected="selected"' : '';
1831
+ } else {
1832
+ $selected = '';
1833
+ }
1834
  if ( (int) $loc->location_id === (int) $event_location ) {
1835
  $location_link = ( current_user_can( 'mc_edit_locations' ) ) ? add_query_arg(
1836
  array(
1840
  admin_url( 'admin.php?page=my-calendar-locations' )
1841
  ) : false;
1842
  // Translators: name of currently selected location.
1843
+ $loc_name = ( $location_link ) ? '<a href="' . esc_url( $location_link ) . '" target="blank">' . sprintf( __( 'Edit %s', 'my-calendar' ), $base_loc ) . ' (' . __( 'New tab', 'my-calendar' ) . ')</a>' : $base_loc;
1844
  // Translators: Link to edit current location, e.g. 'Edit %s'.
1845
  $current_location = "<div id='mc-current-location'><span class='dashicons dashicons-location' aria-hidden='true'></span>" . sprintf( __( 'Current location: %s', 'my-calendar' ), $loc_name ) . '</div>';
1846
  $current_location .= "<input type='hidden' name='preset_location' value='$event_location' />";
1876
  * @return array
1877
  */
1878
  function mc_event_access() {
1879
+ /**
1880
+ * Filter available event accessibility options.
1881
+ *
1882
+ * @hook mc_event_access_choices
1883
+ *
1884
+ * @param {array} Indexed array of choices. Events store only the index.
1885
+ *
1886
+ * @return {array}
1887
+ */
1888
  $event_access = apply_filters(
1889
  'mc_event_access_choices',
1890
  array(
1909
  /**
1910
  * Form to select accessibility features.
1911
  *
1912
+ * @param string $form Form HTML.
1913
+ * @param object $data Event data.
1914
+ * @param string $label Primary label for fields.
1915
  */
1916
  function mc_event_accessibility( $form, $data, $label ) {
1917
  $note_value = '';
1921
  <fieldset class='accessibility'>
1922
  <legend class='$class'>$label</legend>
1923
  <ul class='accessibility-features checkboxes'>";
1924
+ $access = mc_event_access();
1925
  if ( ! empty( $data ) ) {
1926
  if ( property_exists( $data, 'event_post' ) ) {
1927
  $events_access = get_post_meta( $data->event_post, '_mc_event_access', true );
1961
  */
1962
  function mc_check_data( $action, $post, $i, $ignore_required = false ) {
1963
  global $wpdb, $submission;
1964
+ $user = wp_get_current_user();
1965
+ /**
1966
+ * Filter posted data before performing event data checks.
1967
+ *
1968
+ * @hook mc_pre_checkdata
1969
+ *
1970
+ * @param {array} $post Post data.
1971
+ * @param {string} $action Action performed (edit, copy, add).
1972
+ * @param {int} $i Current event index if parsing multiple events.
1973
+ *
1974
+ * @return {array}
1975
+ */
1976
  $post = apply_filters( 'mc_pre_checkdata', $post, $action, $i );
1977
  $submit = array();
1978
  $errors = '';
2013
  $event_longitude = '';
2014
  $event_latitude = '';
2015
  $event_location = '';
2016
+ $event_link = '';
2017
+ $repeats = '';
2018
+ $title = '';
2019
+ $event_link = '';
2020
+ $desc = '';
2021
+ $primary = 1;
2022
 
2023
  if ( version_compare( PHP_VERSION, '7.4', '<' ) && get_magic_quotes_gpc() ) { //phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.get_magic_quotes_gpcDeprecated
2024
  $post = array_map( 'stripslashes_deep', $post );
2044
  // ...AND there's no reason to allow it, since weekday events will NEVER happen on the weekend.
2045
  $begin = trim( $post['event_begin'][ $i ] );
2046
  $end = ( ! empty( $post['event_end'] ) ) ? trim( $post['event_end'][ $i ] ) : $post['event_begin'][ $i ];
2047
+ if ( 'E' === $recur && 0 === ( (int) mc_date( 'w', mc_strtotime( $begin ), false ) || 6 === (int) mc_date( 'w', mc_strtotime( $begin ), false ) ) ) {
2048
+ $newbegin = $begin;
2049
+ $newend = $end;
2050
  if ( 0 === (int) mc_date( 'w', mc_strtotime( $begin ), false ) ) {
2051
  $newbegin = my_calendar_add_date( $begin, 1 );
2052
  if ( ! empty( $post['event_end'][ $i ] ) ) {
2072
  $begin = mc_date( 'Y-m-d', mc_strtotime( $begin ), false );// regardless of entry format, convert.
2073
  $time = ! empty( $post['event_time'][ $i ] ) ? trim( $post['event_time'][ $i ] ) : '';
2074
  if ( '' !== $time ) {
2075
+ /**
2076
+ * Filter default event length. Events without a specified end time are considered to occupy one hour by default.
2077
+ *
2078
+ * @hook mc_default_event_length
2079
+ *
2080
+ * @param {string} $default_modifier Event length in a human-language string interpretable by strtotime(). Default '1 hour'.
2081
+ *
2082
+ * @return {string}
2083
+ */
2084
  $default_modifier = apply_filters( 'mc_default_event_length', '1 hour' );
2085
  $endtime = ! empty( $post['event_endtime'][ $i ] ) ? trim( $post['event_endtime'][ $i ] ) : mc_date( 'H:i:s', mc_strtotime( $time . ' +' . $default_modifier ), false );
2086
  if ( empty( $post['event_endtime'][ $i ] ) && mc_date( 'H', mc_strtotime( $endtime ), false ) === '00' ) {
2136
  $cats = array( $default );
2137
  $primary = $default;
2138
  }
2139
+ /**
2140
+ * Filter primary category decision. Primary category is normally the alphabetically first listed category or the first selected private category.
2141
+ *
2142
+ * @hook mc_set_primary_category
2143
+ *
2144
+ * @param {int} $primary Primary category ID.
2145
+ * @param {array} $cats All selected categories.
2146
+ * @param {array} $post Submitted query.
2147
+ *
2148
+ * @return {int}
2149
+ */
2150
  $primary = apply_filters( 'mc_set_primary_category', $primary, $cats, $post );
2151
  $event_author = ( isset( $post['event_author'] ) && is_numeric( $post['event_author'] ) ) ? $post['event_author'] : 0;
2152
  $event_link = ! empty( $post['event_link'] ) ? trim( $post['event_link'] ) : '';
2234
  );
2235
  $loc_id = mc_insert_location( $add_loc );
2236
  $event_location = $loc_id;
2237
+ /**
2238
+ * Execute an action when a location is created during event editing.
2239
+ *
2240
+ * @hook mc_save_location
2241
+ *
2242
+ * @param {int|false} $loc_id Result of database insertion. Row ID or false.
2243
+ * @param {array} $add_loc Array of location parameters to add.
2244
+ * @param {array} $add_loc Array passed from event creation.
2245
+ */
2246
  do_action( 'mc_save_location', $loc_id, $add_loc, $add_loc );
2247
  }
2248
  }
2291
  }
2292
  if ( isset( $post['mcs_check_conflicts'] ) ) {
2293
  $conflicts = mcs_check_conflicts( $begin, $time, $end, $endtime, $event_label );
2294
+ /**
2295
+ * Filter the results of a check for time/location conflicts.
2296
+ *
2297
+ * @hook mcs_check_conflicts
2298
+ *
2299
+ * @param {array|bool} $conflicts False if no conflicts, array of conflicting events if found.
2300
+ * @param {array} $post Query.
2301
+ *
2302
+ * @return {array|bool}
2303
+ */
2304
  $conflicts = apply_filters( 'mcs_check_conflicts', $conflicts, $post );
2305
  if ( $conflicts ) {
2306
  $conflict_id = $conflicts[0]->occur_id;
2312
  } else {
2313
  if ( mc_can_edit_event( $conflict_ev->event_id ) ) {
2314
  $referer = urlencode( mc_get_current_url() );
2315
+ $link = admin_url( "admin.php?page=my-calendar&amp;mode=edit&amp;event_id=$conflict_ev->event_id&amp;ref=$referer" );
2316
  // Translators: Link to edit event draft.
2317
  $error = sprintf( __( 'That event conflicts with a <a href="%s">previously submitted draft</a>.', 'my-calendar' ), $link );
2318
  } else {
2389
  // Array: removed before DB insertion.
2390
  'event_categories' => $cats,
2391
  );
2392
+
2393
+ /**
2394
+ * Generate errors for required fields.
2395
+ *
2396
+ * @hook mc_fields_required
2397
+ *
2398
+ * @param {string} $errors HTML output for errors.
2399
+ * @param {array} $submit Submitted data being tested.
2400
+ *
2401
+ * @return {string}
2402
+ */
2403
  $errors = ( $ignore_required ) ? $errors : apply_filters( 'mc_fields_required', $errors, $submit );
2404
 
2405
  if ( '' === $errors ) {
2506
  if ( ! empty( $update ) ) {
2507
  $event = mc_get_event( $event_instance );
2508
  $formats = array( '%d', '%s', '%s', '%d' );
2509
+ $begin = $update['event_begin'] . ' ' . $update['event_time'];
2510
+ $end = $update['event_end'] . ' ' . $update['event_endtime'];
2511
  $data = array(
2512
  'occur_event_id' => $event_id,
2513
  'occur_begin' => $begin,
2536
  * @param mixed $value required value for field.
2537
  * @param string $format type of data format.
2538
  *
2539
+ * @return int|false $result Updated row or false.
2540
  */
2541
  function mc_update_data( $event_id, $field, $value, $format = '%d' ) {
2542
  global $wpdb;
2603
  'name' => 'event_end[]',
2604
  );
2605
  $picker_end = mc_datepicker_html( $args );
2606
+ /**
2607
+ * Set the latest time an event can be scheduled for.
2608
+ *
2609
+ * @hook mc_time_max
2610
+ *
2611
+ * @param {string} $max Time string. Default 00:00.
2612
+ *
2613
+ * @return {string}
2614
+ */
2615
+ $max = apply_filters( 'mc_time_max', '00:00' );
2616
+ /**
2617
+ * Set the earliest time an event can be scheduled for.
2618
+ *
2619
+ * @hook mc_time_min
2620
+ *
2621
+ * @param {string} $min Time string. Default 00:00.
2622
+ *
2623
+ * @return {string}
2624
+ */
2625
+ $min = apply_filters( 'mc_time_min', '00:00' );
2626
+ $attrs = ( '00:00' !== $min || '00:00' !== $max ) ? ' max="' . $max . '" min="' . $min . '"' : '';
2627
+ $append = '';
2628
+ $range = '';
2629
+ $aria = '';
2630
  if ( '00:00' !== $max || '00:00' !== $min ) {
2631
  // Translators: starting time, ending time.
2632
  $range = '<p id="mc_time_range_allowed">' . sprintf( __( 'Times must be between %1$s and %2$s', 'my-calendar' ), mc_date( mc_time_format(), strtotime( $min ) ), mc_date( mc_time_format(), strtotime( $max ) ) ) . '</p>';
2797
  <label for='event_registration'>" . __( 'Registration Information', 'my-calendar' ) . "</label> <textarea name='event_registration'id='event_registration'cols='40'rows='4'/>$registration</textarea>
2798
  </p>";
2799
 
2800
+ /**
2801
+ * Filter event registration form for event input.
2802
+ *
2803
+ * @hook mc_event_registration_form
2804
+ *
2805
+ * @param {string} $form Default form HTML output.
2806
+ * @param {bool} $has_data If this event has data.
2807
+ * @param {object} $data Event object.
2808
+ * @param {string} $context Admin context.
2809
+ *
2810
+ * @return {string}
2811
+ */
2812
  return apply_filters( 'mc_event_registration_form', $form, $has_data, $data, 'admin' );
2813
  }
2814
 
2860
  }
2861
  }
2862
  $controls['delete'] = "<span class='dashicons dashicons-no' aria-hidden='true'></span><a href='" . admin_url( "admin.php?page=my-calendar-manage&amp;mode=delete&amp;event_id=$event_id$args" ) . "' class='delete'>" . __( 'Delete', 'my-calendar' ) . '</a>';
2863
+ /**
2864
+ * Check whether permalinks are enabled.
2865
+ *
2866
+ * @hook mc_use_permalinks
2867
+ *
2868
+ * @param {string} $option Value of mc_use_permalinks setting.
2869
+ *
2870
+ * @return {string} 'true' value if permalinks are enabled.
2871
+ */
2872
  if ( 'true' === apply_filters( 'mc_use_permalinks', get_option( 'mc_use_permalinks' ) ) ) {
2873
  $post_id = $event->event_post;
2874
  $post_link = ( $post_id ) ? get_edit_post_link( $post_id ) : false;
2960
  $output = '';
2961
 
2962
  $results = mc_get_grouped_events( $id );
2963
+ if ( ! empty( $results ) ) {
2964
  foreach ( $results as $result ) {
2965
  $first = mc_get_first_event( $result->event_id );
2966
  if ( ! is_object( $first ) ) {
2976
  'end' => $close,
2977
  );
2978
 
2979
+ $current_output = ( '' === $template ) ? $current . $begin : mc_draw_template( $array, $template );
2980
  $output .= "<li>$current_output</li>";
2981
  }
2982
  } else {
3063
  * @param boolean $test true if testing.
3064
  * @param array $instances When rebuilding, an array of all prior event dates & ids.
3065
  *
3066
+ * @return array
3067
  */
3068
  function mc_increment_event( $id, $post = array(), $test = false, $instances = array() ) {
3069
+ $event = mc_get_event_core( $id, true );
3070
+ if ( ! $event ) {
3071
+ return array();
3072
+ }
3073
  $data = array();
3074
  $return = array();
3075
  if ( empty( $post ) ) {
3088
  $end_diff = strtotime( gmdate( 'Y-m-d H:i:s', strtotime( $orig_end ) ) ) - strtotime( gmdate( 'Y-m-d', strtotime( $orig_end ) ) );
3089
 
3090
  $group_id = $event->event_group_id;
 
3091
  $recurs = str_split( $event->event_recur, 1 );
3092
  $recur = $recurs[0];
3093
  // Can't use 2nd value directly if it's two digits.
3120
  'occur_end' => mc_date( 'Y-m-d H:i:s', $end, false ),
3121
  'occur_group_id' => $group_id,
3122
  );
3123
+ if ( true === $test && $i > 0 ) {
3124
  return $data;
3125
  }
3126
  if ( $post_until ) {
3131
  }
3132
  }
3133
  $return[] = $data;
3134
+ mc_insert_recurring( $data, $id, $begin, $instances, $test, 'daily' );
 
 
 
 
 
 
 
3135
  }
3136
  break;
3137
  // Weekdays only.
3147
  'occur_end' => mc_date( 'Y-m-d H:i:s', $end, false ),
3148
  'occur_group_id' => $group_id,
3149
  );
3150
+ if ( true === $test && $i > 0 ) {
3151
  return $data;
3152
  }
3153
  if ( $post_until ) {
3158
  }
3159
  }
3160
  $return[] = $data;
3161
+ mc_insert_recurring( $data, $id, $begin, $instances, $test, 'weekday' );
 
 
 
 
 
 
 
3162
  }
3163
  break;
3164
  // Weekly.
3172
  'occur_end' => mc_date( 'Y-m-d H:i:s', $end, false ),
3173
  'occur_group_id' => $group_id,
3174
  );
3175
+ if ( true === $test && $i > 0 ) {
3176
  return $data;
3177
  }
3178
  if ( $post_until ) {
3183
  }
3184
  }
3185
  $return[] = $data;
3186
+ mc_insert_recurring( $data, $id, $begin, $instances, $test, 'weekly' );
 
 
 
 
 
 
 
3187
  }
3188
  break;
3189
  // Biweekly.
3197
  'occur_end' => mc_date( 'Y-m-d H:i:s', $end, false ),
3198
  'occur_group_id' => $group_id,
3199
  );
3200
+ if ( true === $test && $i > 0 ) {
3201
  return $data;
3202
  }
3203
  if ( $post_until ) {
3208
  }
3209
  }
3210
  $return[] = $data;
3211
+ mc_insert_recurring( $data, $id, $begin, $instances, $test, 'biweekly' );
 
 
 
 
 
 
 
3212
  }
3213
  break;
3214
  // Monthly by date.
3222
  'occur_end' => mc_date( 'Y-m-d H:i:s', $end, false ),
3223
  'occur_group_id' => $group_id,
3224
  );
3225
+ if ( true === $test && $i > 0 ) {
3226
  return $data;
3227
  }
3228
  if ( $post_until ) {
3233
  }
3234
  }
3235
  $return[] = $data;
3236
+ mc_insert_recurring( $data, $id, $begin, $instances, $test, 'monthly' );
 
 
 
 
 
 
 
3237
  }
3238
  break;
3239
  // Monthly by day.
3240
  case 'U':
3241
+ // The numeric week of the month this event occurs in.
3242
  $week_of_event = mc_week_of_month( mc_date( 'd', strtotime( $event->event_begin ), false ) );
3243
  $newbegin = my_calendar_add_date( $orig_begin, 28, 0, 0 );
3244
  $newend = my_calendar_add_date( $orig_end, 28, 0, 0 );
 
3245
  $data = array(
3246
  'occur_event_id' => $id,
3247
  'occur_begin' => mc_date( 'Y-m-d H:i:s', strtotime( $orig_begin ), false ),
3248
  'occur_end' => mc_date( 'Y-m-d H:i:s', strtotime( $orig_end ), false ),
3249
  'occur_group_id' => $group_id,
3250
  );
3251
+ mc_insert_recurring( $data, $id, strtotime( $orig_begin ), $instances, $test, 'month-by-day' );
3252
 
 
 
 
 
 
 
 
 
3253
  $numforward = ( $numforward - 1 );
3254
  for ( $i = 0; $i <= $numforward; $i ++ ) {
3255
+ $skip = false;
3256
+ // If next week is a different month, and we're supposed move the event, return true.
3257
+ $move_event = mc_move_date( $newbegin, $event );
3258
+ $week = mc_week_of_month( mc_date( 'd', $newbegin, false ) );
3259
+ if ( $week === $week_of_event || $move_event ) {
3260
+ // If this is the correct week or is the last week of the month and dates are adjustable.
3261
  } else {
3262
+ // If this is not the correct week generate a new date + 1 week.
3263
+ $oldbegin = $newbegin;
3264
+ $oldend = $newend;
3265
  $newbegin = my_calendar_add_date( mc_date( 'Y-m-d H:i:s', $newbegin, false ), 7, 0, 0 );
3266
  $newend = my_calendar_add_date( mc_date( 'Y-m-d H:i:s', $newend, false ), 7, 0, 0 );
3267
+ $week = mc_week_of_month( mc_date( 'd', $newbegin, false ) );
3268
+ $move_event = mc_move_date( $newbegin, $event );
3269
+ if ( $week === $week_of_event || true === $move_event ) {
3270
+ // If $newbegin is the correct week or is the last week of the month and dates are adjustable.
3271
  } else {
3272
+ // Restore values because we're now in the wrong month.
3273
+ $newbegin = $oldbegin;
3274
+ $newend = $oldend;
3275
+ $skip = true;
3276
  }
3277
  }
3278
+ // Skip on events starting on 5th week but not moved to the 4th week in months without 5 weeks.
3279
+ if ( ! $skip ) {
3280
+ $data = array(
3281
+ 'occur_event_id' => $id,
3282
+ 'occur_begin' => mc_date( 'Y-m-d H:i:s', $newbegin, false ),
3283
+ 'occur_end' => mc_date( 'Y-m-d H:i:s', $newend, false ),
3284
+ 'occur_group_id' => $group_id,
3285
+ );
3286
+ if ( true === $test && $i > 0 ) {
3287
+ return $data;
 
 
 
 
3288
  }
3289
+ if ( $post_until ) {
3290
+ if ( $newbegin <= strtotime( $post_until ) ) {
3291
+ $numforward ++;
3292
+ } else {
3293
+ continue;
3294
+ }
 
 
3295
  }
3296
+ $return[] = $data;
3297
+ mc_insert_recurring( $data, $id, $newbegin, $instances, $test, 'month-by-day' );
3298
+ // Jump forward 4 weeks.
3299
+ } else {
3300
+ $numforward ++;
3301
  }
3302
  $newbegin = my_calendar_add_date( mc_date( 'Y-m-d H:i:s', $newbegin, false ), 28, 0, 0 );
3303
  $newend = my_calendar_add_date( mc_date( 'Y-m-d H:i:s', $newend, false ), 28, 0, 0 );
3314
  'occur_end' => mc_date( 'Y-m-d H:i:s', $end, false ),
3315
  'occur_group_id' => $group_id,
3316
  );
3317
+ if ( true === $test && $i > 0 ) {
3318
  return $data;
3319
  }
3320
  if ( $post_until ) {
3325
  }
3326
  }
3327
  $return[] = $data;
3328
+ mc_insert_recurring( $data, $id, $begin, $instances, $test, 'annual' );
 
 
 
 
 
 
 
3329
  }
3330
  break;
3331
  }
3338
  'occur_end' => mc_date( 'Y-m-d H:i:s', $end, false ),
3339
  'occur_group_id' => $group_id,
3340
  );
3341
+ mc_insert_recurring( $data, $id, $begin, $instances, $test, 'single' );
 
 
 
 
 
 
 
3342
  }
3343
 
3344
  if ( true === $test ) {
3348
  return $data;
3349
  }
3350
 
3351
+ /**
3352
+ * Check whether a last-week of the month recurring date needs to be moved.
3353
+ * Checks a given date & an object to see whether the event should only exist in the 5th week or is intended for the last week, regardless of number, then tests the date to see which week it is.
3354
+ *
3355
+ * @param int $newbegin A datestamp to check against.
3356
+ * @param object $event The My Calendar event object source to generate recurrences for.
3357
+ *
3358
+ * @return bool
3359
+ */
3360
+ function mc_move_date( $newbegin, $event ) {
3361
+ $week_of_event = mc_week_of_month( mc_date( 'd', strtotime( $event->event_begin ), false ) );
3362
+ $fifth_week = ( 1 === (int) $event->event_fifth_week ) ? true : false;
3363
+ if ( ! $fifth_week ) {
3364
+ return false;
3365
+ }
3366
+ // Is the date one week ahead of the current date in the next month. If so, this is the last week.
3367
+ $next_week_diff = ( mc_date( 'm', $newbegin, false ) === mc_date( 'm', my_calendar_add_date( mc_date( 'Y-m-d', $newbegin, false ), 7, 0, 0 ), false ) ) ? false : true;
3368
+ $week = mc_week_of_month( mc_date( 'd', $newbegin, false ) );
3369
+ // If next week is not this month, and event should move, return true. This is the last week of the month.
3370
+ $move_event = ( ( ( $week + 1 ) === $week_of_event ) && ( true === $next_week_diff ) ) ? true : false;
3371
+
3372
+ return $move_event;
3373
+ }
3374
+
3375
+ /**
3376
+ * Insert a recurring event instance.
3377
+ *
3378
+ * @param array $data Instance data.
3379
+ * @param int $id Event ID.
3380
+ * @param string $begin Beginning date.
3381
+ * @param array $instances Array of existing dates used when rebuilding values.
3382
+ * @param bool|string $test Whether we're testing values or generating dates.
3383
+ * @param string $context Type of recurring event.
3384
+ */
3385
+ function mc_insert_recurring( $data, $id, $begin, $instances, $test, $context ) {
3386
+ global $wpdb;
3387
+ $format = array( '%d', '%s', '%s', '%d' );
3388
+ if ( ! $test ) {
3389
+ /**
3390
+ * Short circuit inserting a recurring event. Return true if event should not be inserted.
3391
+ *
3392
+ * @hook mc_insert_recurring
3393
+ *
3394
+ * @param {bool} $insert True to skip inserting.
3395
+ * @param {array} $data Event date info.
3396
+ * @param {int} $id Event ID.
3397
+ * @param {string} $context Type of recurring event.
3398
+ *
3399
+ * @return {bool}
3400
+ */
3401
+ $insert = apply_filters( 'mc_insert_recurring', false, $data, $id, $context );
3402
+ if ( ! $insert ) {
3403
+ /**
3404
+ * Filter recurring event instance data array.
3405
+ *
3406
+ * @hook mc_instance_data
3407
+ *
3408
+ * @param {array} $format Array of data passed to insert query.
3409
+ * @param {string} $begin Beginning date.
3410
+ * @param {array} $instances Original instances.
3411
+ *
3412
+ * @return {array}
3413
+ */
3414
+ $data = apply_filters( 'mc_instance_data', $data, $begin, $instances );
3415
+ /**
3416
+ * Filter recurring event instance database format array.
3417
+ *
3418
+ * @hook mc_instance_format
3419
+ *
3420
+ * @param {array} $format Array of placeholder formats in insert query.
3421
+ * @param {string} $begin Beginning date.
3422
+ * @param {array} $instances Original instances.
3423
+ *
3424
+ * @return {array}
3425
+ */
3426
+ $format = apply_filters( 'mc_instance_format', $format, $begin, $instances );
3427
+ $wpdb->insert( my_calendar_event_table(), $data, $format );
3428
+ }
3429
+ }
3430
+ }
3431
+
3432
  /**
3433
  * Execute a refresh of the My Calendar primary URL cache if caching plug-in installed.
3434
  *
3438
  * @param int $result Result of calendar save query.
3439
  */
3440
  function mc_refresh_cache( $action, $data, $event_id, $result ) {
3441
+ $mc_uri_id = ( get_option( 'mc_uri_id' ) ) ? get_option( 'mc_uri_id' ) : false;
3442
+ /**
3443
+ * Filter URLS that should be refreshed in caches.
3444
+ *
3445
+ * @hook mc_cached_pages_to_refresh
3446
+ *
3447
+ * @param {array} $to_refresh Array of post IDs to clear cache on.
3448
+ * @param {string} $action My Calendar action executing.
3449
+ * @param {array} $data Data passed from event.
3450
+ * @param {int} $event_id Event ID.
3451
+ * @param {int} $result Result of calendar database query.
3452
+ *
3453
+ * @return {array}
3454
+ */
3455
  $to_refresh = apply_filters( 'mc_cached_pages_to_refresh', array( $mc_uri_id ), $action, $data, $event_id, $result );
3456
 
3457
  foreach ( $to_refresh as $calendar ) {
my-calendar-event-manager.php CHANGED
@@ -19,7 +19,7 @@ if ( ! defined( 'ABSPATH' ) ) {
19
  * @param string $action type of action.
20
  * @param array $events Optional. Array of event IDs to act on.
21
  *
22
- * @return array bulk action details.
23
  */
24
  function mc_bulk_action( $action, $events = array() ) {
25
  global $wpdb;
@@ -28,6 +28,7 @@ function mc_bulk_action( $action, $events = array() ) {
28
  $total = 0;
29
  $ids = array();
30
  $prepare = array();
 
31
 
32
  foreach ( $events as $value ) {
33
  $value = (int) $value;
@@ -78,6 +79,16 @@ function mc_bulk_action( $action, $events = array() ) {
78
  if ( is_array( $submitter ) && ! empty( $submitter ) ) {
79
  $name = $submitter['first_name'] . ' ' . $submitter['last_name'];
80
  $email = $submitter['email'];
 
 
 
 
 
 
 
 
 
 
81
  do_action( 'mcs_complete_submission', $name, $email, $id, 'edit' );
82
  }
83
  }
@@ -86,12 +97,14 @@ function mc_bulk_action( $action, $events = array() ) {
86
  /**
87
  * Add custom bulk actions.
88
  *
89
- * @param string $action Declared action.
90
- * @param array $ids Array of event IDs being requested.
 
 
91
  */
92
  do_action( 'mc_bulk_actions', $action, $ids );
93
 
94
- $result = $wpdb->query( $wpdb->prepare( $sql, $ids ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
95
 
96
  mc_update_count_cache();
97
  $results = array(
@@ -113,11 +126,12 @@ function mc_bulk_action( $action, $events = array() ) {
113
  * @return string message
114
  */
115
  function mc_bulk_message( $results, $action ) {
116
- $count = $results['count'];
117
- $total = $results['total'];
118
- $ids = $results['ids'];
119
- $result = $results['result'];
120
-
 
121
  switch ( $action ) {
122
  case 'delete':
123
  // Translators: Number of events deleted, number selected.
@@ -163,6 +177,13 @@ function mc_bulk_message( $results, $action ) {
163
  // Translators: Sprintf as a 3rd argument if this string is appended to prior error. # of unchanged events.
164
  $success .= ' ' . _n( '%3$d event was not changed in that update.', '%3$d events were not changed in that update.', $diff, 'my-calendar' );
165
  }
 
 
 
 
 
 
 
166
  do_action( 'mc_mass_' . $action . '_events', $ids );
167
  $message = mc_show_notice( sprintf( $success, $result, $total, $diff ) );
168
  } else {
@@ -246,7 +267,7 @@ function my_calendar_manage() {
246
  if ( ! empty( $_POST['mc_bulk_actions'] ) ) {
247
  $nonce = $_REQUEST['_wpnonce'];
248
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
249
- die( 'Security check failed' );
250
  }
251
  if ( isset( $_POST['mc_bulk_actions'] ) ) {
252
  $action = $_POST['mc_bulk_actions'];
@@ -308,6 +329,8 @@ function my_calendar_manage() {
308
 
309
  <div class="inside">
310
  <?php
 
 
311
  if ( $grid ) {
312
  $calendar = array(
313
  'name' => 'admin',
@@ -321,6 +344,15 @@ function my_calendar_manage() {
321
  if ( mc_count_locations() > 200 ) {
322
  $calendar['below'] = 'categories,access';
323
  }
 
 
 
 
 
 
 
 
 
324
  apply_filters( 'mc_filter_admin_grid_args', $calendar );
325
  echo my_calendar( $calendar );
326
  } else {
@@ -389,9 +421,11 @@ function mc_show_bulk_actions() {
389
  /**
390
  * Filter Event manager bulk actions.
391
  *
392
- * @param array $bulk_actions Array of bulk actions currently available.
 
 
393
  *
394
- * @return array
395
  */
396
  $bulk_actions = apply_filters( 'mc_bulk_actions', $bulk_actions );
397
  $options = '';
@@ -402,6 +436,20 @@ function mc_show_bulk_actions() {
402
  return $options;
403
  }
404
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405
  /**
406
  * Used on the manage events admin page to display a list of events
407
  */
@@ -409,13 +457,6 @@ function mc_list_events() {
409
  global $wpdb;
410
  if ( current_user_can( 'mc_approve_events' ) || current_user_can( 'mc_manage_events' ) || current_user_can( 'mc_add_events' ) ) {
411
 
412
- $action = ! empty( $_POST['event_action'] ) ? $_POST['event_action'] : '';
413
- $event_id = ! empty( $_POST['event_id'] ) ? $_POST['event_id'] : '';
414
- if ( 'delete' === $action ) {
415
- $message = mc_delete_event( $event_id );
416
- echo wp_kses_post( $message );
417
- }
418
-
419
  if ( isset( $_GET['order'] ) ) {
420
  $sortdir = ( isset( $_GET['order'] ) && 'ASC' === $_GET['order'] ) ? 'ASC' : 'default';
421
  $sortdir = ( isset( $_GET['order'] ) && 'DESC' === $_GET['order'] ) ? 'DESC' : $sortdir;
@@ -656,11 +697,8 @@ function mc_list_events() {
656
  $problem = ( '' !== $check ) ? 'problem' : '';
657
  $edit_url = admin_url( "admin.php?page=my-calendar&amp;mode=edit&amp;event_id=$event->event_id" );
658
  $copy_url = admin_url( "admin.php?page=my-calendar&amp;mode=copy&amp;event_id=$event->event_id" );
659
- if ( ! $invalid ) {
660
- $view_url = mc_get_details_link( $event );
661
- } else {
662
- $view_url = '';
663
- }
664
  $group_url = admin_url( "admin.php?page=my-calendar-manage&amp;groups=true&amp;mode=edit&amp;event_id=$event->event_id&amp;group_id=$event->event_group_id" );
665
  $delete_url = admin_url( "admin.php?page=my-calendar-manage&amp;mode=delete&amp;event_id=$event->event_id" );
666
  $can_edit = mc_can_edit_event( $event );
@@ -892,7 +930,18 @@ function mc_event_is_grouped( $group_id ) {
892
  * @return boolean
893
  */
894
  function mc_can_edit_category( $category, $user ) {
895
- $permissions = get_user_meta( $user, 'mc_user_permissions', true );
 
 
 
 
 
 
 
 
 
 
 
896
  $permissions = apply_filters( 'mc_user_permissions', $permissions, $category, $user );
897
 
898
  if ( ( ! $permissions || empty( $permissions ) ) || in_array( 'all', $permissions, true ) || in_array( $category, $permissions, true ) || current_user_can( 'manage_options' ) ) {
@@ -905,8 +954,8 @@ function mc_can_edit_category( $category, $user ) {
905
  /**
906
  * Unless an admin, authors can only edit their own events if they don't have mc_manage_events capabilities.
907
  *
908
- * @param object|boolean $event Event object.
909
- * @param string $datatype 'event' or 'instance'.
910
  *
911
  * @return boolean
912
  */
@@ -917,6 +966,16 @@ function mc_can_edit_event( $event = false, $datatype = 'event' ) {
917
  return false;
918
  }
919
 
 
 
 
 
 
 
 
 
 
 
920
  $api = apply_filters( 'mc_api_can_edit_event', false, $event );
921
  if ( $api ) {
922
 
@@ -968,6 +1027,16 @@ function mc_can_edit_event( $event = false, $datatype = 'event' ) {
968
  $return = true;
969
  }
970
 
 
 
 
 
 
 
 
 
 
 
971
  return apply_filters( 'mc_can_edit_event', $return, $event_id );
972
  }
973
 
@@ -980,28 +1049,21 @@ function _mc_increment_values( $recur ) {
980
  switch ( $recur ) {
981
  case 'S': // Single.
982
  return 0;
983
- break;
984
  case 'D': // Daily.
985
  return 500;
986
- break;
987
  case 'E': // Weekdays.
988
  return 400;
989
- break;
990
  case 'W': // Weekly.
991
  return 240;
992
- break;
993
  case 'B': // Biweekly.
994
  return 240;
995
- break;
996
  case 'M': // Monthly.
997
  case 'U':
998
  return 240;
999
- break;
1000
  case 'Y':
1001
  return 50;
1002
- break;
1003
  default:
1004
- false;
1005
  }
1006
  }
1007
 
19
  * @param string $action type of action.
20
  * @param array $events Optional. Array of event IDs to act on.
21
  *
22
+ * @return string bulk action details.
23
  */
24
  function mc_bulk_action( $action, $events = array() ) {
25
  global $wpdb;
28
  $total = 0;
29
  $ids = array();
30
  $prepare = array();
31
+ $sql = '';
32
 
33
  foreach ( $events as $value ) {
34
  $value = (int) $value;
79
  if ( is_array( $submitter ) && ! empty( $submitter ) ) {
80
  $name = $submitter['first_name'] . ' ' . $submitter['last_name'];
81
  $email = $submitter['email'];
82
+ /**
83
+ * Run action when a publically submitted event is un-spammed.
84
+ *
85
+ * @hook mcs_complete_submission
86
+ *
87
+ * @param {string} $name Submitter's name.
88
+ * @param {string} $email Submitter's email.
89
+ * @param {int} $id Event ID.
90
+ * @param {string} $action Action performed ('edit').
91
+ */
92
  do_action( 'mcs_complete_submission', $name, $email, $id, 'edit' );
93
  }
94
  }
97
  /**
98
  * Add custom bulk actions.
99
  *
100
+ * @hook mc_bulk_actions
101
+ *
102
+ * @param {string} $action Declared action.
103
+ * @param {array} $ids Array of event IDs being requested.
104
  */
105
  do_action( 'mc_bulk_actions', $action, $ids );
106
 
107
+ $result = ( '' !== $sql ) ? $wpdb->query( $wpdb->prepare( $sql, $ids ) ) : false; // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
108
 
109
  mc_update_count_cache();
110
  $results = array(
126
  * @return string message
127
  */
128
  function mc_bulk_message( $results, $action ) {
129
+ $count = $results['count'];
130
+ $total = $results['total'];
131
+ $ids = $results['ids'];
132
+ $result = $results['result'];
133
+ $error = '';
134
+ $success = '';
135
  switch ( $action ) {
136
  case 'delete':
137
  // Translators: Number of events deleted, number selected.
177
  // Translators: Sprintf as a 3rd argument if this string is appended to prior error. # of unchanged events.
178
  $success .= ' ' . _n( '%3$d event was not changed in that update.', '%3$d events were not changed in that update.', $diff, 'my-calendar' );
179
  }
180
+ /**
181
+ * Dynamic action executed when a group of events is bulk modified.
182
+ *
183
+ * @hook mc_mass_{$action}_events
184
+ *
185
+ * @param {array} $ids Array of event IDs being handled.
186
+ */
187
  do_action( 'mc_mass_' . $action . '_events', $ids );
188
  $message = mc_show_notice( sprintf( $success, $result, $total, $diff ) );
189
  } else {
267
  if ( ! empty( $_POST['mc_bulk_actions'] ) ) {
268
  $nonce = $_REQUEST['_wpnonce'];
269
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
270
+ wp_die( 'My Calendar: Security check failed' );
271
  }
272
  if ( isset( $_POST['mc_bulk_actions'] ) ) {
273
  $action = $_POST['mc_bulk_actions'];
329
 
330
  <div class="inside">
331
  <?php
332
+ // If any subsidiary post actions (e.g. delete an event), handle before display.
333
+ mc_handle_post();
334
  if ( $grid ) {
335
  $calendar = array(
336
  'name' => 'admin',
344
  if ( mc_count_locations() > 200 ) {
345
  $calendar['below'] = 'categories,access';
346
  }
347
+ /**
348
+ * Filter arguments used to display the calendar grid view for admins.
349
+ *
350
+ * @hook mc_filter_admin_grid_args
351
+ *
352
+ * @param {array} $calendar Calendar display arguments.
353
+ *
354
+ * @return {array}
355
+ */
356
  apply_filters( 'mc_filter_admin_grid_args', $calendar );
357
  echo my_calendar( $calendar );
358
  } else {
421
  /**
422
  * Filter Event manager bulk actions.
423
  *
424
+ * @hook mc_bulk_actions
425
+ *
426
+ * @param {array} $bulk_actions Array of bulk actions currently available.
427
  *
428
+ * @return {array}
429
  */
430
  $bulk_actions = apply_filters( 'mc_bulk_actions', $bulk_actions );
431
  $options = '';
436
  return $options;
437
  }
438
 
439
+ /**
440
+ * Handle event list/grid POST actions.
441
+ *
442
+ * @return void
443
+ */
444
+ function mc_handle_post() {
445
+ $action = ! empty( $_POST['event_action'] ) ? $_POST['event_action'] : '';
446
+ $event_id = ! empty( $_POST['event_id'] ) ? $_POST['event_id'] : '';
447
+ if ( 'delete' === $action ) {
448
+ $message = mc_delete_event( $event_id );
449
+ echo wp_kses_post( $message );
450
+ }
451
+ }
452
+
453
  /**
454
  * Used on the manage events admin page to display a list of events
455
  */
457
  global $wpdb;
458
  if ( current_user_can( 'mc_approve_events' ) || current_user_can( 'mc_manage_events' ) || current_user_can( 'mc_add_events' ) ) {
459
 
 
 
 
 
 
 
 
460
  if ( isset( $_GET['order'] ) ) {
461
  $sortdir = ( isset( $_GET['order'] ) && 'ASC' === $_GET['order'] ) ? 'ASC' : 'default';
462
  $sortdir = ( isset( $_GET['order'] ) && 'DESC' === $_GET['order'] ) ? 'DESC' : $sortdir;
697
  $problem = ( '' !== $check ) ? 'problem' : '';
698
  $edit_url = admin_url( "admin.php?page=my-calendar&amp;mode=edit&amp;event_id=$event->event_id" );
699
  $copy_url = admin_url( "admin.php?page=my-calendar&amp;mode=copy&amp;event_id=$event->event_id" );
700
+ $view_url = ( $invalid ) ? '' : mc_get_details_link( $event );
701
+
 
 
 
702
  $group_url = admin_url( "admin.php?page=my-calendar-manage&amp;groups=true&amp;mode=edit&amp;event_id=$event->event_id&amp;group_id=$event->event_group_id" );
703
  $delete_url = admin_url( "admin.php?page=my-calendar-manage&amp;mode=delete&amp;event_id=$event->event_id" );
704
  $can_edit = mc_can_edit_event( $event );
930
  * @return boolean
931
  */
932
  function mc_can_edit_category( $category, $user ) {
933
+ $permissions = ( '' === get_user_meta( $user, 'mc_user_permissions', true ) ) ? array() : get_user_meta( $user, 'mc_user_permissions', true );
934
+ /**
935
+ * Filter permissions for users editing a category.
936
+ *
937
+ * @hook mc_user_permissions
938
+ *
939
+ * @param {array} $permissions User meta data for this user's category permissions.
940
+ * @param {int} $category Category ID.
941
+ * @param {int} $user User ID.
942
+ *
943
+ * @return {array} Array of categories this user can edit.
944
+ */
945
  $permissions = apply_filters( 'mc_user_permissions', $permissions, $category, $user );
946
 
947
  if ( ( ! $permissions || empty( $permissions ) ) || in_array( 'all', $permissions, true ) || in_array( $category, $permissions, true ) || current_user_can( 'manage_options' ) ) {
954
  /**
955
  * Unless an admin, authors can only edit their own events if they don't have mc_manage_events capabilities.
956
  *
957
+ * @param object|boolean|int $event Event object or event ID.
958
+ * @param string $datatype 'event' or 'instance'.
959
  *
960
  * @return boolean
961
  */
966
  return false;
967
  }
968
 
969
+ /**
970
+ * Filter permissions to edit an event via the My Calendar Pro REST API..
971
+ *
972
+ * @hook mc_api_can_edit_event
973
+ *
974
+ * @param {bool} $return True if API user can edit this event.
975
+ * @param {object|int} $event The ID of the current event or an event object.
976
+ *
977
+ * @return {bool}
978
+ */
979
  $api = apply_filters( 'mc_api_can_edit_event', false, $event );
980
  if ( $api ) {
981
 
1027
  $return = true;
1028
  }
1029
 
1030
+ /**
1031
+ * Filter permissions to edit an event.
1032
+ *
1033
+ * @hook mc_can_edit_event
1034
+ *
1035
+ * @param {bool} $return True if user can edit this event.
1036
+ * @param {int} $event_id The ID of the current event.
1037
+ *
1038
+ * @return {bool}
1039
+ */
1040
  return apply_filters( 'mc_can_edit_event', $return, $event_id );
1041
  }
1042
 
1049
  switch ( $recur ) {
1050
  case 'S': // Single.
1051
  return 0;
 
1052
  case 'D': // Daily.
1053
  return 500;
 
1054
  case 'E': // Weekdays.
1055
  return 400;
 
1056
  case 'W': // Weekly.
1057
  return 240;
 
1058
  case 'B': // Biweekly.
1059
  return 240;
 
1060
  case 'M': // Monthly.
1061
  case 'U':
1062
  return 240;
 
1063
  case 'Y':
1064
  return 50;
 
1065
  default:
1066
+ return false;
1067
  }
1068
  }
1069
 
my-calendar-events.php CHANGED
@@ -35,6 +35,15 @@ function mc_event_object( $object ) {
35
  }
36
  $object->uid = $guid;
37
  }
 
 
 
 
 
 
 
 
 
38
  $object = apply_filters( 'mc_event_object', $object );
39
  }
40
 
@@ -67,11 +76,13 @@ function mc_ts( $test = false ) {
67
  global $wpdb;
68
  $offset = $wpdb->get_var( 'SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP);' );
69
  /**
70
- * Filter timezone offset applied when displaying events.
71
  *
72
- * @param string $offset Timezone offset format -HH:MM:SS.
73
  *
74
- * @return string
 
 
75
  */
76
  $offset = apply_filters( 'mc_filter_offset', $offset );
77
  $offset = substr( $offset, 0, -3 );
@@ -152,6 +163,39 @@ function my_calendar_get_events( $args ) {
152
  $arr_events = array();
153
  $ts_string = mc_ts();
154
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  $site = apply_filters( 'mc_get_events_sites', $site, $args );
156
  if ( is_array( $site ) ) {
157
  foreach ( $site as $s ) {
@@ -169,7 +213,7 @@ function my_calendar_get_events( $args ) {
169
  OR ( DATE('$from') BETWEEN DATE(occur_begin) AND DATE(occur_end) )
170
  OR ( DATE('$to') BETWEEN DATE(occur_begin) AND DATE(occur_end) ) )
171
  $exclude_categories
172
- GROUP BY o.occur_id ORDER BY " . apply_filters( 'mc_primary_sort', 'occur_begin' ) . ', ' . apply_filters( 'mc_secondary_sort', 'event_title ASC' );
173
 
174
  $events = $mcdb->get_results( $event_query );
175
 
@@ -219,7 +263,7 @@ function my_calendar_get_events( $args ) {
219
  OR ( DATE('$from') BETWEEN DATE(occur_begin) AND DATE(occur_end) )
220
  OR ( DATE('$to') BETWEEN DATE(occur_begin) AND DATE(occur_end) ) )
221
  $exclude_categories
222
- GROUP BY o.occur_id ORDER BY " . apply_filters( 'mc_primary_sort', 'occur_begin' ) . ', ' . apply_filters( 'mc_secondary_sort', 'event_title ASC' );
223
 
224
  $events = $mcdb->get_results( $event_query );
225
 
@@ -255,6 +299,17 @@ function my_calendar_get_events( $args ) {
255
  }
256
  }
257
 
 
 
 
 
 
 
 
 
 
 
 
258
  return apply_filters( 'mc_filter_events', $arr_events, $args, 'my_calendar_get_events' );
259
  }
260
 
@@ -372,6 +427,17 @@ function mc_get_all_events( $args ) {
372
  }
373
  }
374
 
 
 
 
 
 
 
 
 
 
 
 
375
  return apply_filters( 'mc_filter_events', $arr_events, $args, 'mc_get_all_events' );
376
  }
377
 
@@ -403,9 +469,9 @@ function mc_get_all_holidays( $before, $after, $today ) {
403
  /**
404
  * Get most recently added events.
405
  *
406
- * @param integer $cat_id Category ID.
407
  *
408
- * @return array of event objects
409
  */
410
  function mc_get_new_events( $cat_id = false ) {
411
  global $wpdb;
@@ -420,8 +486,16 @@ function mc_get_new_events( $cat_id = false ) {
420
  $cat = 'WHERE event_approved = 1 AND event_flagged <> 1';
421
  }
422
  $exclude_categories = mc_private_categories();
423
- $limit = apply_filters( 'mc_rss_feed_date_range', 2 );
424
-
 
 
 
 
 
 
 
 
425
  $events = $mcdb->get_results(
426
  'SELECT *, ' . $ts_string . '
427
  FROM ' . my_calendar_event_table() . '
@@ -481,7 +555,7 @@ function mc_get_instances( $id ) {
481
  /**
482
  * Fetch results of an event search.
483
  *
484
- * @param mixed array/string $search mixed array (PRO) or string (Simple).
485
  *
486
  * @return array of event objects
487
  */
@@ -492,8 +566,26 @@ function mc_get_search_results( $search ) {
492
  if ( 'true' === get_option( 'mc_remote' ) && function_exists( 'mc_remote_db' ) ) {
493
  $mcdb = mc_remote_db();
494
  }
 
 
 
 
 
 
 
 
 
495
  $before = apply_filters( 'mc_past_search_results', 0 );
496
- $after = apply_filters( 'mc_future_search_results', 15 ); // return only future events, nearest 10.
 
 
 
 
 
 
 
 
 
497
  if ( is_array( $search ) ) {
498
  // If from & to are set, we need to use a date-based event query.
499
  $from = $search['from'];
@@ -516,6 +608,16 @@ function mc_get_search_results( $search ) {
516
  'source' => 'search',
517
  );
518
 
 
 
 
 
 
 
 
 
 
 
519
  $args = apply_filters( 'mc_search_attributes', $args, $search );
520
  $event_array = my_calendar_events( $args );
521
  } else {
@@ -538,6 +640,16 @@ function mc_get_search_results( $search ) {
538
  }
539
  }
540
 
 
 
 
 
 
 
 
 
 
 
541
  return apply_filters( 'mc_searched_events', $event_array, $args );
542
  }
543
 
@@ -547,11 +659,11 @@ function mc_get_search_results( $search ) {
547
  * @param int $id Event ID in my_calendar db.
548
  * @param boolean $rebuild Get core data only if doing an event rebuild.
549
  *
550
- * @return Event object
551
  */
552
  function mc_get_event_core( $id, $rebuild = false ) {
553
  if ( ! is_numeric( $id ) ) {
554
- return;
555
  }
556
 
557
  global $wpdb;
@@ -576,7 +688,7 @@ function mc_get_event_core( $id, $rebuild = false ) {
576
  *
577
  * @param int $id Event core ID.
578
  *
579
- * @return object Event
580
  */
581
  function mc_get_first_event( $id ) {
582
  global $wpdb;
@@ -600,7 +712,7 @@ function mc_get_first_event( $id ) {
600
  *
601
  * @param int $instance_id Event instance ID.
602
  *
603
- * @return query result
604
  */
605
  function mc_get_instance_data( $instance_id ) {
606
  global $wpdb;
@@ -639,7 +751,7 @@ function mc_get_nearest_event( $id ) {
639
  * @param int $id Event instance ID.
640
  * @param string $type 'object' or 'html'.
641
  *
642
- * @return mixed object/string
643
  */
644
  function mc_get_event( $id, $type = 'object' ) {
645
  if ( ! is_numeric( $id ) ) {
@@ -663,8 +775,6 @@ function mc_get_event( $id, $type = 'object' ) {
663
 
664
  return $value;
665
  }
666
-
667
- return false;
668
  }
669
 
670
  /**
@@ -697,6 +807,15 @@ function mc_get_data( $field, $id ) {
697
  * @return array Array of event objects with dates as keys.
698
  */
699
  function my_calendar_events( $args ) {
 
 
 
 
 
 
 
 
 
700
  $args = apply_filters( 'my_calendar_events_args', $args );
701
  $events = my_calendar_get_events( $args );
702
  $event_array = array();
@@ -723,9 +842,9 @@ function my_calendar_events( $args ) {
723
  /**
724
  * Get one event currently happening.
725
  *
726
- * @param mixed $category string/integer category ID or 'default'.
727
- * @param string $template display Template.
728
- * @param integer $site Site ID if fetching events from a different multisite instance.
729
  *
730
  * @return string output HTML
731
  */
@@ -753,8 +872,30 @@ function my_calendar_events_now( $category = 'default', $template = '<strong>{li
753
  $select_location = '';
754
  $select_author = '';
755
  $select_host = '';
756
- $now = current_time( 'Y-m-d H:i:s' );
757
- $event_query = 'SELECT *, ' . $ts_string . '
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
758
  FROM ' . my_calendar_event_table( $site ) . ' AS o
759
  JOIN ' . my_calendar_table( $site ) . " AS e
760
  ON (event_id=occur_event_id)
@@ -764,9 +905,8 @@ function my_calendar_events_now( $category = 'default', $template = '<strong>{li
764
  WHERE $select_published $select_category $select_location $select_author $select_host
765
  $exclude_categories
766
  AND ( CAST('$now' AS DATETIME) BETWEEN occur_begin AND occur_end )
767
- ORDER BY " . apply_filters( 'mc_primary_sort', 'occur_begin' ) . ', ' . apply_filters( 'mc_secondary_sort', 'event_title ASC' );
768
-
769
- $events = $mcdb->get_results( $event_query );
770
  if ( ! empty( $events ) ) {
771
  foreach ( array_keys( $events ) as $key ) {
772
  $event =& $events[ $key ];
@@ -780,6 +920,16 @@ function my_calendar_events_now( $category = 'default', $template = '<strong>{li
780
  $template = mc_get_custom_template( $template );
781
  }
782
 
 
 
 
 
 
 
 
 
 
 
783
  $output = mc_draw_template( $event, apply_filters( 'mc_happening_now_template', $template, $event ) );
784
  $return = mc_run_shortcodes( $output );
785
  } else {
@@ -796,10 +946,10 @@ function my_calendar_events_now( $category = 'default', $template = '<strong>{li
796
  /**
797
  * Get the next scheduled event, not currently happening.
798
  *
799
- * @param mixed $category string/integer category ID or 'default'.
800
- * @param string $template display Template.
801
- * @param integer $skip Number of events to skip.
802
- * @param integer $site Site ID if fetching events from a different multisite instance.
803
  *
804
  * @return string output HTML
805
  */
@@ -853,6 +1003,16 @@ function my_calendar_events_next( $category = 'default', $template = '<strong>{l
853
  $template = mc_get_custom_template( $template );
854
  }
855
 
 
 
 
 
 
 
 
 
 
 
856
  $output = mc_draw_template( $event, apply_filters( 'mc_happening_next_template', $template, $event ) );
857
  $return = mc_run_shortcodes( $output );
858
  } else {
@@ -895,7 +1055,7 @@ function mc_get_occurrences( $id ) {
895
  function mc_instance_list( $args ) {
896
  $id = isset( $args['event'] ) ? $args['event'] : false;
897
  if ( ! $id ) {
898
- return;
899
  }
900
  $template = isset( $args['template'] ) ? $args['template'] : '<h3>{title}</h3>{description}';
901
  $list = isset( $args['list'] ) ? $args['list'] : '<li>{date}, {time}</li>';
@@ -950,7 +1110,7 @@ function mc_instance_list( $args ) {
950
  *
951
  * @return bool|string
952
  */
953
- function mc_admin_instances( $id, $occur = false ) {
954
  global $wpdb;
955
  $output = '';
956
  $ts_string = mc_ts();
@@ -1001,7 +1161,7 @@ function mc_get_grouped_events( $id ) {
1001
  global $wpdb;
1002
  $id = (int) $id;
1003
  if ( 0 === $id ) {
1004
- return '';
1005
  }
1006
  $results = $wpdb->get_results( $wpdb->prepare( 'SELECT event_id FROM ' . my_calendar_table() . ' WHERE event_group_id=%d', $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
1007
 
35
  }
36
  $object->uid = $guid;
37
  }
38
+ /**
39
+ * Customize the My Calendar event object.
40
+ *
41
+ * @hook mc_event_object
42
+ *
43
+ * @param {object} $object A My Calendar event.
44
+ *
45
+ * @return {object}
46
+ */
47
  $object = apply_filters( 'mc_event_object', $object );
48
  }
49
 
76
  global $wpdb;
77
  $offset = $wpdb->get_var( 'SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP);' );
78
  /**
79
+ * Filter timezone offset applied when displaying events. Can fix issues with an incorrect server time.
80
  *
81
+ * @hook mc_filter_offset
82
  *
83
+ * @param {string} $offset Timezone offset format -HH:MM:SS.
84
+ *
85
+ * @return {string}
86
  */
87
  $offset = apply_filters( 'mc_filter_offset', $offset );
88
  $offset = substr( $offset, 0, -3 );
163
  $arr_events = array();
164
  $ts_string = mc_ts();
165
 
166
+ /**
167
+ * Set primary sort for getting events. Default 'occur_begin'.
168
+ *
169
+ * @hook mc_primary_sort
170
+ *
171
+ * @param {string} $primary_sort SQL sort column.
172
+ * @param {string} $context Current function.
173
+ *
174
+ * @return {string}
175
+ */
176
+ $primary_sort = apply_filters( 'mc_primary_sort', 'occur_begin', 'my_calendar_get_events' );
177
+ /**
178
+ * Set secondary sort for getting events. Default 'event_title ASC'.
179
+ *
180
+ * @hook mc_secondary_sort
181
+ *
182
+ * @param {string} $secondary_sort SQL sort column.
183
+ * @param {string} $context Current function.
184
+ *
185
+ * @return {string}
186
+ */
187
+ $secondary_sort = apply_filters( 'mc_secondary_sort', 'event_title ASC', 'my_calendar_get_events' );
188
+
189
+ /**
190
+ * Filter site parameter in queries on a multisite network. Allows a query to show events merged from multiple sites using a single shortcode.
191
+ *
192
+ * @hook mc_get_events_sites
193
+ *
194
+ * @param {array|int} $site Array of sites or a single site if displaying events from a different site on the network.
195
+ * @param {array} $args Shortcode arguments.
196
+ *
197
+ * @return {array|int}
198
+ */
199
  $site = apply_filters( 'mc_get_events_sites', $site, $args );
200
  if ( is_array( $site ) ) {
201
  foreach ( $site as $s ) {
213
  OR ( DATE('$from') BETWEEN DATE(occur_begin) AND DATE(occur_end) )
214
  OR ( DATE('$to') BETWEEN DATE(occur_begin) AND DATE(occur_end) ) )
215
  $exclude_categories
216
+ GROUP BY o.occur_id ORDER BY $primary_sort, $secondary_sort";
217
 
218
  $events = $mcdb->get_results( $event_query );
219
 
263
  OR ( DATE('$from') BETWEEN DATE(occur_begin) AND DATE(occur_end) )
264
  OR ( DATE('$to') BETWEEN DATE(occur_begin) AND DATE(occur_end) ) )
265
  $exclude_categories
266
+ GROUP BY o.occur_id ORDER BY $primary_sort, $secondary_sort";
267
 
268
  $events = $mcdb->get_results( $event_query );
269
 
299
  }
300
  }
301
 
302
+ /**
303
+ * Filter events returned by my_calendar_get_events queries. Function returns a range of events between a start and end date.
304
+ *
305
+ * @hook mc_filter_events
306
+ *
307
+ * @param {array} $arr_events Array of event objects.
308
+ * @param {array} $args Event query arguments.
309
+ * @param {string} $context Current function context.
310
+ *
311
+ * @return {array}
312
+ */
313
  return apply_filters( 'mc_filter_events', $arr_events, $args, 'my_calendar_get_events' );
314
  }
315
 
427
  }
428
  }
429
 
430
+ /**
431
+ * Filter events returned by mc_get_all_events queries. Function returns a range of events based on proximity to the current date using parameters for number of days/events before or after today.
432
+ *
433
+ * @hook mc_filter_events
434
+ *
435
+ * @param {array} $arr_events Array of event objects.
436
+ * @param {array} $args Event query arguments.
437
+ * @param {string} $context Current function context.
438
+ *
439
+ * @return {array}
440
+ */
441
  return apply_filters( 'mc_filter_events', $arr_events, $args, 'mc_get_all_events' );
442
  }
443
 
469
  /**
470
  * Get most recently added events.
471
  *
472
+ * @param integer|false $cat_id Category ID.
473
  *
474
+ * @return array Event objects, limited by category if category ID passed.
475
  */
476
  function mc_get_new_events( $cat_id = false ) {
477
  global $wpdb;
486
  $cat = 'WHERE event_approved = 1 AND event_flagged <> 1';
487
  }
488
  $exclude_categories = mc_private_categories();
489
+ /**
490
+ * Filter how many days of newly added events will be included in ICS subscription links.
491
+ *
492
+ * @hook mc_rss_feed_date_range
493
+ *
494
+ * @param {int} $limit Number of days. Default 2.
495
+ *
496
+ * @return {int}
497
+ */
498
+ $limit = apply_filters( 'mc_rss_feed_date_range', 2 );
499
  $events = $mcdb->get_results(
500
  'SELECT *, ' . $ts_string . '
501
  FROM ' . my_calendar_event_table() . '
555
  /**
556
  * Fetch results of an event search.
557
  *
558
+ * @param array|string $search array (PRO) or string (Simple).
559
  *
560
  * @return array of event objects
561
  */
566
  if ( 'true' === get_option( 'mc_remote' ) && function_exists( 'mc_remote_db' ) ) {
567
  $mcdb = mc_remote_db();
568
  }
569
+ /**
570
+ * Filter number of past search results to return. Default 0.
571
+ *
572
+ * @hook mc_past_search_results
573
+ *
574
+ * @param {int} $before Number of results.
575
+ *
576
+ * @return {int}
577
+ */
578
  $before = apply_filters( 'mc_past_search_results', 0 );
579
+ /**
580
+ * Filter number of future search results to return. Default 15.
581
+ *
582
+ * @hook mc_future_search_results
583
+ *
584
+ * @param {int} $after Number of results.
585
+ *
586
+ * @return {int}
587
+ */
588
+ $after = apply_filters( 'mc_future_search_results', 15 ); // return only future events, nearest 10.
589
  if ( is_array( $search ) ) {
590
  // If from & to are set, we need to use a date-based event query.
591
  $from = $search['from'];
608
  'source' => 'search',
609
  );
610
 
611
+ /**
612
+ * Filter advanced search query arguments.
613
+ *
614
+ * @hook mc_search_attributes
615
+ *
616
+ * @param {array} $args Search query arguments.
617
+ * @param {string} $search Search term.
618
+ *
619
+ * @return {array}
620
+ */
621
  $args = apply_filters( 'mc_search_attributes', $args, $search );
622
  $event_array = my_calendar_events( $args );
623
  } else {
640
  }
641
  }
642
 
643
+ /**
644
+ * Filter search events.
645
+ *
646
+ * @hook mc_searched_events
647
+ *
648
+ * @param {array} $event_array Array of found event objects.
649
+ * @param {array} $args Search query arguments.
650
+ *
651
+ * @return {array}
652
+ */
653
  return apply_filters( 'mc_searched_events', $event_array, $args );
654
  }
655
 
659
  * @param int $id Event ID in my_calendar db.
660
  * @param boolean $rebuild Get core data only if doing an event rebuild.
661
  *
662
+ * @return object|false My Calendar Event
663
  */
664
  function mc_get_event_core( $id, $rebuild = false ) {
665
  if ( ! is_numeric( $id ) ) {
666
+ return false;
667
  }
668
 
669
  global $wpdb;
688
  *
689
  * @param int $id Event core ID.
690
  *
691
+ * @return object|boolean Event or false if no event found.
692
  */
693
  function mc_get_first_event( $id ) {
694
  global $wpdb;
712
  *
713
  * @param int $instance_id Event instance ID.
714
  *
715
+ * @return object|null query result
716
  */
717
  function mc_get_instance_data( $instance_id ) {
718
  global $wpdb;
751
  * @param int $id Event instance ID.
752
  * @param string $type 'object' or 'html'.
753
  *
754
+ * @return object|string
755
  */
756
  function mc_get_event( $id, $type = 'object' ) {
757
  if ( ! is_numeric( $id ) ) {
775
 
776
  return $value;
777
  }
 
 
778
  }
779
 
780
  /**
807
  * @return array Array of event objects with dates as keys.
808
  */
809
  function my_calendar_events( $args ) {
810
+ /**
811
+ * Filter calendar event query arguments.
812
+ *
813
+ * @hook my_calendar_events_args
814
+ *
815
+ * @param {array} $args Array of arguments for display and limiting of events.
816
+ *
817
+ * @return {array}
818
+ */
819
  $args = apply_filters( 'my_calendar_events_args', $args );
820
  $events = my_calendar_get_events( $args );
821
  $event_array = array();
842
  /**
843
  * Get one event currently happening.
844
  *
845
+ * @param string|integer $category category ID or 'default'.
846
+ * @param string $template display Template.
847
+ * @param integer|string|bool $site Site ID if fetching events from a different multisite instance.
848
  *
849
  * @return string output HTML
850
  */
872
  $select_location = '';
873
  $select_author = '';
874
  $select_host = '';
875
+ /**
876
+ * Set primary sort for getting today's events. Default 'occur_begin'.
877
+ *
878
+ * @hook mc_primary_sort
879
+ *
880
+ * @param {string} $primary_sort SQL sort column.
881
+ * @param {string} $context Current function.
882
+ *
883
+ * @return {string}
884
+ */
885
+ $primary_sort = apply_filters( 'mc_primary_sort', 'occur_begin', 'my_calendar_events_now' );
886
+ /**
887
+ * Set secondary sort for getting today's events. Default 'event_title ASC'.
888
+ *
889
+ * @hook mc_secondary_sort
890
+ *
891
+ * @param {string} $secondary_sort SQL sort column.
892
+ * @param {string} $context Current function.
893
+ *
894
+ * @return {string}
895
+ */
896
+ $secondary_sort = apply_filters( 'mc_secondary_sort', 'event_title ASC', 'my_calendar_events_now' );
897
+ $now = current_time( 'Y-m-d H:i:s' );
898
+ $event_query = 'SELECT *, ' . $ts_string . '
899
  FROM ' . my_calendar_event_table( $site ) . ' AS o
900
  JOIN ' . my_calendar_table( $site ) . " AS e
901
  ON (event_id=occur_event_id)
905
  WHERE $select_published $select_category $select_location $select_author $select_host
906
  $exclude_categories
907
  AND ( CAST('$now' AS DATETIME) BETWEEN occur_begin AND occur_end )
908
+ ORDER BY $primary_sort, $secondary_sort";
909
+ $events = $mcdb->get_results( $event_query );
 
910
  if ( ! empty( $events ) ) {
911
  foreach ( array_keys( $events ) as $key ) {
912
  $event =& $events[ $key ];
920
  $template = mc_get_custom_template( $template );
921
  }
922
 
923
+ /**
924
+ * Customize the template used to draw the "happening now" shortcode output.
925
+ *
926
+ * @hook mc_happening_next_template
927
+ *
928
+ * @param {string} $template HTML and template tags.
929
+ * @param {object} $event Event object to draw.
930
+ *
931
+ * @return {string}
932
+ */
933
  $output = mc_draw_template( $event, apply_filters( 'mc_happening_now_template', $template, $event ) );
934
  $return = mc_run_shortcodes( $output );
935
  } else {
946
  /**
947
  * Get the next scheduled event, not currently happening.
948
  *
949
+ * @param string|integer $category category ID or 'default'.
950
+ * @param string $template display Template.
951
+ * @param integer $skip Number of events to skip.
952
+ * @param integer|string|bool $site Site ID if fetching events from a different multisite instance.
953
  *
954
  * @return string output HTML
955
  */
1003
  $template = mc_get_custom_template( $template );
1004
  }
1005
 
1006
+ /**
1007
+ * Customize the template used to draw the next event shortcode output.
1008
+ *
1009
+ * @hook mc_happening_next_template
1010
+ *
1011
+ * @param {string} $template HTML and template tags.
1012
+ * @param {object} $event Event object to draw.
1013
+ *
1014
+ * @return {string}
1015
+ */
1016
  $output = mc_draw_template( $event, apply_filters( 'mc_happening_next_template', $template, $event ) );
1017
  $return = mc_run_shortcodes( $output );
1018
  } else {
1055
  function mc_instance_list( $args ) {
1056
  $id = isset( $args['event'] ) ? $args['event'] : false;
1057
  if ( ! $id ) {
1058
+ return '';
1059
  }
1060
  $template = isset( $args['template'] ) ? $args['template'] : '<h3>{title}</h3>{description}';
1061
  $list = isset( $args['list'] ) ? $args['list'] : '<li>{date}, {time}</li>';
1110
  *
1111
  * @return bool|string
1112
  */
1113
+ function mc_admin_instances( $id, $occur = 0 ) {
1114
  global $wpdb;
1115
  $output = '';
1116
  $ts_string = mc_ts();
1161
  global $wpdb;
1162
  $id = (int) $id;
1163
  if ( 0 === $id ) {
1164
+ return array();
1165
  }
1166
  $results = $wpdb->get_results( $wpdb->prepare( 'SELECT event_id FROM ' . my_calendar_table() . ' WHERE event_group_id=%d', $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
1167
 
my-calendar-generator.php CHANGED
@@ -27,8 +27,18 @@ function mc_generate( $format = 'shortcode' ) {
27
  $string = '';
28
  $templatekey = '';
29
  $append = '';
30
- $output = apply_filters( 'mc_shortcode_generator', false, $_POST );
31
- $array = array();
 
 
 
 
 
 
 
 
 
 
32
  if ( ! $output ) {
33
  switch ( $_POST['shortcode'] ) {
34
  case 'main':
@@ -45,7 +55,7 @@ function mc_generate( $format = 'shortcode' ) {
45
  default:
46
  $shortcode = 'my_calendar';
47
  }
48
- $keys = array( 'category', 'ltype', 'lvalue', 'search', 'format', 'time', 'year', 'month', 'day', 'months', 'above', 'below', 'author', 'host', 'order', 'from', 'to', 'type', 'show_today', 'skip', 'after', 'before', 'template', 'fallback' );
49
  foreach ( $_POST as $key => $value ) {
50
  if ( in_array( $key, $keys, true ) ) {
51
  if ( 'template' === $key ) {
@@ -120,7 +130,18 @@ function my_calendar_shortcodes() {
120
  <button type="button" role="tab" aria-selected="false" id='tab_mc_main' aria-controls='mc_main'><?php esc_html_e( 'Main', 'my-calendar' ); ?></button>
121
  <button type="button" role="tab" aria-selected="false" id='tab_mc_upcoming' aria-controls='mc_upcoming'><?php esc_html_e( 'Upcoming', 'my-calendar' ); ?></a></button>
122
  <button type="button" role="tab" aria-selected="false" id='tab_mc_today' aria-controls='mc_today'><?php esc_html_e( 'Today', 'my-calendar' ); ?></button>
123
- <?php echo apply_filters( 'mc_generator_tabs', '' ); ?>
 
 
 
 
 
 
 
 
 
 
 
124
  </div>
125
  <div class='wptab mc_main' id='mc_main' aria-live='assertive' aria-labelledby='tab_mc_main' role="tabpanel">
126
  <?php mc_generator( 'main' ); ?>
@@ -131,7 +152,18 @@ function my_calendar_shortcodes() {
131
  <div class='wptab mc_today' id='mc_today' aria-live='assertive' aria-labelledby='tab_mc_today' role="tabpanel">
132
  <?php mc_generator( 'today' ); ?>
133
  </div>
134
- <?php echo apply_filters( 'mc_generator_tab_content', '' ); ?>
 
 
 
 
 
 
 
 
 
 
 
135
  </div>
136
  </div>
137
  </div>
27
  $string = '';
28
  $templatekey = '';
29
  $append = '';
30
+ /**
31
+ * Inject custom shortcode generator output. Handles custom shortcode generation.
32
+ *
33
+ * @hook mc_shortcode_generator
34
+ *
35
+ * @param {string} $output Output from handling a POST request.
36
+ * @param {string} $post $_POST input.
37
+ *
38
+ * @return {string}
39
+ */
40
+ $output = apply_filters( 'mc_shortcode_generator', '', $_POST );
41
+ $array = array();
42
  if ( ! $output ) {
43
  switch ( $_POST['shortcode'] ) {
44
  case 'main':
55
  default:
56
  $shortcode = 'my_calendar';
57
  }
58
+ $keys = array( 'category', 'ltype', 'lvalue', 'search', 'format', 'time', 'year', 'month', 'day', 'months', 'above', 'below', 'author', 'host', 'order', 'from', 'to', 'type', 'show_today', 'skip', 'after', 'before', 'template', 'fallback', 'show_recurring' );
59
  foreach ( $_POST as $key => $value ) {
60
  if ( in_array( $key, $keys, true ) ) {
61
  if ( 'template' === $key ) {
130
  <button type="button" role="tab" aria-selected="false" id='tab_mc_main' aria-controls='mc_main'><?php esc_html_e( 'Main', 'my-calendar' ); ?></button>
131
  <button type="button" role="tab" aria-selected="false" id='tab_mc_upcoming' aria-controls='mc_upcoming'><?php esc_html_e( 'Upcoming', 'my-calendar' ); ?></a></button>
132
  <button type="button" role="tab" aria-selected="false" id='tab_mc_today' aria-controls='mc_today'><?php esc_html_e( 'Today', 'my-calendar' ); ?></button>
133
+ <?php
134
+ /**
135
+ * Insert a tab selector button into the shortcode generator tab list.
136
+ *
137
+ * @hook mc_generator_tabs
138
+ *
139
+ * @param {string} $tabs Tab HTML content.
140
+ *
141
+ * @return {string}
142
+ */
143
+ echo apply_filters( 'mc_generator_tabs', '' );
144
+ ?>
145
  </div>
146
  <div class='wptab mc_main' id='mc_main' aria-live='assertive' aria-labelledby='tab_mc_main' role="tabpanel">
147
  <?php mc_generator( 'main' ); ?>
152
  <div class='wptab mc_today' id='mc_today' aria-live='assertive' aria-labelledby='tab_mc_today' role="tabpanel">
153
  <?php mc_generator( 'today' ); ?>
154
  </div>
155
+ <?php
156
+ /**
157
+ * Insert a tab panel into the shortcode generator tabs.
158
+ *
159
+ * @hook mc_generator_tab_content
160
+ *
161
+ * @param {string} $tabs Tab HTML content.
162
+ *
163
+ * @return {string}
164
+ */
165
+ echo apply_filters( 'mc_generator_tab_content', '' );
166
+ ?>
167
  </div>
168
  </div>
169
  </div>
my-calendar-group-manager.php CHANGED
@@ -35,22 +35,24 @@ function my_calendar_group_edit() {
35
  global $mc_output;
36
  $nonce = $_REQUEST['_wpnonce'];
37
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
38
- wp_die( 'Security check failed' );
39
  }
40
  $message = '';
41
  switch ( $_POST['event_action'] ) {
42
  case 'edit':
43
  if ( isset( $_POST['apply'] ) && is_array( $_POST['apply'] ) ) {
44
- $mc_output = mc_check_group_data( $action, $_POST );
45
- foreach ( $_POST['apply'] as $event_id ) {
 
46
  $event_id = absint( $event_id );
47
- $response = my_calendar_save_group( $action, $mc_output, $event_id, $_POST );
48
  echo $response;
49
  }
50
  }
51
  break;
52
  case 'break':
53
- foreach ( $_POST['break'] as $event_id ) {
 
54
  $update = array( 'event_group_id' => 0 );
55
  $formats = array( '%d' );
56
  $result = $wpdb->update( my_calendar_table(), $update, array( 'event_id' => $event_id ), $formats, '%d' );
@@ -67,7 +69,7 @@ function my_calendar_group_edit() {
67
  break;
68
  case 'group':
69
  if ( isset( $_POST['group'] ) && is_array( $_POST['group'] ) ) {
70
- $events = $_POST['group'];
71
  sort( $events );
72
  foreach ( $events as $event_id ) {
73
  $group_id = $events[0];
@@ -137,9 +139,9 @@ function my_calendar_group_edit() {
137
  * @param int $event_id Event ID.
138
  * @param array $post POST data.
139
  *
140
- * @return message
141
  */
142
- function my_calendar_save_group( $action, $output, $event_id = false, $post = array() ) {
143
  global $wpdb, $event_author;
144
  $proceed = $output[0];
145
  $message = '';
@@ -152,6 +154,18 @@ function my_calendar_save_group( $action, $output, $event_id = false, $post = ar
152
  unset( $update['event_categories'] );
153
  mc_update_category_relationships( $cats, $event_id );
154
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  $update = apply_filters( 'mc_update_group_data', $update, $event_author, $action, $event_id );
156
  $formats = array( '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%d', '%d', '%d', '%f', '%f' );
157
 
@@ -164,7 +178,26 @@ function my_calendar_save_group( $action, $output, $event_id = false, $post = ar
164
  $update = array_merge( $update, $post );
165
  // Same as action on basic save.
166
  mc_event_post( 'edit', $update, $event_id, $result );
 
 
 
 
 
 
 
 
 
 
167
  do_action( 'mc_save_event', 'edit', $update, $event_id, $result );
 
 
 
 
 
 
 
 
 
168
  do_action( 'mc_save_grouped_events', $result, $event_id, $update );
169
  if ( false === $result ) {
170
  $message = mc_show_error( "#$event_id; " . __( 'Your event was not updated.', 'my-calendar' ) . " $url", false );
@@ -185,11 +218,11 @@ function my_calendar_save_group( $action, $output, $event_id = false, $post = ar
185
  /**
186
  * Compare events within a group to see if they currently have the same information.
187
  *
188
- * @param int $group_id Group ID.
189
- * @param string $field Column name of field to compare. Optional.
190
- * @param bool $echo False to return if single field comparison.
191
  *
192
- * @return boolean True if information is the same. Echo string for single field checks.
193
  */
194
  function mc_compare_group_members( $group_id, $field = false, $echo = true ) {
195
  global $wpdb;
@@ -271,7 +304,7 @@ function mc_group_form( $group_id, $type = 'break' ) {
271
  * @param int $event_id Event ID.
272
  * @param int $group_id Group ID.
273
  */
274
- function mc_edit_groups( $mode = 'edit', $event_id = false, $group_id = false ) {
275
  global $submission;
276
  $event_id = ( 0 === $event_id ) ? false : $event_id;
277
  $group_id = ( 0 === $group_id ) ? false : $group_id;
@@ -287,7 +320,7 @@ function mc_edit_groups( $mode = 'edit', $event_id = false, $group_id = false )
287
  mc_show_error( $message );
288
  echo wp_kses( $group, mc_kses_elements() );
289
 
290
- my_calendar_print_group_fields( $data, $mode, $event_id, $group_id );
291
  }
292
 
293
  /**
@@ -296,11 +329,10 @@ function mc_edit_groups( $mode = 'edit', $event_id = false, $group_id = false )
296
  * @param object $data Event object data.
297
  * @param string $mode Editing mode.
298
  * @param int $event_id Event ID.
299
- * @param int $group_id Group ID.
300
  */
301
- function my_calendar_print_group_fields( $data, $mode, $event_id, $group_id = '' ) {
302
  global $user_ID;
303
- $has_data = ( empty( $data ) ) ? false : true;
304
  $user = get_userdata( $user_ID );
305
  $group_id = ( ! empty( $data->event_group_id ) ) ? $data->event_group_id : mc_group_id();
306
  $title = '';
@@ -308,7 +340,7 @@ function my_calendar_print_group_fields( $data, $mode, $event_id, $group_id = ''
308
  $short = '';
309
  $image = '';
310
 
311
- if ( ! empty( $data ) ) {
312
  $title = stripslashes( $data->event_title );
313
  $description = stripslashes( $data->event_desc );
314
  $short = stripslashes( $data->event_short );
@@ -354,9 +386,9 @@ function my_calendar_print_group_fields( $data, $mode, $event_id, $group_id = ''
354
 
355
  if ( '0' === $data->event_repeats && ( 'S1' === $data->event_recur || 'S' === $data->event_recur ) ) {
356
  $span_checked = '';
357
- if ( ! empty( $data ) && '1' === $data->event_span ) {
358
  $span_checked = ' checked="checked"';
359
- } elseif ( ! empty( $data ) && '0' === $data->event_span ) {
360
  $span_checked = '';
361
  }
362
  ?>
@@ -441,6 +473,7 @@ function my_calendar_print_group_fields( $data, $mode, $event_id, $group_id = ''
441
  }
442
  $button_text = __( 'Select Featured Image', 'my-calendar' );
443
  $remove = '';
 
444
  if ( '' !== $image ) {
445
  $alt = ( $image_id ) ? get_post_meta( $image_id, '_wp_attachment_image_alt', true ) : '';
446
  $remove = '<button type="button" class="button remove-image" aria-describedby="event_image">' . esc_html__( 'Remove Featured Image', 'my-calendar' ) . '</button>';
@@ -505,9 +538,10 @@ function my_calendar_print_group_fields( $data, $mode, $event_id, $group_id = ''
505
  </p>
506
  <?php
507
  if ( mc_show_edit_block( 'event_link' ) ) {
508
- if ( ! empty( $data ) && '1' === $data->event_link_expires ) {
 
509
  $exp_checked = ' checked="checked"';
510
- } elseif ( ! empty( $data ) && '0' === $data->event_link_expires ) {
511
  $exp_checked = '';
512
  } elseif ( mc_event_link_expires() ) {
513
  $exp_checked = ' checked="checked"';
@@ -520,7 +554,7 @@ function my_calendar_print_group_fields( $data, $mode, $event_id, $group_id = ''
520
  mc_compare_group_members( $group_id, 'event_link' );
521
  ?>
522
  </label>
523
- <input type="text" id="e_link" name="event_link" size="40" value="<?php echo ( ! empty( $data ) ) ? esc_url( $data->event_link ) : ''; ?>" />
524
  <input type="checkbox" value="1" id="e_link_expires" name="event_link_expires"<?php echo $exp_checked; ?> />
525
  <label for="e_link_expires"><?php esc_html_e( 'Link will expire after event.', 'my-calendar' ); ?></label>
526
  </p>
@@ -531,7 +565,20 @@ function my_calendar_print_group_fields( $data, $mode, $event_id, $group_id = ''
531
  </div>
532
  </div>
533
  <?php
534
- if ( mc_show_edit_block( 'event_open' ) && '' !== apply_filters( 'mc_event_registration', '', $has_data, $data, 'admin' ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
535
  ?>
536
  <div class="ui-sortable meta-box-sortables">
537
  <div class="postbox">
@@ -540,7 +587,7 @@ function my_calendar_print_group_fields( $data, $mode, $event_id, $group_id = ''
540
  <div class="inside">
541
  <fieldset>
542
  <legend><?php esc_html_e( 'Event Registration Status', 'my-calendar' ); ?></legend>
543
- <?php echo apply_filters( 'mc_event_registration', '', $has_data, $data, 'admin' ); ?>
544
  </fieldset>
545
  </div>
546
  </div>
@@ -589,6 +636,16 @@ function my_calendar_print_group_fields( $data, $mode, $event_id, $group_id = ''
589
  * @return mixed array/object $data checked array or object if error found
590
  */
591
  function mc_check_group_data( $action, $post ) {
 
 
 
 
 
 
 
 
 
 
592
  $post = apply_filters( 'mc_groups_pre_checkdata', $post, $action );
593
  global $wpdb, $current_user, $submission;
594
 
@@ -603,10 +660,12 @@ function mc_check_group_data( $action, $post ) {
603
  }
604
  $errors = '';
605
  if ( 'add' === $action || 'edit' === $action || 'copy' === $action ) {
606
- $title = ! empty( $post['event_title'] ) ? trim( $post['event_title'] ) : '';
607
- $desc = ! empty( $post['content'] ) ? trim( $post['content'] ) : '';
608
- $short = ! empty( $post['event_short'] ) ? trim( $post['event_short'] ) : '';
609
- $host = ! empty( $post['event_host'] ) ? $post['event_host'] : $current_user->ID;
 
 
610
  if ( isset( $post['event_category'] ) ) {
611
  $cats = $post['event_category'];
612
  if ( is_array( $cats ) && ! empty( $cats ) ) {
@@ -670,52 +729,51 @@ function mc_check_group_data( $action, $post ) {
670
  if ( ! ( '' === $event_link || preg_match( '/^(http)(s?)(:)\/\//', $event_link ) ) ) {
671
  $event_link = 'http://' . $event_link;
672
  }
673
- }
674
- // A title is required, and can't be more than 255 characters.
675
- $title_length = strlen( $title );
676
- if ( ! ( $title_length >= 1 && $title_length <= 255 ) ) {
677
- $title = __( 'Untitled Event', 'my-calendar' );
678
- }
679
- $proceed = true;
680
- $submit = array(
681
- // Begin strings.
682
- 'event_title' => $title,
683
- 'event_desc' => $desc,
684
- 'event_short' => $short,
685
- 'event_link' => $event_link,
686
- 'event_label' => $event_label,
687
- 'event_street' => $event_street,
688
- 'event_street2' => $event_street2,
689
- 'event_city' => $event_city,
690
- 'event_state' => $event_state,
691
- 'event_postcode' => $event_postcode,
692
- 'event_region' => $event_region,
693
- 'event_country' => $event_country,
694
- 'event_url' => $event_url,
695
- 'event_image' => $event_image,
696
- 'event_phone' => $event_phone,
697
- 'event_access' => serialize( $event_access ),
698
- 'event_tickets' => $event_tickets,
699
- 'event_registration' => $event_registration,
700
- // Begin integers.
701
- 'event_category' => $primary,
702
- 'event_link_expires' => $expires,
703
- 'event_zoom' => $event_zoom,
704
- 'event_host' => $host,
705
- 'event_span' => $event_span,
706
- // Begin floats.
707
- 'event_longitude' => $event_longitude,
708
- 'event_latitude' => $event_latitude,
709
- // Array (not saved directly).
710
- 'event_categories' => $cats,
711
- );
712
-
713
- $submit = array_map( 'mc_kses_post', $submit );
714
 
715
- if ( 'edit' === $action ) {
716
- unset( $submit['event_author'] );
 
 
 
717
  }
718
- $data = array( $proceed, false, $submit, $errors );
719
 
720
  return $data;
721
  }
@@ -888,8 +946,8 @@ function mc_list_groups() {
888
  <td>
889
  <strong>
890
  <?php
 
891
  if ( $can_edit ) {
892
- $edit_link = '';
893
  if ( $is_grouped ) {
894
  $edit_link = admin_url( "admin.php?page=my-calendar-manage&groups=true&amp;mode=edit&amp;event_id=$event->event_id&amp;group_id=$event->event_group_id" );
895
  }
35
  global $mc_output;
36
  $nonce = $_REQUEST['_wpnonce'];
37
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
38
+ wp_die( 'My Calendar: Security check failed' );
39
  }
40
  $message = '';
41
  switch ( $_POST['event_action'] ) {
42
  case 'edit':
43
  if ( isset( $_POST['apply'] ) && is_array( $_POST['apply'] ) ) {
44
+ $post = map_deep( $_POST, 'sanitize_textarea_field' );
45
+ $mc_output = mc_check_group_data( $action, $post );
46
+ foreach ( $post['apply'] as $event_id ) {
47
  $event_id = absint( $event_id );
48
+ $response = my_calendar_save_group( $action, $mc_output, $event_id, $post );
49
  echo $response;
50
  }
51
  }
52
  break;
53
  case 'break':
54
+ $events = map_deep( $_POST['break'], 'absint' );
55
+ foreach ( $events as $event_id ) {
56
  $update = array( 'event_group_id' => 0 );
57
  $formats = array( '%d' );
58
  $result = $wpdb->update( my_calendar_table(), $update, array( 'event_id' => $event_id ), $formats, '%d' );
69
  break;
70
  case 'group':
71
  if ( isset( $_POST['group'] ) && is_array( $_POST['group'] ) ) {
72
+ $events = map_deep( $_POST['group'], 'absint' );
73
  sort( $events );
74
  foreach ( $events as $event_id ) {
75
  $group_id = $events[0];
139
  * @param int $event_id Event ID.
140
  * @param array $post POST data.
141
  *
142
+ * @return string message
143
  */
144
+ function my_calendar_save_group( $action, $output, $event_id, $post = array() ) {
145
  global $wpdb, $event_author;
146
  $proceed = $output[0];
147
  $message = '';
154
  unset( $update['event_categories'] );
155
  mc_update_category_relationships( $cats, $event_id );
156
 
157
+ /**
158
+ * Filter calendar update data before saving an individual event when managing groups.
159
+ *
160
+ * @hook mc_update_group_data
161
+ *
162
+ * @param {array} $update Event update data for groups.
163
+ * @param {string} $event_author Author for these events.
164
+ * @param {string} $action Action performed.
165
+ * @param {int} $event_id Event ID being updated.
166
+ *
167
+ * @return {array}
168
+ */
169
  $update = apply_filters( 'mc_update_group_data', $update, $event_author, $action, $event_id );
170
  $formats = array( '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%d', '%d', '%d', '%f', '%f' );
171
 
178
  $update = array_merge( $update, $post );
179
  // Same as action on basic save.
180
  mc_event_post( 'edit', $update, $event_id, $result );
181
+ /**
182
+ * Run action when an event is saved.
183
+ *
184
+ * @hook mc_save_event
185
+ *
186
+ * @param {string} $action Current action: edit, copy, add.
187
+ * @param {array} $update Data updated.
188
+ * @param {int} $event_id Event ID.
189
+ * @param {int|false} $result Result of the DB update query.
190
+ */
191
  do_action( 'mc_save_event', 'edit', $update, $event_id, $result );
192
+ /**
193
+ * Run action when a group of events are saved.
194
+ *
195
+ * @hook mc_save_grouped_events
196
+ *
197
+ * @param {int|false} $result Current action: edit, copy, add.
198
+ * @param {int} $event_id Event ID.
199
+ * @param {array} $update Updated data.
200
+ */
201
  do_action( 'mc_save_grouped_events', $result, $event_id, $update );
202
  if ( false === $result ) {
203
  $message = mc_show_error( "#$event_id; " . __( 'Your event was not updated.', 'my-calendar' ) . " $url", false );
218
  /**
219
  * Compare events within a group to see if they currently have the same information.
220
  *
221
+ * @param int $group_id Group ID.
222
+ * @param string|false $field Column name of field to compare. False to check all grouped fields.
223
+ * @param bool $echo False to return if single field comparison.
224
  *
225
+ * @return string|boolean True if information is the same for multiple fields; string for single field checks.
226
  */
227
  function mc_compare_group_members( $group_id, $field = false, $echo = true ) {
228
  global $wpdb;
304
  * @param int $event_id Event ID.
305
  * @param int $group_id Group ID.
306
  */
307
+ function mc_edit_groups( $mode = 'edit', $event_id = 0, $group_id = 0 ) {
308
  global $submission;
309
  $event_id = ( 0 === $event_id ) ? false : $event_id;
310
  $group_id = ( 0 === $group_id ) ? false : $group_id;
320
  mc_show_error( $message );
321
  echo wp_kses( $group, mc_kses_elements() );
322
 
323
+ my_calendar_print_group_fields( $data, $mode, $event_id );
324
  }
325
 
326
  /**
329
  * @param object $data Event object data.
330
  * @param string $mode Editing mode.
331
  * @param int $event_id Event ID.
 
332
  */
333
+ function my_calendar_print_group_fields( $data, $mode, $event_id ) {
334
  global $user_ID;
335
+ $has_data = ( is_object( $data ) ) ? true : false;
336
  $user = get_userdata( $user_ID );
337
  $group_id = ( ! empty( $data->event_group_id ) ) ? $data->event_group_id : mc_group_id();
338
  $title = '';
340
  $short = '';
341
  $image = '';
342
 
343
+ if ( is_object( $data ) ) {
344
  $title = stripslashes( $data->event_title );
345
  $description = stripslashes( $data->event_desc );
346
  $short = stripslashes( $data->event_short );
386
 
387
  if ( '0' === $data->event_repeats && ( 'S1' === $data->event_recur || 'S' === $data->event_recur ) ) {
388
  $span_checked = '';
389
+ if ( is_object( $data ) && '1' === $data->event_span ) {
390
  $span_checked = ' checked="checked"';
391
+ } elseif ( is_object( $data ) && '0' === $data->event_span ) {
392
  $span_checked = '';
393
  }
394
  ?>
473
  }
474
  $button_text = __( 'Select Featured Image', 'my-calendar' );
475
  $remove = '';
476
+ $alt = '';
477
  if ( '' !== $image ) {
478
  $alt = ( $image_id ) ? get_post_meta( $image_id, '_wp_attachment_image_alt', true ) : '';
479
  $remove = '<button type="button" class="button remove-image" aria-describedby="event_image">' . esc_html__( 'Remove Featured Image', 'my-calendar' ) . '</button>';
538
  </p>
539
  <?php
540
  if ( mc_show_edit_block( 'event_link' ) ) {
541
+ $exp_checked = '';
542
+ if ( is_object( $data ) && '1' === $data->event_link_expires ) {
543
  $exp_checked = ' checked="checked"';
544
+ } elseif ( is_object( $data ) && '0' === $data->event_link_expires ) {
545
  $exp_checked = '';
546
  } elseif ( mc_event_link_expires() ) {
547
  $exp_checked = ' checked="checked"';
554
  mc_compare_group_members( $group_id, 'event_link' );
555
  ?>
556
  </label>
557
+ <input type="text" id="e_link" name="event_link" size="40" value="<?php echo ( is_object( $data ) ) ? esc_url( $data->event_link ) : ''; ?>" />
558
  <input type="checkbox" value="1" id="e_link_expires" name="event_link_expires"<?php echo $exp_checked; ?> />
559
  <label for="e_link_expires"><?php esc_html_e( 'Link will expire after event.', 'my-calendar' ); ?></label>
560
  </p>
565
  </div>
566
  </div>
567
  <?php
568
+ /**
569
+ * Filter event registration fields.
570
+ *
571
+ * @hook mc_event_registration
572
+ *
573
+ * @param {string} $event_registration_output HTML output. Default empty.
574
+ * @param {bool} $has_data Whether this event has data.
575
+ * @param {object} $data Event data object.
576
+ * @param {string} $context Indicates this is running in the admin.
577
+ *
578
+ * @return {string}
579
+ */
580
+ $event_registration_output = apply_filters( 'mc_event_registration', '', $has_data, $data, 'admin' );
581
+ if ( mc_show_edit_block( 'event_open' ) && '' !== $event_registration_output ) {
582
  ?>
583
  <div class="ui-sortable meta-box-sortables">
584
  <div class="postbox">
587
  <div class="inside">
588
  <fieldset>
589
  <legend><?php esc_html_e( 'Event Registration Status', 'my-calendar' ); ?></legend>
590
+ <?php echo $event_registration_output; ?>
591
  </fieldset>
592
  </div>
593
  </div>
636
  * @return mixed array/object $data checked array or object if error found
637
  */
638
  function mc_check_group_data( $action, $post ) {
639
+ /**
640
+ * Filter posted event group data prior to running event data checks.
641
+ *
642
+ * @hook mc_groups_pre_checkdata
643
+ *
644
+ * @param {array} $post POST data.
645
+ * @param {string} $action Type of action running.(add, edit, or copy.)
646
+ *
647
+ * @return {array}
648
+ */
649
  $post = apply_filters( 'mc_groups_pre_checkdata', $post, $action );
650
  global $wpdb, $current_user, $submission;
651
 
660
  }
661
  $errors = '';
662
  if ( 'add' === $action || 'edit' === $action || 'copy' === $action ) {
663
+ $title = ! empty( $post['event_title'] ) ? trim( $post['event_title'] ) : '';
664
+ $desc = ! empty( $post['content'] ) ? trim( $post['content'] ) : '';
665
+ $short = ! empty( $post['event_short'] ) ? trim( $post['event_short'] ) : '';
666
+ $host = ! empty( $post['event_host'] ) ? $post['event_host'] : $current_user->ID;
667
+ $cats = array();
668
+ $primary = 1;
669
  if ( isset( $post['event_category'] ) ) {
670
  $cats = $post['event_category'];
671
  if ( is_array( $cats ) && ! empty( $cats ) ) {
729
  if ( ! ( '' === $event_link || preg_match( '/^(http)(s?)(:)\/\//', $event_link ) ) ) {
730
  $event_link = 'http://' . $event_link;
731
  }
732
+ // A title is required, and can't be more than 255 characters.
733
+ $title_length = strlen( $title );
734
+ if ( ! ( $title_length >= 1 && $title_length <= 255 ) ) {
735
+ $title = __( 'Untitled Event', 'my-calendar' );
736
+ }
737
+ $proceed = true;
738
+ $submit = array(
739
+ // Begin strings.
740
+ 'event_title' => $title,
741
+ 'event_desc' => $desc,
742
+ 'event_short' => $short,
743
+ 'event_link' => $event_link,
744
+ 'event_label' => $event_label,
745
+ 'event_street' => $event_street,
746
+ 'event_street2' => $event_street2,
747
+ 'event_city' => $event_city,
748
+ 'event_state' => $event_state,
749
+ 'event_postcode' => $event_postcode,
750
+ 'event_region' => $event_region,
751
+ 'event_country' => $event_country,
752
+ 'event_url' => $event_url,
753
+ 'event_image' => $event_image,
754
+ 'event_phone' => $event_phone,
755
+ 'event_access' => serialize( $event_access ),
756
+ 'event_tickets' => $event_tickets,
757
+ 'event_registration' => $event_registration,
758
+ // Begin integers.
759
+ 'event_category' => $primary,
760
+ 'event_link_expires' => $expires,
761
+ 'event_zoom' => $event_zoom,
762
+ 'event_host' => $host,
763
+ 'event_span' => $event_span,
764
+ // Begin floats.
765
+ 'event_longitude' => $event_longitude,
766
+ 'event_latitude' => $event_latitude,
767
+ // Array (not saved directly).
768
+ 'event_categories' => $cats,
769
+ );
 
 
 
770
 
771
+ $submit = array_map( 'mc_kses_post', $submit );
772
+ $data = array( $proceed, false, $submit, $errors );
773
+ } else {
774
+ $proceed = false;
775
+ $data = array( $proceed, false, array(), __( 'Invalid Action', 'my-calendar' ) );
776
  }
 
777
 
778
  return $data;
779
  }
946
  <td>
947
  <strong>
948
  <?php
949
+ $edit_link = '';
950
  if ( $can_edit ) {
 
951
  if ( $is_grouped ) {
952
  $edit_link = admin_url( "admin.php?page=my-calendar-manage&groups=true&amp;mode=edit&amp;event_id=$event->event_id&amp;group_id=$event->event_group_id" );
953
  }
my-calendar-help.php CHANGED
@@ -59,7 +59,14 @@ function my_calendar_help() {
59
  echo '<li>' . sprintf( __( 'When you\'re ready, <a href="%s">read the documentation</a>.', 'my-calendar' ), 'https://docs.joedolson.com/my-calendar/' ) . '</li>';
60
  ?>
61
  </ul>
62
- <?php do_action( 'mc_before_help' ); ?>
 
 
 
 
 
 
 
63
  </div>
64
  </div>
65
  </div>
@@ -115,7 +122,14 @@ function my_calendar_help() {
115
  </div>
116
  </div>
117
 
118
- <?php do_action( 'mc_after_help' ); ?>
 
 
 
 
 
 
 
119
 
120
  <div class="ui-sortable meta-box-sortables" id="get-support">
121
  <div class="wptab postbox" aria-labelledby="tab_support" role="tabpanel" id="my-calendar-support">
59
  echo '<li>' . sprintf( __( 'When you\'re ready, <a href="%s">read the documentation</a>.', 'my-calendar' ), 'https://docs.joedolson.com/my-calendar/' ) . '</li>';
60
  ?>
61
  </ul>
62
+ <?php
63
+ /**
64
+ * Execute action after My Calendar Help data.
65
+ *
66
+ * @hook mc_after_help
67
+ */
68
+ do_action( 'mc_before_help' );
69
+ ?>
70
  </div>
71
  </div>
72
  </div>
122
  </div>
123
  </div>
124
 
125
+ <?php
126
+ /**
127
+ * Execute action after My Calendar Help data.
128
+ *
129
+ * @hook mc_after_help
130
+ */
131
+ do_action( 'mc_after_help' );
132
+ ?>
133
 
134
  <div class="ui-sortable meta-box-sortables" id="get-support">
135
  <div class="wptab postbox" aria-labelledby="tab_support" role="tabpanel" id="my-calendar-support">
my-calendar-iframe.php CHANGED
@@ -32,7 +32,7 @@ function my_calendar_iframe() {
32
  $body = '';
33
  ?>
34
  <!DOCTYPE html>
35
- <html<?php language_attributes(); ?>>
36
  <head>
37
  <?php
38
  wp_head();
32
  $body = '';
33
  ?>
34
  <!DOCTYPE html>
35
+ <html <?php language_attributes(); ?>>
36
  <head>
37
  <?php
38
  wp_head();
my-calendar-install.php CHANGED
@@ -39,6 +39,15 @@ function mc_widget_defaults() {
39
  ),
40
  );
41
 
 
 
 
 
 
 
 
 
 
42
  return apply_filters( 'mc_widget_defaults', $defaults );
43
  }
44
 
@@ -231,6 +240,15 @@ function mc_create_demo_content() {
231
  'location_access' => serialize( $access ),
232
  );
233
  $results = mc_insert_location( $add );
 
 
 
 
 
 
 
 
 
234
  do_action( 'mc_save_location', $results, $add, $add );
235
  // Insert an event.
236
  $submit = array(
@@ -298,9 +316,7 @@ function mc_create_demo_content() {
298
  */
299
  function mc_default_settings() {
300
  $globals = mc_globals();
301
- foreach ( $globals as $key => $global ) {
302
- ${$key} = $global;
303
- }
304
  add_option( 'mc_display_single', array( 'author', 'ical', 'address', 'gcal', 'description', 'image', 'tickets', 'access', 'link', 'gmap_link' ) );
305
  add_option( 'mc_display_main', array( 'address', 'excerpt', 'image', 'tickets', 'access', 'gmap_link', 'more' ) );
306
  add_option( 'mc_display_mini', array( 'excerpt', 'image', 'more' ) );
@@ -329,10 +345,10 @@ function mc_default_settings() {
329
  'title_list' => '{title}',
330
  'title_solo' => '{title}',
331
  'link' => __( 'More information', 'my-calendar' ),
332
- 'grid' => $grid_template,
333
- 'list' => $list_template,
334
- 'mini' => $mini_template,
335
- 'details' => $single_template,
336
  'label' => __( 'Read more', 'my-calendar' ),
337
  )
338
  );
@@ -365,12 +381,12 @@ function mc_default_settings() {
365
  mc_generate_calendar_page( $slug );
366
  }
367
  require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
368
- dbDelta( $initial_db );
369
- dbDelta( $initial_occur_db );
370
- dbDelta( $initial_cat_db );
371
- dbDelta( $initial_rel_db );
372
- dbDelta( $initial_loc_db );
373
- dbDelta( $initial_loc_rel_db );
374
  }
375
 
376
  /**
@@ -515,7 +531,7 @@ function my_calendar_copyr( $source, $dest ) {
515
  *
516
  * @param string $dirname File directory.
517
  *
518
- * @return result.
519
  */
520
  function my_calendar_rmdirr( $dirname ) {
521
  // Sanity check.
39
  ),
40
  );
41
 
42
+ /**
43
+ * Customize the default values used for Upcoming and Today's events widgets.
44
+ *
45
+ * @hook mc_widget_defaults
46
+ *
47
+ * @param {array} $defaults Array of values used to set up upcoming and today's events lists in widgets.
48
+ *
49
+ * @return {array}
50
+ */
51
  return apply_filters( 'mc_widget_defaults', $defaults );
52
  }
53
 
240
  'location_access' => serialize( $access ),
241
  );
242
  $results = mc_insert_location( $add );
243
+ /**
244
+ * Executed an action when the demo location is saved at installation.
245
+ *
246
+ * @hook mc_save_location
247
+ *
248
+ * @param {int|false} $results Result of database insertion. Row ID or false.
249
+ * @param {array} $add Array of location parameters to add.
250
+ * @param {array} $add Demo location array.
251
+ */
252
  do_action( 'mc_save_location', $results, $add, $add );
253
  // Insert an event.
254
  $submit = array(
316
  */
317
  function mc_default_settings() {
318
  $globals = mc_globals();
319
+
 
 
320
  add_option( 'mc_display_single', array( 'author', 'ical', 'address', 'gcal', 'description', 'image', 'tickets', 'access', 'link', 'gmap_link' ) );
321
  add_option( 'mc_display_main', array( 'address', 'excerpt', 'image', 'tickets', 'access', 'gmap_link', 'more' ) );
322
  add_option( 'mc_display_mini', array( 'excerpt', 'image', 'more' ) );
345
  'title_list' => '{title}',
346
  'title_solo' => '{title}',
347
  'link' => __( 'More information', 'my-calendar' ),
348
+ 'grid' => $globals['grid_template'],
349
+ 'list' => $globals['list_template'],
350
+ 'mini' => $globals['mini_template'],
351
+ 'details' => $globals['single_template'],
352
  'label' => __( 'Read more', 'my-calendar' ),
353
  )
354
  );
381
  mc_generate_calendar_page( $slug );
382
  }
383
  require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
384
+ dbDelta( $globals['initial_db'] );
385
+ dbDelta( $globals['initial_occur_db'] );
386
+ dbDelta( $globals['initial_cat_db'] );
387
+ dbDelta( $globals['initial_rel_db'] );
388
+ dbDelta( $globals['initial_loc_db'] );
389
+ dbDelta( $globals['initial_loc_rel_db'] );
390
  }
391
 
392
  /**
531
  *
532
  * @param string $dirname File directory.
533
  *
534
+ * @return bool
535
  */
536
  function my_calendar_rmdirr( $dirname ) {
537
  // Sanity check.
my-calendar-limits.php CHANGED
@@ -28,6 +28,15 @@ function mc_prepare_search_query( $query ) {
28
  if ( '' !== trim( $query ) ) {
29
  $query = esc_sql( urldecode( urldecode( $query ) ) );
30
  if ( 'MyISAM' === $db_type && $length > 3 ) {
 
 
 
 
 
 
 
 
 
31
  $search = ' AND MATCH(' . apply_filters( 'mc_search_fields', 'event_title,event_desc,event_short,event_label,event_city,event_postcode,event_registration' ) . ") AGAINST ( '$query' IN BOOLEAN MODE ) ";
32
  } else {
33
  $search = " AND ( event_title LIKE '%$query%' OR event_desc LIKE '%$query%' OR event_short LIKE '%$query%' OR event_label LIKE '%$query%' OR event_city LIKE '%$query%' OR event_postcode LIKE '%$query%' OR event_registration LIKE '%$query%' ) ";
@@ -41,11 +50,11 @@ function mc_prepare_search_query( $query ) {
41
  /**
42
  * Generate WHERE pattern for a given category passed
43
  *
44
- * @param mixed int/string $category Single or list of categories separated by commas using IDs or names.
45
- * @param string $type context of query.
46
- * @param string $group context of query.
47
  *
48
- * @return string SQL modifiers.
49
  */
50
  function mc_select_category( $category, $type = 'event', $group = 'events' ) {
51
  if ( ! $category || 'all' === $category ) {
@@ -85,7 +94,6 @@ function mc_category_select_ids( $category ) {
85
  global $wpdb;
86
  $mcdb = $wpdb;
87
  $select = array();
88
-
89
  if ( 'true' === get_option( 'mc_remote' ) && function_exists( 'mc_remote_db' ) ) {
90
  $mcdb = mc_remote_db();
91
  }
@@ -98,6 +106,7 @@ function mc_category_select_ids( $category ) {
98
  }
99
  $numcat = count( $categories );
100
  foreach ( $categories as $key ) {
 
101
  $key = trim( $key );
102
  if ( is_numeric( $key ) ) {
103
  $add = (int) $key;
@@ -108,7 +117,9 @@ function mc_category_select_ids( $category ) {
108
  $add = $cat->category_id;
109
  }
110
  }
111
- $select[] = $add;
 
 
112
  }
113
  } else {
114
  $category = trim( $category );
@@ -128,9 +139,9 @@ function mc_category_select_ids( $category ) {
128
  /**
129
  * Get select parameter values for authors & hosts
130
  *
131
- * @param string $author numeric or string tokens for authors or list of authors.
132
- * @param string $type context of query.
133
- * @param string $context context of data.
134
  *
135
  * @return string WHERE limits
136
  */
@@ -139,7 +150,7 @@ function mc_select_author( $author, $type = 'event', $context = 'author' ) {
139
  return '';
140
  }
141
  $author = urldecode( $author );
142
- if ( '' === $author || 'all' === $author || 'default' === $author || null === $author ) {
143
  return '';
144
  }
145
  $select_author = '';
@@ -205,8 +216,8 @@ function mc_author_select_ids( $author ) {
205
  *
206
  * @uses mc_select_author()
207
  *
208
- * @param mixed int/string $host Host ID or name..
209
- * @param string $type context.
210
  *
211
  * @return string SQL
212
  */
@@ -219,8 +230,8 @@ function mc_select_host( $host, $type = 'event' ) {
219
  /**
220
  * Function to limit event query by location.
221
  *
222
- * @param string $ltype {location type}.
223
- * @param mixed string|integer $lvalue {location value}.
224
  *
225
  * @return string
226
  */
@@ -228,8 +239,6 @@ function mc_select_location( $ltype = '', $lvalue = '' ) {
228
  $limit_string = '';
229
  $limit_strings = array();
230
  $location = '';
231
- $ltype = apply_filters( 'mc_select_ltype', $ltype );
232
- $lvalue = apply_filters( 'mc_select_lvalue', $lvalue );
233
  if ( ! $ltype || ! $lvalue ) {
234
  return '';
235
  } else {
@@ -285,6 +294,17 @@ function mc_select_location( $ltype = '', $lvalue = '' ) {
285
  $limit_string = 'AND (' . implode( ' OR ', $limit_strings ) . ')';
286
  }
287
 
 
 
 
 
 
 
 
 
 
 
 
288
  return apply_filters( 'mc_location_limit_sql', $limit_string, $ltype, $lvalue );
289
  }
290
 
@@ -307,7 +327,7 @@ function mc_access_limit( $access ) {
307
  /**
308
  * SQL modifiers for published vs. preview
309
  *
310
- * @return boolean
311
  */
312
  function mc_select_published() {
313
  if ( mc_is_preview() ) {
28
  if ( '' !== trim( $query ) ) {
29
  $query = esc_sql( urldecode( urldecode( $query ) ) );
30
  if ( 'MyISAM' === $db_type && $length > 3 ) {
31
+ /**
32
+ * Customize the MATCH fields for a MyISAM boolean search query.
33
+ *
34
+ * @hook mc_search_fields
35
+ *
36
+ * @param {string} $values Comma-separated list of columns.
37
+ *
38
+ * @return {string}
39
+ */
40
  $search = ' AND MATCH(' . apply_filters( 'mc_search_fields', 'event_title,event_desc,event_short,event_label,event_city,event_postcode,event_registration' ) . ") AGAINST ( '$query' IN BOOLEAN MODE ) ";
41
  } else {
42
  $search = " AND ( event_title LIKE '%$query%' OR event_desc LIKE '%$query%' OR event_short LIKE '%$query%' OR event_label LIKE '%$query%' OR event_city LIKE '%$query%' OR event_postcode LIKE '%$query%' OR event_registration LIKE '%$query%' ) ";
50
  /**
51
  * Generate WHERE pattern for a given category passed
52
  *
53
+ * @param int|string $category Single or list of categories separated by commas using IDs or names.
54
+ * @param string $type context of query.
55
+ * @param string $group context of query.
56
  *
57
+ * @return array<string> SQL clauses.
58
  */
59
  function mc_select_category( $category, $type = 'event', $group = 'events' ) {
60
  if ( ! $category || 'all' === $category ) {
94
  global $wpdb;
95
  $mcdb = $wpdb;
96
  $select = array();
 
97
  if ( 'true' === get_option( 'mc_remote' ) && function_exists( 'mc_remote_db' ) ) {
98
  $mcdb = mc_remote_db();
99
  }
106
  }
107
  $numcat = count( $categories );
108
  foreach ( $categories as $key ) {
109
+ $add = false;
110
  $key = trim( $key );
111
  if ( is_numeric( $key ) ) {
112
  $add = (int) $key;
117
  $add = $cat->category_id;
118
  }
119
  }
120
+ if ( $add ) {
121
+ $select[] = $add;
122
+ }
123
  }
124
  } else {
125
  $category = trim( $category );
139
  /**
140
  * Get select parameter values for authors & hosts
141
  *
142
+ * @param string|int $author numeric or string tokens for authors or list of authors.
143
+ * @param string $type context of query.
144
+ * @param string $context context of data.
145
  *
146
  * @return string WHERE limits
147
  */
150
  return '';
151
  }
152
  $author = urldecode( $author );
153
+ if ( '' === $author || 'all' === $author || 'default' === $author ) {
154
  return '';
155
  }
156
  $select_author = '';
216
  *
217
  * @uses mc_select_author()
218
  *
219
+ * @param int|string $host Host ID or name..
220
+ * @param string $type context.
221
  *
222
  * @return string SQL
223
  */
230
  /**
231
  * Function to limit event query by location.
232
  *
233
+ * @param string $ltype {location type}.
234
+ * @param string|integer $lvalue {location value}.
235
  *
236
  * @return string
237
  */
239
  $limit_string = '';
240
  $limit_strings = array();
241
  $location = '';
 
 
242
  if ( ! $ltype || ! $lvalue ) {
243
  return '';
244
  } else {
294
  $limit_string = 'AND (' . implode( ' OR ', $limit_strings ) . ')';
295
  }
296
 
297
+ /**
298
+ * Customize location limit SQL.
299
+ *
300
+ * @hook mc_location_limit_sql
301
+ *
302
+ * @param {string} $limit_string SQL limit for location query.
303
+ * @param {string} $ltype Ltype value passed.
304
+ * @param {string} $lvalue Lvalue passed.
305
+ *
306
+ * @return {string}
307
+ */
308
  return apply_filters( 'mc_location_limit_sql', $limit_string, $ltype, $lvalue );
309
  }
310
 
327
  /**
328
  * SQL modifiers for published vs. preview
329
  *
330
+ * @return string
331
  */
332
  function mc_select_published() {
333
  if ( mc_is_preview() ) {
my-calendar-location-manager.php CHANGED
@@ -27,7 +27,7 @@ function my_calendar_manage_locations() {
27
  if ( ! empty( $_POST ) && ( ! isset( $_POST['mc_locations'] ) && ! isset( $_POST['mass_delete'] ) ) ) {
28
  $nonce = $_REQUEST['_wpnonce'];
29
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
30
- die( 'Security check failed' );
31
  }
32
  }
33
  if ( isset( $_GET['location_id'] ) && 'delete' === $_GET['mode'] ) {
@@ -108,7 +108,7 @@ function mc_clean_duplicate_locations() {
108
  if ( ! empty( $_POST['mass_edit'] ) && isset( $_POST['mass_replace'] ) ) {
109
  $nonce = $_REQUEST['_wpnonce'];
110
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
111
- die( 'Security check failed' );
112
  }
113
  $locations = $_POST['mass_edit'];
114
  $replace = absint( $_POST['mass_replace_id'] );
@@ -147,8 +147,15 @@ function mc_clean_duplicate_locations() {
147
  $i ++;
148
  }
149
  if ( ! empty( $deleted ) ) {
150
- // Argument: array of event IDs.
151
- do_action( 'mc_clean_duplicate_locations', $deleted );
 
 
 
 
 
 
 
152
  // Translators: Number of locations deleted, number selected.
153
  $message = mc_show_notice( sprintf( __( '%1$d locations deleted successfully out of %2$d selected', 'my-calendar' ), count( $deleted ), $total ), false );
154
  } else {
@@ -169,12 +176,13 @@ function mc_mass_delete_locations() {
169
  if ( ! empty( $_POST['mass_edit'] ) && isset( $_POST['mass_delete'] ) ) {
170
  $nonce = $_REQUEST['_wpnonce'];
171
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
172
- die( 'Security check failed' );
173
  }
174
  $locations = $_POST['mass_edit'];
175
  $i = 0;
176
  $total = 0;
177
  $deleted = array();
 
178
  foreach ( $locations as $value ) {
179
  $total = count( $locations );
180
  $result = mc_delete_location( $value, 'bool' );
@@ -186,7 +194,14 @@ function mc_mass_delete_locations() {
186
  $i ++;
187
  }
188
  if ( ! empty( $deleted ) ) {
189
- // Argument: array of event IDs.
 
 
 
 
 
 
 
190
  do_action( 'mc_mass_delete_locations', $deleted, $failed );
191
  // Translators: Number of locations deleted, number selected.
192
  $message = mc_show_notice( sprintf( __( '%1$d locations deleted successfully out of %2$d selected', 'my-calendar' ), count( $deleted ), $total ), false );
@@ -248,6 +263,15 @@ function mc_manage_locations() {
248
  $db_type = mc_get_db_type();
249
  if ( '' !== $query ) {
250
  if ( 'MyISAM' === $db_type && $length > 3 ) {
 
 
 
 
 
 
 
 
 
251
  $search = ' WHERE MATCH(' . apply_filters( 'mc_search_fields', 'location_label,location_city,location_state,location_region,location_country,location_street,location_street2,location_phone' ) . ") AGAINST ( '$query' IN BOOLEAN MODE ) ";
252
  } else {
253
  $search = " WHERE location_label LIKE '%$query%' OR location_city LIKE '%$query%' OR location_state LIKE '%$query%' OR location_region LIKE '%$query%' OR location_country LIKE '%$query%' OR location_street LIKE '%$query%' OR location_street2 LIKE '%$query%' OR location_phone LIKE '%$query%' ";
@@ -324,8 +348,18 @@ function mc_manage_locations() {
324
  $url = add_query_arg( 'orderby', 'state', $admin_url );
325
  $col_head .= mc_table_header( __( 'State/Province', 'my-calendar' ), $order, $sortby, 'state', $url );
326
  echo wp_kses( $col_head, mc_kses_elements() );
 
 
 
 
 
 
 
 
 
 
 
327
  ?>
328
- <?php echo apply_filters( 'mc_location_manager_headers', '' ); ?>
329
  </tr>
330
  </thead>
331
  <tbody>
@@ -418,6 +452,17 @@ function mc_location_manager_row( $location ) {
418
  $view_link = "<a href='" . esc_url( $view_url ) . "' class='view' aria-describedby='location" . absint( $location->location_id ) . "'>" . esc_html__( 'View', 'my-calendar' ) . '</a> | ';
419
  }
420
 
 
 
 
 
 
 
 
 
 
 
 
421
  $row = '';
422
  $row .= '
423
  <tr>
@@ -432,7 +477,7 @@ function mc_location_manager_row( $location ) {
432
  </div>
433
  </td>
434
  <td>' . esc_html( $location->location_city ) . '</td>
435
- <td>' . esc_html( $location->location_state ) . '</td>' . apply_filters( 'mc_location_manager_cells', '', $location ) . '
436
  </tr>';
437
 
438
  return $row;
27
  if ( ! empty( $_POST ) && ( ! isset( $_POST['mc_locations'] ) && ! isset( $_POST['mass_delete'] ) ) ) {
28
  $nonce = $_REQUEST['_wpnonce'];
29
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
30
+ wp_die( 'My Calendar: Security check failed' );
31
  }
32
  }
33
  if ( isset( $_GET['location_id'] ) && 'delete' === $_GET['mode'] ) {
108
  if ( ! empty( $_POST['mass_edit'] ) && isset( $_POST['mass_replace'] ) ) {
109
  $nonce = $_REQUEST['_wpnonce'];
110
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
111
+ wp_die( 'My Calendar: Security check failed' );
112
  }
113
  $locations = $_POST['mass_edit'];
114
  $replace = absint( $_POST['mass_replace_id'] );
147
  $i ++;
148
  }
149
  if ( ! empty( $deleted ) ) {
150
+ /**
151
+ * Run when action to clean up duplicate locations is run.
152
+ *
153
+ * @hook mc_clean_duplicate_locations
154
+ *
155
+ * @param {array} $deleted Array of location IDs successfully deleted.
156
+ * @param {array} $failed Array of location IDs that were not successfully deleted.
157
+ */
158
+ do_action( 'mc_clean_duplicate_locations', $deleted, $failed );
159
  // Translators: Number of locations deleted, number selected.
160
  $message = mc_show_notice( sprintf( __( '%1$d locations deleted successfully out of %2$d selected', 'my-calendar' ), count( $deleted ), $total ), false );
161
  } else {
176
  if ( ! empty( $_POST['mass_edit'] ) && isset( $_POST['mass_delete'] ) ) {
177
  $nonce = $_REQUEST['_wpnonce'];
178
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
179
+ wp_die( 'My Calendar: Security check failed' );
180
  }
181
  $locations = $_POST['mass_edit'];
182
  $i = 0;
183
  $total = 0;
184
  $deleted = array();
185
+ $failed = array();
186
  foreach ( $locations as $value ) {
187
  $total = count( $locations );
188
  $result = mc_delete_location( $value, 'bool' );
194
  $i ++;
195
  }
196
  if ( ! empty( $deleted ) ) {
197
+ /**
198
+ * Run when multiple locations are deleted.
199
+ *
200
+ * @hook mc_mass_delete_locations
201
+ *
202
+ * @param {array} $deleted Array of location IDs successfully deleted.
203
+ * @param {array} $failed Array of location IDs that were not successfully deleted.
204
+ */
205
  do_action( 'mc_mass_delete_locations', $deleted, $failed );
206
  // Translators: Number of locations deleted, number selected.
207
  $message = mc_show_notice( sprintf( __( '%1$d locations deleted successfully out of %2$d selected', 'my-calendar' ), count( $deleted ), $total ), false );
263
  $db_type = mc_get_db_type();
264
  if ( '' !== $query ) {
265
  if ( 'MyISAM' === $db_type && $length > 3 ) {
266
+ /**
267
+ * Customize admin search MATCH columns when db is MyISAM.
268
+ *
269
+ * @hook mc_search_fields
270
+ *
271
+ * @param {string} $fields Comma-separated list of column names.
272
+ *
273
+ * @return {string}
274
+ */
275
  $search = ' WHERE MATCH(' . apply_filters( 'mc_search_fields', 'location_label,location_city,location_state,location_region,location_country,location_street,location_street2,location_phone' ) . ") AGAINST ( '$query' IN BOOLEAN MODE ) ";
276
  } else {
277
  $search = " WHERE location_label LIKE '%$query%' OR location_city LIKE '%$query%' OR location_state LIKE '%$query%' OR location_region LIKE '%$query%' OR location_country LIKE '%$query%' OR location_street LIKE '%$query%' OR location_street2 LIKE '%$query%' OR location_phone LIKE '%$query%' ";
348
  $url = add_query_arg( 'orderby', 'state', $admin_url );
349
  $col_head .= mc_table_header( __( 'State/Province', 'my-calendar' ), $order, $sortby, 'state', $url );
350
  echo wp_kses( $col_head, mc_kses_elements() );
351
+ /**
352
+ * Add custom column table headers to Location Manager.
353
+ *
354
+ * @hook mc_location_manager_headers
355
+ *
356
+ * @param {string} $headers HTML output. Appends HTML in the end column of the location manager table row.
357
+ *
358
+ * @return {string}
359
+ */
360
+ $headers = apply_filters( 'mc_location_manager_headers', '' );
361
+ echo $headers;
362
  ?>
 
363
  </tr>
364
  </thead>
365
  <tbody>
452
  $view_link = "<a href='" . esc_url( $view_url ) . "' class='view' aria-describedby='location" . absint( $location->location_id ) . "'>" . esc_html__( 'View', 'my-calendar' ) . '</a> | ';
453
  }
454
 
455
+ /**
456
+ * Add custom column table cells to Location Manager.
457
+ *
458
+ * @hook mc_location_manager_cells
459
+ *
460
+ * @param {string} $custom_location_cells HTML output. Appends HTML in the end column of the location manager table row.
461
+ * @param {object} $location Locatino object.
462
+ * @return {string}
463
+ */
464
+ $custom_location_cells = apply_filters( 'mc_location_manager_cells', '', $location );
465
+
466
  $row = '';
467
  $row .= '
468
  <tr>
477
  </div>
478
  </td>
479
  <td>' . esc_html( $location->location_city ) . '</td>
480
+ <td>' . esc_html( $location->location_state ) . '</td>' . $custom_location_cells . '
481
  </tr>';
482
 
483
  return $row;
my-calendar-locations.php CHANGED
@@ -18,7 +18,7 @@ if ( ! defined( 'ABSPATH' ) ) {
18
  *
19
  * @param array $where Array with where query.
20
  * @param array $data saved location data.
21
- * @param int $post POST data.
22
  *
23
  * @return int post ID
24
  */
@@ -48,6 +48,16 @@ function mc_update_location_post( $where, $data, $post ) {
48
  }
49
  $post_id = wp_update_post( $my_post );
50
 
 
 
 
 
 
 
 
 
 
 
51
  do_action( 'mc_update_location_post', $post_id, $_POST, $data, $location_id );
52
  if ( mc_switch_sites() ) {
53
  restore_current_blog();
@@ -64,11 +74,11 @@ add_action( 'mc_modify_location', 'mc_update_location_post', 10, 3 );
64
  * @param array $data Saved event data.
65
  * @param array $post POST data.
66
  *
67
- * @return int newly created post ID
68
  */
69
  function mc_create_location_post( $location_id, $data, $post = array() ) {
70
  if ( ! $location_id ) {
71
- return;
72
  }
73
  $post_id = mc_get_location_post( $location_id, false );
74
  // If not post ID or the post ID has no status.
@@ -92,7 +102,17 @@ function mc_create_location_post( $location_id, $data, $post = array() ) {
92
  mc_transition_location( $location_id, $post_id );
93
  }
94
 
95
- do_action( 'mc_update_location_post', $post_id, $post, $data, $location_id );
 
 
 
 
 
 
 
 
 
 
96
  wp_publish_post( $post_id );
97
  }
98
 
@@ -145,6 +165,14 @@ function mc_location_delete_post( $result, $location_id ) {
145
  wp_delete_post( $post, true );
146
  // Delete post relationship.
147
  $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . my_calendar_location_relationships_table() . ' WHERE post_id = %d', $post ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
 
 
 
 
 
 
 
 
148
  do_action( 'mc_delete_location_posts', $location_id, $post );
149
  }
150
  }
@@ -156,7 +184,7 @@ add_action( 'mc_delete_location', 'mc_location_delete_post', 10, 2 );
156
  * @param int $location_id Location ID.
157
  * @param bool $type True for full post object.
158
  *
159
- * @return object $post
160
  */
161
  function mc_get_location_post( $location_id, $type = true ) {
162
  global $wpdb;
@@ -215,9 +243,9 @@ function mc_get_location_id( $post_ID ) {
215
  /**
216
  * Update a single field in a location.
217
  *
218
- * @param string $field field name.
219
- * @param mixed $data data to update to.
220
- * @param int $location location ID.
221
  *
222
  * @return mixed boolean/int query result
223
  */
@@ -321,6 +349,14 @@ function mc_delete_location( $location, $type = 'string' ) {
321
  global $wpdb;
322
  $location = (int) ( ( isset( $_GET['location_id'] ) ) ? $_GET['location_id'] : $location );
323
  $results = $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . my_calendar_locations_table() . ' WHERE location_id=%d', $location ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
 
 
 
 
 
 
 
 
324
  do_action( 'mc_delete_location', $results, $location );
325
  if ( $results ) {
326
  $value = true;
@@ -351,7 +387,7 @@ function my_calendar_add_locations() {
351
  if ( ! empty( $_POST ) && ( ! isset( $_POST['mc_locations'] ) && ! isset( $_POST['mass_delete'] ) ) ) {
352
  $nonce = $_REQUEST['_wpnonce'];
353
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
354
- die( 'Security check failed' );
355
  }
356
  }
357
  if ( isset( $_POST['mode'] ) && 'add' === $_POST['mode'] ) {
@@ -377,6 +413,15 @@ function my_calendar_add_locations() {
377
  if ( isset( $_POST['mc_default_location'] ) ) {
378
  update_option( 'mc_default_location', (int) $results );
379
  }
 
 
 
 
 
 
 
 
 
380
  do_action( 'mc_save_location', $results, $add, $_POST );
381
  if ( $results ) {
382
  mc_show_notice( __( 'Location added successfully', 'my-calendar' ) );
@@ -418,6 +463,15 @@ function my_calendar_add_locations() {
418
  }
419
  $results = mc_modify_location( $update, $where );
420
 
 
 
 
 
 
 
 
 
 
421
  do_action( 'mc_modify_location', $where, $update, $_POST );
422
  if ( false === $results ) {
423
  mc_show_error( __( 'Location could not be edited.', 'my-calendar' ) );
@@ -438,19 +492,19 @@ function my_calendar_add_locations() {
438
  /**
439
  * Create location editing form.
440
  *
441
- * @param string $view type of view add/edit.
442
- * @param int $loc_id Location ID.
443
  */
444
- function mc_show_location_form( $view = 'add', $loc_id = '' ) {
445
  $cur_loc = false;
446
- if ( '' !== $loc_id ) {
447
  $update_location = false;
448
  if ( isset( $_POST['update_gps'] ) ) {
449
  $update_location = 'force';
450
  }
451
  $cur_loc = mc_get_location( $loc_id, $update_location );
452
  }
453
- $has_data = ( empty( $cur_loc ) ) ? false : true;
454
  if ( 'add' === $view ) {
455
  ?>
456
  <h1><?php esc_html_e( 'Add New Location', 'my-calendar' ); ?></h1>
@@ -565,7 +619,7 @@ function mc_show_location_form( $view = 'add', $loc_id = '' ) {
565
  * @param int $location_id Location ID.
566
  * @param bool|string $update_location Whether to update location on fetch. 'Force' to force update.
567
  *
568
- * @return object location
569
  */
570
  function mc_get_location( $location_id, $update_location = true ) {
571
  global $wpdb;
@@ -629,13 +683,18 @@ function mc_controlled_field( $this_field ) {
629
  /**
630
  * Geolocate latitude and longitude of location.
631
  *
632
- * @param int $location_id Location ID.
633
- * @param array $address Array of address parameters.
634
  *
635
  * @return array
636
  */
637
  function mc_get_location_coordinates( $location_id = false, $address = array() ) {
638
  require_once( 'includes/class-geolocation.php' );
 
 
 
 
 
639
 
640
  new Geolocation;
641
  if ( $location_id ) {
@@ -669,19 +728,19 @@ function mc_get_location_coordinates( $location_id = false, $address = array() )
669
  */
670
  function mc_location_controller( $fieldname, $selected, $context = 'location' ) {
671
  $field = ( 'location' === $context ) ? 'location_' . $fieldname : 'event_' . $fieldname;
672
- $selected = esc_attr( trim( $selected ) );
673
  $options = get_option( 'mc_location_controls' );
674
  $regions = $options[ 'event_' . $fieldname ];
675
  $form = "<select name='$field' id='e_$fieldname'>";
676
  $form .= "<option value=''>" . __( 'Select', 'my-calendar' ) . '</option>';
677
  if ( is_admin() && '' !== $selected ) {
678
- $form .= "<option value='$selected'>$selected :" . __( '(Not a controlled value)', 'my-calendar' ) . '</option>';
679
  }
680
  foreach ( $regions as $key => $value ) {
681
- $key = esc_attr( trim( $key ) );
682
- $value = esc_html( trim( $value ) );
683
  $aselected = ( $selected === $key ) ? ' selected="selected"' : '';
684
- $form .= "<option value='$key'$aselected>$value</option>\n";
685
  }
686
  $form .= '</select>';
687
 
@@ -691,10 +750,10 @@ function mc_location_controller( $fieldname, $selected, $context = 'location' )
691
  /**
692
  * Produce the form to submit location data
693
  *
694
- * @param boolean $has_data Whether currently have data.
695
- * @param object $data event or location data.
696
- * @param string $context whether currently in an event or a location context.
697
- * @param int $group_id Group ID if in group editing.
698
  *
699
  * @return string HTML form fields
700
  */
@@ -718,7 +777,7 @@ function mc_locations_fields( $has_data, $data, $context = 'location', $group_id
718
  $return .= '
719
  <p>
720
  <label for="e_label">' . __( 'Name of Location (required)', 'my-calendar' ) . $compare . '</label>';
721
- $cur_label = ( ! empty( $data ) ) ? ( stripslashes( $data->{$context . '_label'} ) ) : '';
722
  if ( mc_controlled_field( 'label' ) ) {
723
  $return .= mc_location_controller( 'label', $cur_label, $context );
724
  } else {
@@ -727,8 +786,8 @@ function mc_locations_fields( $has_data, $data, $context = 'location', $group_id
727
  $compare1 = ( $group_id ) ? mc_compare_group_members( $group_id, 'event_street', false ) : '';
728
  $compare2 = ( $group_id ) ? mc_compare_group_members( $group_id, 'event_street2', false ) : '';
729
  $compare = ( $group_id ) ? mc_compare_group_members( $group_id, 'event_city', false ) : '';
730
- $street_address = ( $has_data ) ? esc_attr( stripslashes( $data->{$context . '_street'} ) ) : '';
731
- $street_address2 = ( $has_data ) ? esc_attr( stripslashes( $data->{$context . '_street2'} ) ) : '';
732
  $return .= '
733
  </p>
734
  <div class="locations-container columns">
@@ -736,14 +795,14 @@ function mc_locations_fields( $has_data, $data, $context = 'location', $group_id
736
  <fieldset>
737
  <legend>' . __( 'Location Address', 'my-calendar' ) . '</legend>
738
  <p>
739
- <label for="e_street">' . __( 'Street Address', 'my-calendar' ) . $compare1 . '</label> <input type="text" id="e_street" name="' . $context . '_street" value="' . $street_address . '" />
740
  </p>
741
  <p>
742
- <label for="e_street2">' . __( 'Street Address (2)', 'my-calendar' ) . $compare2 . '</label> <input type="text" id="e_street2" name="' . $context . '_street2" value="' . $street_address2 . '" />
743
  </p>
744
  <p>
745
  <label for="e_city">' . __( 'City', 'my-calendar' ) . $compare . '</label> ';
746
- $cur_city = ( ! empty( $data ) ) ? ( stripslashes( $data->{$context . '_city'} ) ) : '';
747
  if ( mc_controlled_field( 'city' ) ) {
748
  $return .= mc_location_controller( 'city', $cur_city, $context );
749
  } else {
@@ -753,7 +812,7 @@ function mc_locations_fields( $has_data, $data, $context = 'location', $group_id
753
 
754
  $compare = ( $group_id ) ? mc_compare_group_members( $group_id, 'event_state', false ) : '';
755
  $return .= '<label for="e_state">' . __( 'State/Province', 'my-calendar' ) . $compare . '</label> ';
756
- $cur_state = ( ! empty( $data ) ) ? ( stripslashes( $data->{$context . '_state'} ) ) : '';
757
  if ( mc_controlled_field( 'state' ) ) {
758
  $return .= mc_location_controller( 'state', $cur_state, $context );
759
  } else {
@@ -761,7 +820,7 @@ function mc_locations_fields( $has_data, $data, $context = 'location', $group_id
761
  }
762
  $compare = ( $group_id ) ? mc_compare_group_members( $group_id, 'event_postcode', false ) : '';
763
  $return .= '</p><p><label for="e_postcode">' . __( 'Postal Code', 'my-calendar' ) . $compare . '</label> ';
764
- $cur_postcode = ( ! empty( $data ) ) ? ( stripslashes( $data->{$context . '_postcode'} ) ) : '';
765
  if ( mc_controlled_field( 'postcode' ) ) {
766
  $return .= mc_location_controller( 'postcode', $cur_postcode, $context );
767
  } else {
@@ -770,7 +829,7 @@ function mc_locations_fields( $has_data, $data, $context = 'location', $group_id
770
  $compare = ( $group_id ) ? mc_compare_group_members( $group_id, 'event_region', false ) : '';
771
  $return .= '</p><p>';
772
  $return .= '<label for="e_region">' . __( 'Region', 'my-calendar' ) . $compare . '</label> ';
773
- $cur_region = ( ! empty( $data ) ) ? ( stripslashes( $data->{$context . '_region'} ) ) : '';
774
  if ( mc_controlled_field( 'region' ) ) {
775
  $return .= mc_location_controller( 'region', $cur_region, $context );
776
  } else {
@@ -793,11 +852,11 @@ function mc_locations_fields( $has_data, $data, $context = 'location', $group_id
793
  $compare_lat = ( $group_id ) ? mc_compare_group_members( $group_id, 'event_latitude', false ) : '';
794
  $compare_lon = ( $group_id ) ? mc_compare_group_members( $group_id, 'event_longitude', false ) : '';
795
  $zoom = ( $has_data ) ? $data->{$context . '_zoom'} : '16';
796
- $event_phone = ( $has_data ) ? esc_attr( stripslashes( $data->{$context . '_phone'} ) ) : '';
797
- $event_phone2 = ( $has_data ) ? esc_attr( stripslashes( $data->{$context . '_phone2'} ) ) : '';
798
- $event_url = ( $has_data ) ? esc_attr( stripslashes( $data->{$context . '_url'} ) ) : '';
799
- $event_lat = ( $has_data ) ? esc_attr( stripslashes( $data->{$context . '_latitude'} ) ) : '';
800
- $event_lon = ( $has_data ) ? esc_attr( stripslashes( $data->{$context . '_longitude'} ) ) : '';
801
  $update_gps = ( $has_data && get_option( 'mc_gmap_api_key', '' ) && 'location' === $context ) ? '<p class="checkboxes"><input type="checkbox" value="1" id="update_gps" name="update_gps" /> <label for="update_gps">' . __( 'Update GPS Coordinates', 'my-calendar' ) . '</label></p>' : '';
802
  $return .= '</p>
803
  <p>
@@ -819,34 +878,55 @@ function mc_locations_fields( $has_data, $data, $context = 'location', $group_id
819
  </p>
820
  <div class="columns-flex">
821
  <p>
822
- <label for="e_latitude">' . __( 'Latitude', 'my-calendar' ) . $compare_lat . '</label> <input type="text" id="e_latitude" name="' . $context . '_latitude" size="10" value="' . $event_lat . '" />
823
  </p>
824
  <p>
825
- <label for="e_longitude">' . __( 'Longitude', 'my-calendar' ) . $compare_lon . '</label> <input type="text" id="e_longitude" name="' . $context . '_longitude" size="10" value="' . $event_lon . '" />
826
  </p>' . $update_gps . '
827
  </div>
828
  </fieldset>';
829
- $return .= apply_filters( 'mc_location_container_primary', '', $data, $context );
830
- $return .= '
 
 
 
 
 
 
 
 
 
 
 
831
  </div>
832
  <div class="location-secondary">
833
  <fieldset>
834
  <legend>' . __( 'Location Contact Information', 'my-calendar' ) . '</legend>
835
  <p>
836
- <label for="e_phone">' . __( 'Phone', 'my-calendar' ) . $compare_phone . '</label> <input type="text" id="e_phone" name="' . $context . '_phone" value="' . $event_phone . '" />
837
  </p>
838
  <p>
839
- <label for="e_phone2">' . __( 'Secondary Phone', 'my-calendar' ) . $compare_phone2 . '</label> <input type="text" id="e_phone2" name="' . $context . '_phone2" value="' . $event_phone2 . '" />
840
  </p>
841
  <p>
842
- <label for="e_url">' . __( 'Location URL', 'my-calendar' ) . $compare_url . '</label> <input type="text" id="e_url" name="' . $context . '_url" value="' . $event_url . '" />
843
  </p>
844
  </fieldset>
845
  <fieldset>
846
  <legend>' . __( 'Location Accessibility', 'my-calendar' ) . '</legend>
847
  <ul class="accessibility-features checkboxes">';
848
 
849
- $access = apply_filters( 'mc_venue_accessibility', mc_location_access() );
 
 
 
 
 
 
 
 
 
 
850
  $access_list = '';
851
  if ( $has_data ) {
852
  if ( 'location' === $context ) {
@@ -877,6 +957,17 @@ function mc_locations_fields( $has_data, $data, $context = 'location', $group_id
877
  </fieldset>';
878
  $fields = mc_display_location_fields( mc_location_fields(), $data, $context );
879
  $return .= ( '' !== $fields ) ? '<div class="mc-custom-fields mc-locations"><fieldset><legend>' . __( 'Custom Fields', 'my-calendar' ) . '</legend>' . $fields . '</fieldset></div>' : '';
 
 
 
 
 
 
 
 
 
 
 
880
  $return .= apply_filters( 'mc_location_container_secondary', '', $data, $context );
881
  $return .= '</div>
882
  </div>
@@ -905,6 +996,23 @@ function mc_locations_fields( $has_data, $data, $context = 'location', $group_id
905
  * @return array
906
  */
907
  function mc_location_fields() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
908
  $fields = apply_filters( 'mc_location_fields', array() );
909
 
910
  return $fields;
@@ -913,9 +1021,9 @@ function mc_location_fields() {
913
  /**
914
  * Get custom data for a location.
915
  *
916
- * @param int $location_id Location ID.
917
- * @param int $location_post Location Post ID.
918
- * @param string $field Custom field name.
919
  *
920
  * @return mixed
921
  */
@@ -983,7 +1091,7 @@ add_filter( 'mc_filter_shortcodes', 'mc_template_location_fields', 10, 2 );
983
  * Expand custom fields from array to field output
984
  *
985
  * @param array $fields Array of field data.
986
- * @param array $data Location data.
987
  * @param string $context Location or event.
988
  *
989
  * @return string
@@ -1004,6 +1112,16 @@ function mc_display_location_fields( $fields, $data, $context ) {
1004
  if ( ! $location_id ) {
1005
  return '';
1006
  }
 
 
 
 
 
 
 
 
 
 
1007
  $custom_fields = apply_filters( 'mc_order_location_fields', $fields, $context );
1008
  foreach ( $custom_fields as $name => $field ) {
1009
  $user_value = mc_location_custom_data( $location_id, false, $name );
@@ -1028,7 +1146,7 @@ function mc_display_location_fields( $fields, $data, $context ) {
1028
  if ( isset( $field['input_values'] ) ) {
1029
  $output = "<select name='$name' id='$name'$required>";
1030
  foreach ( $field['input_values'] as $value ) {
1031
- $value = esc_attr( stripslashes( $value ) );
1032
  if ( $value === $user_value ) {
1033
  $selected = " selected='selected'";
1034
  } else {
@@ -1085,6 +1203,15 @@ function mc_location_access() {
1085
  '12' => __( 'Other', 'my-calendar' ),
1086
  );
1087
 
 
 
 
 
 
 
 
 
 
1088
  return apply_filters( 'mc_location_access_choices', $location_access );
1089
  }
1090
 
@@ -1114,7 +1241,7 @@ function mc_location_data( $field, $id ) {
1114
  /**
1115
  * Get options list of locations to choose from
1116
  *
1117
- * @param object $location location object.
1118
  *
1119
  * @return string set of option elements
1120
  */
@@ -1150,7 +1277,7 @@ function mc_location_select( $location = false ) {
1150
  /**
1151
  * Get list of locations (IDs and labels)
1152
  *
1153
- * @param array $args array of relevant arguments.
1154
  *
1155
  * @return array locations (IDs and labels only)
1156
  */
@@ -1180,7 +1307,17 @@ function mc_get_locations( $args ) {
1180
  }
1181
  $results = $wpdb->get_results( $wpdb->prepare( 'SELECT location_id,location_label,location_street 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
1182
 
1183
- return apply_filters( 'mc_filter_results', $results, $args );
 
 
 
 
 
 
 
 
 
 
1184
  }
1185
 
1186
  /**
@@ -1202,7 +1339,17 @@ function mc_core_search_locations( $query = '' ) {
1202
  if ( '' !== $query ) {
1203
  // Fulltext is supported in InnoDB since MySQL 5.6; minimum required by WP is 5.0 as of WP 5.5.
1204
  // 37% of installs still below 5.6 as of 11/30/2020.
 
1205
  if ( 'MyISAM' === $db_type && $length > 3 ) {
 
 
 
 
 
 
 
 
 
1206
  $search = ' WHERE MATCH(' . apply_filters( 'mc_search_fields', 'location_label' ) . ") AGAINST ( '$query' IN BOOLEAN MODE ) ";
1207
  } else {
1208
  $search = " WHERE location_label LIKE '%$query%' ";
@@ -1224,10 +1371,13 @@ function mc_core_search_locations( $query = '' ) {
1224
  * @return string
1225
  */
1226
  function mc_display_location_details( $content ) {
1227
- if ( is_singular( 'mc-locations' ) ) {
1228
  $location = mc_get_location_id( get_the_ID() );
1229
  $location = mc_get_location( $location );
1230
- $args = array(
 
 
 
1231
  'ltype' => 'name',
1232
  'lvalue' => $location->location_label,
1233
  'type' => 'events',
@@ -1235,16 +1385,36 @@ function mc_display_location_details( $content ) {
1235
  'before' => 0,
1236
  'fallback' => __( 'No events currently scheduled at this location.', 'my-calendar' ),
1237
  );
1238
- $args = apply_filters( 'mc_display_location_events', $args, $location );
1239
- $events = my_calendar_upcoming_events( $args );
1240
- $content = '
 
 
 
 
 
 
 
 
 
 
1241
  <div class="mc-view-location">
1242
  <div class="mc-location-content">' . $content . '</div>
1243
  <div class="mc-location-gmap">' . mc_generate_map( $location, 'location' ) . '</div>
1244
  <div class="mc-location-hcard">' . mc_hcard( $location, 'true', 'true', 'location' ) . '</div>
1245
  <div class="mc-location-upcoming"><h2>' . __( 'Upcoming Events', 'my-calendar' ) . '</h2>' . $events . '</div>
1246
  </div>';
1247
- $content = apply_filters( 'mc_location_output', $content, $location );
 
 
 
 
 
 
 
 
 
 
1248
  }
1249
 
1250
  return $content;
18
  *
19
  * @param array $where Array with where query.
20
  * @param array $data saved location data.
21
+ * @param array $post POST data.
22
  *
23
  * @return int post ID
24
  */
48
  }
49
  $post_id = wp_update_post( $my_post );
50
 
51
+ /**
52
+ * Executed an action when a location post is updated.
53
+ *
54
+ * @hook mc_update_location_posts
55
+ *
56
+ * @param {int} $post_id Post ID.
57
+ * @param {array} $post POST Array of data sent to create post.
58
+ * @param {array} $data Data for this location.
59
+ * @param {int} $location_id Location ID.
60
+ */
61
  do_action( 'mc_update_location_post', $post_id, $_POST, $data, $location_id );
62
  if ( mc_switch_sites() ) {
63
  restore_current_blog();
74
  * @param array $data Saved event data.
75
  * @param array $post POST data.
76
  *
77
+ * @return int|false newly created post ID or false if error.
78
  */
79
  function mc_create_location_post( $location_id, $data, $post = array() ) {
80
  if ( ! $location_id ) {
81
+ return false;
82
  }
83
  $post_id = mc_get_location_post( $location_id, false );
84
  // If not post ID or the post ID has no status.
102
  mc_transition_location( $location_id, $post_id );
103
  }
104
 
105
+ /**
106
+ * Executed an action when a location post is created.
107
+ *
108
+ * @hook mc_create_location_posts
109
+ *
110
+ * @param {int} $post_id Post ID.
111
+ * @param {array} $post POST Array of data sent to create post.
112
+ * @param {array} $data Data for this location.
113
+ * @param {int} $location_id Location ID.
114
+ */
115
+ do_action( 'mc_create_location_post', $post_id, $post, $data, $location_id );
116
  wp_publish_post( $post_id );
117
  }
118
 
165
  wp_delete_post( $post, true );
166
  // Delete post relationship.
167
  $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . my_calendar_location_relationships_table() . ' WHERE post_id = %d', $post ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
168
+ /**
169
+ * Executed an action when a location's post is deleted.
170
+ *
171
+ * @hook mc_delete_location_posts
172
+ *
173
+ * @param {int} $location_id Location deleted.
174
+ * @param {int} $post Post ID.
175
+ */
176
  do_action( 'mc_delete_location_posts', $location_id, $post );
177
  }
178
  }
184
  * @param int $location_id Location ID.
185
  * @param bool $type True for full post object.
186
  *
187
+ * @return object|int|false WP_Post, post ID, or false if not found.
188
  */
189
  function mc_get_location_post( $location_id, $type = true ) {
190
  global $wpdb;
243
  /**
244
  * Update a single field in a location.
245
  *
246
+ * @param string $field field name.
247
+ * @param int|float $data data to update to.
248
+ * @param int $location location ID.
249
  *
250
  * @return mixed boolean/int query result
251
  */
349
  global $wpdb;
350
  $location = (int) ( ( isset( $_GET['location_id'] ) ) ? $_GET['location_id'] : $location );
351
  $results = $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . my_calendar_locations_table() . ' WHERE location_id=%d', $location ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
352
+ /**
353
+ * Executed an action when a location is deleted.
354
+ *
355
+ * @hook mc_delete_location
356
+ *
357
+ * @param {int|false} $results Result of database deletion. False if error; number of rows affected if successful.
358
+ * @param {int} $location Location ID.
359
+ */
360
  do_action( 'mc_delete_location', $results, $location );
361
  if ( $results ) {
362
  $value = true;
387
  if ( ! empty( $_POST ) && ( ! isset( $_POST['mc_locations'] ) && ! isset( $_POST['mass_delete'] ) ) ) {
388
  $nonce = $_REQUEST['_wpnonce'];
389
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
390
+ wp_die( 'My Calendar: Security check failed' );
391
  }
392
  }
393
  if ( isset( $_POST['mode'] ) && 'add' === $_POST['mode'] ) {
413
  if ( isset( $_POST['mc_default_location'] ) ) {
414
  update_option( 'mc_default_location', (int) $results );
415
  }
416
+ /**
417
+ * Executed an action when a location is saved.
418
+ *
419
+ * @hook mc_save_location
420
+ *
421
+ * @param {int|false} $results Result of database insertion. Row ID or false.
422
+ * @param {array} $add Array of location parameters to add.
423
+ * @param {array} $post POST array.
424
+ */
425
  do_action( 'mc_save_location', $results, $add, $_POST );
426
  if ( $results ) {
427
  mc_show_notice( __( 'Location added successfully', 'my-calendar' ) );
463
  }
464
  $results = mc_modify_location( $update, $where );
465
 
466
+ /**
467
+ * Executed an action when a location is modified.
468
+ *
469
+ * @hook mc_modify_location
470
+ *
471
+ * @param {array} $where Array [location_id => $id].
472
+ * @param {array} $update Array of location parameters to update.
473
+ * @param {array} $post POST array.
474
+ */
475
  do_action( 'mc_modify_location', $where, $update, $_POST );
476
  if ( false === $results ) {
477
  mc_show_error( __( 'Location could not be edited.', 'my-calendar' ) );
492
  /**
493
  * Create location editing form.
494
  *
495
+ * @param string $view type of view add/edit.
496
+ * @param int|false $loc_id Location ID or false for new locations.
497
  */
498
+ function mc_show_location_form( $view = 'add', $loc_id = false ) {
499
  $cur_loc = false;
500
+ if ( $loc_id ) {
501
  $update_location = false;
502
  if ( isset( $_POST['update_gps'] ) ) {
503
  $update_location = 'force';
504
  }
505
  $cur_loc = mc_get_location( $loc_id, $update_location );
506
  }
507
+ $has_data = ( is_object( $cur_loc ) ) ? true : false;
508
  if ( 'add' === $view ) {
509
  ?>
510
  <h1><?php esc_html_e( 'Add New Location', 'my-calendar' ); ?></h1>
619
  * @param int $location_id Location ID.
620
  * @param bool|string $update_location Whether to update location on fetch. 'Force' to force update.
621
  *
622
+ * @return object|false location if found
623
  */
624
  function mc_get_location( $location_id, $update_location = true ) {
625
  global $wpdb;
683
  /**
684
  * Geolocate latitude and longitude of location.
685
  *
686
+ * @param int|false $location_id Location ID.
687
+ * @param array $address Array of address parameters.
688
  *
689
  * @return array
690
  */
691
  function mc_get_location_coordinates( $location_id = false, $address = array() ) {
692
  require_once( 'includes/class-geolocation.php' );
693
+ $street = '';
694
+ $street2 = '';
695
+ $city = '';
696
+ $zip = '';
697
+ $country = '';
698
 
699
  new Geolocation;
700
  if ( $location_id ) {
728
  */
729
  function mc_location_controller( $fieldname, $selected, $context = 'location' ) {
730
  $field = ( 'location' === $context ) ? 'location_' . $fieldname : 'event_' . $fieldname;
731
+ $selected = trim( $selected );
732
  $options = get_option( 'mc_location_controls' );
733
  $regions = $options[ 'event_' . $fieldname ];
734
  $form = "<select name='$field' id='e_$fieldname'>";
735
  $form .= "<option value=''>" . __( 'Select', 'my-calendar' ) . '</option>';
736
  if ( is_admin() && '' !== $selected ) {
737
+ $form .= "<option value='" . esc_attr( $selected ) . "'>" . esc_html( $selected ) . ' :' . __( '(Not a controlled value)', 'my-calendar' ) . '</option>';
738
  }
739
  foreach ( $regions as $key => $value ) {
740
+ $key = trim( $key );
741
+ $value = trim( $value );
742
  $aselected = ( $selected === $key ) ? ' selected="selected"' : '';
743
+ $form .= "<option value='" . esc_attr( $key ) . "'$aselected>" . esc_html( $value ) . "</option>\n";
744
  }
745
  $form .= '</select>';
746
 
750
  /**
751
  * Produce the form to submit location data
752
  *
753
+ * @param boolean $has_data Whether currently have data.
754
+ * @param object $data event or location data.
755
+ * @param string $context whether currently in an event or a location context.
756
+ * @param int|false $group_id Group ID if in group editing.
757
  *
758
  * @return string HTML form fields
759
  */
777
  $return .= '
778
  <p>
779
  <label for="e_label">' . __( 'Name of Location (required)', 'my-calendar' ) . $compare . '</label>';
780
+ $cur_label = ( is_object( $data ) ) ? ( stripslashes( $data->{$context . '_label'} ) ) : '';
781
  if ( mc_controlled_field( 'label' ) ) {
782
  $return .= mc_location_controller( 'label', $cur_label, $context );
783
  } else {
786
  $compare1 = ( $group_id ) ? mc_compare_group_members( $group_id, 'event_street', false ) : '';
787
  $compare2 = ( $group_id ) ? mc_compare_group_members( $group_id, 'event_street2', false ) : '';
788
  $compare = ( $group_id ) ? mc_compare_group_members( $group_id, 'event_city', false ) : '';
789
+ $street_address = ( $has_data ) ? stripslashes( $data->{$context . '_street'} ) : '';
790
+ $street_address2 = ( $has_data ) ? stripslashes( $data->{$context . '_street2'} ) : '';
791
  $return .= '
792
  </p>
793
  <div class="locations-container columns">
795
  <fieldset>
796
  <legend>' . __( 'Location Address', 'my-calendar' ) . '</legend>
797
  <p>
798
+ <label for="e_street">' . __( 'Street Address', 'my-calendar' ) . $compare1 . '</label> <input type="text" id="e_street" name="' . $context . '_street" value="' . esc_attr( $street_address ) . '" />
799
  </p>
800
  <p>
801
+ <label for="e_street2">' . __( 'Street Address (2)', 'my-calendar' ) . $compare2 . '</label> <input type="text" id="e_street2" name="' . $context . '_street2" value="' . esc_attr( $street_address2 ) . '" />
802
  </p>
803
  <p>
804
  <label for="e_city">' . __( 'City', 'my-calendar' ) . $compare . '</label> ';
805
+ $cur_city = ( is_object( $data ) ) ? ( stripslashes( $data->{$context . '_city'} ) ) : '';
806
  if ( mc_controlled_field( 'city' ) ) {
807
  $return .= mc_location_controller( 'city', $cur_city, $context );
808
  } else {
812
 
813
  $compare = ( $group_id ) ? mc_compare_group_members( $group_id, 'event_state', false ) : '';
814
  $return .= '<label for="e_state">' . __( 'State/Province', 'my-calendar' ) . $compare . '</label> ';
815
+ $cur_state = ( is_object( $data ) ) ? ( stripslashes( $data->{$context . '_state'} ) ) : '';
816
  if ( mc_controlled_field( 'state' ) ) {
817
  $return .= mc_location_controller( 'state', $cur_state, $context );
818
  } else {
820
  }
821
  $compare = ( $group_id ) ? mc_compare_group_members( $group_id, 'event_postcode', false ) : '';
822
  $return .= '</p><p><label for="e_postcode">' . __( 'Postal Code', 'my-calendar' ) . $compare . '</label> ';
823
+ $cur_postcode = ( is_object( $data ) ) ? ( stripslashes( $data->{$context . '_postcode'} ) ) : '';
824
  if ( mc_controlled_field( 'postcode' ) ) {
825
  $return .= mc_location_controller( 'postcode', $cur_postcode, $context );
826
  } else {
829
  $compare = ( $group_id ) ? mc_compare_group_members( $group_id, 'event_region', false ) : '';
830
  $return .= '</p><p>';
831
  $return .= '<label for="e_region">' . __( 'Region', 'my-calendar' ) . $compare . '</label> ';
832
+ $cur_region = ( is_object( $data ) ) ? ( stripslashes( $data->{$context . '_region'} ) ) : '';
833
  if ( mc_controlled_field( 'region' ) ) {
834
  $return .= mc_location_controller( 'region', $cur_region, $context );
835
  } else {
852
  $compare_lat = ( $group_id ) ? mc_compare_group_members( $group_id, 'event_latitude', false ) : '';
853
  $compare_lon = ( $group_id ) ? mc_compare_group_members( $group_id, 'event_longitude', false ) : '';
854
  $zoom = ( $has_data ) ? $data->{$context . '_zoom'} : '16';
855
+ $event_phone = ( $has_data ) ? stripslashes( $data->{$context . '_phone'} ) : '';
856
+ $event_phone2 = ( $has_data ) ? stripslashes( $data->{$context . '_phone2'} ) : '';
857
+ $event_url = ( $has_data ) ? stripslashes( $data->{$context . '_url'} ) : '';
858
+ $event_lat = ( $has_data ) ? stripslashes( $data->{$context . '_latitude'} ) : '';
859
+ $event_lon = ( $has_data ) ? stripslashes( $data->{$context . '_longitude'} ) : '';
860
  $update_gps = ( $has_data && get_option( 'mc_gmap_api_key', '' ) && 'location' === $context ) ? '<p class="checkboxes"><input type="checkbox" value="1" id="update_gps" name="update_gps" /> <label for="update_gps">' . __( 'Update GPS Coordinates', 'my-calendar' ) . '</label></p>' : '';
861
  $return .= '</p>
862
  <p>
878
  </p>
879
  <div class="columns-flex">
880
  <p>
881
+ <label for="e_latitude">' . __( 'Latitude', 'my-calendar' ) . $compare_lat . '</label> <input type="text" id="e_latitude" name="' . $context . '_latitude" size="10" value="' . esc_attr( $event_lat ) . '" />
882
  </p>
883
  <p>
884
+ <label for="e_longitude">' . __( 'Longitude', 'my-calendar' ) . $compare_lon . '</label> <input type="text" id="e_longitude" name="' . $context . '_longitude" size="10" value="' . esc_attr( $event_lon ) . '" />
885
  </p>' . $update_gps . '
886
  </div>
887
  </fieldset>';
888
+ /**
889
+ * Append content in the primary column of location fields.
890
+ *
891
+ * @hook mc_location_container_primary
892
+ *
893
+ * @param {string} HTML content. Default empty string.
894
+ * @param {object} $data Current display object.
895
+ * @param {string} $context Location or event. Tells us the structure of the $data object.
896
+ *
897
+ * @return {string}
898
+ */
899
+ $return .= apply_filters( 'mc_location_container_primary', '', $data, $context );
900
+ $return .= '
901
  </div>
902
  <div class="location-secondary">
903
  <fieldset>
904
  <legend>' . __( 'Location Contact Information', 'my-calendar' ) . '</legend>
905
  <p>
906
+ <label for="e_phone">' . __( 'Phone', 'my-calendar' ) . $compare_phone . '</label> <input type="text" id="e_phone" name="' . $context . '_phone" value="' . esc_attr( $event_phone ) . '" />
907
  </p>
908
  <p>
909
+ <label for="e_phone2">' . __( 'Secondary Phone', 'my-calendar' ) . $compare_phone2 . '</label> <input type="text" id="e_phone2" name="' . $context . '_phone2" value="' . esc_attr( $event_phone2 ) . '" />
910
  </p>
911
  <p>
912
+ <label for="e_url">' . __( 'Location URL', 'my-calendar' ) . $compare_url . '</label> <input type="text" id="e_url" name="' . $context . '_url" value="' . esc_attr( $event_url ) . '" />
913
  </p>
914
  </fieldset>
915
  <fieldset>
916
  <legend>' . __( 'Location Accessibility', 'my-calendar' ) . '</legend>
917
  <ul class="accessibility-features checkboxes">';
918
 
919
+ /**
920
+ * Filter venue accessibility array.
921
+ *
922
+ * @hook mc_venue_accessibility
923
+ *
924
+ * @param {array} $access Access parameters.
925
+ * @param {object} $data Current data object.
926
+ *
927
+ * @return {array}
928
+ */
929
+ $access = apply_filters( 'mc_venue_accessibility', mc_location_access(), $data );
930
  $access_list = '';
931
  if ( $has_data ) {
932
  if ( 'location' === $context ) {
957
  </fieldset>';
958
  $fields = mc_display_location_fields( mc_location_fields(), $data, $context );
959
  $return .= ( '' !== $fields ) ? '<div class="mc-custom-fields mc-locations"><fieldset><legend>' . __( 'Custom Fields', 'my-calendar' ) . '</legend>' . $fields . '</fieldset></div>' : '';
960
+ /**
961
+ * Append content in the secondary column of location fields.
962
+ *
963
+ * @hook mc_location_container_secondary
964
+ *
965
+ * @param {string} HTML content. Default empty string.
966
+ * @param {object} $data Current display object.
967
+ * @param {string} $context Location or event. Tells us the structure of the $data object.
968
+ *
969
+ * @return {string}
970
+ */
971
  $return .= apply_filters( 'mc_location_container_secondary', '', $data, $context );
972
  $return .= '</div>
973
  </div>
996
  * @return array
997
  */
998
  function mc_location_fields() {
999
+ /**
1000
+ * Return custom fields for use in My Calendar locations. Fields should format like:
1001
+ *
1002
+ * ```$fields['location_type'] = array(
1003
+ * 'title' => 'Location Type',
1004
+ * 'sanitize_callback' => 'sanitize_text_field',
1005
+ * 'display_callback' => 'esc_html',
1006
+ * 'input_type' => 'select',
1007
+ * 'input_values' => array( 'Virtual', 'Private Home', 'Concert Hall', 'Outdoor Venue' ),
1008
+ * );```
1009
+ *
1010
+ * @hook mc_location_fields
1011
+ *
1012
+ * @param {array} Array of custom fields.
1013
+ *
1014
+ * @return {array}
1015
+ */
1016
  $fields = apply_filters( 'mc_location_fields', array() );
1017
 
1018
  return $fields;
1021
  /**
1022
  * Get custom data for a location.
1023
  *
1024
+ * @param int|false $location_id Location ID.
1025
+ * @param int|false $location_post Location Post ID.
1026
+ * @param string|false $field Custom field name.
1027
  *
1028
  * @return mixed
1029
  */
1091
  * Expand custom fields from array to field output
1092
  *
1093
  * @param array $fields Array of field data.
1094
+ * @param object $data Location data.
1095
  * @param string $context Location or event.
1096
  *
1097
  * @return string
1112
  if ( ! $location_id ) {
1113
  return '';
1114
  }
1115
+ /**
1116
+ * Filter available custom fields & set display order.
1117
+ *
1118
+ * @hook mc_order_location_fields
1119
+ *
1120
+ * @param {array} $fields Array of custom fields data.
1121
+ * @param {string} $context Whether we're currently editing a location or an event.
1122
+ *
1123
+ * @return {array}
1124
+ */
1125
  $custom_fields = apply_filters( 'mc_order_location_fields', $fields, $context );
1126
  foreach ( $custom_fields as $name => $field ) {
1127
  $user_value = mc_location_custom_data( $location_id, false, $name );
1146
  if ( isset( $field['input_values'] ) ) {
1147
  $output = "<select name='$name' id='$name'$required>";
1148
  foreach ( $field['input_values'] as $value ) {
1149
+ $value = stripslashes( $value );
1150
  if ( $value === $user_value ) {
1151
  $selected = " selected='selected'";
1152
  } else {
1203
  '12' => __( 'Other', 'my-calendar' ),
1204
  );
1205
 
1206
+ /**
1207
+ * Filter choices available for location accessibility services.
1208
+ *
1209
+ * @hook mc_location_access_choices
1210
+ *
1211
+ * @param {array} Array of location choices (numeric keys, string values.)
1212
+ *
1213
+ * @return {array}
1214
+ */
1215
  return apply_filters( 'mc_location_access_choices', $location_access );
1216
  }
1217
 
1241
  /**
1242
  * Get options list of locations to choose from
1243
  *
1244
+ * @param object|false $location Selected location object or false if nothing selected.
1245
  *
1246
  * @return string set of option elements
1247
  */
1277
  /**
1278
  * Get list of locations (IDs and labels)
1279
  *
1280
+ * @param string|array $args array of relevant arguments. If string, get all locations and set context.
1281
  *
1282
  * @return array locations (IDs and labels only)
1283
  */
1307
  }
1308
  $results = $wpdb->get_results( $wpdb->prepare( 'SELECT location_id,location_label,location_street 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
1309
 
1310
+ /**
1311
+ * Filter returned results when searching locations.
1312
+ *
1313
+ * @hook mc_filter_location_results
1314
+ *
1315
+ * @param {array} $results Array of IDs or objects.
1316
+ * @param {array} $args Query arguments.
1317
+ *
1318
+ * @return {array}
1319
+ */
1320
+ return apply_filters( 'mc_filter_location_results', $results, $args );
1321
  }
1322
 
1323
  /**
1339
  if ( '' !== $query ) {
1340
  // Fulltext is supported in InnoDB since MySQL 5.6; minimum required by WP is 5.0 as of WP 5.5.
1341
  // 37% of installs still below 5.6 as of 11/30/2020.
1342
+ // 2.4% of installs below 5.6 as of 7/14/2022.
1343
  if ( 'MyISAM' === $db_type && $length > 3 ) {
1344
+ /**
1345
+ * Filter the fields used to handle MATCH queries in location searches on MyISAM dbs.
1346
+ *
1347
+ * @hook mc_search_fields
1348
+ *
1349
+ * @param {string} $fields Table columns in locations table.
1350
+ *
1351
+ * @return {string}
1352
+ */
1353
  $search = ' WHERE MATCH(' . apply_filters( 'mc_search_fields', 'location_label' ) . ") AGAINST ( '$query' IN BOOLEAN MODE ) ";
1354
  } else {
1355
  $search = " WHERE location_label LIKE '%$query%' ";
1371
  * @return string
1372
  */
1373
  function mc_display_location_details( $content ) {
1374
+ if ( is_singular( 'mc-locations' ) && in_the_loop() && is_main_query() ) {
1375
  $location = mc_get_location_id( get_the_ID() );
1376
  $location = mc_get_location( $location );
1377
+ if ( ! is_object( $location ) ) {
1378
+ return $content;
1379
+ }
1380
+ $args = array(
1381
  'ltype' => 'name',
1382
  'lvalue' => $location->location_label,
1383
  'type' => 'events',
1385
  'before' => 0,
1386
  'fallback' => __( 'No events currently scheduled at this location.', 'my-calendar' ),
1387
  );
1388
+ /**
1389
+ * Filter the arguments used to generate upcoming events for a location. Default ['ltype' => 'name', 'lvalue' => {location_label}, 'type' => 'events', 'after' => 5, 'before' => 0, 'fallback' => 'No events currently scheduled at this location.'].
1390
+ *
1391
+ * @hook mc_display_location_events
1392
+ *
1393
+ * @param {array} $args Array of upcoming events arguments.
1394
+ * @param {object} $location Location object.
1395
+ *
1396
+ * @return {array}
1397
+ */
1398
+ $args = apply_filters( 'mc_display_location_events', $args, $location );
1399
+ $events = my_calendar_upcoming_events( $args );
1400
+ $content = '
1401
  <div class="mc-view-location">
1402
  <div class="mc-location-content">' . $content . '</div>
1403
  <div class="mc-location-gmap">' . mc_generate_map( $location, 'location' ) . '</div>
1404
  <div class="mc-location-hcard">' . mc_hcard( $location, 'true', 'true', 'location' ) . '</div>
1405
  <div class="mc-location-upcoming"><h2>' . __( 'Upcoming Events', 'my-calendar' ) . '</h2>' . $events . '</div>
1406
  </div>';
1407
+ /**
1408
+ * Filter the HTML output for single location details.
1409
+ *
1410
+ * @hook mc_location_output
1411
+ *
1412
+ * @param {string} $content Full HTML output.
1413
+ * @param {object} $location Calendar location object.
1414
+ *
1415
+ * @return {string}
1416
+ */
1417
+ $content = apply_filters( 'mc_location_output', $content, $location );
1418
  }
1419
 
1420
  return $content;
my-calendar-navigation.php CHANGED
@@ -16,14 +16,14 @@ if ( ! defined( 'ABSPATH' ) ) {
16
  /**
17
  * Create navigation elements used in My Calendar main view
18
  *
19
- * @param array $params Calendar parameters (modified).
20
- * @param int $cat Original category from calendar args.
21
- * @param int $start_of_week First day of week.
22
- * @param int $show_months num months to show (modified).
23
- * @param string $main_class Class/ID.
24
- * @param int $site Which site in multisite.
25
- * @param string $date current date.
26
- * @param string $from date view started from.
27
  *
28
  * @return array of calendar nav for top & bottom
29
  */
@@ -190,6 +190,17 @@ function mc_generate_calendar_nav( $params, $cat, $start_of_week, $show_months,
190
  $jump = mc_date_switcher( $format, $main_class, $time, $date );
191
  }
192
 
 
 
 
 
 
 
 
 
 
 
 
193
  $mc_toporder = apply_filters( 'mc_header_navigation', $mc_toporder, $used, $params );
194
  foreach ( $mc_toporder as $value ) {
195
  if ( 'none' !== $value && in_array( $value, $used, true ) && in_array( $value, $available, true ) ) {
@@ -198,6 +209,17 @@ function mc_generate_calendar_nav( $params, $cat, $start_of_week, $show_months,
198
  }
199
  }
200
 
 
 
 
 
 
 
 
 
 
 
 
201
  $mc_bottomorder = apply_filters( 'mc_footer_navigation', $mc_bottomorder, $used, $params );
202
  foreach ( $mc_bottomorder as $value ) {
203
  if ( 'none' !== $value && 'stop' !== $value && in_array( $value, $used, true ) && in_array( $value, $available, true ) ) {
@@ -258,9 +280,28 @@ function mc_nav( $date, $format, $time, $show_months, $id ) {
258
  array()
259
  );
260
  $next_link = mc_url_in_loop( $next_link );
261
-
262
- $prev_link = apply_filters( 'mc_previous_link', '<li class="my-calendar-prev"><a href="' . $prev_link . '" rel="nofollow" class="mcajax">' . $prev['label'] . '</a></li>', $prev );
263
- $next_link = apply_filters( 'mc_next_link', '<li class="my-calendar-next"><a href="' . $next_link . '" rel="nofollow" class="mcajax">' . $next['label'] . '</a></li>', $next );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
 
265
  $nav = '
266
  <div class="my-calendar-nav">
@@ -340,13 +381,34 @@ function mc_category_key( $category ) {
340
  // If category colors are ignored, don't render HTML for them.
341
  $cat_key .= $cat_name;
342
  }
343
- $key .= '<li class="cat_' . $class . '"><a href="' . esc_url( $url ) . '" class="mcajax"' . $aria_current . '>' . $cat_key . '</a></li>';
344
  }
345
  if ( isset( $_GET['mcat'] ) ) {
346
- $key .= "<li class='all-categories'><a href='" . esc_url( mc_url_in_loop( mc_build_url( array(), array( 'mcat' ), mc_get_current_url() ) ) ) . "' class='mcajax'>" . apply_filters( 'mc_text_all_categories', __( 'All Categories', 'my-calendar' ) ) . '</a></li>';
 
 
 
 
 
 
 
 
 
 
347
  }
348
  $key .= '</ul></div>';
349
- $key = apply_filters( 'mc_category_key', $key, $categories );
 
 
 
 
 
 
 
 
 
 
 
350
 
351
  return $key;
352
  }
@@ -359,9 +421,11 @@ function mc_category_key( $category ) {
359
  * @return string HTML output for subscription links
360
  */
361
  function mc_sub_links( $subtract ) {
 
 
362
 
363
- $google = get_feed_link( 'my-calendar-google' );
364
- $outlook = get_feed_link( 'my-calendar-outlook' );
365
 
366
  $sub_google = "<li class='ics google'><a href='" . esc_url( $google ) . "'>" . __( '<span class="maybe-hide">Subscribe in </span>Google', 'my-calendar' ) . '</a></li>';
367
  $sub_outlook = "<li class='ics outlook'><a href='" . esc_url( $outlook ) . "'>" . __( '<span class="maybe-hide">Subscribe in </span>Outlook', 'my-calendar' ) . '</a></li>';
@@ -414,7 +478,7 @@ function mc_export_links( $y, $m, $next, $add, $subtract ) {
414
  * @param string $time current time view.
415
  * @param int $months number of months shown in list views.
416
  *
417
- * @return string array of parameters for link
418
  */
419
  function my_calendar_next_link( $date, $format, $time = 'month', $months = 1 ) {
420
  $cur_year = $date['year'];
@@ -445,8 +509,34 @@ function my_calendar_next_link( $date, $format, $time = 'month', $months = 1 ) {
445
  }
446
  $day = '';
447
  if ( (int) $yr !== (int) $cur_year ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
448
  $format = apply_filters( 'mc_month_year_format', 'F, Y', $date, $format, $time, $month );
449
  } else {
 
 
 
 
 
 
 
 
 
 
 
 
 
450
  $format = apply_filters( 'mc_month_format', 'F, Y', $date, $format, $time, $month );
451
  }
452
  $date = date_i18n( $format, mktime( 0, 0, 0, $month, 1, $yr ) );
@@ -494,7 +584,7 @@ function my_calendar_next_link( $date, $format, $time = 'month', $months = 1 ) {
494
  * @param string $time current time view.
495
  * @param int $months number of months shown in list views.
496
  *
497
- * @return string array of parameters for link
498
  */
499
  function my_calendar_prev_link( $date, $format, $time = 'month', $months = 1 ) {
500
  $cur_year = $date['year'];
@@ -524,8 +614,34 @@ function my_calendar_prev_link( $date, $format, $time = 'month', $months = 1 ) {
524
  }
525
  }
526
  if ( (int) $yr !== (int) $cur_year ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
527
  $format = apply_filters( 'mc_month_year_format', 'F, Y', $date, $format, $time, $month );
528
  } else {
 
 
 
 
 
 
 
 
 
 
 
 
 
529
  $format = apply_filters( 'mc_month_format', 'F, Y', $date, $format, $time, $month );
530
  }
531
  $date = date_i18n( $format, mktime( 0, 0, 0, $month, 1, $yr ) );
@@ -585,7 +701,7 @@ function mc_filters( $args, $target_url, $ltype = 'name' ) {
585
  $fields = $args;
586
  }
587
  if ( empty( $fields ) ) {
588
- return;
589
  }
590
  $has_multiple = ( count( $fields ) > 1 ) ? true : false;
591
  $return = false;
@@ -681,10 +797,8 @@ function my_calendar_categories_list( $show = 'list', $context = 'public', $grou
681
  $form .= '<input type="hidden" name="cid" value="all" />';
682
  }
683
  foreach ( $qsa as $name => $argument ) {
684
- $name = esc_attr( strip_tags( $name ) );
685
- $argument = esc_attr( strip_tags( $argument ) );
686
- if ( 'mcat' !== $name || 'mc_id' !== $name ) {
687
- $form .= ' <input type="hidden" name="' . $name . '" value="' . $argument . '" />' . "\n";
688
  }
689
  }
690
  }
@@ -727,6 +841,17 @@ function my_calendar_categories_list( $show = 'list', $context = 'public', $grou
727
  }
728
  $output .= ( 'single' === $group ) ? '</div>' : '';
729
  }
 
 
 
 
 
 
 
 
 
 
 
730
  $output = apply_filters( 'mc_category_selector', $output, $categories );
731
 
732
  return $output;
@@ -755,10 +880,8 @@ function mc_access_list( $show = 'list', $group = 'single', $target_url = '' ) {
755
  $form .= '<input type="hidden" name="cid" value="all" />';
756
  }
757
  foreach ( $qsa as $name => $argument ) {
758
- $name = esc_attr( strip_tags( $name ) );
759
- $argument = esc_attr( strip_tags( $argument ) );
760
- if ( 'access' !== $name || 'mc_id' !== $name ) {
761
- $form .= '<input type="hidden" name="' . $name . '" value="' . $argument . '" />' . "\n";
762
  }
763
  }
764
  }
@@ -792,6 +915,16 @@ function mc_access_list( $show = 'list', $group = 'single', $target_url = '' ) {
792
  $output .= ( 'list' !== $show && 'single' === $group ) ? '<p><input type="submit" class="button" value="' . __( 'Limit by Access', 'my-calendar' ) . '" /></p></form>' : '';
793
  $output .= ( 'single' === $group ) ? "\n</div>" : '';
794
  }
 
 
 
 
 
 
 
 
 
 
795
  $output = apply_filters( 'mc_access_selector', $output, $access_options );
796
 
797
  return $output;
@@ -859,16 +992,26 @@ function mc_date_switcher( $type = 'calendar', $cid = 'all', $time = 'month', $d
859
  }
860
  $date_switcher .= '</select>' . "\n" . $day_switcher . ' <label class="maybe-hide" for="' . $cid . '-year">' . __( 'Year', 'my-calendar' ) . '</label> <select id="' . $cid . '-year" name="yr">' . "\n";
861
  // Query to identify oldest start date in the database.
862
- $first = $mcdb->get_var( 'SELECT event_begin FROM ' . my_calendar_table() . ' WHERE event_approved = 1 AND event_flagged <> 1 ORDER BY event_begin ASC LIMIT 0 , 1' );
863
- $first = ( '1970-01-01' === $first ) ? '2000-01-01' : $first;
864
- $year1 = mc_date( 'Y', strtotime( $first, false ) );
865
- $diff1 = mc_date( 'Y' ) - $year1;
866
- $past = $diff1;
 
 
 
 
 
 
 
 
 
 
867
  $future = apply_filters( 'mc_jumpbox_future_years', 5, $cid );
868
  $fut = 1;
869
  $f = '';
870
  $p = '';
871
- $time = current_time( 'Y' );
872
 
873
  while ( $past > 0 ) {
874
  $p .= '<option value="';
@@ -890,7 +1033,19 @@ function mc_date_switcher( $type = 'calendar', $cid = 'all', $time = 'month', $d
890
  $date_switcher .= '<option value="' . $time . '"' . selected( $time, $c_year, false ) . '>' . $time . "</option>\n";
891
  $date_switcher .= $f;
892
  $date_switcher .= '</select> <input type="submit" class="button" data-href="' . esc_attr( $data_href ) . '" value="' . __( 'Go', 'my-calendar' ) . '" /></div></form></div>';
893
- $date_switcher = apply_filters( 'mc_jumpbox', $date_switcher );
 
 
 
 
 
 
 
 
 
 
 
 
894
 
895
  return $date_switcher;
896
  }
@@ -911,12 +1066,12 @@ function mc_format_toggle( $format, $toggle, $time ) {
911
  case 'list':
912
  $url = mc_build_url( array( 'format' => 'calendar' ), array() );
913
  $url = mc_url_in_loop( $url );
914
- $toggle .= "<a href='$url' class='grid mcajax'>" . __( '<span class="maybe-hide">View as </span>Grid', 'my-calendar' ) . '</a>';
915
  break;
916
  default:
917
  $url = mc_build_url( array( 'format' => 'list' ), array() );
918
  $url = mc_url_in_loop( $url );
919
- $toggle .= "<a href='$url' class='list mcajax'>" . __( '<span class="maybe-hide">View as </span>List', 'my-calendar' ) . '</a>';
920
  break;
921
  }
922
  $toggle .= '</div>';
@@ -932,24 +1087,37 @@ function mc_format_toggle( $format, $toggle, $time ) {
932
  $toggle = '';
933
  }
934
 
 
 
 
 
 
 
 
 
 
 
 
935
  return apply_filters( 'mc_format_toggle_html', $toggle, $format, $time );
936
  }
937
 
938
  /**
939
  * Generate toggle for time views between day month & week
940
  *
941
- * @param string $format of current view.
942
- * @param string $time timespan of current view.
943
- * @param string $month current month.
944
- * @param string $year current year.
945
- * @param string $current Current date.
946
- * @param int $start_of_week Day week starts on.
947
- * @param string $from Date started from.
948
  *
949
  * @return string HTML output
950
  */
951
  function mc_time_toggle( $format, $time, $month, $year, $current, $start_of_week, $from ) {
952
  // if dy parameter not set, use today's date instead of first day of month.
 
 
953
  $weeks_day = mc_first_day_of_week( $current );
954
  $adjusted = false;
955
  if ( isset( $_GET['dy'] ) ) {
@@ -963,7 +1131,7 @@ function mc_time_toggle( $format, $time, $month, $year, $current, $start_of_week
963
  $current_day = absint( $_GET['dy'] );
964
  }
965
  $current_set = mktime( 0, 0, 0, $month, $current_day, $year );
966
- if ( mc_date( 'N', $current_set, false ) === $start_of_week ) {
967
  $weeks_day = mc_first_day_of_week( $current_set );
968
  }
969
  } else {
@@ -993,7 +1161,7 @@ function mc_time_toggle( $format, $time, $month, $year, $current, $start_of_week
993
  case 'week':
994
  $url = mc_build_url( array( 'time' => 'month' ), array( 'mc_id' ) );
995
  $url = mc_url_in_loop( $url );
996
- $toggle .= "<a href='$url' class='month mcajax'>" . __( 'Month', 'my-calendar' ) . '</a>';
997
  $toggle .= "<span class='mc-active week'>" . __( 'Week', 'my-calendar' ) . '</span>';
998
  $url = mc_build_url(
999
  array(
@@ -1003,12 +1171,12 @@ function mc_time_toggle( $format, $time, $month, $year, $current, $start_of_week
1003
  array( 'dy', 'mc_id' )
1004
  );
1005
  $url = mc_url_in_loop( $url );
1006
- $toggle .= "<a href='$url' class='day mcajax'>" . __( 'Day', 'my-calendar' ) . '</a>';
1007
  break;
1008
  case 'day':
1009
  $url = mc_build_url( array( 'time' => 'month' ), array() );
1010
  $url = mc_url_in_loop( $url );
1011
- $toggle .= "<a href='$url' class='month mcajax'>" . __( 'Month', 'my-calendar' ) . '</a>';
1012
  $url = mc_build_url(
1013
  array(
1014
  'time' => 'week',
@@ -1019,7 +1187,7 @@ function mc_time_toggle( $format, $time, $month, $year, $current, $start_of_week
1019
  array( 'dy', 'month', 'mc_id' )
1020
  );
1021
  $url = mc_url_in_loop( $url );
1022
- $toggle .= "<a href='$url' class='week mcajax'>" . __( 'Week', 'my-calendar' ) . '</a>';
1023
  $toggle .= "<span class='mc-active day'>" . __( 'Day', 'my-calendar' ) . '</span>';
1024
  break;
1025
  default:
@@ -1033,10 +1201,10 @@ function mc_time_toggle( $format, $time, $month, $year, $current, $start_of_week
1033
  array( 'dy', 'month', 'mc_id' )
1034
  );
1035
  $url = mc_url_in_loop( $url );
1036
- $toggle .= "<a href='$url' class='week mcajax'>" . __( 'Week', 'my-calendar' ) . '</a>';
1037
  $url = mc_build_url( array( 'time' => 'day' ), array() );
1038
  $url = mc_url_in_loop( $url );
1039
- $toggle .= "<a href='$url' class='day mcajax'>" . __( 'Day', 'my-calendar' ) . '</a>';
1040
  break;
1041
  }
1042
  $toggle .= '</div>';
@@ -1044,5 +1212,16 @@ function mc_time_toggle( $format, $time, $month, $year, $current, $start_of_week
1044
  $toggle = '';
1045
  }
1046
 
 
 
 
 
 
 
 
 
 
 
 
1047
  return apply_filters( 'mc_time_toggle_html', $toggle, $format, $time );
1048
  }
16
  /**
17
  * Create navigation elements used in My Calendar main view
18
  *
19
+ * @param array $params Calendar parameters (modified).
20
+ * @param int $cat Original category from calendar args.
21
+ * @param int $start_of_week First day of week.
22
+ * @param int $show_months num months to show (modified).
23
+ * @param string $main_class Class/ID.
24
+ * @param int|string $site Which site in multisite.
25
+ * @param array $date current date.
26
+ * @param string $from date view started from.
27
  *
28
  * @return array of calendar nav for top & bottom
29
  */
190
  $jump = mc_date_switcher( $format, $main_class, $time, $date );
191
  }
192
 
193
+ /**
194
+ * Filter the order in which navigation elements are shown on the top of the calendar.
195
+ *
196
+ * @hook mc_header_navigation
197
+ *
198
+ * @param {array} $mc_toporder Array of navigation elements.
199
+ * @param {array} $used Array of all navigation elements in use for this view.
200
+ * @param {array} $params Current calendar view parameters.
201
+ *
202
+ * @return {array}
203
+ */
204
  $mc_toporder = apply_filters( 'mc_header_navigation', $mc_toporder, $used, $params );
205
  foreach ( $mc_toporder as $value ) {
206
  if ( 'none' !== $value && in_array( $value, $used, true ) && in_array( $value, $available, true ) ) {
209
  }
210
  }
211
 
212
+ /**
213
+ * Filter the order in which navigation elements are shown on the bottom of the calendar.
214
+ *
215
+ * @hook mc_footer_navigation
216
+ *
217
+ * @param {array} $mc_bottomorder Array of navigation elements.
218
+ * @param {array} $used Array of all navigation elements in use for this view.
219
+ * @param {array} $params Current calendar view parameters.
220
+ *
221
+ * @return {array}
222
+ */
223
  $mc_bottomorder = apply_filters( 'mc_footer_navigation', $mc_bottomorder, $used, $params );
224
  foreach ( $mc_bottomorder as $value ) {
225
  if ( 'none' !== $value && 'stop' !== $value && in_array( $value, $used, true ) && in_array( $value, $available, true ) ) {
280
  array()
281
  );
282
  $next_link = mc_url_in_loop( $next_link );
283
+ /**
284
+ * Filter HTML output for navigation 'prev' link.
285
+ *
286
+ * @hook mc_prev_link
287
+ *
288
+ * @param {string} $prev_link HTML output for link.
289
+ * @param {array} $prev Previous link parameters.
290
+ *
291
+ * @return {string}
292
+ */
293
+ $prev_link = apply_filters( 'mc_previous_link', '<li class="my-calendar-prev"><a href="' . $prev_link . '" rel="nofollow">' . $prev['label'] . '</a></li>', $prev );
294
+ /**
295
+ * Filter HTML output for navigation 'next' link.
296
+ *
297
+ * @hook mc_next_link
298
+ *
299
+ * @param {string} $next_link HTML output for link.
300
+ * @param {array} $next Next link parameters.
301
+ *
302
+ * @return {string}
303
+ */
304
+ $next_link = apply_filters( 'mc_next_link', '<li class="my-calendar-next"><a href="' . $next_link . '" rel="nofollow">' . $next['label'] . '</a></li>', $next );
305
 
306
  $nav = '
307
  <div class="my-calendar-nav">
381
  // If category colors are ignored, don't render HTML for them.
382
  $cat_key .= $cat_name;
383
  }
384
+ $key .= '<li class="cat_' . $class . '"><a href="' . esc_url( $url ) . '"' . $aria_current . '>' . $cat_key . '</a></li>';
385
  }
386
  if ( isset( $_GET['mcat'] ) ) {
387
+ /**
388
+ * Filter text label for 'All Categories'.
389
+ *
390
+ * @hook mc_text_all_categories
391
+ *
392
+ * @param {string} $all Text for link to show all categories.
393
+ *
394
+ * @return {string}
395
+ */
396
+ $all = apply_filters( 'mc_text_all_categories', __( 'All Categories', 'my-calendar' ) );
397
+ $key .= "<li class='all-categories'><a href='" . esc_url( mc_url_in_loop( mc_build_url( array(), array( 'mcat' ), mc_get_current_url() ) ) ) . "'>" . $all . '</a></li>';
398
  }
399
  $key .= '</ul></div>';
400
+
401
+ /**
402
+ * Filter the category key output in navigation.
403
+ *
404
+ * @hook mc_category_key
405
+ *
406
+ * @param {string} $key Key HTML output.
407
+ * @param {array} $categories Categories in key.
408
+ *
409
+ * @return {string}
410
+ */
411
+ $key = apply_filters( 'mc_category_key', $key, $categories );
412
 
413
  return $key;
414
  }
421
  * @return string HTML output for subscription links
422
  */
423
  function mc_sub_links( $subtract ) {
424
+ $replace = 'webcal:';
425
+ $search = array( 'http:', 'https:' );
426
 
427
+ $google = str_replace( $search, $replace, get_feed_link( 'my-calendar-google' ) );
428
+ $outlook = str_replace( $search, $replace, get_feed_link( 'my-calendar-outlook' ) );
429
 
430
  $sub_google = "<li class='ics google'><a href='" . esc_url( $google ) . "'>" . __( '<span class="maybe-hide">Subscribe in </span>Google', 'my-calendar' ) . '</a></li>';
431
  $sub_outlook = "<li class='ics outlook'><a href='" . esc_url( $outlook ) . "'>" . __( '<span class="maybe-hide">Subscribe in </span>Outlook', 'my-calendar' ) . '</a></li>';
478
  * @param string $time current time view.
479
  * @param int $months number of months shown in list views.
480
  *
481
+ * @return array of parameters for link
482
  */
483
  function my_calendar_next_link( $date, $format, $time = 'month', $months = 1 ) {
484
  $cur_year = $date['year'];
509
  }
510
  $day = '';
511
  if ( (int) $yr !== (int) $cur_year ) {
512
+ /**
513
+ * Filter the date format used for next link if the next link is in a different year.
514
+ *
515
+ * @hook mc_month_format
516
+ *
517
+ * @param {string} $format PHP Date format string.
518
+ * @param {array} $date Current date array.
519
+ * @param {string} $format View format.
520
+ * @param {string} $time View time frame.
521
+ * @param {string} $month month used in navigation reference (next month.)
522
+ *
523
+ * @return {string}
524
+ */
525
  $format = apply_filters( 'mc_month_year_format', 'F, Y', $date, $format, $time, $month );
526
  } else {
527
+ /**
528
+ * Filter the date format used for next link if the next link is in the same year.
529
+ *
530
+ * @hook mc_month_format
531
+ *
532
+ * @param {string} $format PHP Date format string.
533
+ * @param {array} $date Current date array.
534
+ * @param {string} $format View format.
535
+ * @param {string} $time View time frame.
536
+ * @param {string} $month month used in navigation reference (next month.)
537
+ *
538
+ * @return {string}
539
+ */
540
  $format = apply_filters( 'mc_month_format', 'F, Y', $date, $format, $time, $month );
541
  }
542
  $date = date_i18n( $format, mktime( 0, 0, 0, $month, 1, $yr ) );
584
  * @param string $time current time view.
585
  * @param int $months number of months shown in list views.
586
  *
587
+ * @return array of parameters for link
588
  */
589
  function my_calendar_prev_link( $date, $format, $time = 'month', $months = 1 ) {
590
  $cur_year = $date['year'];
614
  }
615
  }
616
  if ( (int) $yr !== (int) $cur_year ) {
617
+ /**
618
+ * Filter the date format used for previous link if the prev link is in a different year.
619
+ *
620
+ * @hook mc_month_format
621
+ *
622
+ * @param {string} $format PHP Date format string.
623
+ * @param {array} $date Current date array.
624
+ * @param {string} $format View format.
625
+ * @param {string} $time View time frame.
626
+ * @param {string} $month month used in navigation reference (previous month.)
627
+ *
628
+ * @return {string}
629
+ */
630
  $format = apply_filters( 'mc_month_year_format', 'F, Y', $date, $format, $time, $month );
631
  } else {
632
+ /**
633
+ * Filter the date format used for previous link if the previous link is in the same year.
634
+ *
635
+ * @hook mc_month_format
636
+ *
637
+ * @param {string} $format PHP Date format string.
638
+ * @param {array} $date Current date array.
639
+ * @param {string} $format View format.
640
+ * @param {string} $time View time frame.
641
+ * @param {string} $month month used in navigation reference (previous month, generally.)
642
+ *
643
+ * @return {string}
644
+ */
645
  $format = apply_filters( 'mc_month_format', 'F, Y', $date, $format, $time, $month );
646
  }
647
  $date = date_i18n( $format, mktime( 0, 0, 0, $month, 1, $yr ) );
701
  $fields = $args;
702
  }
703
  if ( empty( $fields ) ) {
704
+ return '';
705
  }
706
  $has_multiple = ( count( $fields ) > 1 ) ? true : false;
707
  $return = false;
797
  $form .= '<input type="hidden" name="cid" value="all" />';
798
  }
799
  foreach ( $qsa as $name => $argument ) {
800
+ if ( ! ( 'mcat' === $name || 'mc_id' === $name ) ) {
801
+ $form .= '<input type="hidden" name="' . esc_attr( strip_tags( $name ) ) . '" value="' . esc_attr( strip_tags( $argument ) ) . '" />' . "\n";
 
 
802
  }
803
  }
804
  }
841
  }
842
  $output .= ( 'single' === $group ) ? '</div>' : '';
843
  }
844
+
845
+ /**
846
+ * Filter the HTML for the category filter dropdown in navigation elements.
847
+ *
848
+ * @hook mc_category_selector
849
+ *
850
+ * @param {string} $toggle HTML output for control.
851
+ * @param {array} $categories Available categories.
852
+ *
853
+ * @return {string}
854
+ */
855
  $output = apply_filters( 'mc_category_selector', $output, $categories );
856
 
857
  return $output;
880
  $form .= '<input type="hidden" name="cid" value="all" />';
881
  }
882
  foreach ( $qsa as $name => $argument ) {
883
+ if ( ! ( 'access' === $name || 'mc_id' === $name ) ) {
884
+ $form .= '<input type="hidden" name="' . esc_attr( strip_tags( $name ) ) . '" value="' . esc_attr( strip_tags( $argument ) ) . '" />' . "\n";
 
 
885
  }
886
  }
887
  }
915
  $output .= ( 'list' !== $show && 'single' === $group ) ? '<p><input type="submit" class="button" value="' . __( 'Limit by Access', 'my-calendar' ) . '" /></p></form>' : '';
916
  $output .= ( 'single' === $group ) ? "\n</div>" : '';
917
  }
918
+ /**
919
+ * Filter the HTML for the accessibility feature filter in navigation elements.
920
+ *
921
+ * @hook mc_access_selector
922
+ *
923
+ * @param {string} $output HTML output for control.
924
+ * @param {array} $access_options Available accessibility options.
925
+ *
926
+ * @return {string}
927
+ */
928
  $output = apply_filters( 'mc_access_selector', $output, $access_options );
929
 
930
  return $output;
992
  }
993
  $date_switcher .= '</select>' . "\n" . $day_switcher . ' <label class="maybe-hide" for="' . $cid . '-year">' . __( 'Year', 'my-calendar' ) . '</label> <select id="' . $cid . '-year" name="yr">' . "\n";
994
  // Query to identify oldest start date in the database.
995
+ $first = $mcdb->get_var( 'SELECT event_begin FROM ' . my_calendar_table() . ' WHERE event_approved = 1 AND event_flagged <> 1 ORDER BY event_begin ASC LIMIT 0 , 1' );
996
+ $first = ( '1970-01-01' === $first ) ? '2000-01-01' : $first;
997
+ $year1 = (int) mc_date( 'Y', strtotime( $first, false ) );
998
+ $diff1 = (int) mc_date( 'Y' ) - $year1;
999
+ $past = $diff1;
1000
+ /**
1001
+ * How many years into the future should be shown in the navigation jumpbox. Default '5'.
1002
+ *
1003
+ * @hook mc_jumpbox_future_years
1004
+ *
1005
+ * @param {int} $future Number of years ahead.
1006
+ * @param {string} $cid Current calendar ID. '' when running in the shortcode generator.
1007
+ *
1008
+ * @return {int}
1009
+ */
1010
  $future = apply_filters( 'mc_jumpbox_future_years', 5, $cid );
1011
  $fut = 1;
1012
  $f = '';
1013
  $p = '';
1014
+ $time = (int) current_time( 'Y' );
1015
 
1016
  while ( $past > 0 ) {
1017
  $p .= '<option value="';
1033
  $date_switcher .= '<option value="' . $time . '"' . selected( $time, $c_year, false ) . '>' . $time . "</option>\n";
1034
  $date_switcher .= $f;
1035
  $date_switcher .= '</select> <input type="submit" class="button" data-href="' . esc_attr( $data_href ) . '" value="' . __( 'Go', 'my-calendar' ) . '" /></div></form></div>';
1036
+
1037
+ /**
1038
+ * Filter the HTML for the date jumpbox controls.
1039
+ *
1040
+ * @hook mc_jumpbox
1041
+ *
1042
+ * @param {string} $date_switcher HTML output for control.
1043
+ * @param {string} $type Current view format.
1044
+ * @param {string} $time Current time frame.
1045
+ *
1046
+ * @return {string}
1047
+ */
1048
+ $date_switcher = apply_filters( 'mc_jumpbox', $date_switcher, $type, $time );
1049
 
1050
  return $date_switcher;
1051
  }
1066
  case 'list':
1067
  $url = mc_build_url( array( 'format' => 'calendar' ), array() );
1068
  $url = mc_url_in_loop( $url );
1069
+ $toggle .= "<a href='$url' class='grid'>" . __( '<span class="maybe-hide">View as </span>Grid', 'my-calendar' ) . '</a>';
1070
  break;
1071
  default:
1072
  $url = mc_build_url( array( 'format' => 'list' ), array() );
1073
  $url = mc_url_in_loop( $url );
1074
+ $toggle .= "<a href='$url' class='list'>" . __( '<span class="maybe-hide">View as </span>List', 'my-calendar' ) . '</a>';
1075
  break;
1076
  }
1077
  $toggle .= '</div>';
1087
  $toggle = '';
1088
  }
1089
 
1090
+ /**
1091
+ * Filter the HTML for the list/grid format switcher in navigation elements.
1092
+ *
1093
+ * @hook mc_format_toggle_html
1094
+ *
1095
+ * @param {string} $toggle HTML output for control.
1096
+ * @param {string} $format Current view format.
1097
+ * @param {string} $time Current time frame.
1098
+ *
1099
+ * @return {string}
1100
+ */
1101
  return apply_filters( 'mc_format_toggle_html', $toggle, $format, $time );
1102
  }
1103
 
1104
  /**
1105
  * Generate toggle for time views between day month & week
1106
  *
1107
+ * @param string $format of current view.
1108
+ * @param string $time timespan of current view.
1109
+ * @param string|int $month Numeric value ofcurrent month.
1110
+ * @param string|int $year current year.
1111
+ * @param string $current Current date.
1112
+ * @param int $start_of_week Day week starts on.
1113
+ * @param string $from Date started from.
1114
  *
1115
  * @return string HTML output
1116
  */
1117
  function mc_time_toggle( $format, $time, $month, $year, $current, $start_of_week, $from ) {
1118
  // if dy parameter not set, use today's date instead of first day of month.
1119
+ $month = (int) $month;
1120
+ $year = (int) $year;
1121
  $weeks_day = mc_first_day_of_week( $current );
1122
  $adjusted = false;
1123
  if ( isset( $_GET['dy'] ) ) {
1131
  $current_day = absint( $_GET['dy'] );
1132
  }
1133
  $current_set = mktime( 0, 0, 0, $month, $current_day, $year );
1134
+ if ( (int) mc_date( 'N', $current_set, false ) === $start_of_week ) {
1135
  $weeks_day = mc_first_day_of_week( $current_set );
1136
  }
1137
  } else {
1161
  case 'week':
1162
  $url = mc_build_url( array( 'time' => 'month' ), array( 'mc_id' ) );
1163
  $url = mc_url_in_loop( $url );
1164
+ $toggle .= "<a href='$url' class='month'>" . __( 'Month', 'my-calendar' ) . '</a>';
1165
  $toggle .= "<span class='mc-active week'>" . __( 'Week', 'my-calendar' ) . '</span>';
1166
  $url = mc_build_url(
1167
  array(
1171
  array( 'dy', 'mc_id' )
1172
  );
1173
  $url = mc_url_in_loop( $url );
1174
+ $toggle .= "<a href='$url' class='day'>" . __( 'Day', 'my-calendar' ) . '</a>';
1175
  break;
1176
  case 'day':
1177
  $url = mc_build_url( array( 'time' => 'month' ), array() );
1178
  $url = mc_url_in_loop( $url );
1179
+ $toggle .= "<a href='$url' class='month'>" . __( 'Month', 'my-calendar' ) . '</a>';
1180
  $url = mc_build_url(
1181
  array(
1182
  'time' => 'week',
1187
  array( 'dy', 'month', 'mc_id' )
1188
  );
1189
  $url = mc_url_in_loop( $url );
1190
+ $toggle .= "<a href='$url' class='week'>" . __( 'Week', 'my-calendar' ) . '</a>';
1191
  $toggle .= "<span class='mc-active day'>" . __( 'Day', 'my-calendar' ) . '</span>';
1192
  break;
1193
  default:
1201
  array( 'dy', 'month', 'mc_id' )
1202
  );
1203
  $url = mc_url_in_loop( $url );
1204
+ $toggle .= "<a href='$url' class='week'>" . __( 'Week', 'my-calendar' ) . '</a>';
1205
  $url = mc_build_url( array( 'time' => 'day' ), array() );
1206
  $url = mc_url_in_loop( $url );
1207
+ $toggle .= "<a href='$url' class='day'>" . __( 'Day', 'my-calendar' ) . '</a>';
1208
  break;
1209
  }
1210
  $toggle .= '</div>';
1212
  $toggle = '';
1213
  }
1214
 
1215
+ /**
1216
+ * Filter the HTML for the time format switcher in navigation elements.
1217
+ *
1218
+ * @hook mc_time_toggle_html
1219
+ *
1220
+ * @param {string} $toggle HTML output for control.
1221
+ * @param {string} $format Current view format.
1222
+ * @param {string} $time Current time frame.
1223
+ *
1224
+ * @return {string}
1225
+ */
1226
  return apply_filters( 'mc_time_toggle_html', $toggle, $format, $time );
1227
  }
my-calendar-output.php CHANGED
@@ -36,10 +36,12 @@ function mc_time_html( $e, $type ) {
36
  $dtend = $end . 'T' . $e->event_endtime . $offset;
37
  $notime = '';
38
  if ( ! $has_time ) {
39
- $label = mc_notime_label( $e );
40
- $notime .= " <span class='event-time'>";
41
- $notime .= ( 'N/A' === $label ) ? "<abbr title='" . esc_html__( 'Not Applicable', 'my-calendar' ) . "'>" . esc_html__( 'N/A', 'my-calendar' ) . '</abbr>' : esc_html( $label );
42
- $notime .= '</span>';
 
 
43
  }
44
  $date_start = "<span class='mc-start-date dtstart' title='" . esc_attr( $dtstart ) . "' content='" . esc_attr( $dtstart ) . "'>" . date_i18n( $date_format, strtotime( $e->occur_begin ) ) . '</span>';
45
  $time_start = ( $has_time ) ? "<span class='event-time dtstart'><time class='value-title' datetime='" . esc_attr( $dtstart ) . "' title='" . esc_attr( $dtstart ) . "'>" . date_i18n( $time_format, strtotime( $e->occur_begin ) ) . '</time></span>' : $notime;
@@ -59,8 +61,10 @@ function mc_time_html( $e, $type ) {
59
  /**
60
  * Filter time block output.
61
  *
62
- * @param string $time HTML time block output.
63
- * @param object $e Event object.
 
 
64
  *
65
  * @return string
66
  */
@@ -76,7 +80,7 @@ function mc_time_html( $e, $type ) {
76
  * @param string $template Template to use for drawing individual events.
77
  * @param string $id ID for the calendar calling this function.
78
  *
79
- * @return array [html] Generated HTML & [json] array of schema.org data.
80
  */
81
  function my_calendar_draw_events( $events, $params, $process_date, $template = '', $id = '' ) {
82
  $type = $params['format'];
@@ -87,7 +91,7 @@ function my_calendar_draw_events( $events, $params, $process_date, $template = '
87
  return true;
88
  }
89
  // We need to sort arrays of objects by time.
90
- if ( is_array( $events ) ) {
91
  $output_array = array();
92
  $json = array();
93
  $begin = '';
@@ -135,6 +139,29 @@ function my_calendar_draw_events( $events, $params, $process_date, $template = '
135
  return array();
136
  }
137
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
  /**
140
  * Draw a single event
@@ -154,317 +181,579 @@ function my_calendar_draw_event( $event, $type, $process_date, $time, $template
154
  if ( $exit_early ) {
155
  return '';
156
  }
 
 
 
 
 
 
 
 
157
  do_action( 'my_calendar_drawing_event', $event );
158
-
159
- // assign empty values to template sections.
160
- $header = '';
161
- $address = '';
162
- $more = '';
163
- $author = '';
164
- $host = '';
165
- $list_title = '';
166
- $title = '';
167
- $output = '';
168
- $container = '';
169
- $short = '';
170
- $description = '';
171
- $link = '';
172
- $vcal = '';
173
- $inner_title = '';
174
- $gcal = '';
175
- $access = '';
176
- $image = '';
177
- $tickets = '';
178
- $details = '';
179
- $data = ( empty( $tags ) ) ? mc_create_tags( $event, $id ) : $tags;
180
- $otype = ( 'calendar' === $type ) ? 'grid' : $type;
181
-
182
- if ( mc_show_details( $time, $type ) ) {
183
- $details = apply_filters( 'mc_custom_template', false, $data, $event, $type, $process_date, $time, $template );
184
- $template = apply_filters( 'mc_use_custom_template', $template, $data, $event, $type, $process_date, $time );
185
- if ( false === $details ) {
186
- $details = mc_get_details( $data, $template, $type );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  }
188
- }
189
 
190
- // Fallback display options. Changed in 3.3.0; fallback to old settings if new don't exist.
191
- $display_map = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_show_map' ) : '';
192
- $display_address = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_show_address' ) : '';
193
- $display_gcal = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_show_gcal' ) : '';
194
- $display_vcal = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_show_event_vcal' ) : '';
195
- $open_uri = get_option( 'mc_open_uri' );
196
- $display_author = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_display_author' ) : '';
197
- $display_host = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_display_host' ) : '';
198
- $display_more = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_display_more' ) : '';
199
- $display_desc = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_desc' ) : '';
200
- $display_short = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_short' ) : '';
201
- $display_gmap = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_gmap' ) : '';
202
- $display_link = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_event_link' ) : '';
203
- $display_image = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_image' ) : '';
204
- $display_reg = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_event_registration' ) : '';
205
- $day_id = mc_date( 'd', strtotime( $process_date ), false );
206
- $uid = 'mc_' . $type . '_' . $day_id . '_' . $event->occur_id;
207
- $image = mc_category_icon( $event );
208
- $img = '';
209
- $has_image = ( '' !== $image ) ? ' has-image' : '';
210
- $event_classes = mc_event_classes( $event, $type );
211
- $nofollow = ( stripos( $event_classes, 'past-event' ) !== false ) ? 'rel="nofollow"' : '';
212
- $header .= "\n\n <div id='$uid-$type-$id' class='$event_classes'>\n";
213
-
214
- switch ( $type ) {
215
- case 'calendar':
216
- $title_template = ( mc_get_template( 'title' ) === '' ) ? '{title}' : mc_get_template( 'title' );
217
- break;
218
- case 'list':
219
- $title_template = ( mc_get_template( 'title_list' ) === '' ) ? '{title}' : mc_get_template( 'title_list' );
220
- break;
221
- case 'single':
222
- $title_template = ( mc_get_template( 'title_solo' ) === '' ) ? '{title}' : mc_get_template( 'title_solo' );
223
- break;
224
- default:
225
- $title_template = ( mc_get_template( 'title' ) === '' ) ? '{title}' : mc_get_template( 'title' );
226
- }
227
 
228
- $event_title = mc_draw_template( $data, $title_template );
229
- if ( 0 === strpos( $event_title, ': ' ) ) {
230
- // If the first two characters of the title are ": ", this is the default templates but no time.
231
- $event_title = str_replace( ': ', '', $event_title );
232
- }
233
- $event_title = ( '' === $event_title ) ? $data['title'] : strip_tags( $event_title, mc_strip_tags() );
234
- if ( 'single' === $type ) {
235
- $event_title = apply_filters( 'mc_single_event_title', $event_title, $event );
236
- } else {
237
- $event_title = apply_filters( 'mc_event_title', $event_title, $event, $data['title'], $image );
238
- }
239
- $no_link = apply_filters( 'mc_disable_link', false, $data );
 
240
 
241
- if ( ( ( strpos( $event_title, 'href' ) === false ) && 'mini' !== $type && 'list' !== $type ) && ! $no_link ) {
242
- if ( 'true' === $open_uri ) {
243
- $details_link = esc_url( mc_get_details_link( $event ) );
244
- $wrap = ( _mc_is_url( $details_link ) ) ? "<a href='$details_link' class='url summary$has_image' $nofollow>" : '<span class="no-link">';
245
- $balance = ( _mc_is_url( $details_link ) ) ? '</a>' : '</span>';
 
 
 
 
 
 
 
 
 
 
 
 
 
246
  } else {
247
- $wrap = "<a href='#$uid-$type-details-$id' class='open et_smooth_scroll_disabled opl-link url summary$has_image'>";
248
- $balance = '</a>';
 
 
 
 
 
 
 
 
 
 
 
249
  }
250
- } else {
251
- $wrap = '';
252
- $balance = '';
253
- }
254
-
255
- $group_class = ( 1 === (int) $event->event_span ) ? ' multidate group' . $event->event_group_id : '';
256
- $hlevel = apply_filters( 'mc_heading_level_table', 'h3', $type, $time, $template );
257
- // Set up .summary - required once per page for structured data. Should only be added in cases where heading & anchor are removed.
258
- if ( 'single' === $type ) {
259
- $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>';
260
- } elseif ( 'list' !== $type ) {
261
- $inner_heading = apply_filters( 'mc_heading_inner_title', $wrap . $image . trim( $event_title ) . $balance, $event_title, $event );
262
- $title = " <$hlevel class='event-title summary$group_class' id='mc_$event->occur_id-title-$id'>$inner_heading</$hlevel>\n";
263
- } else {
264
- $title = '';
265
- }
266
- $header .= ( false === stripos( $title, 'summary' ) ) ? ' <span class="summary screen-reader-text">' . strip_tags( $event_title ) . '</span>' : $title;
267
- $close_button = mc_close_button( "$uid-$type-details-$id" );
268
-
269
- if ( mc_show_details( $time, $type ) ) {
270
- // Since 3.2.0, close button is added to event container in mini calendar.
271
- $close = ( 'calendar' === $type ) ? $close_button : '';
272
-
273
- if ( false === $details ) {
274
- if ( ( 'true' === $display_address || 'true' === $display_map ) || ( mc_output_is_visible( 'address', $type, $event ) || mc_output_is_visible( 'gmap_link', $type, $event ) ) ) {
275
- $show_add = ( 'true' === $display_address || mc_output_is_visible( 'address', $type, $event ) ) ? 'true' : 'false';
276
- $show_map = ( 'true' === $display_map || mc_output_is_visible( 'gmap_link', $type, $event ) ) ? 'true' : 'false';
277
- $address = mc_hcard( $event, $show_add, $show_map );
278
- }
279
- $time_html = mc_time_html( $event, $type );
280
- if ( 'list' === $type ) {
281
- $hlevel = apply_filters( 'mc_heading_level_list', 'h3', $type, $time, $template );
282
- $list_title = " <$hlevel class='event-title summary' id='mc_$event->occur_id-title-$id'>$image" . $event_title . "</$hlevel>\n";
283
  }
284
- $avatars = apply_filters( 'mc_use_avatars', true, $event );
285
- if ( 'true' === $display_author || mc_output_is_visible( 'author', $type, $event ) ) {
286
- if ( 0 !== (int) $event->event_author && is_numeric( $event->event_author ) ) {
287
- $avatar = ( $avatars ) ? get_avatar( $event->event_author ) : '';
288
- $a = get_userdata( $event->event_author );
289
- if ( $a ) {
290
- $text = ( '' !== get_option( 'mc_posted_by', '' ) ) ? get_option( 'mc_posted_by' ) : __( 'Posted by', 'my-calendar' );
291
- $author = $avatar . '<p class="event-author"><span class="posted">' . $text . '</span> <span class="author-name">' . $a->display_name . "</span></p>\n";
292
- if ( $avatars ) {
293
- $author = ' <div class="mc-author-card">' . $author . '</div>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  }
295
  }
296
  }
297
- }
298
- if ( 'true' === $display_host || mc_output_is_visible( 'host', $type, $event ) ) {
299
- if ( 0 !== (int) $event->event_host && is_numeric( $event->event_host ) ) {
300
- $havatar = ( $avatars ) ? get_avatar( $event->event_host ) : '';
301
- $h = get_userdata( $event->event_host );
302
- if ( $h ) {
303
- $text = ( '' !== get_option( 'mc_hosted_by', '' ) ) ? get_option( 'mc_hosted_by' ) : __( 'Hosted by', 'my-calendar' );
304
- $host = $havatar . '<p class="event-host"><span class="hosted">' . $text . '</span> <span class="host-name">' . $h->display_name . "</span></p>\n";
305
- if ( $avatars ) {
306
- $host = ' <div class="mc-host-card">' . $host . '</div>';
307
  }
308
  }
309
  }
310
- }
311
- if ( ( 'true' === $display_more && ! isset( $_GET['mc_id'] ) ) || mc_output_is_visible( 'more', $type, $event ) ) {
312
- $details_label = mc_get_details_label( $event, $data );
313
- $details_link = mc_get_details_link( $event );
314
- // Translators: Event title.
315
- $aria = " aria-label='" . esc_attr( sprintf( __( 'Details about %s', 'my-calendar' ), strip_tags( $event_title ) ) ) . "'";
316
- if ( _mc_is_url( $details_link ) ) {
317
- $more = " <p class='mc-details'><a$aria href='" . esc_url( $details_link ) . "'>$details_label</a></p>\n";
318
- } else {
319
- $more = '';
320
  }
321
- }
322
- $more = apply_filters( 'mc_details_grid_link', $more, $event );
323
-
324
- if ( mc_output_is_visible( 'access', $type, $event ) ) {
325
- $access_heading = ( '' !== get_option( 'mc_event_accessibility', '' ) ) ? get_option( 'mc_event_accessibility' ) : __( 'Event Accessibility', 'my-calendar' );
326
- $access_content = mc_expand( get_post_meta( $event->event_post, '_mc_event_access', true ) );
327
- $sublevel = apply_filters( 'mc_subheading_level', 'h4', $type, $time, $template );
328
- if ( $access_content ) {
329
- $access = '<div class="mc-accessibility"><' . $sublevel . '>' . $access_heading . '</' . $sublevel . '>' . $access_content . '</div>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
  }
331
- }
332
 
333
- if ( 'true' === $display_gcal || mc_output_is_visible( 'gcal', $type, $event ) ) {
334
- $gcal = " <p class='gcal'>" . mc_draw_template( $data, '{gcal_link}' ) . '</p>';
335
- }
336
-
337
- if ( 'true' === $display_vcal || mc_output_is_visible( 'ical', $type, $event ) ) {
338
- $vcal = " <p class='ical'>" . mc_draw_template( $data, '{ical_html}' ) . '</p>';
339
- }
340
 
341
- if ( 'true' === $display_image || mc_output_is_visible( 'image', $type, $event ) ) {
342
- $img = mc_get_event_image( $event, $data );
343
- }
344
 
345
- if ( 'calendar' === $type ) {
346
- // This is semantically a duplicate of the title, but can be beneficial for sighted users.
347
- $headingtype = ( 'h3' === $hlevel ) ? 'h4' : 'h' . ( ( (int) str_replace( 'h', '', $hlevel ) ) - 1 );
348
- $inner_title = ' <' . $headingtype . ' class="mc-title" aria-hidden="true">' . $event_title . '</' . $headingtype . '>';
349
- }
350
 
351
- if ( 'true' === $display_desc || mc_output_is_visible( 'description', $type, $event ) ) {
352
- if ( '' !== trim( $event->event_desc ) ) {
353
- $description = wpautop( stripcslashes( mc_kses_post( $event->event_desc ) ), 1 );
354
- $description = " <div class='longdesc description'>$description</div>";
355
  }
356
- }
357
 
358
- if ( 'true' === $display_reg || mc_output_is_visible( 'tickets', $type, $event ) ) {
359
- $info = wpautop( stripcslashes( mc_kses_post( $event->event_registration ) ) );
360
- $url = esc_url( $event->event_tickets );
361
- $external = ( $url && mc_external_link( $url ) ) ? 'external' : '';
362
- $text = ( '' !== get_option( 'mc_buy_tickets', '' ) ) ? get_option( 'mc_buy_tickets' ) : __( 'Buy Tickets', 'my-calendar' );
363
- $tickets = ( $url ) ? "<a class='$external' href='" . $url . "'>" . $text . '</a>' : '';
364
- if ( '' !== trim( $info . $tickets ) ) {
365
- $tickets = '<div class="mc-registration">' . $info . $tickets . '</div>';
366
- } else {
367
- $tickets = '';
368
  }
369
- }
370
 
371
- if ( 'true' === $display_short || mc_output_is_visible( 'excerpt', $type, $event ) ) {
372
- if ( '' !== trim( $event->event_short ) ) {
373
- $short = wpautop( stripcslashes( mc_kses_post( $event->event_short ) ), 1 );
374
- $short = " <div class='shortdesc description'>$short</div>";
 
 
 
 
 
 
 
375
  }
376
- }
377
 
378
- $status = apply_filters( 'mc_registration_state', '', $event );
379
- $return_url = apply_filters( 'mc_return_uri', mc_get_uri( $event ) );
380
- $text = ( '' !== get_option( 'mc_view_full' ) ) ? get_option( 'mc_view_full' ) : __( 'View full calendar', 'my-calendar' );
381
- $return = ( 'single' === $type ) ? " <p class='view-full'><a href='$return_url'>" . $text . '</a></p>' : '';
 
 
382
 
383
- if ( ! mc_show_details( $time, $type ) ) {
384
- $description = '';
385
- $short = '';
386
- $status = '';
387
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
388
 
389
- if ( 'true' === $display_gmap || mc_output_is_visible( 'gmap', $type, $event ) ) {
390
- $map = ( is_singular( 'mc-events' ) || 'single' === $type ) ? mc_generate_map( $event ) : '';
391
- } else {
392
- $map = '';
393
- }
394
- $event_link = mc_event_link( $event );
395
-
396
- if ( '' !== $event_link && ( 'true' === $display_link || mc_output_is_visible( 'link', $type, $event ) ) ) {
397
- $external_class = ( mc_external_link( $event_link ) ) ? "$type-link external url" : "$type-link url";
398
- $link_template = ( '' !== mc_get_template( 'link' ) ) ? mc_get_template( 'link' ) : __( 'More information', 'my-calendar' );
399
- $link_text = mc_draw_template( $data, $link_template );
400
- $link = "
401
- <p>
402
- <a href='" . esc_url( $event_link ) . "' class='$external_class' aria-describedby='mc_{$event->occur_id}-title-$id'>" . $link_text . '</a>
403
- </p>';
404
- }
405
- $access = ( '' !== $access ) ? '<div class="mc-access-information">' . $access . '</div>' : '';
406
- $location = ( '' === trim( $map . $address ) ) ? '' : ' <div class="mc-location">' . $map . $address . '</div>';
407
- $sharing = ( '' === trim( $vcal . $gcal . $more ) ) ? '' : ' <div class="sharing">' . $vcal . $gcal . $more . '</div>';
408
-
409
- $close = ( '' !== $close ) ? PHP_EOL . ' ' . $close : '';
410
- $inner_title = ( $inner_title ) ? PHP_EOL . ' ' . $inner_title : '';
411
- $time_html = ( $time_html ) ? PHP_EOL . ' ' . $time_html : '';
412
- $list_title = ( $list_title ) ? PHP_EOL . ' ' . $list_title : '';
413
- $img = ( $img ) ? PHP_EOL . ' ' . $img : '';
414
- $location = ( $location ) ? PHP_EOL . ' ' . $location : '';
415
- $description = ( $description ) ? PHP_EOL . ' ' . $description : '';
416
- $short = ( $short ) ? PHP_EOL . ' ' . $short : '';
417
- $link = ( $link ) ? PHP_EOL . ' ' . $link : '';
418
- $status = ( $status ) ? PHP_EOL . ' ' . $status : '';
419
- $tickets = ( $tickets ) ? PHP_EOL . ' ' . $tickets : '';
420
- $author = ( $author ) ? PHP_EOL . ' ' . $author : '';
421
- $host = ( $host ) ? PHP_EOL . ' ' . $host : '';
422
- $sharing = ( $sharing ) ? PHP_EOL . ' ' . $sharing : '';
423
- $access = ( $access ) ? PHP_EOL . ' ' . $access : '';
424
- $return = ( $return ) ? PHP_EOL . ' ' . $return : '';
425
-
426
- $order = array( 'close', 'inner_title', 'list_title', 'time_html', 'img', 'description', 'short', 'location', 'access', 'link', 'status', 'tickets', 'author', 'host', 'sharing', 'return' );
427
- $output_order = apply_filters( 'mc_default_output_order', $order, $event );
428
- $details = $close;
429
- if ( ! empty( $output_order ) ) {
430
- foreach ( $output_order as $value ) {
431
- $details .= apply_filters( 'mc_event_detail_' . sanitize_title( $value ), ${$value}, $event );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
432
  }
433
  } else {
434
- $details .= "\n"
435
- . $inner_title
436
- . $list_title
437
- . $time_html
438
- . $img
439
- . $description
440
- . $short
441
- . $location
442
- . $access
443
- . $link
444
- . $status
445
- . $tickets
446
- . $sharing
447
- . $author
448
- . $host
449
- . $return;
450
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
451
  } else {
452
- // If a custom template is in use.
453
- $toggle = ( 'calendar' === $type ) ? $close_button : '';
454
- $details = $toggle . $details . "\n";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
455
  }
456
-
457
- $img_class = ( '' !== $img ) ? ' has-image' : ' no-image';
458
- $container = "\n <div id='$uid-$type-details-$id' class='details$img_class' role='alert' aria-labelledby='mc_$event->occur_id-title" . '-' . $id . "'>\n";
459
- $container = apply_filters( 'mc_before_event', $container, $event, $type, $time );
460
- $details = $header . $container . apply_filters( 'mc_inner_content', $details, $event, $type, $time );
461
- $details .= apply_filters( 'mc_after_event', '', $event, $type, $time );
462
- $details .= "\n" . ' </div><!--end .details-->' . "\n" . ' </div>' . "\n";
463
- $details = apply_filters( 'mc_event_content', $details, $event, $type, $time );
464
- } else {
465
- $details = apply_filters( 'mc_before_event_no_details', $container, $event, $type, $time ) . $header . apply_filters( 'mc_after_event_no_details', '', $event, $type, $time ) . '</div>';
466
  }
467
  $details = apply_filters( 'mc_event_details_output', $details, $event );
 
 
 
 
 
 
 
 
468
  do_action( 'my_calendar_event_drawn', $event );
469
 
470
  return $details;
@@ -478,6 +767,15 @@ function my_calendar_draw_event( $event, $type, $process_date, $time, $template
478
  * @return string
479
  */
480
  function mc_close_button( $controls ) {
 
 
 
 
 
 
 
 
 
481
  $close_image = apply_filters( 'mc_close_button', "<span class='dashicons dashicons-dismiss' aria-hidden='true'></span><span class='screen-reader-text'>Close</span>" );
482
  $close_button = " <button type='button' aria-controls='$controls' class='mc-toggle close' data-action='shiftforward'>$close_image</button>";
483
 
@@ -549,13 +847,42 @@ function mc_get_event_image( $event, $data ) {
549
  } else {
550
  $default_size = 'medium';
551
  }
 
 
 
 
 
 
 
 
 
552
  $default_size = apply_filters( 'mc_default_image_size', $default_size );
553
 
554
  if ( is_numeric( $event->event_post ) && 0 !== (int) $event->event_post && ( isset( $data[ $default_size ] ) && '' !== $data[ $default_size ] ) ) {
555
- $atts = apply_filters( 'mc_post_thumbnail_atts', array( 'class' => 'mc-image photo' ) );
 
 
 
 
 
 
 
 
 
 
556
  $image_url = get_the_post_thumbnail_url( $event->event_post, $default_size );
557
  $image = get_the_post_thumbnail( $event->event_post, $default_size, $atts );
558
  } else {
 
 
 
 
 
 
 
 
 
 
559
  $alt = esc_attr( apply_filters( 'mc_event_image_alt', '', $event ) );
560
  $image_url = $event->event_image;
561
  $image = ( '' !== $event->event_image ) ? "<img src='$event->event_image' alt='$alt' class='mc-image photo' />" : '';
@@ -565,17 +892,16 @@ function mc_get_event_image( $event, $data ) {
565
  global $template;
566
  $template_file_name = basename( $template );
567
  /**
568
- * Fires when displaying an event image in the default template.
569
- *
570
- * Return false to show the template image rather than the theme's featured image.
571
  *
 
572
  * @since 3.3.0
573
  *
574
- * @param bool $return True to return thumbnail in templates.
575
- * @param object $event Event object.
576
- * @param array $data Event template tags.
577
  *
578
- * @return bool
579
  */
580
  $override = apply_filters( 'mc_override_featured_image', $return, $event, $data );
581
  if ( $override && is_singular( 'mc-events' ) && has_post_thumbnail( $event->event_post ) && current_theme_supports( 'post-thumbnails' ) && ( 'single-mc-events.php' !== $template_file_name ) ) {
@@ -663,6 +989,18 @@ function mc_event_classes( $event, $type ) {
663
  $classes[] = 'mc_rel_' . sanitize_html_class( $category->category_name, 'mcat' . $category->category_id );
664
  }
665
 
 
 
 
 
 
 
 
 
 
 
 
 
666
  $classes = apply_filters( 'mc_event_classes', array_unique( $classes ), $event, $uid, $type );
667
  $class_html = strtolower( implode( ' ', $classes ) );
668
 
@@ -678,6 +1016,16 @@ function mc_event_classes( $event, $type ) {
678
  * @return boolean
679
  */
680
  function mc_show_details( $time, $type ) {
 
 
 
 
 
 
 
 
 
 
681
  $no_link = apply_filters( 'mc_disable_link', false, array() );
682
 
683
  return ( ( 'calendar' === $type && 'true' === get_option( 'mc_open_uri' ) && 'day' !== $time ) || $no_link ) ? false : true;
@@ -722,8 +1070,8 @@ function mc_edit_panel( $html, $event, $type, $time ) {
722
  /**
723
  * Create list of classes for a given date.
724
  *
725
- * @param array $events array of event objects.
726
- * @param mixed string/boolean $date current date if a date is being processed.
727
  *
728
  * @return string of classes
729
  */
@@ -767,8 +1115,18 @@ function mc_events_class( $events, $date = false ) {
767
  */
768
  function mc_list_title( $events ) {
769
  usort( $events, 'mc_time_cmp' );
770
- $now = $events[0];
771
- $count = count( $events ) - 1;
 
 
 
 
 
 
 
 
 
 
772
  $event_title = apply_filters( 'mc_list_title_title', strip_tags( stripcslashes( $now->event_title ), mc_strip_tags() ), $now );
773
  if ( 0 === $count ) {
774
  $cstate = $event_title;
@@ -779,6 +1137,17 @@ function mc_list_title( $events ) {
779
  // Translators: %s Title of event, %d number of other events.
780
  $cstate = sprintf( __( '%1$s<span class="mc-list-extended"> and %2$d other events</span>', 'my-calendar' ), $event_title, $count );
781
  }
 
 
 
 
 
 
 
 
 
 
 
782
  $title = apply_filters( 'mc_list_event_title_hint', $cstate, $now, $events );
783
 
784
  return $title;
@@ -796,13 +1165,42 @@ function mc_list_titles( $events ) {
796
  $titles = array();
797
 
798
  foreach ( $events as $now ) {
 
 
 
 
 
 
 
 
 
 
 
799
  $title = apply_filters( 'mc_list_event_title_hint', strip_tags( stripcslashes( $now->event_title ), mc_strip_tags() ), $now, $events );
800
  $titles[] = $title;
801
  }
802
-
 
 
 
 
 
 
 
 
 
803
  $result = apply_filters( 'mc_titles_format', '', $titles );
804
 
805
  if ( '' === $result ) {
 
 
 
 
 
 
 
 
 
806
  $result = implode( apply_filters( 'mc_list_titles_separator', ', ' ), $titles );
807
  }
808
 
@@ -878,9 +1276,10 @@ function mc_output_is_visible( $feature, $type, $event = false ) {
878
  /**
879
  * Filter whether any given piece of information should be output.
880
  *
881
- * @param string $feature Feature key.
882
- * @param string $type Type of view.
883
- * @param object|boolean $event Event object if in event context.
 
884
  *
885
  * @return bool
886
  */
@@ -949,18 +1348,61 @@ function mc_show_event_template( $content ) {
949
  return $content;
950
  }
951
  if ( '1' === get_option( 'mc_use_details_template' ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
952
  $new_content = apply_filters( 'mc_before_event', '', $event, 'single', $time );
953
  if ( isset( $_GET['mc_id'] ) ) {
954
  $shortcode = str_replace( "event='$event_id'", "event='$mc_id' instance='1'", get_post_meta( $post->ID, '_mc_event_shortcode', true ) );
955
  } else {
956
  $shortcode = get_post_meta( $post->ID, '_mc_event_shortcode', true );
957
  }
 
 
 
 
 
 
 
 
 
958
  $new_content .= do_shortcode( apply_filters( 'mc_single_event_shortcode', $shortcode ) );
 
 
 
 
 
 
 
 
 
 
 
 
959
  $new_content .= apply_filters( 'mc_after_event', '', $event, 'single', $time );
960
  } else {
961
  $new_content = my_calendar_draw_event( $event, 'single', $date, $time, '' );
962
  }
963
-
 
 
 
 
 
 
 
 
 
 
964
  $content = do_shortcode( apply_filters( 'mc_event_post_content', $new_content, $content, $post ) );
965
  }
966
  }
@@ -986,8 +1428,26 @@ function mc_list_group( $id, $this_id, $template = '{date}, {time}' ) {
986
  $output = '';
987
  $classes = '';
988
  // If a large number of events, skip this.
 
 
 
 
 
 
 
 
 
989
  if ( $count > apply_filters( 'mc_related_event_limit', 50 ) ) {
990
- // filter to return an subset of grouped events.
 
 
 
 
 
 
 
 
 
991
  return apply_filters( 'mc_grouped_events', '', $results );
992
  }
993
 
@@ -1049,7 +1509,17 @@ function mc_event_is_hidden( $event ) {
1049
  }
1050
  $category = $event->event_category;
1051
  $private = mc_get_private_categories();
1052
- $can_see = apply_filters( 'mc_user_can_see_private_events', is_user_logged_in(), $event );
 
 
 
 
 
 
 
 
 
 
1053
  if ( in_array( $category, $private, true ) && ! $can_see ) {
1054
 
1055
  return true;
@@ -1121,6 +1591,16 @@ function mc_calendar_params( $args ) {
1121
  $search = $_GET['mcs'];
1122
  }
1123
 
 
 
 
 
 
 
 
 
 
 
1124
  $format = apply_filters( 'mc_display_format', $format, $args );
1125
  $params = array(
1126
  'name' => $name, // Not used in my_calendar(); what is/was it for.
@@ -1169,11 +1649,31 @@ function mc_get_calendar_header( $params, $id, $tr, $start_of_week ) {
1169
  $name_days = $days['name_days'];
1170
  $abbrevs = $days['abbrevs'];
1171
 
 
 
 
 
 
 
 
 
 
 
1172
  $th = apply_filters( 'mc_grid_header_wrapper', 'th', $params['format'] );
1173
  $close_th = ( 'th' === $th ) ? 'th' : $th;
1174
  $th .= ( 'th' === $th ) ? ' scope="col"' : '';
1175
  $body = '';
1176
  if ( 'calendar' === $params['format'] || 'mini' === $params['format'] ) {
 
 
 
 
 
 
 
 
 
 
1177
  $table = apply_filters( 'mc_grid_wrapper', 'table', $params['format'] );
1178
  $body .= "\n<$table class='my-calendar-table' aria-labelledby='mc_head_$id'>\n";
1179
  }
@@ -1183,6 +1683,16 @@ function mc_get_calendar_header( $params, $id, $tr, $start_of_week ) {
1183
  } else {
1184
  $body .= ( 'tr' === $tr ) ? '<thead>' : '<div class="mc-table-body">';
1185
  $body .= "\n <$tr class='mc-row'>\n";
 
 
 
 
 
 
 
 
 
 
1186
  if ( apply_filters( 'mc_show_week_number', false, $params ) ) {
1187
  $body .= " <$th class='mc-week-number'>" . __( 'Week', 'my-calendar' ) . "</$close_th>\n";
1188
  }
@@ -1256,10 +1766,20 @@ function my_calendar( $args ) {
1256
  $show_weekends = ( get_option( 'mc_show_weekends' ) === 'true' ) ? true : false;
1257
  $skip_holidays = get_option( 'mc_skip_holidays_category' );
1258
  $month_format = ( get_option( 'mc_month_format', '' ) === '' ) ? 'F Y' : get_option( 'mc_month_format' );
1259
- $show_months = absint( apply_filters( 'mc_show_months', get_option( 'mc_show_months' ), $args ) );
1260
- $show_months = ( '0' === $show_months ) ? 1 : $show_months;
1261
- $caption_text = ' ' . stripslashes( trim( get_option( 'mc_caption' ) ) );
1262
- $week_format = ( ! get_option( 'mc_week_format' ) ) ? 'M j, \'y' : get_option( 'mc_week_format' );
 
 
 
 
 
 
 
 
 
 
1263
  // Translators: Template tag with date format.
1264
  $week_template = ( get_option( 'mc_week_caption', '' ) !== '' ) ? get_option( 'mc_week_caption' ) : sprintf( __( 'Week of %s', 'my-calendar' ), '{date format="M jS"}' );
1265
  $open_day_uri = ( ! get_option( 'mc_open_day_uri' ) ) ? 'false' : get_option( 'mc_open_day_uri' ); // This is not a URL. It's a behavior reference.
@@ -1273,7 +1793,17 @@ function my_calendar( $args ) {
1273
  my_calendar_check();
1274
 
1275
  $params = mc_calendar_params( $args );
1276
- $body = apply_filters( 'mc_before_calendar', '', $params );
 
 
 
 
 
 
 
 
 
 
1277
 
1278
  $id = $params['id'];
1279
  $main_class = ( '' !== $id ) ? sanitize_title( $id ) : 'all';
@@ -1284,8 +1814,31 @@ function my_calendar( $args ) {
1284
  $mc_closer = '
1285
  </div>';
1286
 
 
 
 
 
 
 
 
 
 
 
 
1287
  $date_format = apply_filters( 'mc_date_format', $date_format, $params['format'], $params['time'] );
1288
- $hl = apply_filters( 'mc_heading_level', 'h2', $params['format'], $params['time'], $template );
 
 
 
 
 
 
 
 
 
 
 
 
1289
 
1290
  if ( isset( $_GET['mc_id'] ) && 'widget' !== $source ) {
1291
  // single event, main calendar only.
@@ -1304,7 +1857,25 @@ function my_calendar( $args ) {
1304
  }
1305
 
1306
  $dates = mc_get_from_to( $show_months, $params, $date );
1307
- $from = apply_filters( 'mc_from_date', $dates['from'] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1308
  $to = apply_filters( 'mc_to_date', $dates['to'] );
1309
  $from = ( 'day' === $params['time'] ) ? mc_date( 'Y-m-d', $current, false ) : $from;
1310
  $to = ( 'day' === $params['time'] ) ? mc_date( 'Y-m-d', $current, false ) : $to;
@@ -1320,6 +1891,16 @@ function my_calendar( $args ) {
1320
  'source' => 'calendar',
1321
  'site' => $site,
1322
  );
 
 
 
 
 
 
 
 
 
 
1323
  $query = apply_filters( 'mc_calendar_attributes', $query, $params );
1324
  if ( 'mc-print-view' === $id && isset( $_GET['searched'] ) && $_GET['searched'] ) {
1325
  $event_array = mc_get_searched_events();
@@ -1339,6 +1920,17 @@ function my_calendar( $args ) {
1339
  $bottom = $nav['bottom'];
1340
 
1341
  if ( 'day' === $params['time'] ) {
 
 
 
 
 
 
 
 
 
 
 
1342
  $heading = "<$hl id='mc_head_$id' class='mc-single heading my-calendar-$params[time]'><span>" . apply_filters( 'mc_heading', date_i18n( $date_format, $current ), $params['format'], $params['time'] ) . "</span></$hl>";
1343
  $dateclass = mc_dateclass( $current );
1344
  $mc_events = '';
@@ -1384,17 +1976,70 @@ function my_calendar( $args ) {
1384
  } else {
1385
  $heading = mc_draw_template( $values, stripslashes( $week_template ) );
1386
  }
1387
- $h2 = apply_filters( 'mc_heading_level', 'h2', $params['format'], $params['time'], $template );
1388
- $heading = apply_filters( 'mc_heading', $heading, $params['format'], $params['time'] );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1389
  $body .= "<$h2 id=\"mc_head_$id\" class=\"heading my-calendar-$params[time]\"><span>$heading</span></$h2>\n";
1390
  $body .= $top;
1391
 
1392
- // Add the calendar table and heading.
 
 
 
 
 
 
 
 
 
1393
  $table = apply_filters( 'mc_grid_wrapper', 'table', $params['format'] );
 
 
 
 
 
 
 
 
 
 
1394
  $tr = apply_filters( 'mc_grid_week_wrapper', 'tr', $params['format'] );
1395
  $body .= mc_get_calendar_header( $params, $id, $tr, $start_of_week );
1396
  $odd = 'odd';
1397
 
 
 
 
 
 
 
 
 
 
 
1398
  $show_all = apply_filters( 'mc_all_list_dates', false, $args );
1399
  if ( $no_events && 'list' === $params['format'] && false === $show_all ) {
1400
  // If there are no events in list format, just display that info.
@@ -1407,13 +2052,23 @@ function my_calendar( $args ) {
1407
  $json = array();
1408
  do {
1409
  $date_is = mc_date( 'Y-m-d', $start, false );
1410
- $is_weekend = ( mc_date( 'N', $start, false ) < 6 ) ? false : true;
1411
  if ( $show_weekends || ( ! $show_weekends && ! $is_weekend ) ) {
1412
  if ( mc_date( 'N', $start, false ) === (string) $start_of_week && 'list' !== $params['format'] ) {
1413
  $body .= "<$tr class='mc-row'>";
1414
  }
1415
- $events = ( isset( $event_array[ $date_is ] ) ) ? $event_array[ $date_is ] : array();
1416
- $week_header = date_i18n( $week_format, $start );
 
 
 
 
 
 
 
 
 
 
1417
  $thisday_heading = ( 'week' === $params['time'] ) ? "<small>$week_header</small>" : mc_date( apply_filters( 'mc_grid_date', 'j', $params ), $start, false );
1418
 
1419
  // Generate event classes & attributes.
@@ -1422,6 +2077,16 @@ function my_calendar( $args ) {
1422
  $dateclass = mc_dateclass( $start );
1423
  $ariacurrent = ( false !== strpos( $dateclass, 'current-day' ) ) ? ' aria-current="date"' : '';
1424
 
 
 
 
 
 
 
 
 
 
 
1425
  $td = apply_filters( 'mc_grid_day_wrapper', 'td', $params['format'] );
1426
  if ( ! $week_number_shown ) {
1427
  $weeknumber = mc_show_week_number( $events, $args, $params['format'], $td, $start );
@@ -1432,6 +2097,15 @@ function my_calendar( $args ) {
1432
  }
1433
 
1434
  if ( ! empty( $events ) ) {
 
 
 
 
 
 
 
 
 
1435
  $hide_nextmonth = apply_filters( 'mc_hide_nextmonth', false );
1436
  if ( true === $hide_nextmonth && 'nextmonth' === $monthclass ) {
1437
  $event_output = ' ';
@@ -1511,7 +2185,6 @@ function my_calendar( $args ) {
1511
  } while ( $start <= $end );
1512
  }
1513
 
1514
- $table = apply_filters( 'mc_grid_wrapper', 'table', $params['format'] );
1515
  $end = ( 'table' === $table ) ? "\n</tbody>\n</table>" : "</div></$table>";
1516
  $body .= ( 'list' === $params['format'] ) ? "\n</ul>" : $end;
1517
  }
@@ -1519,7 +2192,16 @@ function my_calendar( $args ) {
1519
  $body .= ( 'day' === $params['time'] ) ? '' : '</div><!-- .mc-content -->';
1520
  $body .= $bottom;
1521
  }
1522
- // The actual printing is done by the shortcode function.
 
 
 
 
 
 
 
 
 
1523
  $body .= apply_filters( 'mc_after_calendar', '', $args );
1524
 
1525
  if ( $site ) {
@@ -1533,6 +2215,15 @@ function my_calendar( $args ) {
1533
  }
1534
  }
1535
 
 
 
 
 
 
 
 
 
 
1536
  $output = $mc_wrapper . $json_ld . apply_filters( 'my_calendar_body', $body ) . $mc_closer;
1537
  if ( $language ) {
1538
  mc_switch_language( $language, $locale );
@@ -1553,6 +2244,16 @@ function my_calendar( $args ) {
1553
  */
1554
  function mc_show_week_number( $events, $args, $format, $td, $start ) {
1555
  $body = '';
 
 
 
 
 
 
 
 
 
 
1556
  if ( apply_filters( 'mc_show_week_number', false, $args ) ) {
1557
  $weeknumber = mc_date( 'W', $start, false );
1558
  if ( 'list' !== $format ) {
@@ -1600,6 +2301,7 @@ function mc_get_current_date( $main_class, $cid, $params ) {
1600
  $syear = $params['syear'];
1601
  $sday = $params['sday'];
1602
  $c_m = 0;
 
1603
  if ( isset( $_GET['dy'] ) && $main_class === $cid && ( 'day' === $time || 'week' === $time ) ) {
1604
  if ( '' === $_GET['dy'] ) {
1605
  $today = current_time( 'j' );
@@ -1641,10 +2343,10 @@ function mc_get_current_date( $main_class, $cid, $params ) {
1641
  if ( $is_start_of_week ) {
1642
  $c_year = ( current_time( 'Y' ) );
1643
  } else {
1644
- $current_year = current_time( 'Y' );
1645
  $c_year = ( 0 === (int) $dm[1] ) ? $current_year : false;
1646
  if ( ! $c_year ) {
1647
- $c_year = ( mc_date( 'Y', strtotime( '-1 month' ), false ) === $current_year ) ? $current_year : $current_year - 1;
1648
  }
1649
  }
1650
  } else {
@@ -1663,10 +2365,39 @@ function mc_get_current_date( $main_class, $cid, $params ) {
1663
  $shortcode_month = ( false !== $smonth ) ? $smonth : $c_month;
1664
  $shortcode_year = ( false !== $syear ) ? $syear : $c_year;
1665
  $shortcode_day = ( false !== $sday ) ? $sday : $c_day;
1666
- // Override with filters.
1667
- $c_year = apply_filters( 'mc_filter_year', $shortcode_year, $params );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1668
  $c_month = apply_filters( 'mc_filter_month', $shortcode_month, $params );
1669
- $c_day = apply_filters( 'mc_filter_day', $shortcode_day, $params );
 
 
 
 
 
 
 
 
 
 
1670
  }
1671
  $c_day = ( 0 === (int) $c_day ) ? 1 : $c_day; // c_day can't equal 0.
1672
  $current = mktime( 0, 0, 0, (int) $c_month, (int) $c_day, (int) $c_year );
@@ -1690,6 +2421,15 @@ add_filter( 'my_calendar_body', 'mc_run_shortcodes', 10, 1 );
1690
  * @return string Calendar body with shortcodes processed
1691
  */
1692
  function mc_run_shortcodes( $content ) {
 
 
 
 
 
 
 
 
 
1693
  $content = ( 'true' === apply_filters( 'mc_process_shortcodes', 'true' ) ) ? do_shortcode( $content ) : $content;
1694
 
1695
  return $content;
@@ -1722,17 +2462,27 @@ function mc_wrap_title( $title ) {
1722
  *
1723
  * @return string HTML form.
1724
  */
1725
- function my_calendar_searchform( $type, $url ) {
1726
  $query = ( isset( $_GET['mcs'] ) ) ? $_GET['mcs'] : '';
1727
  if ( 'simple' === $type ) {
1728
- if ( ! $url || '' === $url ) {
1729
  $url = mc_get_uri( false, array( 'type' => $type ) );
1730
  }
 
 
 
 
 
 
 
 
 
 
1731
  return '
1732
  <div class="mc-search-container" role="search">
1733
- <form class="mc-search-form" method="get" action="' . apply_filters( 'mc_search_page', esc_url( $url ) ) . '" >
1734
  <div class="mc-search">
1735
- <label class="screen-reader-text" for="mcs">' . __( 'Search Events', 'my-calendar' ) . '</label><input type="text" value="' . esc_attr( stripslashes( urldecode( $query ) ) ) . '" name="mcs" id="mcs" /><input type="submit" data-href="' . esc_url( $url ) . '" class="button" id="searchsubmit" value="' . __( 'Search Events', 'my-calendar' ) . '" />
1736
  </div>
1737
  </form>
1738
  </div>';
@@ -1744,15 +2494,16 @@ function my_calendar_searchform( $type, $url ) {
1744
  /**
1745
  * Get list of locations.
1746
  *
1747
- * @param string $datatype Type of data to sort by and return.
1748
- * @param boolean $full If need to return full location object.
1749
- * @param constant $return_type valid query return type.
1750
  *
1751
  * @return array of location objects.
1752
  */
1753
- function mc_get_list_locations( $datatype, $full = true, $return_type = OBJECT ) {
1754
  global $wpdb;
1755
- $mcdb = $wpdb;
 
1756
  if ( 'true' === get_option( 'mc_remote' ) && function_exists( 'mc_remote_db' ) ) {
1757
  $mcdb = mc_remote_db();
1758
  }
@@ -1783,7 +2534,16 @@ function mc_get_list_locations( $datatype, $full = true, $return_type = OBJECT )
1783
  default:
1784
  $data = 'location_label';
1785
  }
1786
-
 
 
 
 
 
 
 
 
 
1787
  $where = esc_sql( apply_filters( 'mc_filter_location_list', '', $datatype ) );
1788
  if ( true !== $full ) {
1789
  $select = esc_sql( $data );
@@ -1851,7 +2611,16 @@ function my_calendar_show_locations( $datatype = 'name', $template = '' ) {
1851
  }
1852
  $output = '<ul class="mc-locations">' . $output . '</ul>';
1853
  }
1854
-
 
 
 
 
 
 
 
 
 
1855
  $output = apply_filters( 'mc_location_list', $output, $locations );
1856
 
1857
  return $output;
@@ -1948,6 +2717,16 @@ function my_calendar_locations_list( $show = 'list', $datatype = 'name', $group
1948
  </form>' : '';
1949
  $output .= ( 'single' === $group ) ? '</div>' : '';
1950
  }
 
 
 
 
 
 
 
 
 
 
1951
  $output = apply_filters( 'mc_location_selector', $output, $locations );
1952
 
1953
  return $output;
36
  $dtend = $end . 'T' . $e->event_endtime . $offset;
37
  $notime = '';
38
  if ( ! $has_time ) {
39
+ $label = mc_notime_label( $e );
40
+ if ( $label ) {
41
+ $notime .= " <span class='event-time'>";
42
+ $notime .= ( 'N/A' === $label ) ? "<abbr title='" . esc_html__( 'Not Applicable', 'my-calendar' ) . "'>" . esc_html__( 'N/A', 'my-calendar' ) . '</abbr>' : esc_html( $label );
43
+ $notime .= '</span>';
44
+ }
45
  }
46
  $date_start = "<span class='mc-start-date dtstart' title='" . esc_attr( $dtstart ) . "' content='" . esc_attr( $dtstart ) . "'>" . date_i18n( $date_format, strtotime( $e->occur_begin ) ) . '</span>';
47
  $time_start = ( $has_time ) ? "<span class='event-time dtstart'><time class='value-title' datetime='" . esc_attr( $dtstart ) . "' title='" . esc_attr( $dtstart ) . "'>" . date_i18n( $time_format, strtotime( $e->occur_begin ) ) . '</time></span>' : $notime;
61
  /**
62
  * Filter time block output.
63
  *
64
+ * @hook mcs_time_block
65
+ *
66
+ * @param {string} $time HTML time block output.
67
+ * @param {object} $e Event object.
68
  *
69
  * @return string
70
  */
80
  * @param string $template Template to use for drawing individual events.
81
  * @param string $id ID for the calendar calling this function.
82
  *
83
+ * @return array|true [html] Generated HTML & [json] array of schema.org data. True if event details not included.
84
  */
85
  function my_calendar_draw_events( $events, $params, $process_date, $template = '', $id = '' ) {
86
  $type = $params['format'];
91
  return true;
92
  }
93
  // We need to sort arrays of objects by time.
94
+ if ( ! empty( $events ) ) {
95
  $output_array = array();
96
  $json = array();
97
  $begin = '';
139
  return array();
140
  }
141
 
142
+ /**
143
+ * Check whether legacy templates are enabled.
144
+ *
145
+ * @return bool
146
+ */
147
+ function mc_legacy_templates_enabled() {
148
+ /**
149
+ * Filter legacy templates status. New templates are intended for release with version 3.4.0 and will be in alpha at least through then.
150
+ *
151
+ * @hook mc_legacy_templates_enabled
152
+ *
153
+ * @param {bool} $enabled Return 'true' to use legacy templates.
154
+ *
155
+ * @return {bool}
156
+ */
157
+ $enabled = apply_filters( 'mc_legacy_templates_enabled', true );
158
+ // New templates require at least WP 5.5.
159
+ if ( version_compare( $GLOBALS['wp_version'], '5.5', '<' ) ) {
160
+ $enabled = true;
161
+ }
162
+
163
+ return $enabled;
164
+ }
165
 
166
  /**
167
  * Draw a single event
181
  if ( $exit_early ) {
182
  return '';
183
  }
184
+ /**
185
+ * Runs right before a calendar event template is handled.
186
+ *
187
+ * @since 3.3.15.
188
+ * @hook my_calendar_drawing_event
189
+ *
190
+ * @param {object} $event My Calendar Event Object.
191
+ */
192
  do_action( 'my_calendar_drawing_event', $event );
193
+ $legacy_templates = mc_legacy_templates_enabled();
194
+ $details = false;
195
+ if ( ! $legacy_templates ) {
196
+ $templates = new Mc_Template_Loader();
197
+ ob_start();
198
+ $data = array(
199
+ 'event' => $event,
200
+ 'process_date' => $process_date,
201
+ 'time' => $time,
202
+ 'id' => $id,
203
+ 'tags' => $tags,
204
+ );
205
+ $templates->set_template_data( $data );
206
+ $templates->get_template_part( 'event', $type );
207
+ $details = ob_get_clean();
208
+ }
209
+
210
+ if ( ! $details ) {
211
+ // assign empty values to template sections.
212
+ $header = '';
213
+ $address = '';
214
+ $more = '';
215
+ $author = '';
216
+ $host = '';
217
+ $list_title = '';
218
+ $title = '';
219
+ $output = '';
220
+ $container = '';
221
+ $short = '';
222
+ $description = '';
223
+ $link = '';
224
+ $vcal = '';
225
+ $inner_title = '';
226
+ $gcal = '';
227
+ $access = '';
228
+ $image = '';
229
+ $tickets = '';
230
+ $details = '';
231
+ $data = ( empty( $tags ) ) ? mc_create_tags( $event, $id ) : $tags;
232
+ $otype = ( 'calendar' === $type ) ? 'grid' : $type;
233
+
234
+ if ( mc_show_details( $time, $type ) ) {
235
+ /**
236
+ * Filter My Calendar view output. Returning any content will shortcircuit drawing event output.
237
+ *
238
+ * @hook mc_custom_template
239
+ *
240
+ * @param {string|bool} $details Output HTML for event. Default boolean false.
241
+ * @param {array} $data Event data array passed to template function.
242
+ * @param {object} $event My Calendar event object.
243
+ * @param {string} $type View type.
244
+ * @param {string} $process_date Current date being processed.
245
+ * @param {string} $time View timeframe.
246
+ * @param {string} $template Existing template.
247
+ *
248
+ * @return {string}
249
+ */
250
+ $details = apply_filters( 'mc_custom_template', false, $data, $event, $type, $process_date, $time, $template );
251
+ /**
252
+ * Filter My Calendar view template.
253
+ *
254
+ * @hook mc_use_custom_template
255
+ *
256
+ * @param {string} $template HTML with template tags.
257
+ * @param {array} $data Event data array passed to template function.
258
+ * @param {object} $event My Calendar event object.
259
+ * @param {string} $type View type.
260
+ * @param {string} $process_date Current date being processed.
261
+ * @param {string} $time View timeframe.
262
+ *
263
+ * @return {string}
264
+ */
265
+ $template = apply_filters( 'mc_use_custom_template', $template, $data, $event, $type, $process_date, $time );
266
+ if ( false === $details ) {
267
+ $details = mc_get_details( $data, $template, $type );
268
+ }
269
  }
 
270
 
271
+ // Fallback display options. Changed in 3.3.0; fallback to old settings if new don't exist.
272
+ $display_map = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_show_map' ) : '';
273
+ $display_address = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_show_address' ) : '';
274
+ $display_gcal = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_show_gcal' ) : '';
275
+ $display_vcal = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_show_event_vcal' ) : '';
276
+ $open_uri = get_option( 'mc_open_uri' );
277
+ $display_author = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_display_author' ) : '';
278
+ $display_host = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_display_host' ) : '';
279
+ $display_more = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_display_more' ) : '';
280
+ $display_desc = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_desc' ) : '';
281
+ $display_short = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_short' ) : '';
282
+ $display_gmap = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_gmap' ) : '';
283
+ $display_link = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_event_link' ) : '';
284
+ $display_image = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_image' ) : '';
285
+ $display_reg = ( '' === get_option( 'mc_display_' . $otype, '' ) ) ? get_option( 'mc_event_registration' ) : '';
286
+ $day_id = mc_date( 'd', strtotime( $process_date ), false );
287
+ $uid = 'mc_' . $type . '_' . $day_id . '_' . $event->occur_id;
288
+ $image = mc_category_icon( $event );
289
+ $img = '';
290
+ $has_image = ( '' !== $image ) ? ' has-image' : '';
291
+ $event_classes = mc_event_classes( $event, $type );
292
+ $nofollow = ( stripos( $event_classes, 'past-event' ) !== false ) ? 'rel="nofollow"' : '';
293
+ $header .= "\n\n <div id='$uid-$type-$id' class='$event_classes'>\n";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
 
295
+ switch ( $type ) {
296
+ case 'calendar':
297
+ $title_template = ( mc_get_template( 'title' ) === '' ) ? '{title}' : mc_get_template( 'title' );
298
+ break;
299
+ case 'list':
300
+ $title_template = ( mc_get_template( 'title_list' ) === '' ) ? '{title}' : mc_get_template( 'title_list' );
301
+ break;
302
+ case 'single':
303
+ $title_template = ( mc_get_template( 'title_solo' ) === '' ) ? '{title}' : mc_get_template( 'title_solo' );
304
+ break;
305
+ default:
306
+ $title_template = ( mc_get_template( 'title' ) === '' ) ? '{title}' : mc_get_template( 'title' );
307
+ }
308
 
309
+ $event_title = mc_draw_template( $data, $title_template );
310
+ if ( 0 === strpos( $event_title, ': ' ) ) {
311
+ // If the first two characters of the title are ": ", this is the default templates but no time.
312
+ $event_title = str_replace( ': ', '', $event_title );
313
+ }
314
+ $event_title = ( '' === $event_title ) ? $data['title'] : strip_tags( $event_title, mc_strip_tags() );
315
+ if ( 'single' === $type ) {
316
+ /**
317
+ * Customize event title in single view.
318
+ *
319
+ * @hook mc_single_event_title
320
+ *
321
+ * @param {string} $event_title Event title.
322
+ * @param {object} $event My Calendar event object.
323
+ *
324
+ * @return {string}
325
+ */
326
+ $event_title = apply_filters( 'mc_single_event_title', $event_title, $event );
327
  } else {
328
+ /**
329
+ * Customize event title in group views.
330
+ *
331
+ * @hook mc_event_title
332
+ *
333
+ * @param {string} $event_title Event title.
334
+ * @param {object} $event My Calendar event object.
335
+ * @param {string} $title Title in event template array.
336
+ * @param {string} $image Category icon.
337
+ *
338
+ * @return {string}
339
+ */
340
+ $event_title = apply_filters( 'mc_event_title', $event_title, $event, $data['title'], $image );
341
  }
342
+ /**
343
+ * Disable links on grid view.
344
+ *
345
+ * @hook mc_disable_link
346
+ *
347
+ * @param {bool} $no_link True to disable link.
348
+ * @param {array} $data Event data array.
349
+ *
350
+ * @return {bool}
351
+ */
352
+ $no_link = apply_filters( 'mc_disable_link', false, $data );
353
+
354
+ if ( ( ( strpos( $event_title, 'href' ) === false ) && 'mini' !== $type && 'list' !== $type ) && ! $no_link ) {
355
+ if ( 'true' === $open_uri ) {
356
+ $details_link = esc_url( mc_get_details_link( $event ) );
357
+ $wrap = ( _mc_is_url( $details_link ) ) ? "<a href='$details_link' class='url summary$has_image' $nofollow>" : '<span class="no-link">';
358
+ $balance = ( _mc_is_url( $details_link ) ) ? '</a>' : '</span>';
359
+ } else {
360
+ $wrap = "<a href='#$uid-$type-details-$id' class='open et_smooth_scroll_disabled opl-link url summary$has_image'>";
361
+ $balance = '</a>';
 
 
 
 
 
 
 
 
 
 
 
 
 
362
  }
363
+ } else {
364
+ $wrap = '';
365
+ $balance = '';
366
+ }
367
+
368
+ $group_class = ( 1 === (int) $event->event_span ) ? ' multidate group' . $event->event_group_id : '';
369
+ /**
370
+ * Filter default event heading when in a table.
371
+ *
372
+ * @hook mc_heading_level_table
373
+ *
374
+ * @param {string} $hlevel HTML element. Default 'h3'.
375
+ * @param {string} $type View type.
376
+ * @param {string} $time View timeframe.
377
+ * @param {string} $template Current template.
378
+ *
379
+ * @return {string}
380
+ */
381
+ $hlevel = apply_filters( 'mc_heading_level_table', 'h3', $type, $time, $template );
382
+ // Set up .summary - required once per page for structured data. Should only be added in cases where heading & anchor are removed.
383
+ if ( 'single' === $type ) {
384
+ $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>';
385
+ } elseif ( 'list' !== $type ) {
386
+ /**
387
+ * Filter event title inside event heading.
388
+ *
389
+ * @hook mc_heading_inner_title
390
+ *
391
+ * @param {string} $inner_heading Heading HTML and text.
392
+ * @param {string} $event_title Title as passed.
393
+ * @param {object} $event My Calendar event object.
394
+ *
395
+ * @return {string}
396
+ */
397
+ $inner_heading = apply_filters( 'mc_heading_inner_title', $wrap . $image . trim( $event_title ) . $balance, $event_title, $event );
398
+ $title = " <$hlevel class='event-title summary$group_class' id='mc_$event->occur_id-title-$id'>$inner_heading</$hlevel>\n";
399
+ } else {
400
+ $title = '';
401
+ }
402
+ $header .= ( false === stripos( $title, 'summary' ) ) ? ' <span class="summary screen-reader-text">' . strip_tags( $event_title ) . '</span>' : $title;
403
+ $close_button = mc_close_button( "$uid-$type-details-$id" );
404
+
405
+ if ( mc_show_details( $time, $type ) ) {
406
+ // Since 3.2.0, close button is added to event container in mini calendar.
407
+ $close = ( 'calendar' === $type ) ? $close_button : '';
408
+
409
+ if ( false === $details ) {
410
+ if ( ( 'true' === $display_address || 'true' === $display_map ) || ( mc_output_is_visible( 'address', $type, $event ) || mc_output_is_visible( 'gmap_link', $type, $event ) ) ) {
411
+ $show_add = ( 'true' === $display_address || mc_output_is_visible( 'address', $type, $event ) ) ? 'true' : 'false';
412
+ $show_map = ( 'true' === $display_map || mc_output_is_visible( 'gmap_link', $type, $event ) ) ? 'true' : 'false';
413
+ $address = mc_hcard( $event, $show_add, $show_map );
414
+ }
415
+ $time_html = mc_time_html( $event, $type );
416
+ if ( 'list' === $type ) {
417
+ /**
418
+ * Filter list event heading level. Default 'h3'.
419
+ *
420
+ * @hook mc_heading_level_list
421
+ *
422
+ * @param {string} $hlevel Default heading level element.
423
+ * @param {string} $type View type.
424
+ * @param {string} $time View timeframe.
425
+ * @param {string} $template Current template.
426
+ *
427
+ * @return {string}
428
+ */
429
+ $hlevel = apply_filters( 'mc_heading_level_list', 'h3', $type, $time, $template );
430
+ $list_title = " <$hlevel class='event-title summary' id='mc_$event->occur_id-title-$id'>$image" . $event_title . "</$hlevel>\n";
431
+ }
432
+ /**
433
+ * Filter to enable or disable avatars.
434
+ *
435
+ * @hook mc_use_avatars
436
+ *
437
+ * @param {bool} $avatars false to disable avatars.
438
+ * @param {object} $event My Calendar event object.
439
+ *
440
+ * @return {bool}
441
+ */
442
+ $avatars = apply_filters( 'mc_use_avatars', true, $event );
443
+ if ( 'true' === $display_author || mc_output_is_visible( 'author', $type, $event ) ) {
444
+ if ( 0 !== (int) $event->event_author && is_numeric( $event->event_author ) ) {
445
+ $avatar = ( $avatars ) ? get_avatar( $event->event_author ) : '';
446
+ $a = get_userdata( $event->event_author );
447
+ if ( $a ) {
448
+ $text = ( '' !== get_option( 'mc_posted_by', '' ) ) ? get_option( 'mc_posted_by' ) : __( 'Posted by', 'my-calendar' );
449
+ $author = $avatar . '<p class="event-author"><span class="posted">' . $text . '</span> <span class="author-name">' . $a->display_name . "</span></p>\n";
450
+ if ( $avatars ) {
451
+ $author = ' <div class="mc-author-card">' . $author . '</div>';
452
+ }
453
  }
454
  }
455
  }
456
+ if ( 'true' === $display_host || mc_output_is_visible( 'host', $type, $event ) ) {
457
+ if ( 0 !== (int) $event->event_host && is_numeric( $event->event_host ) ) {
458
+ $havatar = ( $avatars ) ? get_avatar( $event->event_host ) : '';
459
+ $h = get_userdata( $event->event_host );
460
+ if ( $h ) {
461
+ $text = ( '' !== get_option( 'mc_hosted_by', '' ) ) ? get_option( 'mc_hosted_by' ) : __( 'Hosted by', 'my-calendar' );
462
+ $host = $havatar . '<p class="event-host"><span class="hosted">' . $text . '</span> <span class="host-name">' . $h->display_name . "</span></p>\n";
463
+ if ( $avatars ) {
464
+ $host = ' <div class="mc-host-card">' . $host . '</div>';
465
+ }
466
  }
467
  }
468
  }
469
+ if ( ( 'true' === $display_more && ! isset( $_GET['mc_id'] ) ) || mc_output_is_visible( 'more', $type, $event ) ) {
470
+ $details_label = mc_get_details_label( $event, $data );
471
+ $details_link = mc_get_details_link( $event );
472
+ // Translators: Event title.
473
+ $aria = " aria-label='" . esc_attr( sprintf( __( 'Details about %s', 'my-calendar' ), strip_tags( $event_title ) ) ) . "'";
474
+ if ( _mc_is_url( $details_link ) ) {
475
+ $more = " <p class='mc-details'><a$aria href='" . esc_url( $details_link ) . "'>$details_label</a></p>\n";
476
+ } else {
477
+ $more = '';
478
+ }
479
  }
480
+ /**
481
+ * Filter link to event details in grid.
482
+ *
483
+ * @hook mc_details_grid_link
484
+ *
485
+ * @param {string} $more More link.
486
+ * @param {object} $event My Calendar event object.
487
+ *
488
+ * @return {string}
489
+ */
490
+ $more = apply_filters( 'mc_details_grid_link', $more, $event );
491
+
492
+ if ( mc_output_is_visible( 'access', $type, $event ) ) {
493
+ $access_heading = ( '' !== get_option( 'mc_event_accessibility', '' ) ) ? get_option( 'mc_event_accessibility' ) : __( 'Event Accessibility', 'my-calendar' );
494
+ $access_content = mc_expand( get_post_meta( $event->event_post, '_mc_event_access', true ) );
495
+ /**
496
+ * Filter subheading levels inside event content.
497
+ *
498
+ * @hook mc_subheading_level
499
+ *
500
+ * @param {string} $el Element name. Default 'h4'.
501
+ * @param {string} $type View type.
502
+ * @param {string} $time View timeframe.
503
+ * @param {string} $template Current template.
504
+ *
505
+ * @return {string}
506
+ */
507
+ $sublevel = apply_filters( 'mc_subheading_level', 'h4', $type, $time, $template );
508
+ if ( $access_content ) {
509
+ $access = '<div class="mc-accessibility"><' . $sublevel . '>' . $access_heading . '</' . $sublevel . '>' . $access_content . '</div>';
510
+ }
511
  }
 
512
 
513
+ if ( 'true' === $display_gcal || mc_output_is_visible( 'gcal', $type, $event ) ) {
514
+ $gcal = " <p class='gcal'>" . mc_draw_template( $data, '{gcal_link}' ) . '</p>';
515
+ }
 
 
 
 
516
 
517
+ if ( 'true' === $display_vcal || mc_output_is_visible( 'ical', $type, $event ) ) {
518
+ $vcal = " <p class='ical'>" . mc_draw_template( $data, '{ical_html}' ) . '</p>';
519
+ }
520
 
521
+ if ( 'true' === $display_image || mc_output_is_visible( 'image', $type, $event ) ) {
522
+ $img = mc_get_event_image( $event, $data );
523
+ }
 
 
524
 
525
+ if ( 'calendar' === $type ) {
526
+ // This is semantically a duplicate of the title, but can be beneficial for sighted users.
527
+ $headingtype = ( 'h3' === $hlevel ) ? 'h4' : 'h' . ( ( (int) str_replace( 'h', '', $hlevel ) ) - 1 );
528
+ $inner_title = ' <' . $headingtype . ' class="mc-title" aria-hidden="true">' . $event_title . '</' . $headingtype . '>';
529
  }
 
530
 
531
+ if ( 'true' === $display_desc || mc_output_is_visible( 'description', $type, $event ) ) {
532
+ if ( '' !== trim( $event->event_desc ) ) {
533
+ $description = wpautop( stripcslashes( mc_kses_post( $event->event_desc ) ), 1 );
534
+ $description = " <div class='longdesc description'>$description</div>";
535
+ }
 
 
 
 
 
536
  }
 
537
 
538
+ if ( 'true' === $display_reg || mc_output_is_visible( 'tickets', $type, $event ) ) {
539
+ $info = wpautop( stripcslashes( mc_kses_post( $event->event_registration ) ) );
540
+ $url = esc_url( $event->event_tickets );
541
+ $external = ( $url && mc_external_link( $url ) ) ? 'external' : '';
542
+ $text = ( '' !== get_option( 'mc_buy_tickets', '' ) ) ? get_option( 'mc_buy_tickets' ) : __( 'Buy Tickets', 'my-calendar' );
543
+ $tickets = ( $url ) ? "<a class='$external' href='" . $url . "'>" . $text . '</a>' : '';
544
+ if ( '' !== trim( $info . $tickets ) ) {
545
+ $tickets = '<div class="mc-registration">' . $info . $tickets . '</div>';
546
+ } else {
547
+ $tickets = '';
548
+ }
549
  }
 
550
 
551
+ if ( 'true' === $display_short || mc_output_is_visible( 'excerpt', $type, $event ) ) {
552
+ if ( '' !== trim( $event->event_short ) ) {
553
+ $short = wpautop( stripcslashes( mc_kses_post( $event->event_short ) ), 1 );
554
+ $short = " <div class='shortdesc description'>$short</div>";
555
+ }
556
+ }
557
 
558
+ /**
559
+ * Filter registration status of event. Default empty.
560
+ *
561
+ * @hook mc_registration_state
562
+ *
563
+ * @param {string} $status String.
564
+ * @param {object} $event My Calendar object.
565
+ *
566
+ * @return {string}
567
+ */
568
+ $status = apply_filters( 'mc_registration_state', '', $event );
569
+ /**
570
+ * Filter URL appended on single event view to return to calendar.
571
+ *
572
+ * @hook mc_return_uri
573
+ *
574
+ * @param {string} $url Calendar URL.
575
+ *
576
+ * @return {string}
577
+ */
578
+ $return_url = apply_filters( 'mc_return_uri', mc_get_uri( $event ) );
579
+ $text = ( '' !== get_option( 'mc_view_full', '' ) ) ? get_option( 'mc_view_full' ) : __( 'View full calendar', 'my-calendar' );
580
+ $return = ( 'single' === $type ) ? " <p class='view-full'><a href='$return_url'>" . $text . '</a></p>' : '';
581
+
582
+ if ( ! mc_show_details( $time, $type ) ) {
583
+ $description = '';
584
+ $short = '';
585
+ $status = '';
586
+ }
587
 
588
+ if ( 'true' === $display_gmap || mc_output_is_visible( 'gmap', $type, $event ) ) {
589
+ $map = ( is_singular( 'mc-events' ) || 'single' === $type ) ? mc_generate_map( $event ) : '';
590
+ } else {
591
+ $map = '';
592
+ }
593
+ $event_link = mc_event_link( $event );
594
+
595
+ if ( '' !== $event_link && ( 'true' === $display_link || mc_output_is_visible( 'link', $type, $event ) ) ) {
596
+ $external_class = ( mc_external_link( $event_link ) ) ? "$type-link external url" : "$type-link url";
597
+ $link_template = ( '' !== mc_get_template( 'link' ) ) ? mc_get_template( 'link' ) : __( 'More information', 'my-calendar' );
598
+ $link_text = mc_draw_template( $data, $link_template );
599
+ $link = "
600
+ <p>
601
+ <a href='" . esc_url( $event_link ) . "' class='$external_class' aria-describedby='mc_{$event->occur_id}-title-$id'>" . $link_text . '</a>
602
+ </p>';
603
+ }
604
+ $access = ( '' !== $access ) ? '<div class="mc-access-information">' . $access . '</div>' : '';
605
+ $location = ( '' === trim( $map . $address ) ) ? '' : ' <div class="mc-location">' . $map . $address . '</div>';
606
+ $sharing = ( '' === trim( $vcal . $gcal . $more ) ) ? '' : ' <div class="sharing">' . $vcal . $gcal . $more . '</div>';
607
+
608
+ $close = ( '' !== $close ) ? PHP_EOL . ' ' . $close : '';
609
+ $inner_title = ( $inner_title ) ? PHP_EOL . ' ' . $inner_title : '';
610
+ $time_html = ( $time_html ) ? PHP_EOL . ' ' . $time_html : '';
611
+ $list_title = ( $list_title ) ? PHP_EOL . ' ' . $list_title : '';
612
+ $img = ( $img ) ? PHP_EOL . ' ' . $img : '';
613
+ $location = ( $location ) ? PHP_EOL . ' ' . $location : '';
614
+ $description = ( $description ) ? PHP_EOL . ' ' . $description : '';
615
+ $short = ( $short ) ? PHP_EOL . ' ' . $short : '';
616
+ $link = ( $link ) ? PHP_EOL . ' ' . $link : '';
617
+ $status = ( $status ) ? PHP_EOL . ' ' . $status : '';
618
+ $tickets = ( $tickets ) ? PHP_EOL . ' ' . $tickets : '';
619
+ $author = ( $author ) ? PHP_EOL . ' ' . $author : '';
620
+ $host = ( $host ) ? PHP_EOL . ' ' . $host : '';
621
+ $sharing = ( $sharing ) ? PHP_EOL . ' ' . $sharing : '';
622
+ $access = ( $access ) ? PHP_EOL . ' ' . $access : '';
623
+ $return = ( $return ) ? PHP_EOL . ' ' . $return : '';
624
+ $order = array( 'close', 'inner_title', 'list_title', 'time_html', 'img', 'description', 'short', 'location', 'access', 'link', 'status', 'tickets', 'author', 'host', 'sharing', 'return' );
625
+ /**
626
+ * Filter the order in which event template display elements are appended.
627
+ *
628
+ * @hook mc_default_output_order
629
+ *
630
+ * @param {array} $order Array of ordered keywords representing items in template.
631
+ * @param {object} $event Event object.
632
+ *
633
+ * @return {array}
634
+ */
635
+ $output_order = apply_filters( 'mc_default_output_order', $order, $event );
636
+ $details = $close;
637
+ if ( ! empty( $output_order ) ) {
638
+ foreach ( $output_order as $value ) {
639
+ /**
640
+ * Filter individual display output items. Variable filter name based on `array( 'close', 'inner_title', 'list_title', 'time_html', 'img', 'description', 'short', 'location', 'access', 'link', 'status', 'tickets', 'author', 'host', 'sharing', 'return' );`
641
+ *
642
+ * @hook mc_event_detail_{name}
643
+ *
644
+ * @param {string} $details HTML content for section.
645
+ * @param {object} $event My Calendar event object.
646
+ *
647
+ * @return {string}
648
+ */
649
+ $details .= apply_filters( 'mc_event_detail_' . sanitize_title( $value ), ${$value}, $event );
650
+ }
651
+ } else {
652
+ $details .= "\n"
653
+ . $inner_title
654
+ . $list_title
655
+ . $time_html
656
+ . $img
657
+ . $description
658
+ . $short
659
+ . $location
660
+ . $access
661
+ . $link
662
+ . $status
663
+ . $tickets
664
+ . $sharing
665
+ . $author
666
+ . $host
667
+ . $return;
668
  }
669
  } else {
670
+ // If a custom template is in use.
671
+ $toggle = ( 'calendar' === $type ) ? $close_button : '';
672
+ $details = $toggle . $details . "\n";
 
 
 
 
 
 
 
 
 
 
 
 
 
673
  }
674
+
675
+ $img_class = ( $img ) ? ' has-image' : ' no-image';
676
+ $container = "\n <div id='$uid-$type-details-$id' class='details$img_class' role='alert' aria-labelledby='mc_$event->occur_id-title" . '-' . $id . "'>\n";
677
+ /**
678
+ * Filter details before the event content..
679
+ *
680
+ * @hook mc_before_event
681
+ *
682
+ * @param {string} $details HTML content.
683
+ * @param {object} $event My Calendar event object.
684
+ * @param {string} $type View type.
685
+ * @param {string} $time View timeframe.
686
+ *
687
+ * @return {string}
688
+ */
689
+ $container = apply_filters( 'mc_before_event', $container, $event, $type, $time );
690
+ $details = $header . $container . apply_filters( 'mc_inner_content', $details, $event, $type, $time );
691
+ /**
692
+ * Filter details appended after the event content.
693
+ *
694
+ * @hook mc_after_event
695
+ *
696
+ * @param {string} $details HTML content. Default empty.
697
+ * @param {object} $event My Calendar event object.
698
+ * @param {string} $type View type.
699
+ * @param {string} $time View timeframe.
700
+ *
701
+ * @return {string}
702
+ */
703
+ $details .= apply_filters( 'mc_after_event', '', $event, $type, $time );
704
+ $details .= "\n" . ' </div><!--end .details-->' . "\n" . ' </div>' . "\n";
705
+ /**
706
+ * Filter details output.
707
+ *
708
+ * @hook mc_event_content
709
+ *
710
+ * @param {string} $details HTML content.
711
+ * @param {object} $event My Calendar event object.
712
+ * @param {string} $type View type.
713
+ * @param {string} $time View timeframe.
714
+ *
715
+ * @return {string}
716
+ */
717
+ $details = apply_filters( 'mc_event_content', $details, $event, $type, $time );
718
  } else {
719
+ /**
720
+ * Filter container before event details when view details panel is disabled.
721
+ *
722
+ * @hook mc_before_event_no_details
723
+ *
724
+ * @param {string} $container HTML string.
725
+ * @param {object} $event My Calendar event.
726
+ * @param {string} $type View type.
727
+ * @param {string} $time View timeframe.
728
+ *
729
+ * @return {string}
730
+ */
731
+ $before = apply_filters( 'mc_before_event_no_details', $container, $event, $type, $time );
732
+ /**
733
+ * Filter container after event details when view details panel is disabled.
734
+ *
735
+ * @hook mc_after_event_no_details
736
+ *
737
+ * @param {string} $container HTML string. Default empty.
738
+ * @param {object} $event My Calendar event.
739
+ * @param {string} $type View type.
740
+ * @param {string} $time View timeframe.
741
+ *
742
+ * @return {string}
743
+ */
744
+ $after = apply_filters( 'mc_after_event_no_details', '', $event, $type, $time ) . '</div>';
745
+ $details = $before . $header . $after;
746
  }
 
 
 
 
 
 
 
 
 
 
747
  }
748
  $details = apply_filters( 'mc_event_details_output', $details, $event );
749
+ /**
750
+ * Runs right after a calendar event template is run.
751
+ *
752
+ * @since 3.3.15.
753
+ * @hook my_calendar_event_drawn
754
+ *
755
+ * @param {object} My Calendar event object.
756
+ */
757
  do_action( 'my_calendar_event_drawn', $event );
758
 
759
  return $details;
767
  * @return string
768
  */
769
  function mc_close_button( $controls ) {
770
+ /**
771
+ * Filter event modal close button label.
772
+ *
773
+ * @hook mc_close_button
774
+ *
775
+ * @param {string} $close HTML or text string to use as label of close button.
776
+ *
777
+ * @return {string}
778
+ */
779
  $close_image = apply_filters( 'mc_close_button', "<span class='dashicons dashicons-dismiss' aria-hidden='true'></span><span class='screen-reader-text'>Close</span>" );
780
  $close_button = " <button type='button' aria-controls='$controls' class='mc-toggle close' data-action='shiftforward'>$close_image</button>";
781
 
847
  } else {
848
  $default_size = 'medium';
849
  }
850
+ /**
851
+ * Customize default image size for event output. Default is 'large' if it exists, 'medium' if not.
852
+ *
853
+ * @hook mc_default_image_size
854
+ *
855
+ * @param {string} $default_size Image size designator.
856
+ *
857
+ * @return {string}
858
+ */
859
  $default_size = apply_filters( 'mc_default_image_size', $default_size );
860
 
861
  if ( is_numeric( $event->event_post ) && 0 !== (int) $event->event_post && ( isset( $data[ $default_size ] ) && '' !== $data[ $default_size ] ) ) {
862
+ /**
863
+ * Customize featured image attributes.
864
+ *
865
+ * @hook mc_post_thumbnail_atts
866
+ *
867
+ * @param {array} $atts Array of attributes passed to `get_the_post_thumbnail`
868
+ * @param {object} $event Event object.
869
+ *
870
+ * @return {array}
871
+ */
872
+ $atts = apply_filters( 'mc_post_thumbnail_atts', array( 'class' => 'mc-image photo' ), $event );
873
  $image_url = get_the_post_thumbnail_url( $event->event_post, $default_size );
874
  $image = get_the_post_thumbnail( $event->event_post, $default_size, $atts );
875
  } else {
876
+ /**
877
+ * Customize alt attribute for an event featured image that is not attached to a post.
878
+ *
879
+ * @hook mc_event_image_alt
880
+ *
881
+ * @param {string} $alt Empty string.
882
+ * @param {object} $event Event object.
883
+ *
884
+ * @return {string}
885
+ */
886
  $alt = esc_attr( apply_filters( 'mc_event_image_alt', '', $event ) );
887
  $image_url = $event->event_image;
888
  $image = ( '' !== $event->event_image ) ? "<img src='$event->event_image' alt='$alt' class='mc-image photo' />" : '';
892
  global $template;
893
  $template_file_name = basename( $template );
894
  /**
895
+ * Fires when displaying an event image in the default template. Return false to show the template image rather than the theme's featured image.
 
 
896
  *
897
+ * @hook mc_override_featured_image
898
  * @since 3.3.0
899
  *
900
+ * @param {bool} $return True to return thumbnail in templates.
901
+ * @param {object} $event Event object.
902
+ * @param {array} $data Event template tags.
903
  *
904
+ * @return {bool}
905
  */
906
  $override = apply_filters( 'mc_override_featured_image', $return, $event, $data );
907
  if ( $override && is_singular( 'mc-events' ) && has_post_thumbnail( $event->event_post ) && current_theme_supports( 'post-thumbnails' ) && ( 'single-mc-events.php' !== $template_file_name ) ) {
989
  $classes[] = 'mc_rel_' . sanitize_html_class( $category->category_name, 'mcat' . $category->category_id );
990
  }
991
 
992
+ /**
993
+ * Filter event classes.
994
+ *
995
+ * @hook mc_event_classes
996
+ *
997
+ * @param {array} $classes Array of classes for event.
998
+ * @param {object} $event Event object.
999
+ * @param {string} $uid Unique ID for this event.
1000
+ * @param {string} $type View type.
1001
+ *
1002
+ * @return {array}
1003
+ */
1004
  $classes = apply_filters( 'mc_event_classes', array_unique( $classes ), $event, $uid, $type );
1005
  $class_html = strtolower( implode( ' ', $classes ) );
1006
 
1016
  * @return boolean
1017
  */
1018
  function mc_show_details( $time, $type ) {
1019
+ /**
1020
+ * Display details links globally from main calendar view.
1021
+ *
1022
+ * @hook mc_disable_link
1023
+ *
1024
+ * @param {bool} $no_link true to disable link.
1025
+ * @param {array} $array Empty array. (deprecated).
1026
+ *
1027
+ * @return {bool}
1028
+ */
1029
  $no_link = apply_filters( 'mc_disable_link', false, array() );
1030
 
1031
  return ( ( 'calendar' === $type && 'true' === get_option( 'mc_open_uri' ) && 'day' !== $time ) || $no_link ) ? false : true;
1070
  /**
1071
  * Create list of classes for a given date.
1072
  *
1073
+ * @param array $events array of event objects.
1074
+ * @param string|false $date current date if a date is being processed.
1075
  *
1076
  * @return string of classes
1077
  */
1115
  */
1116
  function mc_list_title( $events ) {
1117
  usort( $events, 'mc_time_cmp' );
1118
+ $now = $events[0];
1119
+ $count = count( $events ) - 1;
1120
+ /**
1121
+ * Format a single title for display in single titles on list view.
1122
+ *
1123
+ * @hook mc_list_event_title_hint
1124
+ *
1125
+ * @param {string} $title Event title.
1126
+ * @param {object} $now Event object.
1127
+ *
1128
+ * @return {string}
1129
+ */
1130
  $event_title = apply_filters( 'mc_list_title_title', strip_tags( stripcslashes( $now->event_title ), mc_strip_tags() ), $now );
1131
  if ( 0 === $count ) {
1132
  $cstate = $event_title;
1137
  // Translators: %s Title of event, %d number of other events.
1138
  $cstate = sprintf( __( '%1$s<span class="mc-list-extended"> and %2$d other events</span>', 'my-calendar' ), $event_title, $count );
1139
  }
1140
+ /**
1141
+ * Format display output on a single titles on list view.
1142
+ *
1143
+ * @hook mc_list_event_title_hint
1144
+ *
1145
+ * @param {string} $title Event title.
1146
+ * @param {object} $now Event object.
1147
+ * @param {array} $events Array of event objects.
1148
+ *
1149
+ * @return {string}
1150
+ */
1151
  $title = apply_filters( 'mc_list_event_title_hint', $cstate, $now, $events );
1152
 
1153
  return $title;
1165
  $titles = array();
1166
 
1167
  foreach ( $events as $now ) {
1168
+ /**
1169
+ * Format a single title for display in multiple titles on list view.
1170
+ *
1171
+ * @hook mc_list_event_title_hint
1172
+ *
1173
+ * @param {string} $title Event title.
1174
+ * @param {object} $now Event object.
1175
+ * @param {array} $events Array of event objects.
1176
+ *
1177
+ * @return {string}
1178
+ */
1179
  $title = apply_filters( 'mc_list_event_title_hint', strip_tags( stripcslashes( $now->event_title ), mc_strip_tags() ), $now, $events );
1180
  $titles[] = $title;
1181
  }
1182
+ /**
1183
+ * Format titles displayed in multiple titles on list view. Default ''. Returning any value will shortcircuit standard formatting.
1184
+ *
1185
+ * @hook mc_titles_format
1186
+ *
1187
+ * @param {string} $result Custom format of data in results.
1188
+ * @param {array} $titles Array of titles of events on this date.
1189
+ *
1190
+ * @return {string}
1191
+ */
1192
  $result = apply_filters( 'mc_titles_format', '', $titles );
1193
 
1194
  if ( '' === $result ) {
1195
+ /**
1196
+ * Filter separator used to separate a collection of event titles on the list view day while collapsed.
1197
+ *
1198
+ * @hook mc_list_titles_separator
1199
+ *
1200
+ * @param {string} $separator Separator between titles in list view titles.
1201
+ *
1202
+ * @return {string}
1203
+ */
1204
  $result = implode( apply_filters( 'mc_list_titles_separator', ', ' ), $titles );
1205
  }
1206
 
1276
  /**
1277
  * Filter whether any given piece of information should be output.
1278
  *
1279
+ * @param {bool} $return Should this piece of data be output.
1280
+ * @param {string} $feature Feature key.
1281
+ * @param {string} $type Type of view.
1282
+ * @param {object|boolean} $event Event object if in event context.
1283
  *
1284
  * @return bool
1285
  */
1348
  return $content;
1349
  }
1350
  if ( '1' === get_option( 'mc_use_details_template' ) ) {
1351
+ /**
1352
+ * Prepend content before single event. Default empty string.
1353
+ *
1354
+ * @hook mc_before_event
1355
+ *
1356
+ * @param {string} $new_content Content to prepend before the event.
1357
+ * @param {object} $event Event object.
1358
+ * @param {string} $view View type.
1359
+ * @param {string} $time Time view.
1360
+ *
1361
+ * @return {string}
1362
+ */
1363
  $new_content = apply_filters( 'mc_before_event', '', $event, 'single', $time );
1364
  if ( isset( $_GET['mc_id'] ) ) {
1365
  $shortcode = str_replace( "event='$event_id'", "event='$mc_id' instance='1'", get_post_meta( $post->ID, '_mc_event_shortcode', true ) );
1366
  } else {
1367
  $shortcode = get_post_meta( $post->ID, '_mc_event_shortcode', true );
1368
  }
1369
+ /**
1370
+ * Filter shortcode before appending to single event content.
1371
+ *
1372
+ * @hook mc_single_event_shortcode
1373
+ *
1374
+ * @param {string} $shortcode Shortcode for single event.
1375
+ *
1376
+ * @return {string}
1377
+ */
1378
  $new_content .= do_shortcode( apply_filters( 'mc_single_event_shortcode', $shortcode ) );
1379
+ /**
1380
+ * Append content after single event. Default empty string.
1381
+ *
1382
+ * @hook mc_after_event
1383
+ *
1384
+ * @param {string} $new_content Content to append after the event.
1385
+ * @param {object} $event Event object.
1386
+ * @param {string} $view View type.
1387
+ * @param {string} $time Time view.
1388
+ *
1389
+ * @return {string}
1390
+ */
1391
  $new_content .= apply_filters( 'mc_after_event', '', $event, 'single', $time );
1392
  } else {
1393
  $new_content = my_calendar_draw_event( $event, 'single', $date, $time, '' );
1394
  }
1395
+ /**
1396
+ * Filter single event content prior to running shortcodes.
1397
+ *
1398
+ * @hook mc_event_post_content
1399
+ *
1400
+ * @param {string} $new_content Event content with event shortcode appended.
1401
+ * @param {string} $content Original event content.
1402
+ * @param {WP_Post} $post Post object.
1403
+ *
1404
+ * @return {string}
1405
+ */
1406
  $content = do_shortcode( apply_filters( 'mc_event_post_content', $new_content, $content, $post ) );
1407
  }
1408
  }
1428
  $output = '';
1429
  $classes = '';
1430
  // If a large number of events, skip this.
1431
+ /**
1432
+ * How many related events should there be before they are not shown? Default `50`.
1433
+ *
1434
+ * @hook mc_related_event_limit
1435
+ *
1436
+ * @param {int} Number of events where the large limit triggers.
1437
+ *
1438
+ * @return {int}.
1439
+ */
1440
  if ( $count > apply_filters( 'mc_related_event_limit', 50 ) ) {
1441
+ /**
1442
+ * For large lists of related events, related events are not shown by default. Only runs if number of related events higher than `mc_related_event_limit`.
1443
+ *
1444
+ * @hook mc_grouped_events
1445
+ *
1446
+ * @param {string} $output HTML output of events to shown. Default empty string.
1447
+ * @param {array} $results Array of related event objects.
1448
+ *
1449
+ * @return {bool}
1450
+ */
1451
  return apply_filters( 'mc_grouped_events', '', $results );
1452
  }
1453
 
1509
  }
1510
  $category = $event->event_category;
1511
  $private = mc_get_private_categories();
1512
+ /**
1513
+ * Filter whether an event is visible to the current user.
1514
+ *
1515
+ * @hook mc_user_can_see_private_events
1516
+ *
1517
+ * @param {bool} $can_see 'true' if the event should be shown.
1518
+ * @param {object} $event Event object.
1519
+ *
1520
+ * @return {bool}
1521
+ */
1522
+ $can_see = apply_filters( 'mc_user_can_see_private_events', is_user_logged_in(), $event );
1523
  if ( in_array( $category, $private, true ) && ! $can_see ) {
1524
 
1525
  return true;
1591
  $search = $_GET['mcs'];
1592
  }
1593
 
1594
+ /**
1595
+ * Filter the display format.
1596
+ *
1597
+ * @hook mc_display_format
1598
+ *
1599
+ * @param {string} $format Current view format. E.g. 'grid', 'list'.
1600
+ * @param {array} $args Calendar view arguments.
1601
+ *
1602
+ * @return {string}
1603
+ */
1604
  $format = apply_filters( 'mc_display_format', $format, $args );
1605
  $params = array(
1606
  'name' => $name, // Not used in my_calendar(); what is/was it for.
1649
  $name_days = $days['name_days'];
1650
  $abbrevs = $days['abbrevs'];
1651
 
1652
+ /**
1653
+ * Alter the HTML date header element for the grid view. Default `th`
1654
+ *
1655
+ * @hook mc_grid_header_wrapper
1656
+ *
1657
+ * @param {string} $th HTML element tag without `<>`.
1658
+ * @param {string} $format Viewed format.
1659
+ *
1660
+ * @return {string}
1661
+ */
1662
  $th = apply_filters( 'mc_grid_header_wrapper', 'th', $params['format'] );
1663
  $close_th = ( 'th' === $th ) ? 'th' : $th;
1664
  $th .= ( 'th' === $th ) ? ' scope="col"' : '';
1665
  $body = '';
1666
  if ( 'calendar' === $params['format'] || 'mini' === $params['format'] ) {
1667
+ /**
1668
+ * Alter the outer wrapper HTML element for the grid view. Default `table`.
1669
+ *
1670
+ * @hook mc_grid_wrapper
1671
+ *
1672
+ * @param {string} $table HTML element tag without `<>`.
1673
+ * @param {string} $format Viewed format.
1674
+ *
1675
+ * @return {string}
1676
+ */
1677
  $table = apply_filters( 'mc_grid_wrapper', 'table', $params['format'] );
1678
  $body .= "\n<$table class='my-calendar-table' aria-labelledby='mc_head_$id'>\n";
1679
  }
1683
  } else {
1684
  $body .= ( 'tr' === $tr ) ? '<thead>' : '<div class="mc-table-body">';
1685
  $body .= "\n <$tr class='mc-row'>\n";
1686
+ /**
1687
+ * Should the number of the week (1-52) appear in the calendar grid.
1688
+ *
1689
+ * @hook mc_show_week_number
1690
+ *
1691
+ * @param {bool} $show `true` to add a column with the week number.
1692
+ * @param {array} $params Calendar view arguments.
1693
+ *
1694
+ * @return {bool}
1695
+ */
1696
  if ( apply_filters( 'mc_show_week_number', false, $params ) ) {
1697
  $body .= " <$th class='mc-week-number'>" . __( 'Week', 'my-calendar' ) . "</$close_th>\n";
1698
  }
1766
  $show_weekends = ( get_option( 'mc_show_weekends' ) === 'true' ) ? true : false;
1767
  $skip_holidays = get_option( 'mc_skip_holidays_category' );
1768
  $month_format = ( get_option( 'mc_month_format', '' ) === '' ) ? 'F Y' : get_option( 'mc_month_format' );
1769
+ /**
1770
+ * Filter how many months to show in list views.
1771
+ *
1772
+ * @hook mc_show_months
1773
+ *
1774
+ * @param {int} $show_months Number of months to show at once.
1775
+ * @param {array} $args Current view arguments.
1776
+ *
1777
+ * @return {int}
1778
+ */
1779
+ $show_months = absint( apply_filters( 'mc_show_months', get_option( 'mc_show_months' ), $args ) );
1780
+ $show_months = ( 0 === $show_months ) ? 1 : $show_months;
1781
+ $caption_text = ' ' . stripslashes( trim( get_option( 'mc_caption' ) ) );
1782
+ $week_format = ( ! get_option( 'mc_week_format' ) ) ? 'M j, \'y' : get_option( 'mc_week_format' );
1783
  // Translators: Template tag with date format.
1784
  $week_template = ( get_option( 'mc_week_caption', '' ) !== '' ) ? get_option( 'mc_week_caption' ) : sprintf( __( 'Week of %s', 'my-calendar' ), '{date format="M jS"}' );
1785
  $open_day_uri = ( ! get_option( 'mc_open_day_uri' ) ) ? 'false' : get_option( 'mc_open_day_uri' ); // This is not a URL. It's a behavior reference.
1793
  my_calendar_check();
1794
 
1795
  $params = mc_calendar_params( $args );
1796
+ /**
1797
+ * Prepend content before the calendar.
1798
+ *
1799
+ * @hook mc_before_calendar
1800
+ *
1801
+ * @param {string} $before HTML content.
1802
+ * @param {array} $params Current view arguments.
1803
+ *
1804
+ * @return {string}
1805
+ */
1806
+ $body = apply_filters( 'mc_before_calendar', '', $params );
1807
 
1808
  $id = $params['id'];
1809
  $main_class = ( '' !== $id ) ? sanitize_title( $id ) : 'all';
1814
  $mc_closer = '
1815
  </div>';
1816
 
1817
+ /**
1818
+ * Filter date format in main calendar view.
1819
+ *
1820
+ * @hook mc_date_format
1821
+ *
1822
+ * @param {string} $date_format Date format in PHP date format structure.
1823
+ * @param {string} $format Current view format.
1824
+ * @param {string} $time Current view time frame.
1825
+ *
1826
+ * @return {string}
1827
+ */
1828
  $date_format = apply_filters( 'mc_date_format', $date_format, $params['format'], $params['time'] );
1829
+ /**
1830
+ * Main heading level. Default `h2`.
1831
+ *
1832
+ * @hook mc_heading_level
1833
+ *
1834
+ * @param {string} $h1 Main heading level.
1835
+ * @param {string} $format Current view format.
1836
+ * @param {string} $time Current view time frame.
1837
+ * @param {string} $template Current view template.
1838
+ *
1839
+ * @return {string}
1840
+ */
1841
+ $hl = apply_filters( 'mc_heading_level', 'h2', $params['format'], $params['time'], $template );
1842
 
1843
  if ( isset( $_GET['mc_id'] ) && 'widget' !== $source ) {
1844
  // single event, main calendar only.
1857
  }
1858
 
1859
  $dates = mc_get_from_to( $show_months, $params, $date );
1860
+ /**
1861
+ * Filter the calendar start date.
1862
+ *
1863
+ * @hook mc_from_date
1864
+ *
1865
+ * @param {string} $from Start date of events shown in main calendar shortcode in format `yyyy-mm-dd`.
1866
+ *
1867
+ * @return {string}
1868
+ */
1869
+ $from = apply_filters( 'mc_from_date', $dates['from'] );
1870
+ /**
1871
+ * Filter the calendar end date.
1872
+ *
1873
+ * @hook mc_to_date
1874
+ *
1875
+ * @param {string} $to End date of events shown in main calendar shortcode in format `yyyy-mm-dd`.
1876
+ *
1877
+ * @return {string}
1878
+ */
1879
  $to = apply_filters( 'mc_to_date', $dates['to'] );
1880
  $from = ( 'day' === $params['time'] ) ? mc_date( 'Y-m-d', $current, false ) : $from;
1881
  $to = ( 'day' === $params['time'] ) ? mc_date( 'Y-m-d', $current, false ) : $to;
1891
  'source' => 'calendar',
1892
  'site' => $site,
1893
  );
1894
+ /**
1895
+ * Filter calendar view attributes.
1896
+ *
1897
+ * @hook mc_calendar_attributes
1898
+ *
1899
+ * @param {array} $query Current view arguments.
1900
+ * @param {array} $params Shortcode parameters passed to view.
1901
+ *
1902
+ * @return {array}
1903
+ */
1904
  $query = apply_filters( 'mc_calendar_attributes', $query, $params );
1905
  if ( 'mc-print-view' === $id && isset( $_GET['searched'] ) && $_GET['searched'] ) {
1906
  $event_array = mc_get_searched_events();
1920
  $bottom = $nav['bottom'];
1921
 
1922
  if ( 'day' === $params['time'] ) {
1923
+ /**
1924
+ * Filter the main calendar heading content in single day view.
1925
+ *
1926
+ * @hook mc_heading
1927
+ *
1928
+ * @param {string} $heading HTML heading for calendar.
1929
+ * @param {string} $format Viewed format.
1930
+ * @param {string} $time Time frame currently viewed.
1931
+ *
1932
+ * @return {string}
1933
+ */
1934
  $heading = "<$hl id='mc_head_$id' class='mc-single heading my-calendar-$params[time]'><span>" . apply_filters( 'mc_heading', date_i18n( $date_format, $current ), $params['format'], $params['time'] ) . "</span></$hl>";
1935
  $dateclass = mc_dateclass( $current );
1936
  $mc_events = '';
1976
  } else {
1977
  $heading = mc_draw_template( $values, stripslashes( $week_template ) );
1978
  }
1979
+ /**
1980
+ * Filter the main calendar heading level. Default `h2`.
1981
+ *
1982
+ * @hook mc_heading_level
1983
+ *
1984
+ * @param {string} $heading HTML heading element.
1985
+ * @param {string} $format Viewed format.
1986
+ * @param {string} $time Time frame currently viewed.
1987
+ * @param {string} $template Heading template.
1988
+ *
1989
+ * @return {string}
1990
+ */
1991
+ $h2 = apply_filters( 'mc_heading_level', 'h2', $params['format'], $params['time'], $template );
1992
+ /**
1993
+ * Filter the main calendar heading in multiday views.
1994
+ *
1995
+ * @hook mc_heading
1996
+ *
1997
+ * @param {string} $heading HTML heading for calendar.
1998
+ * @param {string} $format Viewed format.
1999
+ * @param {string} $time Time frame currently viewed.
2000
+ * @param {string} $template Heading template.
2001
+ *
2002
+ * @return {string}
2003
+ */
2004
+ $heading = apply_filters( 'mc_heading', $heading, $params['format'], $params['time'], $template );
2005
  $body .= "<$h2 id=\"mc_head_$id\" class=\"heading my-calendar-$params[time]\"><span>$heading</span></$h2>\n";
2006
  $body .= $top;
2007
 
2008
+ /**
2009
+ * Alter the outer wrapper HTML element for the grid view. Default `table`.
2010
+ *
2011
+ * @hook mc_grid_wrapper
2012
+ *
2013
+ * @param {string} $table HTML element tag without `<>`.
2014
+ * @param {string} $format Viewed format.
2015
+ *
2016
+ * @return {string}
2017
+ */
2018
  $table = apply_filters( 'mc_grid_wrapper', 'table', $params['format'] );
2019
+ /**
2020
+ * Change the element wrapping a week of events in grid view. Default `tr`.
2021
+ *
2022
+ * @hook mc_grid_week_wrapper
2023
+ *
2024
+ * @param {string} $format Date format per PHP date formatting.
2025
+ * @param {string} $format Current view format.
2026
+ *
2027
+ * @return {string}
2028
+ */
2029
  $tr = apply_filters( 'mc_grid_week_wrapper', 'tr', $params['format'] );
2030
  $body .= mc_get_calendar_header( $params, $id, $tr, $start_of_week );
2031
  $odd = 'odd';
2032
 
2033
+ /**
2034
+ * Change whether list format removes dates with no events.
2035
+ *
2036
+ * @hook mc_all_list_dates
2037
+ *
2038
+ * @param {bool} $show_all `true` to show all dates in list format.
2039
+ * @param {array} $args Array of view arguments.
2040
+ *
2041
+ * @return {bool}
2042
+ */
2043
  $show_all = apply_filters( 'mc_all_list_dates', false, $args );
2044
  if ( $no_events && 'list' === $params['format'] && false === $show_all ) {
2045
  // If there are no events in list format, just display that info.
2052
  $json = array();
2053
  do {
2054
  $date_is = mc_date( 'Y-m-d', $start, false );
2055
+ $is_weekend = ( (int) mc_date( 'N', $start, false ) < 6 ) ? false : true;
2056
  if ( $show_weekends || ( ! $show_weekends && ! $is_weekend ) ) {
2057
  if ( mc_date( 'N', $start, false ) === (string) $start_of_week && 'list' !== $params['format'] ) {
2058
  $body .= "<$tr class='mc-row'>";
2059
  }
2060
+ $events = ( isset( $event_array[ $date_is ] ) ) ? $event_array[ $date_is ] : array();
2061
+ $week_header = date_i18n( $week_format, $start );
2062
+ /**
2063
+ * Alter the date format used to show the current day in grid view. Default 'j'.
2064
+ *
2065
+ * @hook mc_grid_date
2066
+ *
2067
+ * @param {string} $format Date format per PHP date formatting.
2068
+ * @param {array} $params Array of view arguments.
2069
+ *
2070
+ * @return {string}
2071
+ */
2072
  $thisday_heading = ( 'week' === $params['time'] ) ? "<small>$week_header</small>" : mc_date( apply_filters( 'mc_grid_date', 'j', $params ), $start, false );
2073
 
2074
  // Generate event classes & attributes.
2077
  $dateclass = mc_dateclass( $start );
2078
  $ariacurrent = ( false !== strpos( $dateclass, 'current-day' ) ) ? ' aria-current="date"' : '';
2079
 
2080
+ /**
2081
+ * Alter the date wrapper HTML element for the grid view. Default `td`.
2082
+ *
2083
+ * @hook mc_grid_day_wrapper
2084
+ *
2085
+ * @param {string} $td HTML element tag without `<>`.
2086
+ * @param {string} $format Viewed format.
2087
+ *
2088
+ * @return {string}
2089
+ */
2090
  $td = apply_filters( 'mc_grid_day_wrapper', 'td', $params['format'] );
2091
  if ( ! $week_number_shown ) {
2092
  $weeknumber = mc_show_week_number( $events, $args, $params['format'], $td, $start );
2097
  }
2098
 
2099
  if ( ! empty( $events ) ) {
2100
+ /**
2101
+ * Hide events in next or previous month in grid view.
2102
+ *
2103
+ * @hook mc_hide_nextmonth
2104
+ *
2105
+ * @param {bool} $hide true to hide events not in the currently viewed month.
2106
+ *
2107
+ * @return {bool}
2108
+ */
2109
  $hide_nextmonth = apply_filters( 'mc_hide_nextmonth', false );
2110
  if ( true === $hide_nextmonth && 'nextmonth' === $monthclass ) {
2111
  $event_output = ' ';
2185
  } while ( $start <= $end );
2186
  }
2187
 
 
2188
  $end = ( 'table' === $table ) ? "\n</tbody>\n</table>" : "</div></$table>";
2189
  $body .= ( 'list' === $params['format'] ) ? "\n</ul>" : $end;
2190
  }
2192
  $body .= ( 'day' === $params['time'] ) ? '' : '</div><!-- .mc-content -->';
2193
  $body .= $bottom;
2194
  }
2195
+ /**
2196
+ * Append content after the calendar.
2197
+ *
2198
+ * @hook mc_after_calendar
2199
+ *
2200
+ * @param {string} $after HTML content.
2201
+ * @param {array} $args Current view arguments.
2202
+ *
2203
+ * @return {string}
2204
+ */
2205
  $body .= apply_filters( 'mc_after_calendar', '', $args );
2206
 
2207
  if ( $site ) {
2215
  }
2216
  }
2217
 
2218
+ /**
2219
+ * Filter the calendar shortcode main body output.
2220
+ *
2221
+ * @hook my_calendar_body
2222
+ *
2223
+ * @param {string} $body HTML output.
2224
+ *
2225
+ * @return {string}
2226
+ */
2227
  $output = $mc_wrapper . $json_ld . apply_filters( 'my_calendar_body', $body ) . $mc_closer;
2228
  if ( $language ) {
2229
  mc_switch_language( $language, $locale );
2244
  */
2245
  function mc_show_week_number( $events, $args, $format, $td, $start ) {
2246
  $body = '';
2247
+ /**
2248
+ * Should the number of the week (1-52) appear in the calendar grid.
2249
+ *
2250
+ * @hook mc_show_week_number
2251
+ *
2252
+ * @param {bool} $show `true` to add a column with the week number.
2253
+ * @param {array} $args Calendar view arguments.
2254
+ *
2255
+ * @return {bool}
2256
+ */
2257
  if ( apply_filters( 'mc_show_week_number', false, $args ) ) {
2258
  $weeknumber = mc_date( 'W', $start, false );
2259
  if ( 'list' !== $format ) {
2301
  $syear = $params['syear'];
2302
  $sday = $params['sday'];
2303
  $c_m = 0;
2304
+ $dm = array();
2305
  if ( isset( $_GET['dy'] ) && $main_class === $cid && ( 'day' === $time || 'week' === $time ) ) {
2306
  if ( '' === $_GET['dy'] ) {
2307
  $today = current_time( 'j' );
2343
  if ( $is_start_of_week ) {
2344
  $c_year = ( current_time( 'Y' ) );
2345
  } else {
2346
+ $current_year = (int) current_time( 'Y' );
2347
  $c_year = ( 0 === (int) $dm[1] ) ? $current_year : false;
2348
  if ( ! $c_year ) {
2349
+ $c_year = ( (int) mc_date( 'Y', strtotime( '-1 month' ), false ) === $current_year ) ? $current_year : $current_year - 1;
2350
  }
2351
  }
2352
  } else {
2365
  $shortcode_month = ( false !== $smonth ) ? $smonth : $c_month;
2366
  $shortcode_year = ( false !== $syear ) ? $syear : $c_year;
2367
  $shortcode_day = ( false !== $sday ) ? $sday : $c_day;
2368
+ /**
2369
+ * Filter the starting year for the [my_calendar] shortcode.
2370
+ *
2371
+ * @hook mc_filter_year
2372
+ *
2373
+ * @param {int} $year An integer between 0 and 3000.
2374
+ * @param {array} Shortcode parameters.
2375
+ *
2376
+ * @return {int}
2377
+ */
2378
+ $c_year = apply_filters( 'mc_filter_year', $shortcode_year, $params );
2379
+ /**
2380
+ * Filter the starting month for the [my_calendar] shortcode.
2381
+ *
2382
+ * @hook mc_filter_month
2383
+ *
2384
+ * @param {int} $month An integer between 1 and 12.
2385
+ * @param {array} Shortcode parameters.
2386
+ *
2387
+ * @return {int}
2388
+ */
2389
  $c_month = apply_filters( 'mc_filter_month', $shortcode_month, $params );
2390
+ /**
2391
+ * Filter the starting day for the [my_calendar] shortcode.
2392
+ *
2393
+ * @hook mc_filter_day
2394
+ *
2395
+ * @param {int} $day An integer between 1 and 31.
2396
+ * @param {array} Shortcode parameters.
2397
+ *
2398
+ * @return {int}
2399
+ */
2400
+ $c_day = apply_filters( 'mc_filter_day', $shortcode_day, $params );
2401
  }
2402
  $c_day = ( 0 === (int) $c_day ) ? 1 : $c_day; // c_day can't equal 0.
2403
  $current = mktime( 0, 0, 0, (int) $c_month, (int) $c_day, (int) $c_year );
2421
  * @return string Calendar body with shortcodes processed
2422
  */
2423
  function mc_run_shortcodes( $content ) {
2424
+ /**
2425
+ * Disable shortcode parsing in content. Default 'true'.
2426
+ *
2427
+ * @hook mc_process_shortcodes
2428
+ *
2429
+ * @param {string} $return String 'true' to run do_shortcode() on calendar content.
2430
+ *
2431
+ * @return {string}
2432
+ */
2433
  $content = ( 'true' === apply_filters( 'mc_process_shortcodes', 'true' ) ) ? do_shortcode( $content ) : $content;
2434
 
2435
  return $content;
2462
  *
2463
  * @return string HTML form.
2464
  */
2465
+ function my_calendar_searchform( $type, $url = '' ) {
2466
  $query = ( isset( $_GET['mcs'] ) ) ? $_GET['mcs'] : '';
2467
  if ( 'simple' === $type ) {
2468
+ if ( ! $url ) {
2469
  $url = mc_get_uri( false, array( 'type' => $type ) );
2470
  }
2471
+ /**
2472
+ * Filter the target action URL for search query form. Should target a page with a [my_calendar} shortcode.
2473
+ *
2474
+ * @hook mc_search_page
2475
+ *
2476
+ * @param {string} $url Target URL.
2477
+ *
2478
+ * @return {string}
2479
+ */
2480
+ $url = apply_filters( 'mc_search_page', $url );
2481
  return '
2482
  <div class="mc-search-container" role="search">
2483
+ <form class="mc-search-form" method="get" action="' . esc_url( $url ) . '" >
2484
  <div class="mc-search">
2485
+ <label class="screen-reader-text" for="mcs">' . __( 'Search Events', 'my-calendar' ) . '</label><input type="text" value="' . esc_attr( stripslashes( urldecode( $query ) ) ) . '" name="mcs" id="mcs" /><input type="submit" data-href="' . esc_url( add_query_arg( 'source', 'widget', $url ) ) . '" class="button" id="searchsubmit" value="' . __( 'Search Events', 'my-calendar' ) . '" />
2486
  </div>
2487
  </form>
2488
  </div>';
2494
  /**
2495
  * Get list of locations.
2496
  *
2497
+ * @param string $datatype Type of data to sort by and return.
2498
+ * @param boolean $full If need to return full location object.
2499
+ * @param string $return_type valid query return type.
2500
  *
2501
  * @return array of location objects.
2502
  */
2503
+ function mc_get_list_locations( $datatype, $full = true, $return_type = 'OBJECT' ) {
2504
  global $wpdb;
2505
+ $mcdb = $wpdb;
2506
+ $return_type = ( 'OBJECT' === $return_type ) ? OBJECT : ARRAY_A;
2507
  if ( 'true' === get_option( 'mc_remote' ) && function_exists( 'mc_remote_db' ) ) {
2508
  $mcdb = mc_remote_db();
2509
  }
2534
  default:
2535
  $data = 'location_label';
2536
  }
2537
+ /**
2538
+ * Filter the `where` clause of the location list SQL query. Default empty string.
2539
+ *
2540
+ * @hook mc_filter_location_list
2541
+ *
2542
+ * @param {string} $where WHERE portion of a SQL query.
2543
+ * @param {string} $datatype Type of data this list is ordered by.
2544
+ *
2545
+ * @return {string}
2546
+ */
2547
  $where = esc_sql( apply_filters( 'mc_filter_location_list', '', $datatype ) );
2548
  if ( true !== $full ) {
2549
  $select = esc_sql( $data );
2611
  }
2612
  $output = '<ul class="mc-locations">' . $output . '</ul>';
2613
  }
2614
+ /**
2615
+ * Filter the output HTML of the location list.
2616
+ *
2617
+ * @hook mc_location_list
2618
+ *
2619
+ * @param {string} $output Output HTML for the form.
2620
+ * @param {array} $add Array of locations represented in this list.
2621
+ *
2622
+ * @return {string}
2623
+ */
2624
  $output = apply_filters( 'mc_location_list', $output, $locations );
2625
 
2626
  return $output;
2717
  </form>' : '';
2718
  $output .= ( 'single' === $group ) ? '</div>' : '';
2719
  }
2720
+ /**
2721
+ * Filter the output HTML of the location selector.
2722
+ *
2723
+ * @hook mc_location_selector
2724
+ *
2725
+ * @param {string} $output Output HTML for the form.
2726
+ * @param {array} $add Array of locations represented in this list.
2727
+ *
2728
+ * @return {string}
2729
+ */
2730
  $output = apply_filters( 'mc_location_selector', $output, $locations );
2731
 
2732
  return $output;
my-calendar-print.php CHANGED
@@ -39,6 +39,41 @@ function my_calendar_print() {
39
  } else {
40
  $stylesheet = $url . 'css/mc-print.css';
41
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  ?>
43
  <!DOCTYPE html>
44
  <html <?php language_attributes(); ?>>
@@ -51,18 +86,19 @@ function my_calendar_print() {
51
  <meta name="robots" content="noindex,nofollow" />
52
  <!-- Copy mc-print.css to your theme directory if you wish to replace the default print styles -->
53
  <link rel="stylesheet" href="<?php echo esc_url( $stylesheet ); ?>" type="text/css" media="screen,print" />
54
- <?php do_action( 'mc_print_view_head', '' ); ?>
 
 
 
 
 
 
 
 
 
55
  </head>
56
  <body>
57
  <?php
58
- $args = array(
59
- 'type' => 'print',
60
- 'category' => $category,
61
- 'time' => $time,
62
- 'ltype' => $ltype,
63
- 'lvalue' => $lvalue,
64
- );
65
-
66
  $calendar = array(
67
  'name' => 'print',
68
  'format' => 'calendar',
@@ -76,20 +112,21 @@ function my_calendar_print() {
76
  );
77
 
78
  echo mc_kses_post( my_calendar( $calendar ) );
79
- $return_url = mc_get_uri( false, $args );
80
- $return_url = apply_filters( 'mc_print_return_url', $return_url, $category, $time, $ltype, $lvalue );
81
-
82
- if ( isset( $_GET['href'] ) ) {
83
- $ref_url = esc_url( urldecode( $_GET['href'] ) );
84
- if ( $ref_url ) {
85
- $return_url = $ref_url;
86
- }
87
- }
88
 
89
  $add = array_map( 'esc_html', $_GET );
90
  unset( $add['cid'] );
91
  unset( $add['feed'] );
92
  unset( $add['href'] );
 
 
 
 
 
 
 
 
 
 
93
  $return_url = apply_filters( 'mc_return_to_calendar', mc_build_url( $add, array( 'feed', 'cid', 'href', 'searched' ), $return_url ), $add );
94
  if ( $return_url ) {
95
  echo wp_kses_post( "<p class='return'>&larr; <a href='" . esc_url( $return_url ) . "'>" . esc_html__( 'Return to calendar', 'my-calendar' ) . '</a></p>' );
39
  } else {
40
  $stylesheet = $url . 'css/mc-print.css';
41
  }
42
+ $args = array(
43
+ 'type' => 'print',
44
+ 'category' => $category,
45
+ 'time' => $time,
46
+ 'ltype' => $ltype,
47
+ 'lvalue' => $lvalue,
48
+ );
49
+ $return_url = mc_get_uri( false, $args );
50
+ /**
51
+ * Filter the root URL used to generate the return URL.
52
+ *
53
+ * @hook mc_print_return_url
54
+ *
55
+ * @param {string} $return_url Referer URL for calendar print view arrived from.
56
+ * @param {string} $category Category argument.
57
+ * @param {string} $time Time argument.
58
+ * @param {string} $ltype Location type argument.
59
+ * @param {string} $lvalue Location value argument.
60
+ *
61
+ * @return {string}
62
+ */
63
+ $return_url = apply_filters( 'mc_print_return_url', $return_url, $category, $time, $ltype, $lvalue );
64
+
65
+ if ( isset( $_GET['href'] ) ) {
66
+ // Only support URLs on the same home_url().
67
+ $ref_url = esc_url( urldecode( $_GET['href'] ) );
68
+ $ref_root = parse_url( $ref_url )['host'];
69
+ $root = parse_url( home_url() )['host'];
70
+ $local = ( false !== stripos( $ref_url, home_url() ) && false !== stripos( $root, $ref_root ) ) ? true : false;
71
+ if ( $ref_url && $local ) {
72
+ $return_url = $ref_url;
73
+ } else {
74
+ wp_die( 'My Calendar: invalid print URL provided.' );
75
+ }
76
+ }
77
  ?>
78
  <!DOCTYPE html>
79
  <html <?php language_attributes(); ?>>
86
  <meta name="robots" content="noindex,nofollow" />
87
  <!-- Copy mc-print.css to your theme directory if you wish to replace the default print styles -->
88
  <link rel="stylesheet" href="<?php echo esc_url( $stylesheet ); ?>" type="text/css" media="screen,print" />
89
+ <?php
90
+ /**
91
+ * Execute action in the `head` element of the My Calendar print view, where wp_head() won't be run.
92
+ *
93
+ * @hook mc_print_view_head
94
+ *
95
+ * @param {string} $output Potential output for My Calendar; default empty string.
96
+ */
97
+ do_action( 'mc_print_view_head', '' );
98
+ ?>
99
  </head>
100
  <body>
101
  <?php
 
 
 
 
 
 
 
 
102
  $calendar = array(
103
  'name' => 'print',
104
  'format' => 'calendar',
112
  );
113
 
114
  echo mc_kses_post( my_calendar( $calendar ) );
 
 
 
 
 
 
 
 
 
115
 
116
  $add = array_map( 'esc_html', $_GET );
117
  unset( $add['cid'] );
118
  unset( $add['feed'] );
119
  unset( $add['href'] );
120
+ /**
121
+ * Return to calendar URL from print view.
122
+ *
123
+ * @hook mc_return_to_calendar
124
+ *
125
+ * @param {string} $return_url URL to return to previous page.
126
+ * @param {array} $add Array of parameters added to this URL.
127
+ *
128
+ * @return {string}
129
+ */
130
  $return_url = apply_filters( 'mc_return_to_calendar', mc_build_url( $add, array( 'feed', 'cid', 'href', 'searched' ), $return_url ), $add );
131
  if ( $return_url ) {
132
  echo wp_kses_post( "<p class='return'>&larr; <a href='" . esc_url( $return_url ) . "'>" . esc_html__( 'Return to calendar', 'my-calendar' ) . '</a></p>' );
my-calendar-search.php CHANGED
@@ -16,37 +16,139 @@ if ( ! defined( 'ABSPATH' ) ) {
16
  /**
17
  * Output search results for a given query
18
  *
19
- * @param mixed string/array $query Search query.
20
  *
21
  * @return string HTML output
22
  */
23
  function mc_search_results( $query ) {
24
- $before = apply_filters( 'mc_past_search_results', 0, 'basic' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  $after = apply_filters( 'mc_future_search_results', 10, 'basic' ); // Return only future events, nearest 10.
26
  $exports = '';
27
  if ( is_string( $query ) ) {
28
  $search = mc_prepare_search_query( $query );
29
  $term = $query;
30
  } else {
 
 
 
 
 
 
 
 
 
 
31
  $search = apply_filters( 'mc_advanced_search', '', $query );
32
  $term = $query['mcs'];
 
 
 
 
 
 
 
 
 
 
33
  $before = apply_filters( 'mc_past_search_results', 0, 'advanced' );
34
- $after = apply_filters( 'mc_future_search_results', 20, 'advanced' );
 
 
 
 
 
 
 
 
 
 
35
  }
36
 
37
  $event_array = mc_get_search_results( $search );
38
 
39
  if ( ! empty( $event_array ) ) {
40
- $template = '<strong>{date}</strong> {title} {details}';
41
- $template = apply_filters( 'mc_search_template', $template );
 
 
 
 
 
 
 
 
 
 
42
  // No filters parameter prevents infinite looping on the_content filters.
43
- $output = mc_produce_upcoming_events( $event_array, $template, 'list', 'ASC', 0, $before, $after, 'yes', 'yes', 'nofilters' );
 
 
 
 
 
 
 
 
 
 
44
  $exports = apply_filters( 'mc_search_exportlinks', '', $output );
45
  } else {
46
- $output = apply_filters( 'mc_search_no_results', "<li class='no-results'>" . __( 'Sorry, your search produced no results.', 'my-calendar' ) . '</li>' );
 
 
 
 
 
 
 
 
 
 
47
  }
48
 
49
- $header = apply_filters( 'mc_search_before', '<ol class="mc-search-results">', $term );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  $footer = apply_filters( 'mc_search_after', '</ol>', $term );
51
 
52
  return $header . $output . $footer . $exports;
@@ -61,7 +163,7 @@ add_filter( 'the_title', 'mc_search_results_title', 10, 2 );
61
  *
62
  * @return string New title
63
  */
64
- function mc_search_results_title( $title, $id = false ) {
65
  if ( ( isset( $_GET['mcs'] ) || isset( $_POST['mcs'] ) ) && ( is_page( $id ) || is_single( $id ) ) && in_the_loop() ) {
66
  $query = ( isset( $_GET['mcs'] ) ) ? $_GET['mcs'] : $_POST['mcs'];
67
  // Translators: entered search query.
@@ -122,6 +224,9 @@ function mc_search_exportlinks() {
122
  $mc_print_url = mc_build_url( $print_add, '', home_url() );
123
  $print = "<div class='mc-print'><a href='$mc_print_url'>" . __( 'Print<span class="maybe-hide"> View</span>', 'my-calendar' ) . '</a></div>';
124
 
 
 
 
125
  // Set up exports.
126
  if ( '' !== get_option( 'mc_topnav', '' ) ) {
127
  $above = array_map( 'trim', explode( ',', get_option( 'mc_topnav' ) ) );
@@ -168,8 +273,9 @@ function mc_searched_events( $event_array ) {
168
  */
169
  function mc_get_searched_events() {
170
  if ( ! session_id() || ! isset( $_SESSION['MC_SEARCH_RESULT'] ) ) {
171
- return;
172
  }
 
173
  $event_searched = json_decode( $_SESSION['MC_SEARCH_RESULT'], true );
174
  foreach ( $event_searched as $key => $value ) {
175
  $daily_events = array();
16
  /**
17
  * Output search results for a given query
18
  *
19
+ * @param string|array $query Search query.
20
  *
21
  * @return string HTML output
22
  */
23
  function mc_search_results( $query ) {
24
+ /**
25
+ * Number of past results to show. Default `0`.
26
+ *
27
+ * @hook mc_past_search_results
28
+ *
29
+ * @param {int} $before Number of past results to display.
30
+ * @param {string} $context 'basic' for basic search results.
31
+ *
32
+ * @return {string}
33
+ */
34
+ $before = apply_filters( 'mc_past_search_results', 0, 'basic' );
35
+ /**
36
+ * Number of future results to show. Default `20`.
37
+ *
38
+ * @hook mc_future_search_results
39
+ *
40
+ * @param {int} $after Number of future results to display.
41
+ * @param {string} $context 'basic' for basic search results.
42
+ *
43
+ * @return {string}
44
+ */
45
  $after = apply_filters( 'mc_future_search_results', 10, 'basic' ); // Return only future events, nearest 10.
46
  $exports = '';
47
  if ( is_string( $query ) ) {
48
  $search = mc_prepare_search_query( $query );
49
  $term = $query;
50
  } else {
51
+ /**
52
+ * Build the advanced search query. Default ''
53
+ *
54
+ * @hook mc_advanced_search
55
+ *
56
+ * @param {string} $search Placeholder to create search results.
57
+ * @param {array|string} $query User search query parameters.
58
+ *
59
+ * @return {array}
60
+ */
61
  $search = apply_filters( 'mc_advanced_search', '', $query );
62
  $term = $query['mcs'];
63
+ /**
64
+ * Number of past results to show. Default `0`.
65
+ *
66
+ * @hook mc_past_search_results
67
+ *
68
+ * @param {int} $before Number of past results to display.
69
+ * @param {string} $context 'advanced' for advanced search results.
70
+ *
71
+ * @return {string}
72
+ */
73
  $before = apply_filters( 'mc_past_search_results', 0, 'advanced' );
74
+ /**
75
+ * Number of future results to show. Default `20`.
76
+ *
77
+ * @hook mc_future_search_results
78
+ *
79
+ * @param {int} $after Number of future results to display.
80
+ * @param {string} $context 'advanced' for advanced search results.
81
+ *
82
+ * @return {string}
83
+ */
84
+ $after = apply_filters( 'mc_future_search_results', 20, 'advanced' );
85
  }
86
 
87
  $event_array = mc_get_search_results( $search );
88
 
89
  if ( ! empty( $event_array ) ) {
90
+ $template = '<h3><strong>{timerange after=", "}{daterange}</strong> &#8211; {linking_title}</h3><div class="mcs-search-excerpt">{search_excerpt}</div>';
91
+ /**
92
+ * Template for outputting search results. Default `<strong>{date}</strong> {title} {details}`.
93
+ *
94
+ * @hook mc_search_template
95
+ *
96
+ * @param {string} $template String with HTML and template tags.
97
+ * @param {string|array} $term The search query arguments. Can be a string or an array of search parameters.
98
+ *
99
+ * @return {string}
100
+ */
101
+ $template = apply_filters( 'mc_search_template', $template, $term );
102
  // No filters parameter prevents infinite looping on the_content filters.
103
+ $output = mc_produce_upcoming_events( $event_array, $template, 'list', 'ASC', 0, $before, $after, 'yes', 'yes', 'nofilters', $term );
104
+ /**
105
+ * Filter that inserts search export links. Default empty string.
106
+ *
107
+ * @hook mc_search_exportlinks
108
+ *
109
+ * @param {string} $exports String.
110
+ * @param {array} $output Search results.
111
+ *
112
+ * @return {string}
113
+ */
114
  $exports = apply_filters( 'mc_search_exportlinks', '', $output );
115
  } else {
116
+ /**
117
+ * HTML template if no search results. Default `<li class='no-results'>" . __( 'Sorry, your search produced no results.', 'my-calendar' ) . '</li>`.
118
+ *
119
+ * @hook mc_search_no_results
120
+ *
121
+ * @param {string} $output HTML output.
122
+ * @param {string|array} $term The search query arguments. Can be a string or an array of search parameters.
123
+ *
124
+ * @return {string}
125
+ */
126
+ $output = apply_filters( 'mc_search_no_results', "<li class='no-results'>" . __( 'Sorry, your search produced no results.', 'my-calendar' ) . '</li>', $term );
127
  }
128
 
129
+ /**
130
+ * HTML template before the search results. Default `<ol class="mc-search-results">`.
131
+ *
132
+ * @hook mc_search_before
133
+ *
134
+ * @param {string} $header HTML output.
135
+ * @param {string|array} $term The search query arguments. Can be a string or an array of search parameters.
136
+ *
137
+ * @return {string}
138
+ */
139
+ $header = apply_filters( 'mc_search_before', '<h2>%s</h2><ol class="mc-search-results" role="list">', $term );
140
+ // Translators: search term.
141
+ $header = sprintf( $header, sprintf( __( 'Search Results for "%s"', 'my-calendar' ), esc_html( $term ) ) );
142
+ /**
143
+ * HTML template after the search results. Default `</ol>`.
144
+ *
145
+ * @hook mc_search_after
146
+ *
147
+ * @param {string} $footer HTML output.
148
+ * @param {string|array} $term The search query arguments. Can be a string or an array of search parameters.
149
+ *
150
+ * @return {string}
151
+ */
152
  $footer = apply_filters( 'mc_search_after', '</ol>', $term );
153
 
154
  return $header . $output . $footer . $exports;
163
  *
164
  * @return string New title
165
  */
166
+ function mc_search_results_title( $title, $id ) {
167
  if ( ( isset( $_GET['mcs'] ) || isset( $_POST['mcs'] ) ) && ( is_page( $id ) || is_single( $id ) ) && in_the_loop() ) {
168
  $query = ( isset( $_GET['mcs'] ) ) ? $_GET['mcs'] : $_POST['mcs'];
169
  // Translators: entered search query.
224
  $mc_print_url = mc_build_url( $print_add, '', home_url() );
225
  $print = "<div class='mc-print'><a href='$mc_print_url'>" . __( 'Print<span class="maybe-hide"> View</span>', 'my-calendar' ) . '</a></div>';
226
 
227
+ $above = array();
228
+ $below = array();
229
+
230
  // Set up exports.
231
  if ( '' !== get_option( 'mc_topnav', '' ) ) {
232
  $above = array_map( 'trim', explode( ',', get_option( 'mc_topnav' ) ) );
273
  */
274
  function mc_get_searched_events() {
275
  if ( ! session_id() || ! isset( $_SESSION['MC_SEARCH_RESULT'] ) ) {
276
+ return array();
277
  }
278
+ $event_array = array();
279
  $event_searched = json_decode( $_SESSION['MC_SEARCH_RESULT'], true );
280
  foreach ( $event_searched as $key => $value ) {
281
  $daily_events = array();
my-calendar-settings.php CHANGED
@@ -16,17 +16,18 @@ if ( ! defined( 'ABSPATH' ) ) {
16
  /**
17
  * Generate input & field for a My Calendar setting.
18
  *
19
- * @param string $name Name of option.
20
- * @param string $label Label for input.
21
- * @param string $default default value if not set.
22
- * @param string $note Note to associate with field via aria-describedby.
23
- * @param array $atts Array of keys and values to use as input attributes.
24
- * @param string $type Field type for option.
25
- * @param boolean $echo True to echo, false to return.
26
  */
27
  function mc_settings_field( $name, $label, $default = '', $note = '', $atts = array(), $type = 'text', $echo = true ) {
28
  $options = '';
29
  $attributes = '';
 
30
  if ( 'text' === $type || 'url' === $type || 'email' === $type ) {
31
  $base_atts = array(
32
  'size' => '30',
@@ -243,13 +244,12 @@ function mc_update_management_settings( $post ) {
243
  }
244
 
245
  /**
246
- * Update Permissions settings.
247
  *
248
- * @param array $post POST data.
249
  */
250
- function mc_update_permissions_settings( $post ) {
251
- $perms = $post['mc_caps'];
252
- $caps = array(
253
  'mc_add_events' => __( 'Add Events', 'my-calendar' ),
254
  'mc_publish_events' => __( 'Publish Events', 'my-calendar' ),
255
  'mc_approve_events' => __( 'Approve Events', 'my-calendar' ),
@@ -262,7 +262,29 @@ function mc_update_permissions_settings( $post ) {
262
  'mc_edit_settings' => __( 'Edit Settings', 'my-calendar' ),
263
  'mc_view_help' => __( 'View Help', 'my-calendar' ),
264
  );
265
- $caps = apply_filters( 'mc_capabilities', $caps );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  foreach ( $perms as $key => $value ) {
267
  $role = get_role( $key );
268
  if ( is_object( $role ) ) {
@@ -329,18 +351,18 @@ function mc_update_output_settings( $post ) {
329
  * @param array $post POST data.
330
  */
331
  function mc_update_input_settings( $post ) {
332
- $mc_input_options_administrators = ( ! empty( $post['mc_input_options_administrators'] ) && 'on' === $post['mc_input_options_administrators'] ) ? 'true' : 'false';
333
  $mc_input_options = array(
334
- 'event_short' => ( ! empty( $post['mci_event_short'] ) && $post['mci_event_short'] ) ? 'on' : 'off',
335
- 'event_desc' => ( ! empty( $post['mci_event_desc'] ) && $post['mci_event_desc'] ) ? 'on' : 'off',
336
- 'event_category' => ( ! empty( $post['mci_event_category'] ) && $post['mci_event_category'] ) ? 'on' : 'off',
337
- 'event_image' => ( ! empty( $post['mci_event_image'] ) && $post['mci_event_image'] ) ? 'on' : 'off',
338
- 'event_link' => ( ! empty( $post['mci_event_link'] ) && $post['mci_event_link'] ) ? 'on' : 'off',
339
- 'event_recurs' => ( ! empty( $post['mci_event_recurs'] ) && $post['mci_event_recurs'] ) ? 'on' : 'off',
340
- 'event_open' => ( ! empty( $post['mci_event_open'] ) && $post['mci_event_open'] ) ? 'on' : 'off',
341
- 'event_location' => ( ! empty( $post['mci_event_location'] ) && $post['mci_event_location'] ) ? 'on' : 'off',
342
- 'event_access' => ( ! empty( $post['mci_event_access'] ) && $post['mci_event_access'] ) ? 'on' : 'off',
343
- 'event_host' => ( ! empty( $post['mci_event_host'] ) && $post['mci_event_host'] ) ? 'on' : 'off',
344
  );
345
  update_option( 'mc_input_options', $mc_input_options );
346
  update_option( 'mc_input_options_administrators', $mc_input_options_administrators );
@@ -403,7 +425,7 @@ function my_calendar_settings() {
403
  if ( ! empty( $_POST ) ) {
404
  $nonce = $_REQUEST['_wpnonce'];
405
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
406
- die( 'Security check failed' );
407
  }
408
  if ( isset( $_POST['mc_manage'] ) ) {
409
  $before_permalinks = get_option( 'mc_use_permalinks' );
@@ -457,8 +479,20 @@ function my_calendar_settings() {
457
  mc_show_notice( __( 'Email notice settings saved', 'my-calendar' ) );
458
  }
459
 
460
- $settings = apply_filters( 'mc_save_settings', '', $_POST );
 
 
 
 
 
 
 
 
 
 
 
461
  }
 
462
  // Pull templates for passing into functions.
463
  $templates = get_option( 'mc_templates' );
464
  $mc_title_template = ( isset( $templates['title'] ) ) ? esc_attr( stripslashes( $templates['title'] ) ) : '';
@@ -486,7 +520,19 @@ function my_calendar_settings() {
486
  ?>
487
  <button type="button" role="tab" aria-selected="false" id="tab_permissions" aria-controls="my-calendar-permissions"><?php esc_html_e( 'Permissions', 'my-calendar' ); ?></button>
488
  <button type="button" role="tab" id="tab_email" aria-selected="false" aria-controls="my-calendar-email"><?php esc_html_e( 'Notifications', 'my-calendar' ); ?></button>
489
- <?php echo apply_filters( 'mc_settings_section_links', '' ); ?>
 
 
 
 
 
 
 
 
 
 
 
 
490
  </div>
491
  <div class="settings postbox-container jcd-wide">
492
  <div class="metabox-holder">
@@ -494,7 +540,7 @@ function my_calendar_settings() {
494
  if ( isset( $_POST['import'] ) && 'true' === $_POST['import'] ) {
495
  $nonce = $_REQUEST['_wpnonce'];
496
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
497
- die( 'Security check failed' );
498
  }
499
  my_calendar_import();
500
  }
@@ -750,7 +796,7 @@ function mc_remote_db() {
750
  <li><?php mc_settings_field( 'mc_posted_by', __( 'Posted by', 'my-calendar' ), 'Posted by' ); ?></li>
751
  <li><?php mc_settings_field( 'mc_buy_tickets', __( 'Buy tickets', 'my-calendar' ), 'Buy tickets' ); ?></li>
752
  <li><?php mc_settings_field( 'mc_event_accessibility', __( 'Event Accessibility Heading', 'my-calendar' ), 'Event Accessibility' ); ?></li>
753
- <li><?php mc_settings_field( 'mc_view_full', __( 'View full calendar', 'my-calendar' ), 'View full calendar' ); ?></li>
754
  <li><?php mc_settings_field( 'mc_details_label', __( 'Read more text', 'my-calendar' ), $mc_details_label, __( 'Tags: <code>{title}</code>, <code>{location}</code>, <code>{color}</code>, <code>{icon}</code>, <code>{date}</code>, <code>{time}</code>.', 'my-calendar' ) ); ?></li>
755
  <li><?php mc_settings_field( 'mc_link_label', __( 'More Information text', 'my-calendar' ), $mc_link_label, "<a href='" . admin_url( 'admin.php?page=my-calendar-design#my-calendar-templates' ) . "'>" . __( 'Templating Help', 'my-calendar' ) . '</a>' ); ?></li>
756
  <li>
@@ -1200,20 +1246,7 @@ function mc_remote_db() {
1200
  global $wp_roles;
1201
  $role_container = '';
1202
  $roles = $wp_roles->get_names();
1203
- $caps = array(
1204
- 'mc_add_events' => __( 'Add Events', 'my-calendar' ),
1205
- 'mc_publish_events' => __( 'Publish Events', 'my-calendar' ),
1206
- 'mc_approve_events' => __( 'Approve Events', 'my-calendar' ),
1207
- 'mc_manage_events' => __( 'Manage Events', 'my-calendar' ),
1208
- 'mc_edit_locations' => __( 'Edit Locations', 'my-calendar' ),
1209
- 'mc_edit_cats' => __( 'Edit Categories', 'my-calendar' ),
1210
- 'mc_edit_styles' => __( 'Edit Styles', 'my-calendar' ),
1211
- 'mc_edit_behaviors' => __( 'Manage Scripts', 'my-calendar' ),
1212
- 'mc_edit_templates' => __( 'Edit Templates', 'my-calendar' ),
1213
- 'mc_edit_settings' => __( 'Edit Settings', 'my-calendar' ),
1214
- 'mc_view_help' => __( 'View Help', 'my-calendar' ),
1215
- );
1216
- $caps = apply_filters( 'mc_capabilities', $caps );
1217
 
1218
  foreach ( $roles as $role => $rolename ) {
1219
  if ( 'administrator' === $role ) {
@@ -1300,7 +1333,18 @@ function mc_remote_db() {
1300
  </div>
1301
  </div>
1302
 
1303
- <?php echo apply_filters( 'mc_after_settings', '' ); ?>
 
 
 
 
 
 
 
 
 
 
 
1304
 
1305
  </div>
1306
  </div>
@@ -1384,6 +1428,8 @@ function mc_location_controls() {
1384
 
1385
  return $output;
1386
  }
 
 
1387
  }
1388
 
1389
  /**
16
  /**
17
  * Generate input & field for a My Calendar setting.
18
  *
19
+ * @param string $name Name of option.
20
+ * @param string $label Label for input.
21
+ * @param string|array $default default value or values if option not set.
22
+ * @param string $note Note to associate with field via aria-describedby.
23
+ * @param array $atts Array of keys and values to use as input attributes.
24
+ * @param string $type Field type for option.
25
+ * @param boolean $echo True to echo, false to return.
26
  */
27
  function mc_settings_field( $name, $label, $default = '', $note = '', $atts = array(), $type = 'text', $echo = true ) {
28
  $options = '';
29
  $attributes = '';
30
+ $return = '';
31
  if ( 'text' === $type || 'url' === $type || 'email' === $type ) {
32
  $base_atts = array(
33
  'size' => '30',
244
  }
245
 
246
  /**
247
+ * Get array of My Calendar user capabilities.
248
  *
249
+ * @return array
250
  */
251
+ function mc_get_user_capabilities() {
252
+ $caps = array(
 
253
  'mc_add_events' => __( 'Add Events', 'my-calendar' ),
254
  'mc_publish_events' => __( 'Publish Events', 'my-calendar' ),
255
  'mc_approve_events' => __( 'Approve Events', 'my-calendar' ),
262
  'mc_edit_settings' => __( 'Edit Settings', 'my-calendar' ),
263
  'mc_view_help' => __( 'View Help', 'my-calendar' ),
264
  );
265
+ /**
266
+ * Add custom capabilities to the array of My Calendar permissions. New capabilities can be assigned to roles in the My Calendar settings.
267
+ *
268
+ * @hook mc_capabilities
269
+ *
270
+ * @param {array} Array of My Calendar capabilities in format ['capability' => 'Visible Label'].
271
+ *
272
+ * @return {array}
273
+ */
274
+ $caps = apply_filters( 'mc_capabilities', $caps );
275
+
276
+ return $caps;
277
+ }
278
+
279
+ /**
280
+ * Update Permissions settings.
281
+ *
282
+ * @param array $post POST data.
283
+ */
284
+ function mc_update_permissions_settings( $post ) {
285
+ $perms = $post['mc_caps'];
286
+ $caps = mc_get_user_capabilities();
287
+
288
  foreach ( $perms as $key => $value ) {
289
  $role = get_role( $key );
290
  if ( is_object( $role ) ) {
351
  * @param array $post POST data.
352
  */
353
  function mc_update_input_settings( $post ) {
354
+ $mc_input_options_administrators = ( isset( $post['mc_input_options_administrators'] ) ) ? 'true' : 'false';
355
  $mc_input_options = array(
356
+ 'event_short' => ( isset( $post['mci_event_short'] ) ) ? 'on' : 'off',
357
+ 'event_desc' => ( isset( $post['mci_event_desc'] ) ) ? 'on' : 'off',
358
+ 'event_category' => ( isset( $post['mci_event_category'] ) ) ? 'on' : 'off',
359
+ 'event_image' => ( isset( $post['mci_event_image'] ) ) ? 'on' : 'off',
360
+ 'event_link' => ( isset( $post['mci_event_link'] ) ) ? 'on' : 'off',
361
+ 'event_recurs' => ( isset( $post['mci_event_recurs'] ) ) ? 'on' : 'off',
362
+ 'event_open' => ( isset( $post['mci_event_open'] ) ) ? 'on' : 'off',
363
+ 'event_location' => ( isset( $post['mci_event_location'] ) ) ? 'on' : 'off',
364
+ 'event_access' => ( isset( $post['mci_event_access'] ) ) ? 'on' : 'off',
365
+ 'event_host' => ( isset( $post['mci_event_host'] ) ) ? 'on' : 'off',
366
  );
367
  update_option( 'mc_input_options', $mc_input_options );
368
  update_option( 'mc_input_options_administrators', $mc_input_options_administrators );
425
  if ( ! empty( $_POST ) ) {
426
  $nonce = $_REQUEST['_wpnonce'];
427
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
428
+ wp_die( 'My Calendar: Security check failed' );
429
  }
430
  if ( isset( $_POST['mc_manage'] ) ) {
431
  $before_permalinks = get_option( 'mc_use_permalinks' );
479
  mc_show_notice( __( 'Email notice settings saved', 'my-calendar' ) );
480
  }
481
 
482
+ /**
483
+ * Run when settings are saved. Default ''.
484
+ *
485
+ * @hook mc_save_settings
486
+ *
487
+ * @param {string} Message after updating settings sent to `mc_show_notice()`.
488
+ * @param {array} $post POST global.
489
+ */
490
+ $settings = do_action( 'mc_save_settings', '', $_POST );
491
+ if ( '' !== $settings ) {
492
+ mc_show_notice( $settings );
493
+ }
494
  }
495
+
496
  // Pull templates for passing into functions.
497
  $templates = get_option( 'mc_templates' );
498
  $mc_title_template = ( isset( $templates['title'] ) ) ? esc_attr( stripslashes( $templates['title'] ) ) : '';
520
  ?>
521
  <button type="button" role="tab" aria-selected="false" id="tab_permissions" aria-controls="my-calendar-permissions"><?php esc_html_e( 'Permissions', 'my-calendar' ); ?></button>
522
  <button type="button" role="tab" id="tab_email" aria-selected="false" aria-controls="my-calendar-email"><?php esc_html_e( 'Notifications', 'my-calendar' ); ?></button>
523
+ <?php
524
+ /**
525
+ * Add additional buttons to collection of settings tabs.
526
+ *
527
+ * @hook mc_settings_section_links
528
+ *
529
+ * @param {string} HTML to output.
530
+ *
531
+ * @return {string}
532
+ */
533
+ $links = apply_filters( 'mc_settings_section_links', '' );
534
+ echo $links;
535
+ ?>
536
  </div>
537
  <div class="settings postbox-container jcd-wide">
538
  <div class="metabox-holder">
540
  if ( isset( $_POST['import'] ) && 'true' === $_POST['import'] ) {
541
  $nonce = $_REQUEST['_wpnonce'];
542
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
543
+ wp_die( 'My Calendar: Security check failed' );
544
  }
545
  my_calendar_import();
546
  }
796
  <li><?php mc_settings_field( 'mc_posted_by', __( 'Posted by', 'my-calendar' ), 'Posted by' ); ?></li>
797
  <li><?php mc_settings_field( 'mc_buy_tickets', __( 'Buy tickets', 'my-calendar' ), 'Buy tickets' ); ?></li>
798
  <li><?php mc_settings_field( 'mc_event_accessibility', __( 'Event Accessibility Heading', 'my-calendar' ), 'Event Accessibility' ); ?></li>
799
+ <li><?php mc_settings_field( 'mc_view_full', __( 'View full calendar', 'my-calendar' ), __( 'View full calendar', 'my-calendar' ) ); ?></li>
800
  <li><?php mc_settings_field( 'mc_details_label', __( 'Read more text', 'my-calendar' ), $mc_details_label, __( 'Tags: <code>{title}</code>, <code>{location}</code>, <code>{color}</code>, <code>{icon}</code>, <code>{date}</code>, <code>{time}</code>.', 'my-calendar' ) ); ?></li>
801
  <li><?php mc_settings_field( 'mc_link_label', __( 'More Information text', 'my-calendar' ), $mc_link_label, "<a href='" . admin_url( 'admin.php?page=my-calendar-design#my-calendar-templates' ) . "'>" . __( 'Templating Help', 'my-calendar' ) . '</a>' ); ?></li>
802
  <li>
1246
  global $wp_roles;
1247
  $role_container = '';
1248
  $roles = $wp_roles->get_names();
1249
+ $caps = mc_get_user_capabilities();
 
 
 
 
 
 
 
 
 
 
 
 
 
1250
 
1251
  foreach ( $roles as $role => $rolename ) {
1252
  if ( 'administrator' === $role ) {
1333
  </div>
1334
  </div>
1335
 
1336
+ <?php
1337
+ /**
1338
+ * Render content after settings panels have displayed. Default ''.
1339
+ *
1340
+ * @hook mc_after_settings
1341
+ * @param {string} $after_settings Output content.
1342
+ *
1343
+ * @return {string}
1344
+ */
1345
+ $after_settings = apply_filters( 'mc_after_settings', '' );
1346
+ echo $after_settings;
1347
+ ?>
1348
 
1349
  </div>
1350
  </div>
1428
 
1429
  return $output;
1430
  }
1431
+
1432
+ return '';
1433
  }
1434
 
1435
  /**
my-calendar-shortcodes.php CHANGED
@@ -67,9 +67,29 @@ function my_calendar_insert( $atts, $content = null ) {
67
 
68
  global $user_ID;
69
  if ( 'current' === $args['author'] ) {
 
 
 
 
 
 
 
 
 
 
70
  $args['author'] = apply_filters( 'mc_display_author', $user_ID, 'main' );
71
  }
72
  if ( 'current' === $args['host'] ) {
 
 
 
 
 
 
 
 
 
 
73
  $args['host'] = apply_filters( 'mc_display_host', $user_ID, 'main' );
74
  }
75
 
@@ -111,9 +131,29 @@ function my_calendar_insert_upcoming( $atts ) {
111
 
112
  global $user_ID;
113
  if ( 'current' === $args['author'] ) {
 
 
 
 
 
 
 
 
 
 
114
  $args['author'] = apply_filters( 'mc_display_author', $user_ID, 'upcoming' );
115
  }
116
  if ( 'current' === $args['host'] ) {
 
 
 
 
 
 
 
 
 
 
117
  $args['host'] = apply_filters( 'mc_display_host', $user_ID, 'upcoming' );
118
  }
119
 
@@ -145,9 +185,29 @@ function my_calendar_insert_today( $atts ) {
145
 
146
  global $user_ID;
147
  if ( 'current' === $args['author'] ) {
 
 
 
 
 
 
 
 
 
 
148
  $args['author'] = apply_filters( 'mc_display_author', $user_ID, 'today' );
149
  }
150
  if ( 'current' === $args['host'] ) {
 
 
 
 
 
 
 
 
 
 
151
  $args['host'] = apply_filters( 'mc_display_host', $user_ID, 'today' );
152
  }
153
 
@@ -360,7 +420,7 @@ add_action( 'add_meta_boxes', 'mc_calendar_view' );
360
  /**
361
  * Settings to configure My Calendar view or build shortcode.
362
  *
363
- * @param object $post Post object.
364
  * @param array|string $callback_args Post callback args or selected type.
365
  */
366
  function mc_calendar_generator_fields( $post, $callback_args ) {
@@ -390,6 +450,7 @@ function mc_calendar_generator_fields( $post, $callback_args ) {
390
  ?>
391
  <div id="mc-generator" class="generator">
392
  <?php
 
393
  switch ( $type ) {
394
  case 'main':
395
  $message = __( 'Generate the <code>[my_calendar]</code> shortcode. Generates the main grid, list, and mini calendar views.', 'my-calendar' );
@@ -567,26 +628,36 @@ function mc_calendar_generator_fields( $post, $callback_args ) {
567
  if ( 'true' === get_option( 'mc_remote' ) && function_exists( 'mc_remote_db' ) ) {
568
  $mcdb = mc_remote_db();
569
  }
570
- $query = 'SELECT event_begin FROM ' . my_calendar_table() . ' WHERE event_approved = 1 AND event_flagged <> 1 ORDER BY event_begin ASC LIMIT 0 , 1';
571
- $year1 = mc_date( 'Y', strtotime( $mcdb->get_var( $query ) ) );
572
- $diff1 = mc_date( 'Y' ) - $year1;
573
- $past = $diff1;
574
- $future = apply_filters( 'mc_jumpbox_future_years', 5, false );
 
 
 
 
 
 
 
 
 
 
575
  $fut = 1;
576
  $f = '';
577
  $p = '';
578
  while ( $past > 0 ) {
579
  $p .= '<option value="';
580
- $p .= current_time( 'Y' ) - $past;
581
  $p .= '">';
582
- $p .= current_time( 'Y' ) - $past . "</option>\n";
583
  $past = $past - 1;
584
  }
585
  while ( $fut < $future ) {
586
  $f .= '<option value="';
587
- $f .= current_time( 'Y' ) + $fut;
588
  $f .= '">';
589
- $f .= current_time( 'Y' ) + $fut . "</option>\n";
590
  $fut = $fut + 1;
591
  }
592
  echo wp_kses( $p . '<option value="' . current_time( 'Y' ) . '"' . selected( current_time( 'Y' ), $year ) . '>' . current_time( 'Y' ) . "</option>\n" . $f, mc_kses_elements() );
@@ -639,13 +710,13 @@ function mc_calendar_generator_fields( $post, $callback_args ) {
639
  <label for="skip"><?php esc_html_e( 'Events/Days to Skip', 'my-calendar' ); ?></label>
640
  <input type="number" name="skip" id="skip" value="" />
641
  </p>
642
- <p>
643
- <label for="show_today"><?php esc_html_e( "Show Today's Events", 'my-calendar' ); ?></label>
644
  <input type="checkbox" name="show_today" id="show_today" value="yes"/>
 
645
  </p>
646
- <p>
647
- <label for="show_recurring"><?php esc_html_e( 'Show only the first recurring event in a series', 'my-calendar' ); ?></label>
648
  <input type="checkbox" name="show_recurring" id="show_recurring" value="no" />
 
649
  </p>
650
  <p>
651
  <label for="type"><?php esc_html_e( 'Type of Upcoming Events List', 'my-calendar' ); ?></label>
67
 
68
  global $user_ID;
69
  if ( 'current' === $args['author'] ) {
70
+ /**
71
+ * Filter the author parameter for a My Calendar view if set as 'current'. Default current user ID.
72
+ *
73
+ * @hook mc_display_author
74
+ *
75
+ * @param {int} $user_ID Logged-in user ID.
76
+ * @param {string} $context 'main' to indicate the `my_calendar` shortcode is running.
77
+ *
78
+ * @return {int} Valid author ID.
79
+ */
80
  $args['author'] = apply_filters( 'mc_display_author', $user_ID, 'main' );
81
  }
82
  if ( 'current' === $args['host'] ) {
83
+ /**
84
+ * Filter the host parameter for a My Calendar view if set as 'current'. Default current user ID.
85
+ *
86
+ * @hook mc_display_host
87
+ *
88
+ * @param {int} $user_ID Logged-in user ID.
89
+ * @param {string} $context 'main' to indicate the `my_calendar` shortcode is running.
90
+ *
91
+ * @return {int} Valid author ID.
92
+ */
93
  $args['host'] = apply_filters( 'mc_display_host', $user_ID, 'main' );
94
  }
95
 
131
 
132
  global $user_ID;
133
  if ( 'current' === $args['author'] ) {
134
+ /**
135
+ * Filter the author parameter for a My Calendar view if set as 'current'. Default current user ID.
136
+ *
137
+ * @hook mc_display_author
138
+ *
139
+ * @param {int} $user_ID Logged-in user ID.
140
+ * @param {string} $context 'upcoming' to indicate the `my_calendar_upcoming` shortcode is running.
141
+ *
142
+ * @return {int} Valid author ID.
143
+ */
144
  $args['author'] = apply_filters( 'mc_display_author', $user_ID, 'upcoming' );
145
  }
146
  if ( 'current' === $args['host'] ) {
147
+ /**
148
+ * Filter the host parameter for a My Calendar view if set as 'current'. Default current user ID.
149
+ *
150
+ * @hook mc_display_host
151
+ *
152
+ * @param {int} $user_ID Logged-in user ID.
153
+ * @param {string} $context 'upcoming' to indicate the `my_calendar_upcoming` shortcode is running.
154
+ *
155
+ * @return {int} Valid author ID.
156
+ */
157
  $args['host'] = apply_filters( 'mc_display_host', $user_ID, 'upcoming' );
158
  }
159
 
185
 
186
  global $user_ID;
187
  if ( 'current' === $args['author'] ) {
188
+ /**
189
+ * Filter the author parameter for a My Calendar view if set as 'current'. Default current user ID.
190
+ *
191
+ * @hook mc_display_author
192
+ *
193
+ * @param {int} $user_ID Logged-in user ID.
194
+ * @param {string} $context 'today' to indicate the `my_calendar_today` shortcode is running.
195
+ *
196
+ * @return {int} Valid author ID.
197
+ */
198
  $args['author'] = apply_filters( 'mc_display_author', $user_ID, 'today' );
199
  }
200
  if ( 'current' === $args['host'] ) {
201
+ /**
202
+ * Filter the host parameter for a My Calendar view if set as 'current'. Default current user ID.
203
+ *
204
+ * @hook mc_display_host
205
+ *
206
+ * @param {int} $user_ID Logged-in user ID.
207
+ * @param {string} $context 'today' to indicate the `my_calendar_today` shortcode is running.
208
+ *
209
+ * @return {int} Valid author ID.
210
+ */
211
  $args['host'] = apply_filters( 'mc_display_host', $user_ID, 'today' );
212
  }
213
 
420
  /**
421
  * Settings to configure My Calendar view or build shortcode.
422
  *
423
+ * @param object|false $post WP_Post object or false if no data.
424
  * @param array|string $callback_args Post callback args or selected type.
425
  */
426
  function mc_calendar_generator_fields( $post, $callback_args ) {
450
  ?>
451
  <div id="mc-generator" class="generator">
452
  <?php
453
+ $message = '';
454
  switch ( $type ) {
455
  case 'main':
456
  $message = __( 'Generate the <code>[my_calendar]</code> shortcode. Generates the main grid, list, and mini calendar views.', 'my-calendar' );
628
  if ( 'true' === get_option( 'mc_remote' ) && function_exists( 'mc_remote_db' ) ) {
629
  $mcdb = mc_remote_db();
630
  }
631
+ $query = 'SELECT event_begin FROM ' . my_calendar_table() . ' WHERE event_approved = 1 AND event_flagged <> 1 ORDER BY event_begin ASC LIMIT 0 , 1';
632
+ $year1 = mc_date( 'Y', strtotime( $mcdb->get_var( $query ) ) );
633
+ $diff1 = mc_date( 'Y' ) - $year1;
634
+ $past = $diff1;
635
+ /**
636
+ * How many years into the future should be shown in the navigation jumpbox. Default '5'.
637
+ *
638
+ * @hook mc_jumpbox_future_years
639
+ *
640
+ * @param {int} $future Number of years ahead.
641
+ * @param {string} $cid Current calendar ID. '' when running in the shortcode generator.
642
+ *
643
+ * @return {int}
644
+ */
645
+ $future = apply_filters( 'mc_jumpbox_future_years', 5, '' );
646
  $fut = 1;
647
  $f = '';
648
  $p = '';
649
  while ( $past > 0 ) {
650
  $p .= '<option value="';
651
+ $p .= (int) current_time( 'Y' ) - $past;
652
  $p .= '">';
653
+ $p .= (int) current_time( 'Y' ) - $past . "</option>\n";
654
  $past = $past - 1;
655
  }
656
  while ( $fut < $future ) {
657
  $f .= '<option value="';
658
+ $f .= (int) current_time( 'Y' ) + $fut;
659
  $f .= '">';
660
+ $f .= (int) current_time( 'Y' ) + $fut . "</option>\n";
661
  $fut = $fut + 1;
662
  }
663
  echo wp_kses( $p . '<option value="' . current_time( 'Y' ) . '"' . selected( current_time( 'Y' ), $year ) . '>' . current_time( 'Y' ) . "</option>\n" . $f, mc_kses_elements() );
710
  <label for="skip"><?php esc_html_e( 'Events/Days to Skip', 'my-calendar' ); ?></label>
711
  <input type="number" name="skip" id="skip" value="" />
712
  </p>
713
+ <p class="checkbox">
 
714
  <input type="checkbox" name="show_today" id="show_today" value="yes"/>
715
+ <label for="show_today"><?php esc_html_e( "Show Today's Events", 'my-calendar' ); ?></label>
716
  </p>
717
+ <p class="checkbox">
 
718
  <input type="checkbox" name="show_recurring" id="show_recurring" value="no" />
719
+ <label for="show_recurring"><?php esc_html_e( 'Show only the first recurring event in a series', 'my-calendar' ); ?></label>
720
  </p>
721
  <p>
722
  <label for="type"><?php esc_html_e( 'Type of Upcoming Events List', 'my-calendar' ); ?></label>
my-calendar-styles.php CHANGED
@@ -30,7 +30,7 @@ function my_calendar_style_edit() {
30
  if ( isset( $_POST['mc_edit_style'] ) || isset( $_POST['mc_reset_style'] ) ) {
31
  $nonce = $_REQUEST['_wpnonce'];
32
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
33
- die( 'Security check failed' );
34
  }
35
  if ( isset( $_POST['mc_reset_style'] ) ) {
36
  if ( ! empty( $_POST['reset_styles'] ) ) {
@@ -96,7 +96,7 @@ function my_calendar_style_edit() {
96
  if ( isset( $_POST['mc_choose_style'] ) ) {
97
  $nonce = $_REQUEST['_wpnonce'];
98
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
99
- die( 'Security check failed' );
100
  }
101
  $mc_css_file = stripcslashes( $_POST['mc_css_file'] );
102
 
@@ -231,7 +231,7 @@ function my_calendar_style_edit() {
231
  * @return string
232
  */
233
  function mc_display_contrast_variables() {
234
- $styles = get_option( 'mc_style_vars' );
235
  $comp = $styles;
236
  $body = '';
237
  $head = '<th>' . __( 'Variable', 'my-calendar' ) . '</th>';
@@ -336,8 +336,8 @@ function mc_stylesheet_selector() {
336
  /**
337
  * Get path for given filename or current selected stylesheet.
338
  *
339
- * @param string $filename File name.
340
- * @param string $type path or url.
341
  *
342
  * @return mixed string/boolean
343
  */
@@ -371,10 +371,10 @@ function mc_get_style_path( $filename = false, $type = 'path' ) {
371
  /**
372
  * Fetch the styles for the current selected style
373
  *
374
- * @param string $filename File name.
375
- * @param string $return content or filename.
376
  *
377
- * @return string
378
  */
379
  function mc_default_style( $filename = false, $return = 'content' ) {
380
  if ( ! $filename ) {
@@ -391,13 +391,10 @@ function mc_default_style( $filename = false, $return = 'content' ) {
391
  switch ( $return ) {
392
  case 'content':
393
  return $mc_current_style;
394
- break;
395
  case 'path':
396
  return $mc_current_file;
397
- break;
398
  case 'both':
399
  return array( $mc_current_file, $mc_current_style );
400
- break;
401
  }
402
  }
403
 
@@ -443,17 +440,18 @@ function mc_write_styles( $file, $style ) {
443
  return false;
444
  }
445
 
446
- $standard = dirname( __FILE__ ) . '/styles/';
447
- $files = mc_css_list( $standard );
 
448
  foreach ( $files as $f ) {
449
  $filepath = mc_get_style_path( $f );
450
  $path = pathinfo( $filepath );
451
  if ( 'css' === $path['extension'] ) {
452
- $styles_whitelist[] = $filepath;
453
  }
454
  }
455
 
456
- if ( in_array( $file, $styles_whitelist, true ) ) {
457
  if ( function_exists( 'wp_is_writable' ) ) {
458
  $is_writable = wp_is_writable( $file );
459
  } else {
@@ -512,7 +510,7 @@ add_action(
512
  * @param int $b Blue value 1.
513
  * @param int $b2 Blue value 2.
514
  *
515
- * @return luminosity ratio.
516
  */
517
  function mc_luminosity( $r, $r2, $g, $g2, $b, $b2 ) {
518
  $rs_rgb = $r / 255;
@@ -544,11 +542,11 @@ function mc_luminosity( $r, $r2, $g, $g2, $b, $b2 ) {
544
  /**
545
  * Convert an RGB value to a HEX value.
546
  *
547
- * @param int $r Red value.
548
- * @param int $g Green value.
549
- * @param int $b Blue value.
550
  *
551
- * @return Hexadecimal color equivalent.
552
  */
553
  function mc_rgb2hex( $r, $g = - 1, $b = - 1 ) {
554
  if ( is_array( $r ) && sizeof( $r ) === 3 ) {
30
  if ( isset( $_POST['mc_edit_style'] ) || isset( $_POST['mc_reset_style'] ) ) {
31
  $nonce = $_REQUEST['_wpnonce'];
32
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
33
+ wp_die( 'My Calendar: Security check failed' );
34
  }
35
  if ( isset( $_POST['mc_reset_style'] ) ) {
36
  if ( ! empty( $_POST['reset_styles'] ) ) {
96
  if ( isset( $_POST['mc_choose_style'] ) ) {
97
  $nonce = $_REQUEST['_wpnonce'];
98
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
99
+ wp_die( 'My Calendar: Security check failed' );
100
  }
101
  $mc_css_file = stripcslashes( $_POST['mc_css_file'] );
102
 
231
  * @return string
232
  */
233
  function mc_display_contrast_variables() {
234
+ $styles = get_option( 'mc_style_vars', array() );
235
  $comp = $styles;
236
  $body = '';
237
  $head = '<th>' . __( 'Variable', 'my-calendar' ) . '</th>';
336
  /**
337
  * Get path for given filename or current selected stylesheet.
338
  *
339
+ * @param string|false $filename File name or false for current selection.
340
+ * @param string $type path or url.
341
  *
342
  * @return mixed string/boolean
343
  */
371
  /**
372
  * Fetch the styles for the current selected style
373
  *
374
+ * @param string|false $filename File name or false to return defined default stylesheet.
375
+ * @param string $return content, filename, or both.
376
  *
377
+ * @return string|array File name, file content, or array with both.
378
  */
379
  function mc_default_style( $filename = false, $return = 'content' ) {
380
  if ( ! $filename ) {
391
  switch ( $return ) {
392
  case 'content':
393
  return $mc_current_style;
 
394
  case 'path':
395
  return $mc_current_file;
 
396
  case 'both':
397
  return array( $mc_current_file, $mc_current_style );
 
398
  }
399
  }
400
 
440
  return false;
441
  }
442
 
443
+ $standard = dirname( __FILE__ ) . '/styles/';
444
+ $files = mc_css_list( $standard );
445
+ $accepted_styles = array();
446
  foreach ( $files as $f ) {
447
  $filepath = mc_get_style_path( $f );
448
  $path = pathinfo( $filepath );
449
  if ( 'css' === $path['extension'] ) {
450
+ $accepted_styles[] = $filepath;
451
  }
452
  }
453
 
454
+ if ( in_array( $file, $accepted_styles, true ) ) {
455
  if ( function_exists( 'wp_is_writable' ) ) {
456
  $is_writable = wp_is_writable( $file );
457
  } else {
510
  * @param int $b Blue value 1.
511
  * @param int $b2 Blue value 2.
512
  *
513
+ * @return float luminosity ratio between 1.0 and 21.0.
514
  */
515
  function mc_luminosity( $r, $r2, $g, $g2, $b, $b2 ) {
516
  $rs_rgb = $r / 255;
542
  /**
543
  * Convert an RGB value to a HEX value.
544
  *
545
+ * @param int|array $r Red value or array with r, g, b keys.
546
+ * @param int $g Green value.
547
+ * @param int $b Blue value.
548
  *
549
+ * @return string Hexadecimal color equivalent of passed RGB value.
550
  */
551
  function mc_rgb2hex( $r, $g = - 1, $b = - 1 ) {
552
  if ( is_array( $r ) && sizeof( $r ) === 3 ) {
my-calendar-templates.php CHANGED
@@ -34,7 +34,7 @@ function mc_draw_template( $array, $template, $type = 'list', $event = false ) {
34
  return '';
35
  }
36
  foreach ( $array as $key => $value ) {
37
- if ( is_object( $value ) && ! empty( $value ) ) {
38
  // If a value is an object, ignore it.
39
  } else {
40
  if ( strpos( $template, '{' . $key ) !== false ) {
@@ -73,6 +73,18 @@ function mc_draw_template( $array, $template, $type = 'list', $event = false ) {
73
  // End {$key check.
74
  }
75
  }
 
 
 
 
 
 
 
 
 
 
 
 
76
  $template = apply_filters( 'mc_template', $template, $array, $type, $event );
77
 
78
  return stripslashes( trim( $template ) );
@@ -84,7 +96,7 @@ function mc_draw_template( $array, $template, $type = 'list', $event = false ) {
84
  * @param object $event object containing location properties.
85
  * @param string $source event or location.
86
  *
87
- * @return stringified address info
88
  */
89
  function mc_map_string( $event, $source = 'event' ) {
90
  if ( ! is_object( $event ) ) {
@@ -143,8 +155,29 @@ function mc_maplink( $event, $request = 'map', $source = 'event' ) {
143
  }
144
  }
145
  // Translators: Name of location.
146
- $label = sprintf( apply_filters( 'mc_map_label', __( 'Map<span> to %s</span>', 'my-calendar' ), $event ), $map_label );
 
 
 
 
 
 
 
 
 
 
 
147
  if ( strlen( trim( $map_string ) ) > 6 ) {
 
 
 
 
 
 
 
 
 
 
148
  $map_url = apply_filters( 'mc_map_url', "http://maps.google.com/maps?z=$zoom&amp;daddr=$map_string", $event );
149
  $map = '<a href="' . esc_url( $map_url ) . '" class="map-link external">' . $label . '</a>';
150
  } elseif ( esc_url( $url ) ) {
@@ -178,10 +211,30 @@ function mc_google_cal( $dtstart, $dtend, $url, $title, $location, $description
178
  $base = "&dates=$dtstart/$dtend";
179
  $base .= '&sprop=website:' . $url;
180
  $base .= '&text=' . urlencode( $title );
181
- $base .= apply_filters( 'mc_gcal_location', '&location=' . urlencode( trim( $location ) ), $location );
182
- $base .= '&sprop=name:' . urlencode( get_bloginfo( 'name' ) );
183
- $base .= apply_filters( 'mc_gcal_description', '&details=' . urlencode( stripcslashes( trim( $description ) ) ), $description );
184
- $base .= '&sf=true&output=xml';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
 
186
  return $source . $base;
187
  }
@@ -240,11 +293,13 @@ function mc_hcard( $event, $address = 'true', $map = 'true', $source = 'event' )
240
  /**
241
  * Filter link to location-specific events in hcard.
242
  *
243
- * @param string $events HTML link to location permalink.
244
- * @param object $post Location post object.
245
- * @param object $event Object being mapped.
 
 
246
  *
247
- * @return string
248
  */
249
  $events = apply_filters( 'mc_location_events_link', $events, $post, $event );
250
  $hcard = '<div class="address location vcard">';
@@ -272,6 +327,19 @@ function mc_hcard( $event, $address = 'true', $map = 'true', $source = 'event' )
272
  $hcard .= '</div>';
273
  $hcard = ( ( false !== $the_map && 'true' === $map ) || ( '' !== $link && 'true' === $address ) ) ? $hcard : '';
274
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  return apply_filters( 'mc_hcard', $hcard, $event, $address, $map, $source );
276
  }
277
 
@@ -285,18 +353,46 @@ function mc_hcard( $event, $address = 'true', $map = 'true', $source = 'event' )
285
  */
286
  function mc_create_tags( $event, $context = 'filters' ) {
287
  if ( ! is_object( $event ) ) {
288
- return;
289
  }
 
 
 
 
 
 
 
 
290
  do_action( 'mc_create_tags', $event, $context );
291
  $calendar_id = '';
292
  if ( 'filters' !== $context && 'related' !== $context ) {
293
  $calendar_id = $context;
294
  }
295
- $site = ( isset( $event->site_id ) ) ? $event->site_id : false;
296
- $e = array();
297
- $e['post'] = $event->event_post;
298
- $date_format = mc_date_format();
299
- $e = apply_filters( 'mc_insert_author_data', $e, $event );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300
  $e = apply_filters( 'mc_filter_image_data', $e, $event );
301
  $sitelink_html = "<div class='url link'><a href='" . esc_url( $event->event_url ) . "' class='location-link external'>";
302
 
@@ -312,8 +408,29 @@ function mc_create_tags( $event, $context = 'filters' ) {
312
  $real_begin_date = ( isset( $event->occur_begin ) ) ? $event->occur_begin : $event->event_begin . ' ' . $event->event_time;
313
  $dtstart = mc_format_timestamp( strtotime( $real_begin_date ), $context );
314
  $dtend = mc_format_timestamp( strtotime( $real_end_date ), $context );
315
-
316
- $e['date_utc'] = date_i18n( apply_filters( 'mc_date_format', $date_format, 'template_begin_ts' ), $event->ts_occur_begin );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
317
  $e['date_end_utc'] = date_i18n( apply_filters( 'mc_date_format', $date_format, 'template_end_ts' ), $event->ts_occur_end );
318
  $notime = esc_html( mc_notime_label( $event ) );
319
  $e['time'] = ( '00:00:00' === mc_date( 'H:i:s', strtotime( $real_begin_date ), false ) ) ? $notime : mc_date( mc_time_format(), strtotime( $real_begin_date ), false );
@@ -326,13 +443,33 @@ function mc_create_tags( $event, $context = 'filters' ) {
326
  $e['dtend'] = mc_date( 'Y-m-d\TH:i:s', strtotime( $real_end_date ), false ); // Date: hcal formatted end.
327
  $e['userstart'] = '<time class="mc-user-time" data-label="' . __( 'Local time:', 'my-calendar' ) . '">' . mc_date( 'Y-m-d\TH:i:s\Z', $event->ts_occur_begin, false ) . '</time>';
328
  $e['userend'] = '<time class="mc-user-time" data-label="' . __( 'Local time:', 'my-calendar' ) . '">' . mc_date( 'Y-m-d\TH:i:s\Z', $event->ts_occur_end, false ) . '</time>';
329
- $date = date_i18n( apply_filters( 'mc_date_format', $date_format, 'template_begin' ), strtotime( $real_begin_date ) );
330
- $date_end = date_i18n( apply_filters( 'mc_date_format', $date_format, 'template_end' ), strtotime( $real_end_date ) );
331
- $date_arr = array(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
332
  'occur_begin' => $real_begin_date,
333
  'occur_end' => $real_end_date,
334
  );
335
- $date_obj = (object) $date_arr;
336
  if ( '1' === $event->event_span ) {
337
  $dates = mc_event_date_span( $event->event_group_id, $event->event_span, array( 0 => $date_obj ) );
338
  } else {
@@ -374,7 +511,8 @@ function mc_create_tags( $event, $context = 'filters' ) {
374
  $e['event_status'] = ( 1 === (int) $event->event_approved ) ? __( 'Published', 'my-calendar' ) : __( 'Draft', 'my-calendar' );
375
 
376
  // General text fields.
377
- $e['title'] = stripslashes( $event->event_title );
 
378
  $e['description'] = wpautop( stripslashes( $event->event_desc ) );
379
  $e['description_raw'] = stripslashes( $event->event_desc );
380
  $e['description_stripped'] = strip_tags( stripslashes( $event->event_desc ) );
@@ -387,9 +525,19 @@ function mc_create_tags( $event, $context = 'filters' ) {
387
  $e['event_registration'] = stripslashes( wp_kses_data( $event->event_registration ) );
388
 
389
  // Links.
390
- $templates = get_option( 'mc_templates' );
391
- $e_template = ( ! empty( $templates['label'] ) ) ? stripcslashes( $templates['label'] ) : __( 'Details about', 'my-calendar' ) . ' {title}';
392
- $e_template = apply_filters( 'mc_details_template', $e_template );
 
 
 
 
 
 
 
 
 
 
393
  $tags = array( '{title}', '{location}', '{color}', '{icon}', '{date}', '{time}' );
394
  $replacements = array(
395
  stripslashes( $e['title'] ),
@@ -429,6 +577,16 @@ function mc_create_tags( $event, $context = 'filters' ) {
429
  $e['linking_title'] = ( '' !== $e['linking'] ) ? "<a href='" . esc_url( $e['linking'] ) . "' $rel>" . $e['title'] . '</a>' : $e['title'];
430
 
431
  if ( 'related' !== $context && ( mc_is_single_event() ) ) {
 
 
 
 
 
 
 
 
 
 
432
  $related_template = apply_filters( 'mc_related_template', '{date}, {time}', $event );
433
  $e['related'] = '<ul class="related-events">' . mc_list_group( $event->event_group_id, $event->event_id, $related_template ) . '</ul>';
434
  } else {
@@ -441,14 +599,34 @@ function mc_create_tags( $event, $context = 'filters' ) {
441
  if ( property_exists( $event, 'location' ) ) {
442
  $location = $event->location;
443
  if ( is_object( $location ) ) {
444
- $map = mc_maplink( $location, 'map', 'location' );
445
- $map_url = mc_maplink( $location, 'url', 'location' );
446
- $map_gcal = mc_maplink( $location, 'gcal', 'location' );
447
- $e['location'] = stripslashes( $location->location_label );
448
- $e['street'] = stripslashes( $location->location_street );
449
- $e['street2'] = stripslashes( $location->location_street2 );
450
- $e['phone'] = apply_filters( 'mc_phone_format', stripslashes( $location->location_phone ) );
451
- $e['phone2'] = apply_filters( 'mc_phone_format', stripslashes( $location->location_phone2 ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
452
  $e['city'] = stripslashes( $location->location_city );
453
  $e['state'] = stripslashes( $location->location_state );
454
  $e['postcode'] = stripslashes( $location->location_postcode );
@@ -462,14 +640,34 @@ function mc_create_tags( $event, $context = 'filters' ) {
462
  $e['ical_location'] = trim( $location->location_label . ' ' . $location->location_street . ' ' . $location->location_street2 . ' ' . $location->location_city . ' ' . $location->location_state . ' ' . $location->location_postcode );
463
  }
464
  } else {
465
- $map = mc_maplink( $event );
466
- $map_url = mc_maplink( $event, 'url' );
467
- $map_gcal = mc_maplink( $event, 'gcal' );
468
- $e['location'] = stripslashes( $event->event_label );
469
- $e['street'] = stripslashes( $event->event_street );
470
- $e['street2'] = stripslashes( $event->event_street2 );
471
- $e['phone'] = apply_filters( 'mc_phone_format', stripslashes( $event->event_phone ) );
472
- $e['phone2'] = apply_filters( 'mc_phone_format', stripslashes( $event->event_phone2 ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
473
  $e['city'] = stripslashes( $event->event_city );
474
  $e['state'] = stripslashes( $event->event_state );
475
  $e['postcode'] = stripslashes( $event->event_postcode );
@@ -496,8 +694,8 @@ function mc_create_tags( $event, $context = 'filters' ) {
496
  // ICAL.
497
  $e['ical_description'] = str_replace( "\r", '=0D=0A=', $event->event_desc );
498
  $e['ical_desc'] = $strip_desc;
499
- $e['ical_start'] = $dtstart;
500
- $e['ical_end'] = ( mc_is_all_day( $event ) ) ? mc_date( 'Ymd\THi00', strtotime( $dtend ) + 60, false ) : $dtend;
501
  $e['ical_recur'] = mc_generate_rrule( $event );
502
  $ical_link = mc_build_url(
503
  array( 'vcal' => $event->occur_id ),
@@ -515,7 +713,26 @@ function mc_create_tags( $event, $context = 'filters' ) {
515
  );
516
  $e['ical'] = $ical_link;
517
  $e['ical_html'] = "<a class='ical' rel='nofollow' href='" . esc_url( $ical_link ) . "' aria-describedby='mc_$event->occur_id-title-$calendar_id'>" . __( 'iCal', 'my-calendar' ) . '</a>';
518
- $e = apply_filters( 'mc_filter_shortcodes', $e, $event );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
519
  do_action( 'mc_tags_created', $event, $context );
520
 
521
  return $e;
@@ -537,13 +754,23 @@ function mc_notime_label( $event ) {
537
  }
538
  $notime = ( '' !== $notime ) ? $notime : $default;
539
 
 
 
 
 
 
 
 
 
 
 
540
  return apply_filters( 'mc_notime_label', $notime, $event );
541
  }
542
 
543
  /**
544
  * Get link to event's details page.
545
  *
546
- * @param mixed object/int $event Full event object or event occurrence ID.
547
  *
548
  * @return string URL.
549
  */
@@ -552,7 +779,7 @@ function mc_get_details_link( $event ) {
552
  $event = mc_get_event( $event );
553
  }
554
  if ( ! is_object( $event ) ) {
555
- return;
556
  }
557
  $restore = false;
558
  if ( is_multisite() && property_exists( $event, 'site_id' ) && get_current_blog_id() !== $event->site_id ) {
@@ -561,7 +788,15 @@ function mc_get_details_link( $event ) {
561
  }
562
  $uri = mc_get_uri( $event );
563
 
564
- // If available, and not querying remotely, use permalink.
 
 
 
 
 
 
 
 
565
  $permalinks = apply_filters( 'mc_use_permalinks', get_option( 'mc_use_permalinks' ) );
566
  $permalinks = ( 1 === $permalinks || true === $permalinks || 'true' === $permalinks ) ? true : false;
567
  $details_link = mc_event_link( $event );
@@ -592,6 +827,16 @@ function mc_get_details_link( $event ) {
592
  );
593
  }
594
  }
 
 
 
 
 
 
 
 
 
 
595
  $details_link = apply_filters( 'mc_customize_details_link', $details_link, $event );
596
 
597
  if ( $restore ) {
@@ -636,7 +881,17 @@ function mc_get_uri( $event = false, $args = array() ) {
636
  if ( ! $uri ) {
637
  $uri = home_url();
638
  }
639
-
 
 
 
 
 
 
 
 
 
 
640
  return apply_filters( 'mc_get_uri', $uri, $event, $args );
641
  }
642
 
@@ -769,6 +1024,16 @@ function mc_event_link( $event ) {
769
  $link = esc_url( $event->event_link );
770
  } else {
771
  if ( $expired ) {
 
 
 
 
 
 
 
 
 
 
772
  $link = apply_filters( 'mc_event_expired_link', '', $event );
773
  } else {
774
  $link = esc_url( $event->event_link );
@@ -788,6 +1053,13 @@ function mc_event_link( $event ) {
788
  function mc_event_expired( $event ) {
789
  if ( is_object( $event ) && property_exists( $event, 'occur_end' ) ) {
790
  if ( my_calendar_date_xcomp( $event->occur_end, current_time( 'Y-m-d' ) ) ) {
 
 
 
 
 
 
 
791
  do_action( 'mc_event_expired', $event );
792
 
793
  return true;
@@ -820,9 +1092,29 @@ function mc_generate_map( $event, $source = 'event', $multiple = false, $geoloca
820
  $markers = '';
821
  $loc_list = '';
822
  $out = '';
823
- $width = apply_filters( 'mc_map_height', '100%', $event );
824
- $height = apply_filters( 'mc_map_height', '300px', $event );
825
- $styles = " style='width: $width;height: $height'";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
826
  // This is an event with a location object property.
827
  if ( 'event' === $source && property_exists( $event, 'location' ) && is_object( $event->location ) ) {
828
  $event = $event->location;
@@ -838,9 +1130,20 @@ function mc_generate_map( $event, $source = 'event', $multiple = false, $geoloca
838
  if ( is_array( $locations ) ) {
839
  $multiple = ( count( $locations ) > 1 ) ? true : false;
840
  foreach ( $locations as $location ) {
841
- $id = rand();
842
- $loc_id = $location->{$source . '_id'};
843
- $source = ( 'event' === $source ) ? 'event' : 'location';
 
 
 
 
 
 
 
 
 
 
 
844
  $category_icon = apply_filters( 'mc_map_icon', '//maps.google.com/mapfiles/marker_green.png', $location, $source );
845
  $address = addslashes( mc_map_string( $location, $source ) );
846
 
@@ -857,9 +1160,9 @@ function mc_generate_map( $event, $source = 'event', $multiple = false, $geoloca
857
  if ( strlen( $address ) < 10 && ! $latlng ) {
858
  return '';
859
  }
860
- $hcard = mc_hcard( $location, 'true', false, $source );
861
- $title = esc_attr( $location->{$source . '_label'} );
862
- $marker = wp_kses(
863
  str_replace(
864
  array( '</div>', '<br />', '<br><br>' ),
865
  '<br>',
@@ -870,16 +1173,35 @@ function mc_generate_map( $event, $source = 'event', $multiple = false, $geoloca
870
  'strong' => array(),
871
  )
872
  );
 
 
 
 
 
 
 
 
 
 
873
  $html = apply_filters( 'mc_map_html', $marker, $location );
874
  $markers .= PHP_EOL . "<div class='marker' data-address='$address' data-title='$title' data-icon='$category_icon' data-lat='$lat' data-lng='$lng'>$html</div>" . PHP_EOL;
875
  $loc_list .= ( $multiple ) ? '<div class="mc-location-details" id="mc-location-' . $id . '-' . $loc_id . '">' . $hcard . '</div>' : '';
876
  }
877
- $class = ( $geolocate ) ? 'mc-geolocated' : 'mc-address';
878
- $map = "<div class='mc-gmap-markers $class' id='mc_gmap_$id' $styles>" . apply_filters( 'mc_gmap_html', $markers, $event ) . '</div>';
879
- $locs = ( $loc_list ) ? '<div class="mc-gmap-location-list"><h2 class="screen-reader-text">' . __( 'Locations', 'my-calendar' ) . '</h2>' . $loc_list . '</div>' : '';
880
- $out = '<div class="mc-maps">' . $map . $locs . '</div>';
881
- } else {
882
- $out = '';
 
 
 
 
 
 
 
 
 
883
  }
884
  }
885
 
@@ -907,6 +1229,16 @@ function mc_expand( $data ) {
907
  $output = ( $output ) ? "<ul class='mc-access'>" . $output . '</ul>' : '';
908
  }
909
 
 
 
 
 
 
 
 
 
 
 
910
  return apply_filters( 'mc_expand', $output, $data );
911
  }
912
 
@@ -917,7 +1249,7 @@ function mc_expand( $data ) {
917
  * @param int $event_span Whether these events constitute one event.
918
  * @param array $dates Start and end dates of current event.
919
  *
920
- * @return string
921
  */
922
  function mc_event_date_span( $group_id, $event_span, $dates = array() ) {
923
  global $wpdb;
@@ -957,9 +1289,30 @@ function mc_format_date_span( $dates, $display = 'simple', $default = '' ) {
957
  $count = count( $dates );
958
  $last = $count - 1;
959
  if ( 'simple' === $display ) {
960
- $begin = $dates[0]->occur_begin;
961
- $end = $dates[ $last ]->occur_end;
962
- $begin = date_i18n( apply_filters( 'mc_date_format', mc_date_format(), 'date_span_begin' ), strtotime( $begin ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
963
  $end = date_i18n( apply_filters( 'mc_date_format', mc_date_format(), 'date_span_end' ), strtotime( $end ) );
964
  $return = $begin . ' <span>&ndash;</span> ' . $end;
965
  } else {
@@ -1002,10 +1355,10 @@ function mc_author_data( $e, $event ) {
1002
  $e['author_id'] = $event->event_author;
1003
  }
1004
  if ( $host ) {
1005
- $e['host'] = ( ! $host || '' === $host->display_name ) ? $author->display_name : $host->display_name;
1006
  $e['host_id'] = $event->event_host;
1007
- $e['host_email'] = ( ! $host || '' === $host->user_email ) ? $author->user_email : $host->user_email;
1008
- $e['host_gravatar'] = ( ! $host || '' === $host->user_email ) ? $e['gravatar'] : get_avatar( $host->user_email );
1009
  }
1010
  } else {
1011
  $e['author'] = 'Public Submitter';
@@ -1035,17 +1388,121 @@ function mc_auto_excerpt( $e, $event ) {
1035
  $shortdesc = $e['shortdesc'];
1036
  $excerpt = '';
1037
  if ( '' !== $description && '' === $shortdesc ) { // if description is empty, this won't work, so skip it.
 
 
 
 
 
 
 
 
 
1038
  $num_words = apply_filters( 'mc_excerpt_length', 55 );
1039
  $excerpt = wp_trim_words( $description, $num_words );
1040
  } else {
1041
  $excerpt = $shortdesc;
1042
  }
1043
 
1044
- $e['excerpt'] = $excerpt;
 
1045
 
1046
  return $e;
1047
  }
1048
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1049
  /**
1050
  * Get template for a specific usage.
1051
  *
@@ -1076,6 +1533,15 @@ add_filter( 'mc_filter_image_data', 'mc_image_data', 10, 2 );
1076
  * @return array $e
1077
  */
1078
  function mc_image_data( $e, $event ) {
 
 
 
 
 
 
 
 
 
1079
  $atts = apply_filters( 'mc_post_thumbnail_atts', array( 'class' => 'mc-image' ) );
1080
  if ( isset( $event->event_post ) && is_numeric( $event->event_post ) && get_post_status( $event->event_post ) && has_post_thumbnail( $event->event_post ) ) {
1081
  $e['full'] = get_the_post_thumbnail( $event->event_post );
@@ -1090,6 +1556,15 @@ function mc_image_data( $e, $event ) {
1090
  $e['image_url'] = strip_tags( $e['large'] );
1091
  $e['image'] = $e['large'];
1092
  } else {
 
 
 
 
 
 
 
 
 
1093
  $image_size = apply_filters( 'mc_default_image_size', 'thumbnail' );
1094
  $e['image_url'] = strip_tags( $e[ $image_size ] );
1095
  $e['image'] = $e[ $image_size ];
@@ -1174,6 +1649,16 @@ function mc_event_recur_string( $event, $begin ) {
1174
  $event_recur = '';
1175
  }
1176
 
 
 
 
 
 
 
 
 
 
 
1177
  return apply_filters( 'mc_event_recur_string', $event_recur, $event );
1178
  }
1179
 
@@ -1213,11 +1698,13 @@ function mc_event_schema( $e, $tags = array() ) {
1213
  }
1214
  $schema['location'] = $loc;
1215
  /**
1216
- * Filter event schema data.
1217
  *
1218
- * @param array $schema Schema data.
1219
- * @param object $e Event data.
1220
- * @param array $event Event tag array.
 
 
1221
  *
1222
  * @return array
1223
  */
@@ -1257,10 +1744,12 @@ function mc_location_schema( $location ) {
1257
  );
1258
  }
1259
  /**
1260
- * Filter location schema data.
 
 
1261
  *
1262
- * @param array $schema Schema data.
1263
- * @param object $location Location data.
1264
  *
1265
  * @return array
1266
  */
34
  return '';
35
  }
36
  foreach ( $array as $key => $value ) {
37
+ if ( is_object( $value ) ) {
38
  // If a value is an object, ignore it.
39
  } else {
40
  if ( strpos( $template, '{' . $key ) !== false ) {
73
  // End {$key check.
74
  }
75
  }
76
+ /**
77
+ * Filter a rendered My Calendar template after parsing.
78
+ *
79
+ * @hook mc_template
80
+ *
81
+ * @param {string} $template Formatted HTML output of a template.
82
+ * @param {array} $array Array of arguments passed to template.
83
+ * @param {string} $type Type of view being rendered.
84
+ * @param {object} $event Event object.
85
+ *
86
+ * @return {string} Formatted HTML.
87
+ */
88
  $template = apply_filters( 'mc_template', $template, $array, $type, $event );
89
 
90
  return stripslashes( trim( $template ) );
96
  * @param object $event object containing location properties.
97
  * @param string $source event or location.
98
  *
99
+ * @return string stringified address info
100
  */
101
  function mc_map_string( $event, $source = 'event' ) {
102
  if ( ! is_object( $event ) ) {
155
  }
156
  }
157
  // Translators: Name of location.
158
+ $label = sprintf( __( 'Map<span> to %s</span>', 'my-calendar' ), $map_label );
159
+ /**
160
+ * Label for link to Google Maps for event.
161
+ *
162
+ * @hook mc_map_label
163
+ *
164
+ * @param {string} $label Map to {event name}.
165
+ * @param {string} $map_label Location name.
166
+ *
167
+ * @return {string} Label used inside map link.
168
+ */
169
+ $label = apply_filters( 'mc_map_label', $label, $map_label );
170
  if ( strlen( trim( $map_string ) ) > 6 ) {
171
+ /**
172
+ * Google maps URL.
173
+ *
174
+ * @hook mc_map_url
175
+ *
176
+ * @param {string} $url Link to event location on Google Maps.
177
+ * @param {object} $event Event object.
178
+ *
179
+ * @return {string} Link.
180
+ */
181
  $map_url = apply_filters( 'mc_map_url', "http://maps.google.com/maps?z=$zoom&amp;daddr=$map_string", $event );
182
  $map = '<a href="' . esc_url( $map_url ) . '" class="map-link external">' . $label . '</a>';
183
  } elseif ( esc_url( $url ) ) {
211
  $base = "&dates=$dtstart/$dtend";
212
  $base .= '&sprop=website:' . $url;
213
  $base .= '&text=' . urlencode( $title );
214
+ /**
215
+ * Filter `location` parameter added to Google Calendar link. Default `&location=$location`. Return value needs to be URL encoded.
216
+ *
217
+ * @hook mc_gcal_location
218
+ *
219
+ * @param {string} $param Encoded parameter.
220
+ * @param {string} $location Unencoded original stringified location..
221
+ *
222
+ * @return {string} Encoded parameter.
223
+ */
224
+ $base .= apply_filters( 'mc_gcal_location', '&location=' . urlencode( trim( $location ) ), $location );
225
+ $base .= '&sprop=name:' . urlencode( get_bloginfo( 'name' ) );
226
+ /**
227
+ * Filter `details` parameter added to Google Calendar link. Default `&details=$description`. Return value needs to be URL encoded.
228
+ *
229
+ * @hook mc_gcal_description
230
+ *
231
+ * @param {string} $param Encoded parameter.
232
+ * @param {string} $description Unencoded original description.
233
+ *
234
+ * @return {string} Encoded parameter.
235
+ */
236
+ $base .= apply_filters( 'mc_gcal_description', '&details=' . urlencode( stripcslashes( trim( $description ) ) ), $description );
237
+ $base .= '&sf=true&output=xml';
238
 
239
  return $source . $base;
240
  }
293
  /**
294
  * Filter link to location-specific events in hcard.
295
  *
296
+ * @hook mc_location_events_link
297
+ *
298
+ * @param {string} $events HTML link to location permalink.
299
+ * @param {object} $post Location post object.
300
+ * @param {object} $event Event object being mapped.
301
  *
302
+ * @return {string} Link.
303
  */
304
  $events = apply_filters( 'mc_location_events_link', $events, $post, $event );
305
  $hcard = '<div class="address location vcard">';
327
  $hcard .= '</div>';
328
  $hcard = ( ( false !== $the_map && 'true' === $map ) || ( '' !== $link && 'true' === $address ) ) ? $hcard : '';
329
 
330
+ /**
331
+ * Filter location hcard HTML output.
332
+ *
333
+ * @hook mc_hcard
334
+ *
335
+ * @param {string} $hcard Formatted HTML output.
336
+ * @param {object} $event Event or location object.
337
+ * @param {string} $address 'true' to include the location address on the card.
338
+ * @param {string} $map 'true' to include the map link on the card.
339
+ * @param {string} $source 'event' or 'location'.
340
+ *
341
+ * @return {string} Formatted HTML hcard.
342
+ */
343
  return apply_filters( 'mc_hcard', $hcard, $event, $address, $map, $source );
344
  }
345
 
353
  */
354
  function mc_create_tags( $event, $context = 'filters' ) {
355
  if ( ! is_object( $event ) ) {
356
+ return array();
357
  }
358
+ /**
359
+ * Execute action before tags are created.
360
+ *
361
+ * @hook mc_tags_created
362
+ *
363
+ * @param {object} $object Event object.
364
+ * @param {string} $context Current execution context.
365
+ */
366
  do_action( 'mc_create_tags', $event, $context );
367
  $calendar_id = '';
368
  if ( 'filters' !== $context && 'related' !== $context ) {
369
  $calendar_id = $context;
370
  }
371
+ $site = ( isset( $event->site_id ) ) ? $event->site_id : false;
372
+ $e = array();
373
+ $e['post'] = $event->event_post;
374
+ $date_format = mc_date_format();
375
+ /**
376
+ * Filter template tag array and add author data. Runs before other template tags are created. Use `mc_filter_shortcodes` to modify existing template tags.
377
+ *
378
+ * @hook mc_insert_author_data
379
+ *
380
+ * @param {array} $e Array to hold event template tags.
381
+ * @param {object} $event Event object.
382
+ *
383
+ * @return {array} Template tag array.
384
+ */
385
+ $e = apply_filters( 'mc_insert_author_data', $e, $event );
386
+ /**
387
+ * Filter template tag array and add image data. Runs before other template tags are created. Use `mc_filter_shortcodes` to modify existing template tags.
388
+ *
389
+ * @hook mc_filter_image_data
390
+ *
391
+ * @param {array} $e Array to hold event template tags.
392
+ * @param {object} $event Event object.
393
+ *
394
+ * @return {array} Template tag array.
395
+ */
396
  $e = apply_filters( 'mc_filter_image_data', $e, $event );
397
  $sitelink_html = "<div class='url link'><a href='" . esc_url( $event->event_url ) . "' class='location-link external'>";
398
 
408
  $real_begin_date = ( isset( $event->occur_begin ) ) ? $event->occur_begin : $event->event_begin . ' ' . $event->event_time;
409
  $dtstart = mc_format_timestamp( strtotime( $real_begin_date ), $context );
410
  $dtend = mc_format_timestamp( strtotime( $real_end_date ), $context );
411
+ $recur_start = mc_format_timestamp( strtotime( $event->event_begin . ' ' . $event->event_time ), $context );
412
+ $recur_end = mc_format_timestamp( strtotime( $event->event_end . ' ' . $event->event_endtime ), $context );
413
+ /**
414
+ * Start date format used in 'date_utc'. Default from My Calendar settings.
415
+ *
416
+ * @hook mc_date_format
417
+ *
418
+ * @param {string} $format Date Format in PHP date format.
419
+ * @param {string} $context 'template_begin_ts'.
420
+ *
421
+ * @return {string} Date format.
422
+ */
423
+ $e['date_utc'] = date_i18n( apply_filters( 'mc_date_format', $date_format, 'template_begin_ts' ), $event->ts_occur_begin );
424
+ /**
425
+ * End date format used in 'date_end_utc'. Default from My Calendar settings.
426
+ *
427
+ * @hook mc_date_format
428
+ *
429
+ * @param {string} $format Date Format in PHP date format.
430
+ * @param {string} $context 'template_end_ts'.
431
+ *
432
+ * @return {string} Date format.
433
+ */
434
  $e['date_end_utc'] = date_i18n( apply_filters( 'mc_date_format', $date_format, 'template_end_ts' ), $event->ts_occur_end );
435
  $notime = esc_html( mc_notime_label( $event ) );
436
  $e['time'] = ( '00:00:00' === mc_date( 'H:i:s', strtotime( $real_begin_date ), false ) ) ? $notime : mc_date( mc_time_format(), strtotime( $real_begin_date ), false );
443
  $e['dtend'] = mc_date( 'Y-m-d\TH:i:s', strtotime( $real_end_date ), false ); // Date: hcal formatted end.
444
  $e['userstart'] = '<time class="mc-user-time" data-label="' . __( 'Local time:', 'my-calendar' ) . '">' . mc_date( 'Y-m-d\TH:i:s\Z', $event->ts_occur_begin, false ) . '</time>';
445
  $e['userend'] = '<time class="mc-user-time" data-label="' . __( 'Local time:', 'my-calendar' ) . '">' . mc_date( 'Y-m-d\TH:i:s\Z', $event->ts_occur_end, false ) . '</time>';
446
+ /**
447
+ * Start date format used in 'date' and 'daterange' template tags. Fallback value for `datespan`. Default from My Calendar settings.
448
+ *
449
+ * @hook mc_date_format
450
+ *
451
+ * @param {string} $format Date Format in PHP date format.
452
+ * @param {string} $context 'template_begin'.
453
+ *
454
+ * @return {string} Date format.
455
+ */
456
+ $date = date_i18n( apply_filters( 'mc_date_format', $date_format, 'template_begin' ), strtotime( $real_begin_date ) );
457
+ /**
458
+ * End date format used in 'enddate' and 'daterange' template tags. Default from My Calendar settings.
459
+ *
460
+ * @hook mc_date_format
461
+ *
462
+ * @param {string} $format Date Format in PHP date format.
463
+ * @param {string} $context 'template_end'.
464
+ *
465
+ * @return {string} Date format.
466
+ */
467
+ $date_end = date_i18n( apply_filters( 'mc_date_format', $date_format, 'template_end' ), strtotime( $real_end_date ) );
468
+ $date_arr = array(
469
  'occur_begin' => $real_begin_date,
470
  'occur_end' => $real_end_date,
471
  );
472
+ $date_obj = (object) $date_arr;
473
  if ( '1' === $event->event_span ) {
474
  $dates = mc_event_date_span( $event->event_group_id, $event->event_span, array( 0 => $date_obj ) );
475
  } else {
511
  $e['event_status'] = ( 1 === (int) $event->event_approved ) ? __( 'Published', 'my-calendar' ) : __( 'Draft', 'my-calendar' );
512
 
513
  // General text fields.
514
+ $title = mc_search_highlight( $event->event_title );
515
+ $e['title'] = stripslashes( $title );
516
  $e['description'] = wpautop( stripslashes( $event->event_desc ) );
517
  $e['description_raw'] = stripslashes( $event->event_desc );
518
  $e['description_stripped'] = strip_tags( stripslashes( $event->event_desc ) );
525
  $e['event_registration'] = stripslashes( wp_kses_data( $event->event_registration ) );
526
 
527
  // Links.
528
+ $templates = get_option( 'mc_templates' );
529
+ $e_template = ( ! empty( $templates['label'] ) ) ? stripcslashes( $templates['label'] ) : __( 'Details about', 'my-calendar' ) . ' {title}';
530
+ /**
531
+ * Filter template for the `{details}` output. Default: `Details about {title}`.
532
+ *
533
+ * @hook mc_details_template
534
+ *
535
+ * @param {string} $e_template String with template tags.
536
+ * @param {object} $event Event object.
537
+ *
538
+ * @return {string} Unparsed template.
539
+ */
540
+ $e_template = apply_filters( 'mc_details_template', $e_template, $event );
541
  $tags = array( '{title}', '{location}', '{color}', '{icon}', '{date}', '{time}' );
542
  $replacements = array(
543
  stripslashes( $e['title'] ),
577
  $e['linking_title'] = ( '' !== $e['linking'] ) ? "<a href='" . esc_url( $e['linking'] ) . "' $rel>" . $e['title'] . '</a>' : $e['title'];
578
 
579
  if ( 'related' !== $context && ( mc_is_single_event() ) ) {
580
+ /**
581
+ * HTML format for displaying related events on a single event view. Default `{date}, {time}`.
582
+ *
583
+ * @hook mc_related_template
584
+ *
585
+ * @param {string} $template Template to use to draw a related event.
586
+ * @param {object} $event Event object.
587
+ *
588
+ * @return {string} Unparsed template.
589
+ */
590
  $related_template = apply_filters( 'mc_related_template', '{date}, {time}', $event );
591
  $e['related'] = '<ul class="related-events">' . mc_list_group( $event->event_group_id, $event->event_id, $related_template ) . '</ul>';
592
  } else {
599
  if ( property_exists( $event, 'location' ) ) {
600
  $location = $event->location;
601
  if ( is_object( $location ) ) {
602
+ $map = mc_maplink( $location, 'map', 'location' );
603
+ $map_url = mc_maplink( $location, 'url', 'location' );
604
+ $map_gcal = mc_maplink( $location, 'gcal', 'location' );
605
+ $e['location'] = stripslashes( $location->location_label );
606
+ $e['street'] = stripslashes( $location->location_street );
607
+ $e['street2'] = stripslashes( $location->location_street2 );
608
+ /**
609
+ * Format a phone number for display in template tags.
610
+ *
611
+ * @hook mc_phone_format
612
+ *
613
+ * @param {string} $number Phone number as saved in `$location->location_phone`.
614
+ * @param {string} $context 'phone'.
615
+ *
616
+ * @return {string} Formatted number.
617
+ */
618
+ $e['phone'] = apply_filters( 'mc_phone_format', stripslashes( $location->location_phone ), 'phone' );
619
+ /**
620
+ * Format a phone number for display in template tags.
621
+ *
622
+ * @hook mc_phone_format
623
+ *
624
+ * @param {string} $number Phone number as saved in `$location->location_phone`.
625
+ * @param {string} $context 'phone2'.
626
+ *
627
+ * @return {string} Formatted number.
628
+ */
629
+ $e['phone2'] = apply_filters( 'mc_phone_format', stripslashes( $location->location_phone2 ), 'phone2' );
630
  $e['city'] = stripslashes( $location->location_city );
631
  $e['state'] = stripslashes( $location->location_state );
632
  $e['postcode'] = stripslashes( $location->location_postcode );
640
  $e['ical_location'] = trim( $location->location_label . ' ' . $location->location_street . ' ' . $location->location_street2 . ' ' . $location->location_city . ' ' . $location->location_state . ' ' . $location->location_postcode );
641
  }
642
  } else {
643
+ $map = mc_maplink( $event );
644
+ $map_url = mc_maplink( $event, 'url' );
645
+ $map_gcal = mc_maplink( $event, 'gcal' );
646
+ $e['location'] = stripslashes( $event->event_label );
647
+ $e['street'] = stripslashes( $event->event_street );
648
+ $e['street2'] = stripslashes( $event->event_street2 );
649
+ /**
650
+ * Format a phone number for display in template tags.
651
+ *
652
+ * @hook mc_phone_format
653
+ *
654
+ * @param {string} $number Phone number as saved in `$event->event_phone`.
655
+ * @param {stirng} $context 'phone'.
656
+ *
657
+ * @return {string} Formatted number.
658
+ */
659
+ $e['phone'] = apply_filters( 'mc_phone_format', stripslashes( $event->event_phone ), 'phone' );
660
+ /**
661
+ * Format a phone number for display in template tags.
662
+ *
663
+ * @hook mc_phone_format
664
+ *
665
+ * @param {string} $number Phone number as saved in `$event->event_phone2`.
666
+ * @param {string} $context 'phone2'.
667
+ *
668
+ * @return {string} Formatted number.
669
+ */
670
+ $e['phone2'] = apply_filters( 'mc_phone_format', stripslashes( $event->event_phone2 ), 'phone2' );
671
  $e['city'] = stripslashes( $event->event_city );
672
  $e['state'] = stripslashes( $event->event_state );
673
  $e['postcode'] = stripslashes( $event->event_postcode );
694
  // ICAL.
695
  $e['ical_description'] = str_replace( "\r", '=0D=0A=', $event->event_desc );
696
  $e['ical_desc'] = $strip_desc;
697
+ $e['ical_start'] = $recur_start;
698
+ $e['ical_end'] = ( mc_is_all_day( $event ) ) ? mc_date( 'Ymd\THi00', strtotime( $recur_end ) + 60, false ) : $recur_end;
699
  $e['ical_recur'] = mc_generate_rrule( $event );
700
  $ical_link = mc_build_url(
701
  array( 'vcal' => $event->occur_id ),
713
  );
714
  $e['ical'] = $ical_link;
715
  $e['ical_html'] = "<a class='ical' rel='nofollow' href='" . esc_url( $ical_link ) . "' aria-describedby='mc_$event->occur_id-title-$calendar_id'>" . __( 'iCal', 'my-calendar' ) . '</a>';
716
+
717
+ /**
718
+ * Filter all template tags after generation.
719
+ *
720
+ * @hook mc_filter_shortcodes
721
+ *
722
+ * @param {array} $e Array of values to be used in template tags.
723
+ * @param {object} $event Event object.
724
+ *
725
+ * @return {array} Array of template tags.
726
+ */
727
+ $e = apply_filters( 'mc_filter_shortcodes', $e, $event );
728
+ /**
729
+ * Execute action when tags are created.
730
+ *
731
+ * @hook mc_tags_created
732
+ *
733
+ * @param {object} $object Event object.
734
+ * @param {string} $context Current execution context.
735
+ */
736
  do_action( 'mc_tags_created', $event, $context );
737
 
738
  return $e;
754
  }
755
  $notime = ( '' !== $notime ) ? $notime : $default;
756
 
757
+ /**
758
+ * Label to use in place of time for an event with no fixed time.
759
+ *
760
+ * @hook mc_notime_label
761
+ *
762
+ * @param {string} $notime Default value from settings or event post meta.
763
+ * @param {object} $event Event object.
764
+ *
765
+ * @return {string} Text describing when the event occurs. E.g. 'all day' or 'to be determined'.
766
+ */
767
  return apply_filters( 'mc_notime_label', $notime, $event );
768
  }
769
 
770
  /**
771
  * Get link to event's details page.
772
  *
773
+ * @param object|int $event Full event object or event occurrence ID.
774
  *
775
  * @return string URL.
776
  */
779
  $event = mc_get_event( $event );
780
  }
781
  if ( ! is_object( $event ) ) {
782
+ return '';
783
  }
784
  $restore = false;
785
  if ( is_multisite() && property_exists( $event, 'site_id' ) && get_current_blog_id() !== $event->site_id ) {
788
  }
789
  $uri = mc_get_uri( $event );
790
 
791
+ /**
792
+ * Check whether permalinks are enabled.
793
+ *
794
+ * @hook mc_use_permalinks
795
+ *
796
+ * @param {string} $option Value of mc_use_permalinks setting.
797
+ *
798
+ * @return {bool} True value if permalinks are enabled.
799
+ */
800
  $permalinks = apply_filters( 'mc_use_permalinks', get_option( 'mc_use_permalinks' ) );
801
  $permalinks = ( 1 === $permalinks || true === $permalinks || 'true' === $permalinks ) ? true : false;
802
  $details_link = mc_event_link( $event );
827
  );
828
  }
829
  }
830
+ /**
831
+ * URL to an event's permalink page.
832
+ *
833
+ * @hook mc_customize_details_link
834
+ *
835
+ * @param {string} $details_link Link to event details page/permalink.
836
+ * @param {object} $event Event object.
837
+ *
838
+ * @return {string} URL.
839
+ */
840
  $details_link = apply_filters( 'mc_customize_details_link', $details_link, $event );
841
 
842
  if ( $restore ) {
881
  if ( ! $uri ) {
882
  $uri = home_url();
883
  }
884
+ /**
885
+ * Link to the My Calendar main calendar view.
886
+ *
887
+ * @hook mc_get_uri
888
+ *
889
+ * @param {string} $link String to return if event link is expired.
890
+ * @param {object} $event Event object.
891
+ * @param {array} $args Current view arguments. (Optional).
892
+ *
893
+ * @return {string} URL.
894
+ */
895
  return apply_filters( 'mc_get_uri', $uri, $event, $args );
896
  }
897
 
1024
  $link = esc_url( $event->event_link );
1025
  } else {
1026
  if ( $expired ) {
1027
+ /**
1028
+ * Link to return if an event's link has expired. Default empty string.
1029
+ *
1030
+ * @hook mc_event_expired_link
1031
+ *
1032
+ * @param {string} $link String to return if event link is expired.
1033
+ * @param {object} $event Event object.
1034
+ *
1035
+ * @return {string} Link or empty string.
1036
+ */
1037
  $link = apply_filters( 'mc_event_expired_link', '', $event );
1038
  } else {
1039
  $link = esc_url( $event->event_link );
1053
  function mc_event_expired( $event ) {
1054
  if ( is_object( $event ) && property_exists( $event, 'occur_end' ) ) {
1055
  if ( my_calendar_date_xcomp( $event->occur_end, current_time( 'Y-m-d' ) ) ) {
1056
+ /**
1057
+ * Execute action once an event is over.
1058
+ *
1059
+ * @hook mc_event_expired
1060
+ *
1061
+ * @param {object} $object Event object.
1062
+ */
1063
  do_action( 'mc_event_expired', $event );
1064
 
1065
  return true;
1092
  $markers = '';
1093
  $loc_list = '';
1094
  $out = '';
1095
+ /**
1096
+ * Default map width. Default value `100%`.
1097
+ *
1098
+ * @hook mc_map_width
1099
+ *
1100
+ * @param {string} $width Width parameter passed to map container style attribute.
1101
+ * @param {object} $event Event or location object containing location information.
1102
+ *
1103
+ * @return {string} Value.
1104
+ */
1105
+ $width = apply_filters( 'mc_map_width', '100%', $event );
1106
+ /**
1107
+ * Default map height. Default value `300px`.
1108
+ *
1109
+ * @hook mc_map_height
1110
+ *
1111
+ * @param {string} $height Height parameter passed to map container style attribute.
1112
+ * @param {object} $event Event or location object containing location information.
1113
+ *
1114
+ * @return {string} Value.
1115
+ */
1116
+ $height = apply_filters( 'mc_map_height', '300px', $event );
1117
+ $styles = " style='width: $width;height: $height'";
1118
  // This is an event with a location object property.
1119
  if ( 'event' === $source && property_exists( $event, 'location' ) && is_object( $event->location ) ) {
1120
  $event = $event->location;
1130
  if ( is_array( $locations ) ) {
1131
  $multiple = ( count( $locations ) > 1 ) ? true : false;
1132
  foreach ( $locations as $location ) {
1133
+ $id = rand();
1134
+ $loc_id = $location->{$source . '_id'};
1135
+ $source = ( 'event' === $source ) ? 'event' : 'location';
1136
+ /**
1137
+ * URL to Google Map marker image.
1138
+ *
1139
+ * @hook mc_map_icon
1140
+ *
1141
+ * @param {string} $icon Formatted HTML to be returned.
1142
+ * @param {object} $location Event or location object containing location information.
1143
+ * @param {string} $source Either 'event' or 'location' indicating type of object.
1144
+ *
1145
+ * @return {string} URL to icon.
1146
+ */
1147
  $category_icon = apply_filters( 'mc_map_icon', '//maps.google.com/mapfiles/marker_green.png', $location, $source );
1148
  $address = addslashes( mc_map_string( $location, $source ) );
1149
 
1160
  if ( strlen( $address ) < 10 && ! $latlng ) {
1161
  return '';
1162
  }
1163
+ $hcard = mc_hcard( $location, 'true', false, $source );
1164
+ $title = esc_attr( $location->{$source . '_label'} );
1165
+ $marker = wp_kses(
1166
  str_replace(
1167
  array( '</div>', '<br />', '<br><br>' ),
1168
  '<br>',
1173
  'strong' => array(),
1174
  )
1175
  );
1176
+ /**
1177
+ * Source HTML for a single location marker.
1178
+ *
1179
+ * @hook mc_map_html
1180
+ *
1181
+ * @param {string} $marker Formatted HTML to be returned.
1182
+ * @param {object} $location Event object containing location information.
1183
+ *
1184
+ * @return {string} Formatted HTML to be parsed by Google Maps JS.
1185
+ */
1186
  $html = apply_filters( 'mc_map_html', $marker, $location );
1187
  $markers .= PHP_EOL . "<div class='marker' data-address='$address' data-title='$title' data-icon='$category_icon' data-lat='$lat' data-lng='$lng'>$html</div>" . PHP_EOL;
1188
  $loc_list .= ( $multiple ) ? '<div class="mc-location-details" id="mc-location-' . $id . '-' . $loc_id . '">' . $hcard . '</div>' : '';
1189
  }
1190
+ /**
1191
+ * Source HTML for generating a map of calendar locations.
1192
+ *
1193
+ * @hook mc_gmap_html
1194
+ *
1195
+ * @param {string} $output Formatted HTML to be returned.
1196
+ * @param {object|array} $event Object or array of objects containing one or more objects with location information.
1197
+ *
1198
+ * @return {string} Formatted HTML to be parsed by Google Maps JS.
1199
+ */
1200
+ $markers = apply_filters( 'mc_gmap_html', $markers, $event );
1201
+ $class = ( $geolocate ) ? 'mc-geolocated' : 'mc-address';
1202
+ $map = "<div class='mc-gmap-markers $class' id='mc_gmap_$id' $styles>" . $markers . '</div>';
1203
+ $locs = ( $loc_list ) ? '<div class="mc-gmap-location-list"><h2 class="screen-reader-text">' . __( 'Locations', 'my-calendar' ) . '</h2>' . $loc_list . '</div>' : '';
1204
+ $out = '<div class="mc-maps">' . $map . $locs . '</div>';
1205
  }
1206
  }
1207
 
1229
  $output = ( $output ) ? "<ul class='mc-access'>" . $output . '</ul>' : '';
1230
  }
1231
 
1232
+ /**
1233
+ * HTML output from an internal data array, e.g. accessibility features.
1234
+ *
1235
+ * @hook mc_expand
1236
+ *
1237
+ * @param {string} $output Formatted HTML to be returned.
1238
+ * @param {array} $data Array of data being parsed.
1239
+ *
1240
+ * @return {string} Formatted HTML.
1241
+ */
1242
  return apply_filters( 'mc_expand', $output, $data );
1243
  }
1244
 
1249
  * @param int $event_span Whether these events constitute one event.
1250
  * @param array $dates Start and end dates of current event.
1251
  *
1252
+ * @return array
1253
  */
1254
  function mc_event_date_span( $group_id, $event_span, $dates = array() ) {
1255
  global $wpdb;
1289
  $count = count( $dates );
1290
  $last = $count - 1;
1291
  if ( 'simple' === $display ) {
1292
+ $begin = $dates[0]->occur_begin;
1293
+ $end = $dates[ $last ]->occur_end;
1294
+
1295
+ /**
1296
+ * Starting date format used in 'date', 'datespan', and 'multidate' template tags. Default from My Calendar settings.
1297
+ *
1298
+ * @hook mc_date_format
1299
+ *
1300
+ * @param {string} $format Date Format in PHP date format.
1301
+ * @param {string} $context 'date_span_begin'.
1302
+ *
1303
+ * @return {string} Date format.
1304
+ */
1305
+ $begin = date_i18n( apply_filters( 'mc_date_format', mc_date_format(), 'date_span_begin' ), strtotime( $begin ) );
1306
+ /**
1307
+ * End date format used in 'date', 'datespan', and 'multidate' template tags. Default from My Calendar settings.
1308
+ *
1309
+ * @hook mc_date_format
1310
+ *
1311
+ * @param {string} $format Date Format in PHP date format.
1312
+ * @param {string} $context 'date_span_end'.
1313
+ *
1314
+ * @return {string} Date format.
1315
+ */
1316
  $end = date_i18n( apply_filters( 'mc_date_format', mc_date_format(), 'date_span_end' ), strtotime( $end ) );
1317
  $return = $begin . ' <span>&ndash;</span> ' . $end;
1318
  } else {
1355
  $e['author_id'] = $event->event_author;
1356
  }
1357
  if ( $host ) {
1358
+ $e['host'] = ( '' === $host->display_name ) ? $author->display_name : $host->display_name;
1359
  $e['host_id'] = $event->event_host;
1360
+ $e['host_email'] = ( '' === $host->user_email ) ? $author->user_email : $host->user_email;
1361
+ $e['host_gravatar'] = ( '' === $host->user_email ) ? $e['gravatar'] : get_avatar( $host->user_email );
1362
  }
1363
  } else {
1364
  $e['author'] = 'Public Submitter';
1388
  $shortdesc = $e['shortdesc'];
1389
  $excerpt = '';
1390
  if ( '' !== $description && '' === $shortdesc ) { // if description is empty, this won't work, so skip it.
1391
+ /**
1392
+ * Length of My Calendar generated excerpts in words. Default 55.
1393
+ *
1394
+ * @hook mc_excerpt_length
1395
+ *
1396
+ * @param {int} $num_words Number of words to use.
1397
+ *
1398
+ * @return {int}
1399
+ */
1400
  $num_words = apply_filters( 'mc_excerpt_length', 55 );
1401
  $excerpt = wp_trim_words( $description, $num_words );
1402
  } else {
1403
  $excerpt = $shortdesc;
1404
  }
1405
 
1406
+ $e['search_excerpt'] = mc_search_highlight( $description, $shortdesc );
1407
+ $e['excerpt'] = $excerpt;
1408
 
1409
  return $e;
1410
  }
1411
 
1412
+ /**
1413
+ * Generate a string with highlighted search terms.
1414
+ *
1415
+ * @param string $string1 Default highlight text.
1416
+ * @param string $string2 Alternate text to use if first might be blank.
1417
+ * @param string $term Search term.
1418
+ *
1419
+ * @return string
1420
+ */
1421
+ function mc_search_highlight( $string1, $string2 = '', $term = '' ) {
1422
+ $append = '';
1423
+ if ( '' !== $term ) {
1424
+ $append = ' ' . trim( $term );
1425
+ }
1426
+ if ( isset( $_REQUEST['mcs'] ) ) {
1427
+ $term = sanitize_text_field( trim( $_REQUEST['mcs'] ) ) . $append;
1428
+ } else {
1429
+ return $string1;
1430
+ }
1431
+ $terms = explode( ' ', $term );
1432
+ // If neither description nor short, return early.
1433
+ if ( '' === $string1 . $string2 ) {
1434
+ return '';
1435
+ }
1436
+ // If no full description, use short.
1437
+ if ( '' === $string1 ) {
1438
+ $use = $string2;
1439
+ } else {
1440
+ $use = $string1;
1441
+ }
1442
+ $use = wp_strip_all_tags( $use );
1443
+ $length = strlen( $use );
1444
+ $start = 0;
1445
+ if ( $length > 160 ) {
1446
+ foreach ( $terms as $t ) {
1447
+ $positions[] = stripos( $use, $t );
1448
+ }
1449
+ // Use the first term referenced for positioning.
1450
+ sort( $positions );
1451
+ $position = $positions[0];
1452
+ // Search term not found.
1453
+ if ( false === $position ) {
1454
+ return substr( $use, 0, 160 );
1455
+ }
1456
+ if ( 0 === $position ) {
1457
+ $start = 0;
1458
+ } else {
1459
+ $start = ( ( $position - 20 ) > 0 ) ? ( $position - 15 ) : 0;
1460
+ }
1461
+ }
1462
+ $extract = substr( $use, $start, 160 );
1463
+ $ellipsis = '';
1464
+ if ( strlen( $extract ) < $length ) {
1465
+ $ellipsis = '...';
1466
+ // Remove first and last words, which are likely to be partial.
1467
+ $clean = explode( ' ', $extract );
1468
+ unset( $clean[0] );
1469
+ array_pop( $clean );
1470
+ $extract = implode( ' ', $clean );
1471
+ }
1472
+
1473
+ foreach ( $terms as $t ) {
1474
+ $extract = mc_str_replace_word_i( $t, $extract );
1475
+ }
1476
+
1477
+ $excerpt = $ellipsis . $extract . $ellipsis;
1478
+
1479
+ return $excerpt;
1480
+ }
1481
+
1482
+ /**
1483
+ * Search text and wrap search terms.
1484
+ *
1485
+ * @param string $needle Word to find.
1486
+ * @param string $haystack Source text.
1487
+ *
1488
+ * @return string
1489
+ */
1490
+ function mc_str_replace_word_i( $needle, $haystack ) {
1491
+ $keyword = $needle;
1492
+ $needle = str_replace( '/', '\\/', preg_quote( $needle ) ); // allow '/' in keywords.
1493
+ $pattern = "/\b$needle(?!([^<]+)?>)\b/i";
1494
+ $type = 'all';
1495
+ $haystack = preg_replace_callback(
1496
+ $pattern,
1497
+ function( $m ) use ( $type, $keyword ) {
1498
+ return '<strong class="mc_search_term">' . $m[0] . '</strong>';
1499
+ },
1500
+ $haystack
1501
+ );
1502
+
1503
+ return $haystack;
1504
+ }
1505
+
1506
  /**
1507
  * Get template for a specific usage.
1508
  *
1533
  * @return array $e
1534
  */
1535
  function mc_image_data( $e, $event ) {
1536
+ /**
1537
+ * Attributes added to My Calendar event images. Default `array( 'class' => 'mc-image' )`. See `get_the_post_thumbnail()` docs at WordPress.org.
1538
+ *
1539
+ * @hook mc_post_thumbnail_atts
1540
+ *
1541
+ * @param {array} $atts Array of image attributes.
1542
+ *
1543
+ * @return {array} Array of image attributes.
1544
+ */
1545
  $atts = apply_filters( 'mc_post_thumbnail_atts', array( 'class' => 'mc-image' ) );
1546
  if ( isset( $event->event_post ) && is_numeric( $event->event_post ) && get_post_status( $event->event_post ) && has_post_thumbnail( $event->event_post ) ) {
1547
  $e['full'] = get_the_post_thumbnail( $event->event_post );
1556
  $e['image_url'] = strip_tags( $e['large'] );
1557
  $e['image'] = $e['large'];
1558
  } else {
1559
+ /**
1560
+ * Default image size used for event listings when 'large' image not available. Default 'thumbnail'.
1561
+ *
1562
+ * @hook mc_default_image_size
1563
+ *
1564
+ * @param {string} $size Default image size
1565
+ *
1566
+ * @return {string} Image size description key.
1567
+ */
1568
  $image_size = apply_filters( 'mc_default_image_size', 'thumbnail' );
1569
  $e['image_url'] = strip_tags( $e[ $image_size ] );
1570
  $e['image'] = $e[ $image_size ];
1649
  $event_recur = '';
1650
  }
1651
 
1652
+ /**
1653
+ * Text representation of a recurring event pattern.
1654
+ *
1655
+ * @hook mc_event_recur_string
1656
+ *
1657
+ * @param {string} $event_recur Template HTML closing tag.
1658
+ * @param {object} $event Event object.
1659
+ *
1660
+ * @return {string}
1661
+ */
1662
  return apply_filters( 'mc_event_recur_string', $event_recur, $event );
1663
  }
1664
 
1698
  }
1699
  $schema['location'] = $loc;
1700
  /**
1701
+ * Filter JSON/LD event schema data. https://schema.org/event.
1702
  *
1703
+ * @hook mc_event_schema
1704
+ *
1705
+ * @param {array } $schema Schema data.
1706
+ * @param {object} $e Event data.
1707
+ * @param {array} $event Event tag array.
1708
  *
1709
  * @return array
1710
  */
1744
  );
1745
  }
1746
  /**
1747
+ * Filter array used to generate location schema. See https://schema.org/location.
1748
+ *
1749
+ * @hook mc_location_schema
1750
  *
1751
+ * @param {array} $schema Schema data for an event venue.
1752
+ * @param {object} $location My Calendar location object.
1753
  *
1754
  * @return array
1755
  */
my-calendar-templating.php CHANGED
@@ -21,7 +21,7 @@ function mc_templates_do_edit() {
21
  if ( ! empty( $_POST ) ) {
22
  $nonce = $_REQUEST['_wpnonce'];
23
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
24
- die( 'Security check failed' );
25
  }
26
  }
27
  if ( isset( $_POST['mc_template_key'] ) ) {
@@ -309,6 +309,7 @@ function mc_templates_edit() {
309
  */
310
  function mc_get_template_tag_preview( $mc_id, $return = 'array' ) {
311
  $event = ( 'array' === $return ) ? array() : 0;
 
312
  if ( ! isset( $_GET['mc-event'] ) && ! $mc_id ) {
313
  $args = array(
314
  'before' => 1,
21
  if ( ! empty( $_POST ) ) {
22
  $nonce = $_REQUEST['_wpnonce'];
23
  if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
24
+ wp_die( 'My Calendar: Security check failed' );
25
  }
26
  }
27
  if ( isset( $_POST['mc_template_key'] ) ) {
309
  */
310
  function mc_get_template_tag_preview( $mc_id, $return = 'array' ) {
311
  $event = ( 'array' === $return ) ? array() : 0;
312
+ $data = array();
313
  if ( ! isset( $_GET['mc-event'] ) && ! $mc_id ) {
314
  $args = array(
315
  'before' => 1,
my-calendar-upgrade-db.php CHANGED
@@ -72,17 +72,14 @@ function my_calendar_check_db() {
72
  */
73
  function mc_upgrade_db() {
74
  $globals = mc_globals();
75
- foreach ( $globals as $key => $global ) {
76
- ${$key} = $global;
77
- }
78
  global $mc_version;
79
 
80
  require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
81
- dbDelta( $initial_db );
82
- dbDelta( $initial_occur_db );
83
- dbDelta( $initial_cat_db );
84
- dbDelta( $initial_rel_db );
85
- dbDelta( $initial_loc_db );
86
- dbDelta( $initial_loc_rel_db );
87
  update_option( 'mc_db_version', $mc_version );
88
  }
72
  */
73
  function mc_upgrade_db() {
74
  $globals = mc_globals();
 
 
 
75
  global $mc_version;
76
 
77
  require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
78
+ dbDelta( $globals['initial_db'] );
79
+ dbDelta( $globals['initial_occur_db'] );
80
+ dbDelta( $globals['initial_cat_db'] );
81
+ dbDelta( $globals['initial_rel_db'] );
82
+ dbDelta( $globals['initial_loc_db'] );
83
+ dbDelta( $globals['initial_loc_rel_db'] );
84
  update_option( 'mc_db_version', $mc_version );
85
  }
my-calendar-widgets.php CHANGED
@@ -71,6 +71,17 @@ function my_calendar_upcoming_events( $args ) {
71
  $before = ( '' === $before ) ? 0 : $before;
72
  $category = ( 'default' === $category ) ? '' : $category;
73
 
 
 
 
 
 
 
 
 
 
 
 
74
  // allow reference by file to external template.
75
  if ( '' !== $template && mc_file_exists( $template ) ) {
76
  $template = file_get_contents( mc_get_file( $template ) );
@@ -81,7 +92,6 @@ function my_calendar_upcoming_events( $args ) {
81
  $template = mc_get_custom_template( $template );
82
  }
83
 
84
- $template = apply_filters( 'mc_upcoming_events_template', $template );
85
  $no_event_text = ( '' === $substitute ) ? $defaults['upcoming']['text'] : $substitute;
86
  $lang = ( $switched ) ? ' lang="' . esc_attr( $switched ) . '"' : '';
87
  $header = "<ul id='upcoming-events-$hash' class='upcoming-events'$lang>";
@@ -154,10 +164,30 @@ function my_calendar_upcoming_events( $args ) {
154
  $from = mc_date( 'Y-1-1' );
155
  $to = mc_date( 'Y-12-31' );
156
  }
 
 
 
 
 
 
 
 
 
 
157
  $from = apply_filters( 'mc_upcoming_date_from', $from, $args );
158
- $to = apply_filters( 'mc_upcoming_date_to', $to, $args );
159
-
160
- $query = array(
 
 
 
 
 
 
 
 
 
 
161
  'from' => $from,
162
  'to' => $to,
163
  'category' => $category,
@@ -169,6 +199,16 @@ function my_calendar_upcoming_events( $args ) {
169
  'source' => 'upcoming',
170
  'site' => $site,
171
  );
 
 
 
 
 
 
 
 
 
 
172
  $query = apply_filters( 'mc_upcoming_attributes', $query, $args );
173
  $event_array = my_calendar_events( $query );
174
 
@@ -191,7 +231,19 @@ function my_calendar_upcoming_events( $args ) {
191
  $omit = array();
192
  foreach ( reverse_array( $temp_array, true, $order ) as $event ) {
193
  $details = mc_create_tags( $event );
194
- $item = apply_filters( 'mc_draw_upcoming_event', '', $details, $template, $args );
 
 
 
 
 
 
 
 
 
 
 
 
195
  // if an event is a multidate group, only display first found.
196
  if ( in_array( $event->event_group_id, $omit, true ) ) {
197
  continue;
@@ -208,9 +260,32 @@ function my_calendar_upcoming_events( $args ) {
208
  $today = current_time( 'Y-m-d H:i' );
209
  $date = mc_date( 'Y-m-d H:i', strtotime( $details['dtstart'], false ) );
210
  $classes = mc_event_classes( $event, 'upcoming' );
211
-
212
- $prepend = apply_filters( 'mc_event_upcoming_before', "<li class='$classes'>", $classes );
213
- $append = apply_filters( 'mc_event_upcoming_after', '</li>', $classes );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  // Recurring events should only appear once.
215
  if ( ! in_array( $details['dateid'], $skips, true ) ) {
216
  $output .= ( $item === $last_item ) ? '' : $prepend . $item . $append;
@@ -249,7 +324,28 @@ function my_calendar_upcoming_events( $args ) {
249
  }
250
  }
251
  if ( '' !== $output ) {
252
- $output = apply_filters( 'mc_upcoming_events_header', $header ) . $output . apply_filters( 'mc_upcoming_events_footer', $footer );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  $return = mc_run_shortcodes( $output );
254
  } else {
255
  $return = '<div class="no-events-fallback upcoming-events">' . stripcslashes( $no_event_text ) . '</div>';
@@ -371,6 +467,16 @@ function mc_produce_upcoming_events( $events, $template, $type = 'list', $order
371
  }
372
 
373
  if ( 'yes' === $show_today && my_calendar_date_equal( $beginning, $current ) ) {
 
 
 
 
 
 
 
 
 
 
374
  $in_total = apply_filters( 'mc_include_today_in_total', 'yes' ); // count todays events in total.
375
  if ( 'no' !== $in_total ) {
376
  $near_events[] = $e;
@@ -446,19 +552,63 @@ function mc_produce_upcoming_events( $events, $template, $type = 'list', $order
446
  $prepend = '';
447
  $append = '';
448
  }
449
- $prepend = apply_filters( 'mc_event_upcoming_before', $prepend, $classes, '', $date );
450
- $append = apply_filters( 'mc_event_upcoming_after', $append, $classes, '', $date );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
451
 
452
  if ( $i < $skip && 0 !== $skip ) {
453
  $i ++;
454
  } else {
455
  if ( ! in_array( $details['dateid'], $skips, true ) ) {
456
 
 
 
 
 
 
 
 
 
 
 
 
 
457
  $item = apply_filters( 'mc_draw_upcoming_event', '', $details, $template, $type );
458
  if ( '' === $item ) {
459
  $item = mc_draw_template( $details, $template, $type, $event );
460
  }
461
 
 
 
 
 
 
 
 
 
 
 
462
  $output[] = apply_filters( 'mc_event_upcoming', $prepend . $item . $append, $event );
463
  $skips[] = $details['dateid'];
464
  }
@@ -545,7 +695,7 @@ function my_calendar_todays_events( $args ) {
545
  $to = current_time( 'Y-m-d' );
546
  }
547
 
548
- $args = array(
549
  'from' => $from,
550
  'to' => $to,
551
  'category' => $category,
@@ -557,7 +707,17 @@ function my_calendar_todays_events( $args ) {
557
  'source' => 'upcoming',
558
  'site' => $site,
559
  );
560
- $args = apply_filters( 'mc_upcoming_attributes', $args, $params );
 
 
 
 
 
 
 
 
 
 
561
  $events = my_calendar_events( $args );
562
 
563
  $today = ( isset( $events[ $from ] ) ) ? $events[ $from ] : false;
@@ -574,16 +734,58 @@ function my_calendar_todays_events( $args ) {
574
  $ts = $e->ts_occur_begin;
575
  $classes = mc_event_classes( $e, 'today' );
576
 
 
 
 
 
 
 
 
 
 
 
 
577
  $prepend = apply_filters( 'mc_todays_events_before', "<li class='$classes'>", $classes, $category );
578
- $append = apply_filters( 'mc_todays_events_after', '</li>' );
579
-
580
- $item = apply_filters( 'mc_draw_todays_event', '', $event_details, $template );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
581
  if ( '' === $item ) {
582
  $item = mc_draw_template( $event_details, $template, 'list', $e );
583
  }
584
  $todays_events[ $ts ][] = $prepend . $item . $append;
585
  }
586
  }
 
 
 
 
 
 
 
 
 
 
587
  $todays_events = apply_filters( 'mc_event_today', $todays_events, $events );
588
  foreach ( $todays_events as $k => $t ) {
589
  foreach ( $t as $now ) {
@@ -591,7 +793,27 @@ function my_calendar_todays_events( $args ) {
591
  }
592
  }
593
  if ( 0 !== count( $events ) ) {
594
- $return = apply_filters( 'mc_todays_events_header', $header ) . $output . apply_filters( 'mc_todays_events_footer', $footer );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
595
  } else {
596
  $return = '<div class="no-events-fallback todays-events">' . stripcslashes( $no_event_text ) . '</div>';
597
  }
71
  $before = ( '' === $before ) ? 0 : $before;
72
  $category = ( 'default' === $category ) ? '' : $category;
73
 
74
+ /**
75
+ * Pass a custom template to the upcoming events list. Template can either be a template key referencing a stored template or a template pattern using {} template tags.
76
+ *
77
+ * @hook mc_upcoming_events_template
78
+ *
79
+ * @param {string} $template Un-parsed template.
80
+ *
81
+ * @return {string} Template string.
82
+ */
83
+ $template = apply_filters( 'mc_upcoming_events_template', $template );
84
+
85
  // allow reference by file to external template.
86
  if ( '' !== $template && mc_file_exists( $template ) ) {
87
  $template = file_get_contents( mc_get_file( $template ) );
92
  $template = mc_get_custom_template( $template );
93
  }
94
 
 
95
  $no_event_text = ( '' === $substitute ) ? $defaults['upcoming']['text'] : $substitute;
96
  $lang = ( $switched ) ? ' lang="' . esc_attr( $switched ) . '"' : '';
97
  $header = "<ul id='upcoming-events-$hash' class='upcoming-events'$lang>";
164
  $from = mc_date( 'Y-1-1' );
165
  $to = mc_date( 'Y-12-31' );
166
  }
167
+ /**
168
+ * Custom upcoming events date start value for upcoming events lists using date parameters.
169
+ *
170
+ * @hook mc_upcoming_date_from
171
+ *
172
+ * @param {string} $from Starting date for this list of upcoming events in Y-m-d format.
173
+ * @param {array} $args Associative array holding the arguments used to generate this list of upcoming events.
174
+ *
175
+ * @return {string} List starting date.
176
+ */
177
  $from = apply_filters( 'mc_upcoming_date_from', $from, $args );
178
+ /**
179
+ * Custom upcoming events date end value for upcoming events lists using date parameters.
180
+ *
181
+ * @hook mc_upcoming_date_to
182
+ *
183
+ * @param {string} $to Ending date for this list of upcoming events in Y-m-d format.
184
+ * @param {array} $args Associative array holding the arguments used to generate this list of upcoming events.
185
+ *
186
+ * @return {string} List ending date.
187
+ */
188
+ $to = apply_filters( 'mc_upcoming_date_to', $to, $args );
189
+
190
+ $query = array(
191
  'from' => $from,
192
  'to' => $to,
193
  'category' => $category,
199
  'source' => 'upcoming',
200
  'site' => $site,
201
  );
202
+ /**
203
+ * Modify the arguments used to generate upcoming events.
204
+ *
205
+ * @hook mc_upcoming_attributes
206
+ *
207
+ * @param {array} $query All arguments used to generate this list.
208
+ * @param {array} $args Subset of parameters used to generate this list's ID hash.
209
+ *
210
+ * @return {array} Array of event listing arguments.
211
+ */
212
  $query = apply_filters( 'mc_upcoming_attributes', $query, $args );
213
  $event_array = my_calendar_events( $query );
214
 
231
  $omit = array();
232
  foreach ( reverse_array( $temp_array, true, $order ) as $event ) {
233
  $details = mc_create_tags( $event );
234
+ /**
235
+ * Draw a custom template for upcoming events. Returning any non-empty string short circuits other template settings.
236
+ *
237
+ * @hook mc_draw_upcoming_event
238
+ *
239
+ * @param {string} $item Empty string before event template is drawn.
240
+ * @param {array} $details Associative array of event template tags.
241
+ * @param {string} $template Template string passed from widget or shortcode.
242
+ * @param {array} $args Associative array holding the arguments used to generate this list of upcoming events.
243
+ *
244
+ * @return {string} Event template details.
245
+ */
246
+ $item = apply_filters( 'mc_draw_upcoming_event', '', $details, $template, $args );
247
  // if an event is a multidate group, only display first found.
248
  if ( in_array( $event->event_group_id, $omit, true ) ) {
249
  continue;
260
  $today = current_time( 'Y-m-d H:i' );
261
  $date = mc_date( 'Y-m-d H:i', strtotime( $details['dtstart'], false ) );
262
  $classes = mc_event_classes( $event, 'upcoming' );
263
+ $prepend = "<li class='$classes'>";
264
+ $append = '</li>';
265
+ /**
266
+ * Opening elements for upcoming events list items. Default `<li class="$classes">`.
267
+ *
268
+ * @hook mc_event_upcoming_before
269
+ *
270
+ * @param {string} $append Template HTML closing tag.
271
+ * @param {string} $classes Space-separated list of classes for the event.
272
+ * @param {string} $date Event date in Y-m-d H:i:s format.
273
+ *
274
+ * @return {string} HTML following each event in upcoming events lists.
275
+ */
276
+ $prepend = apply_filters( 'mc_event_upcoming_before', $prepend, $classes, $date );
277
+ /**
278
+ * Closing elements for upcoming events list items. Default `</li>`.
279
+ *
280
+ * @hook mc_event_upcoming_after
281
+ *
282
+ * @param {string} $append Template HTML closing tag.
283
+ * @param {string} $classes Space-separated list of classes for the event.
284
+ * @param {string} $date Event date in Y-m-d H:i:s format.
285
+ *
286
+ * @return {string} HTML following each event in upcoming events lists.
287
+ */
288
+ $append = apply_filters( 'mc_event_upcoming_after', $append, $classes, $date );
289
  // Recurring events should only appear once.
290
  if ( ! in_array( $details['dateid'], $skips, true ) ) {
291
  $output .= ( $item === $last_item ) ? '' : $prepend . $item . $append;
324
  }
325
  }
326
  if ( '' !== $output ) {
327
+ /**
328
+ * Replace the list header for upcoming events lists. Default value `<ul id='upcoming-events-$hash' class='upcoming-events'$lang>`.
329
+ *
330
+ * @hook mc_upcoming_events_header
331
+ *
332
+ * @param {string} $header Existing upcoming events header HTML.
333
+ *
334
+ * @return {string} List header HTML.
335
+ */
336
+ $header = apply_filters( 'mc_upcoming_events_header', $header );
337
+ /**
338
+ * Replace the list footer for upcoming events lists. Default value `</ul>`.
339
+ *
340
+ * @hook mc_upcoming_events_footer
341
+ *
342
+ * @param {string} $header Existing upcoming events footer HTML.
343
+ *
344
+ * @return {string} List header HTML.
345
+ */
346
+
347
+ $footer = apply_filters( 'mc_upcoming_events_footer', $footer );
348
+ $output = $header . $output . $footer;
349
  $return = mc_run_shortcodes( $output );
350
  } else {
351
  $return = '<div class="no-events-fallback upcoming-events">' . stripcslashes( $no_event_text ) . '</div>';
467
  }
468
 
469
  if ( 'yes' === $show_today && my_calendar_date_equal( $beginning, $current ) ) {
470
+
471
+ /**
472
+ * Should today's events be counted towards total number of upcoming events. Default `yes`. Any value other than 'no' will be interpreted as 'yes'.
473
+ *
474
+ * @hook mc_include_today_in_total
475
+ *
476
+ * @param {string} $in_total Return 'no' to exclude today's events from event count.
477
+ *
478
+ * @return {string} 'yes' or 'no'.
479
+ */
480
  $in_total = apply_filters( 'mc_include_today_in_total', 'yes' ); // count todays events in total.
481
  if ( 'no' !== $in_total ) {
482
  $near_events[] = $e;
552
  $prepend = '';
553
  $append = '';
554
  }
555
+ /**
556
+ * Opening elements for today's events list items. Default `<li class="$classes">`.
557
+ *
558
+ * @hook mc_event_upcoming_before
559
+ *
560
+ * @param {string} $append Template HTML closing tag.
561
+ * @param {string} $classes Space-separated list of classes for the event.
562
+ * @param {string} $date Event date in Y-m-d H:i:s format.
563
+ *
564
+ * @return {string} HTML following each event in upcoming events lists.
565
+ */
566
+ $prepend = apply_filters( 'mc_event_upcoming_before', $prepend, $classes, $date );
567
+ /**
568
+ * Closing elements for today's events list items. Default `</li>`.
569
+ *
570
+ * @hook mc_event_upcoming_after
571
+ *
572
+ * @param {string} $append Template HTML closing tag.
573
+ * @param {string} $classes Space-separated list of classes for the event.
574
+ * @param {string} $date Event date in Y-m-d H:i:s format.
575
+ *
576
+ * @return {string} HTML following each event in upcoming events lists.
577
+ */
578
+ $append = apply_filters( 'mc_event_upcoming_after', $append, $classes, $date );
579
 
580
  if ( $i < $skip && 0 !== $skip ) {
581
  $i ++;
582
  } else {
583
  if ( ! in_array( $details['dateid'], $skips, true ) ) {
584
 
585
+ /**
586
+ * Draw a custom template for upcoming events. Returning any non-empty string short circuits other template settings.
587
+ *
588
+ * @hook mc_draw_upcoming_event
589
+ *
590
+ * @param {string} $item Empty string before event template is drawn.
591
+ * @param {array} $details Associative array of event template tags.
592
+ * @param {string} $template Template string passed from widget or shortcode.
593
+ * @param {array} $args Associative array holding the arguments used to generate this list of upcoming events.
594
+ *
595
+ * @return {string} Event template details.
596
+ */
597
  $item = apply_filters( 'mc_draw_upcoming_event', '', $details, $template, $type );
598
  if ( '' === $item ) {
599
  $item = mc_draw_template( $details, $template, $type, $event );
600
  }
601
 
602
+ /**
603
+ * Filter upcoming events list item output.
604
+ *
605
+ * @hook mc_event_upcoming
606
+ *
607
+ * @param {string} $item List item HTML for upcoming event.
608
+ * @param {object} $event Event object.
609
+ *
610
+ * @return {array} Array of event listing arguments.
611
+ */
612
  $output[] = apply_filters( 'mc_event_upcoming', $prepend . $item . $append, $event );
613
  $skips[] = $details['dateid'];
614
  }
695
  $to = current_time( 'Y-m-d' );
696
  }
697
 
698
+ $args = array(
699
  'from' => $from,
700
  'to' => $to,
701
  'category' => $category,
707
  'source' => 'upcoming',
708
  'site' => $site,
709
  );
710
+ /**
711
+ * Modify the arguments used to generate today's events.
712
+ *
713
+ * @hook mc_today_attributes
714
+ *
715
+ * @param {array} $args All arguments used to generate this list.
716
+ * @param {array} $params Subset of parameters used to generate this list's ID hash.
717
+ *
718
+ * @return {array} Array of event listing arguments.
719
+ */
720
+ $args = apply_filters( 'mc_today_attributes', $args, $params );
721
  $events = my_calendar_events( $args );
722
 
723
  $today = ( isset( $events[ $from ] ) ) ? $events[ $from ] : false;
734
  $ts = $e->ts_occur_begin;
735
  $classes = mc_event_classes( $e, 'today' );
736
 
737
+ /**
738
+ * Modify the HTML preceding each list item in a list of today's events.
739
+ *
740
+ * @hook mc_todays_events_before
741
+ *
742
+ * @param {string} $item HTML string before each event.
743
+ * @param {string} $classes Space separated list of classes for this event.
744
+ * @param {string} $category Category argument passed to this list.
745
+ *
746
+ * @return {string} HTML preceding each event in today's events lists.
747
+ */
748
  $prepend = apply_filters( 'mc_todays_events_before', "<li class='$classes'>", $classes, $category );
749
+ /**
750
+ * Closing elements for today's events list items. Default `</li>`.
751
+ *
752
+ * @hook mc_todays_events_after
753
+ *
754
+ * @param {string} $item Template HTML closing tag.
755
+ *
756
+ * @return {string} HTML following each event in today's events lists.
757
+ */
758
+ $append = apply_filters( 'mc_todays_events_after', '</li>' );
759
+
760
+ /**
761
+ * Draw a custom template for today's events. Returning any non-empty string short circuits other template settings.
762
+ *
763
+ * @hook mc_draw_todays_event
764
+ *
765
+ * @param {string} $item Empty string before event template is drawn.
766
+ * @param {array} $event_details Associative array of event template tags.
767
+ * @param {string} $template Template string passed from widget or shortcode.
768
+ * @param {array} $args Associative array holding the arguments used to generate this list of events.
769
+ *
770
+ * @return {string} Event output details.
771
+ */
772
+ $item = apply_filters( 'mc_draw_todays_event', '', $event_details, $template, $args );
773
  if ( '' === $item ) {
774
  $item = mc_draw_template( $event_details, $template, 'list', $e );
775
  }
776
  $todays_events[ $ts ][] = $prepend . $item . $append;
777
  }
778
  }
779
+ /**
780
+ * Filter the array of events listed in today's events lists.
781
+ *
782
+ * @hook mc_event_today
783
+ *
784
+ * @param {array} $todays_events A multidimensional array of event items with today's date as a key with an array of formatted HTML on event templates on the current date.
785
+ * @param {array} $events Array of events without private events removed. Values are event objects.
786
+ *
787
+ * @return {array} A multidimensional array of event items with today's date as a key with an array of formatted HTML on event templates on the current date.
788
+ */
789
  $todays_events = apply_filters( 'mc_event_today', $todays_events, $events );
790
  foreach ( $todays_events as $k => $t ) {
791
  foreach ( $t as $now ) {
793
  }
794
  }
795
  if ( 0 !== count( $events ) ) {
796
+ /**
797
+ * Replace the list header for today's events lists. Default value `<ul id='todays-events-$hash' class='todays-events'$lang>`.
798
+ *
799
+ * @hook mc_todays_events_header
800
+ *
801
+ * @param {string} $header Existing today's events header HTML.
802
+ *
803
+ * @return {string} List header HTML.
804
+ */
805
+ $return = apply_filters( 'mc_todays_events_header', $header );
806
+ $return .= $output;
807
+ /**
808
+ * Replace the list footer for today's events lists. Default value `</ul>`.
809
+ *
810
+ * @hook mc_todays_events_header
811
+ *
812
+ * @param {string} $header Existing today's events header HTML.
813
+ *
814
+ * @return {string} List header HTML.
815
+ */
816
+ $return .= apply_filters( 'mc_todays_events_footer', $footer );
817
  } else {
818
  $return = '<div class="no-events-fallback todays-events">' . stripcslashes( $no_event_text ) . '</div>';
819
  }
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.3.16
21
  */
22
 
23
  /*
@@ -42,9 +42,14 @@ if ( ! defined( 'ABSPATH' ) ) {
42
  }
43
 
44
  global $mc_version, $wpdb;
45
- $mc_version = '3.3.16';
46
 
47
  define( 'MC_DEBUG', false );
 
 
 
 
 
48
 
49
  register_activation_hook( __FILE__, 'mc_plugin_activated' );
50
  register_deactivation_hook( __FILE__, 'mc_plugin_deactivated' );
@@ -168,7 +173,7 @@ function mc_load_textdomain() {
168
  // Add actions.
169
  add_action( 'admin_menu', 'my_calendar_menu' );
170
  add_action( 'wp_head', 'mc_head' );
171
- add_action( 'delete_user', 'mc_deal_with_deleted_user' );
172
  add_action( 'widgets_init', 'mc_register_widgets' );
173
  add_action( 'init', 'mc_add_feed' );
174
  add_action( 'wp_footer', 'mc_footer_js' );
@@ -268,17 +273,19 @@ function mc_canonical() {
268
  /**
269
  * Produce My Calendar admin sidebar
270
  *
271
- * @param string $show deprecated.
272
- * @param mixed array/boolean $add boolean or array.
273
- * @param boolean $remove Hide commercial blocks.
274
  */
275
  function mc_show_sidebar( $show = '', $add = false, $remove = false ) {
276
  /**
277
- * Inject a sidebar panel in the My Calendar admin.
 
 
278
  *
279
- * @param array $add Array with headings as keys and content as values.
280
  *
281
- * @return array
282
  */
283
  $add = apply_filters( 'mc_custom_sidebar_panels', $add );
284
 
@@ -452,9 +459,11 @@ function my_calendar_menu() {
452
  /**
453
  * Filter user capability required to use the My Calendar Pro front-end submissions.
454
  *
455
- * @param string $capability A capability string.
 
 
456
  *
457
- * @return string
458
  */
459
  $permission = apply_filters( 'mcs_submission_permissions', $capability );
460
  add_action( 'admin_head', 'my_calendar_sub_js' );
17
  * License: GPL-2.0+
18
  * License URI: http://www.gnu.org/license/gpl-2.0.txt
19
  * Domain Path: lang
20
+ * Version: 3.3.23
21
  */
22
 
23
  /*
42
  }
43
 
44
  global $mc_version, $wpdb;
45
+ $mc_version = '3.3.23';
46
 
47
  define( 'MC_DEBUG', false );
48
+ define( 'MC_DIRECTORY', plugin_dir_path( __FILE__ ) );
49
+ if ( ! class_exists( 'Gamajo_Template_Loader' ) ) {
50
+ require MC_DIRECTORY . 'includes/class-gamajo-template-loader.php';
51
+ }
52
+ require MC_DIRECTORY . 'includes/class-mc-template-loader.php';
53
 
54
  register_activation_hook( __FILE__, 'mc_plugin_activated' );
55
  register_deactivation_hook( __FILE__, 'mc_plugin_deactivated' );
173
  // Add actions.
174
  add_action( 'admin_menu', 'my_calendar_menu' );
175
  add_action( 'wp_head', 'mc_head' );
176
+ add_action( 'delete_user', 'mc_deal_with_deleted_user', 10, 2 );
177
  add_action( 'widgets_init', 'mc_register_widgets' );
178
  add_action( 'init', 'mc_add_feed' );
179
  add_action( 'wp_footer', 'mc_footer_js' );
273
  /**
274
  * Produce My Calendar admin sidebar
275
  *
276
+ * @param string $show deprecated.
277
+ * @param array|boolean $add boolean or array.
278
+ * @param boolean $remove Hide commercial blocks.
279
  */
280
  function mc_show_sidebar( $show = '', $add = false, $remove = false ) {
281
  /**
282
+ * Inject a sidebar panel in the My Calendar admin. Does not replace existing panels.
283
+ *
284
+ * @hook mc_custom_sidebar_panels
285
  *
286
+ * @param {array} $add Associative array with headings as keys and content as values.
287
  *
288
+ * @return {array} Associative array with all extra sidebars.
289
  */
290
  $add = apply_filters( 'mc_custom_sidebar_panels', $add );
291
 
459
  /**
460
  * Filter user capability required to use the My Calendar Pro front-end submissions.
461
  *
462
+ * @hook mcs_submission_permissions
463
+ *
464
+ * @param {string} $capability A string representing a WordPress capability.
465
  *
466
+ * @return {string} A string representing a WordPress capability.
467
  */
468
  $permission = apply_filters( 'mcs_submission_permissions', $capability );
469
  add_action( 'admin_head', 'my_calendar_sub_js' );
readme.txt CHANGED
@@ -6,7 +6,7 @@ Requires at least: 4.4
6
  Tested up to: 6.0
7
  Requires PHP: 7.0
8
  Text domain: my-calendar
9
- Stable tag: 3.3.16
10
  License: GPLv2 or later
11
 
12
  Accessible WordPress event calendar plugin. Show events from multiple calendars on pages, in posts, or in widgets.
@@ -25,24 +25,28 @@ Do you sell tickets for your events? [Use My Tickets](https://wordpress.org/plug
25
 
26
  = Features: =
27
 
28
- * Calendar grid and list views of events
29
  * Monthly, weekly, or daily view.
30
- * Mini-calendar for compact displays (as widget or as shortcode)
31
- * Widgets: today's events, upcoming events, compact calendar, event search
32
- * Custom templates for event output
33
  * Limit views by categories, location, author, or host
34
- * Editable CSS styles and JavaScript behaviors
35
- * Schedule recurring events.
36
- * Edit single occurrences of recurring events
37
  * Rich permissions handling to restrict access to parts of My Calendar
38
- * Email notification to administrator when events are scheduled or reserved
39
  * Post to Twitter when events are created (using [WP to Twitter](http://wordpress.org/extend/plugins/wp-to-twitter/))
40
- * Managing locations
41
  * Fetch events from a remote database. (Sharing events in a network of sites.)
42
  * Multisite-friendly
43
  * Integrated help page
44
  * Shortcode Generator to create customized views of My Calendar
45
 
 
 
 
 
46
  = What's in My Calendar Pro? =
47
 
48
  * Let your site visitors submit events to your site (pay to post or free!).
@@ -84,6 +88,57 @@ Translating my plugins is always appreciated. Visit <a href="https://translate.w
84
 
85
  == Changelog ==
86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  = 3.3.16 =
88
 
89
  * Bug fix: Incorrectly passed list type caused templates to encode html entities.
@@ -335,162 +390,6 @@ Developer Changes:
335
  * Code reorganization.
336
  * PHP 8.0 compatibility.
337
 
338
- = 3.2.19 =
339
-
340
- * Resolve svn problem causing missing files.
341
-
342
- = 3.2.18 =
343
-
344
- * Security: Fixes reflected XSS flaw in admin. Props to @erwinr and WPScan.
345
-
346
- = 3.2.17 =
347
-
348
- * Bug fix: Add parameter for required fields handling to ignore during imports.
349
- * Add filter handling calendar URLs when using Polylang or WPML.
350
-
351
- = 3.2.16 =
352
-
353
- * Bug fix: Check for undefined objects in localization, not for undefined object props.
354
- * Change: Set parameter for location autocomplete switchover to 50 instead of 25 locations.
355
- * Change: Tweak directory removal process slightly.
356
-
357
- = 3.2.15 =
358
-
359
- * Bug fix: Hide event details section if no fields are visible for section.
360
- * Bug fix: Update localization to correct usage of l10n parameter.
361
- * Bug fix: Location AJAX query executed function that only existed in Pro.
362
-
363
- = 3.2.14 =
364
-
365
- * Bug fixes: Misc. type casting issues.
366
- * Add filters `mc_filter_events` to filter results of main event queries.
367
- * Add $args array to `mc_searched_events` filter parameters.
368
- * Avoid running My Calendar core functionality through My Calendar's own hooks.
369
- * When using REST API, variables are not submitted in a POST query.
370
- * [Performance] Move custom location query into object creation to reduce DB calls.
371
- * Use try/catch in mc-ajax.js to handle case where href does not contain a full URL.
372
- * Autocomplete support for locations in admin.
373
- * Reset select elements in My Calendar nav to inline.
374
- * Minor refactoring in settings pages.
375
-
376
- = 3.2.13 =
377
-
378
- * Bug fix: Using embed targets is more complicated than expected; disable by default. Enable with 'mc_use_embed_targets' filter.
379
- * Bug fix: Strip embed from parameters when building links (when embed target enabled.)
380
-
381
- = 3.2.12 =
382
-
383
- * Bug fix: Don't use embed target link when AJAX disabled.
384
- * Improvement: Add AJAX navigation into browser history & address bar.
385
-
386
- = 3.2.11 =
387
-
388
- * Bug fix: switching to week view display broken.
389
- * Bug fix: links to template help pointed to old location for help.
390
- * Bug fix: AJAX nav pulled height from first rendered calendar, not current navigating calendar.
391
- * Change: filter to pass custom notices for front end submissions and editing.
392
- * Remove fallback function for is_ssl()
393
- * Improve conflicting event errors when the conflicting event is still unpublished.
394
- * Add custom template to pass a calendar that's embeddable via iframe.
395
- * Bug fix: Multisite environments need to use navigation on current site, not from remote site.
396
-
397
- = 3.2.10 =
398
-
399
- * Change: Fallback text should have a stylable wrapper.
400
- * Bug fix: Missing translatable string.
401
- * Bug fix: When multiple categories selected, events in more than one category would appear multiple times.
402
- * Bug fix: Missing space in MySQL filters in event manager.
403
- * Bug fix: PHP Notice thrown in location manager.
404
- * Bug fix: Add note to open events link field if no URI configured.
405
- * Layout fix: Ensure there's always a space between date & time.
406
-
407
- = 3.2.9 =
408
-
409
- * Bug fix: Additional of required fields testing erased error messages generated prior to required fields testing.
410
- * Bug fix: If an individual occurrence title is edited, event permalinks show the single change on all events.
411
- * Bug fix: Prev/next event links don't include unique event IDs.
412
- * Bug fix: Remove irrelevant arguments from prev/next event link generation.
413
- * Bug fix: Ignore templates if no data passed.
414
-
415
- = 3.2.8 =
416
-
417
- * Bug fix: Extraneous screen-reader-text summary generated in event views.
418
- * Bug fix: Fixes to missing parameters in Schema.org microdata.
419
- * Bug fix: Incorrect type comparison caused custom templates not to render in single event view.
420
- * New feature: Default location.
421
-
422
- = 3.2.7 =
423
-
424
- * Bug fix: Prevent events from being created without categories.
425
- * Bug fix: Ensure category relationships are deleted when related events are deleted.
426
- * Add handling for seeing & managing events that are invalid.
427
- * Add styles for invalid rows.
428
-
429
- = 3.2.6 =
430
-
431
- * Added filter to change date format on calendar grid.
432
- * New filter for modifying user selection output.
433
- * Bug fix: only check for get_magic_quotes_gpc() if below PHP 7.4
434
- * Bug fix: invalid query in mc_get_locations() if arguments passed as array.
435
-
436
- = 3.2.5 =
437
-
438
- * Bug fix: CSV exported text fields contained newline characters.
439
-
440
- = 3.2.4 =
441
-
442
- * Bug fix: Permissions issue caused by variable type mismatch.
443
-
444
- = 3.2.3 =
445
-
446
- * Bug fix: 3.2.2 created multiple post types with the same slug, triggering 404 errors.
447
- * Bug fix: Templates could return the name of the template if template empty/missing.
448
-
449
- = 3.2.2 =
450
-
451
- * Bug fix: Curly brace offset access deprecated
452
- * Bug fix: Make next/prev post link arguments optional.
453
- * Bug fix: Template queries could return an empty template.
454
- * Change: Remove trashed events from default events list.
455
-
456
- = 3.2.1 =
457
-
458
- * PHP Notice: undefined variable.
459
- * Bug fix: screen options not saving.
460
- * Bug fix: Accidental auto-assigning of first category to events when editing.
461
-
462
- = 3.2.0 =
463
-
464
- * Auto-toggle admin time format if display time format set to European format.
465
- * Show API endpoint when API enabled.
466
- * Add alternate alias for API endpoint.
467
- * Add style variables with category colors to style output.
468
- * Add color output icon with CSS variables in style editor.
469
- * Add new default stylesheet: Twentytwenty.css
470
- * Move permalink setting to general settings panel.
471
- * Change event timestamps to use a real UTC timestamp for reference.
472
- * Switch from using date() to gmdate().
473
- * Update Pickadate to 3.6.4. Resolves some bugs, but introduces an accessibility issue.
474
- * Customizations to Pickadate 3.6.4 to repair accessibility
475
- * don't move focus to picker
476
- * add 'close' button to time picker.
477
- * Switch Pickadate to classic theme (modified).
478
- * Improvements to output code layout.
479
- * Eliminate empty HTML wrappers in content.
480
- * New filter: mc_get_users. Use custom arguments to get users.
481
- * New filters: mc_header_navigation, mc_footer_navigation
482
- * New template tags: {userstart}, {userend} - date/time in local users timezone.
483
- * Bug fix: Misc. ARIA/id relationships broken.
484
- * Bug fix: remote locations sometimes pulled from local database.
485
- * Bug fix: Long-standing issues in user input settings.
486
- * Bug fix: Don't duplicate .summary values.
487
- * Bug fix: Only render one close button in mini calendar.
488
- * Collapse 'View Calendar' and 'Add Event' adminbar menus into a single menu.
489
- * Remove upgrade path from 2.2.10.
490
- * Remove .mc-event-visible from style output. Unused since 2011.
491
- * Remove numerous deprecated functions.
492
- * Conformance with latest WordPress PHPCS ruleset.
493
-
494
  = Future Changes =
495
 
496
  * Refactor options storage
@@ -499,11 +398,11 @@ Developer Changes:
499
 
500
  = Hey! Why don't you have any Frequently Asked Questions here! =
501
 
502
- Because the majority of users end up on my web site asking for help anyway -- and it's simply more work to maintain two copies. Please visit [my web site FAQ](http://www.joedolson.com/my-calendar/faq/) to read my Frequently Asked Questions!
503
 
504
  = This plugin is complicated. Why won't you help me figure out how to use it? =
505
 
506
- I will! But not in person. Take a look at my [documentation website for My Calendar](http://docs.joedolson.com/my-calendar/) before making your request, and consider [making a donation](https://www.joedolson.com/donate/) or [buying My Calendar Pro](https://www.joedolson.com/my-calendar/pro/)!
507
 
508
  = Can my visitors or members submit events? =
509
 
@@ -524,4 +423,6 @@ The search feature in My Calendar is pretty basic; but [buying My Calendar Pro](
524
  7. Style editing
525
  8. Template editing
526
 
527
- == Upgrade Notice ==
 
 
6
  Tested up to: 6.0
7
  Requires PHP: 7.0
8
  Text domain: my-calendar
9
+ Stable tag: 3.3.23
10
  License: GPLv2 or later
11
 
12
  Accessible WordPress event calendar plugin. Show events from multiple calendars on pages, in posts, or in widgets.
25
 
26
  = Features: =
27
 
28
+ * Calendar grid or list views of events
29
  * Monthly, weekly, or daily view.
30
+ * Mini-calendar for compact displays (as widget or shortcode)
31
+ * Widgets: today's events, upcoming events, mini calendar, event search
32
+ * Customize templates for event output
33
  * Limit views by categories, location, author, or host
34
+ * Editable CSS styles.
35
+ * Extensive support for recurring events.
36
+ * Edit or add single dates in recurring events
37
  * Rich permissions handling to restrict access to parts of My Calendar
38
+ * Email notifications when events are scheduled or drafted
39
  * Post to Twitter when events are created (using [WP to Twitter](http://wordpress.org/extend/plugins/wp-to-twitter/))
40
+ * Manage locations
41
  * Fetch events from a remote database. (Sharing events in a network of sites.)
42
  * Multisite-friendly
43
  * Integrated help page
44
  * Shortcode Generator to create customized views of My Calendar
45
 
46
+ = Accessibility =
47
+
48
+ My Calendar is designed with accessibility in mind. All interfaces - both front and back end - are tested with various assistive technology. The plugin includes features for indicating the accessibility services available for events and at physical venues, as well as providing access to the content for users with disabilities.
49
+
50
  = What's in My Calendar Pro? =
51
 
52
  * Let your site visitors submit events to your site (pay to post or free!).
88
 
89
  == Changelog ==
90
 
91
+ = 3.3.23 =
92
+
93
+ * Bug fix: Don't set to default location if location already set.
94
+ * Bug fix: Add stopImmediatePropgation to click handlers to prevent other script's scroll effects
95
+ * Change: Remove .mcajax class as unneeded.
96
+
97
+ = 3.3.22 =
98
+
99
+ * Bug fix: Mismatched variable type broke default week view.
100
+
101
+ = 3.3.21 =
102
+
103
+ * Bug fix: Accidentally stripped HTML out of all event titles with search excerpt highlighting.
104
+
105
+ = 3.3.20 =
106
+
107
+ * Bug fix: Recurring month by day not propagating correctly.
108
+ * Bug fix: Available admin input settings not displaying correctly.
109
+ * Bug fix: Unset style variables array could throw PHP warning.
110
+ * Change: Improvements to structure of search results.
111
+ * Change: Change default search result template.
112
+ * Add: search_results template tag with search term highlighting.
113
+
114
+ = 3.3.19 =
115
+
116
+ * Bug fix: Missing support for 'show_recurring' parameter in shortcode builder.
117
+ * Bug fix: Checkbox input layout in shortcode builder
118
+ * Bug fix: Locations content filter needs to be restricted to main query only.
119
+ * Complete documentation of hooks.
120
+
121
+ = 3.3.18 =
122
+
123
+ * Bug fix: img and svg category icon styles applied to list items in category admin.
124
+ * Bug fix: duplicate sprintf call missing arguments.
125
+ * Bug fix: Globally review & align var type declarations with params & returns in functions & documentation.
126
+ * Bug fix: Fix some date iteration on recurring events in iCal exports.
127
+ * Change: Return http 500 if invalid URL passed to print view return URL.
128
+ * Continuing hook documentation.
129
+
130
+ = 3.3.17 =
131
+
132
+ * Security Fix: XSS flaw in print view.
133
+ * Bug fix: View full calendar could be empty if settings not edited.
134
+ * Bug fix: View full calendar text default not translatable.
135
+ * Bug fix: Allow class attribute on time element.
136
+ * Change open in new tab text to 'new tab'.
137
+ * Label error source in cases where wp_die() is used.
138
+ * Switch subscription links to webcal: protocol.
139
+ * Begin adding hook documentation at https://joedolson.github.io/my-calendar/
140
+ * Begin adding framework for future version of template handling.
141
+
142
  = 3.3.16 =
143
 
144
  * Bug fix: Incorrectly passed list type caused templates to encode html entities.
390
  * Code reorganization.
391
  * PHP 8.0 compatibility.
392
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
393
  = Future Changes =
394
 
395
  * Refactor options storage
398
 
399
  = Hey! Why don't you have any Frequently Asked Questions here! =
400
 
401
+ Because the majority of users end up on my web site asking for help anyway -- and it's simply more work to maintain two copies. Please visit [my web site FAQ](https://www.joedolson.com/my-calendar/faq/) to read my Frequently Asked Questions!
402
 
403
  = This plugin is complicated. Why won't you help me figure out how to use it? =
404
 
405
+ I will! But not in person. Take a look at my [documentation website for My Calendar](https://docs.joedolson.com/my-calendar/) or the [developer hook documentation](https://joedolson.github.io/my-calendar/) before making your request, and consider [making a donation](https://www.joedolson.com/donate/) or [buying My Calendar Pro](https://www.joedolson.com/my-calendar/pro/)!
406
 
407
  = Can my visitors or members submit events? =
408
 
423
  7. Style editing
424
  8. Template editing
425
 
426
+ == Upgrade Notice ==
427
+
428
+ Security Update: Please update to version 3.3.17 or later as soon as possible.