├── .gitignore ├── composer.json ├── wp-event-calendar ├── assets │ ├── js │ │ └── event.js │ └── css │ │ ├── calendar.css │ │ └── datepicker.css └── includes │ ├── events │ ├── user-dashboard.php │ ├── post-statuses.php │ ├── user-alerts.php │ ├── editor.php │ ├── cron.php │ ├── hooks.php │ ├── post-types.php │ ├── capabilities.php │ ├── taxonomies.php │ ├── admin.php │ └── metaboxes.php │ └── common │ ├── list-table-month.php │ ├── event.php │ ├── list-table-day.php │ ├── list-table-week.php │ ├── admin.php │ ├── functions.php │ └── list-table-base.php ├── README.md ├── wp-event-calendar.php ├── readme.txt └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | .idea 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stuttter/wp-event-calendar", 3 | "description": "The best way to manage events in WordPress", 4 | "homepage": "https://github.com/stuttter/wp-event-calendar", 5 | "type": "wordpress-plugin", 6 | "require": { 7 | "php": ">=5.2", 8 | "composer/installers": "^1.0" 9 | }, 10 | "license": "GPL-2.0-or-later", 11 | "authors": [ 12 | { 13 | "name": "John James Jacoby", 14 | "email": "johnjamesjacoby@me.com" 15 | } 16 | ] 17 | } 18 | 19 | -------------------------------------------------------------------------------- /wp-event-calendar/assets/js/event.js: -------------------------------------------------------------------------------- 1 | jQuery( document ).ready( function( $ ) { 2 | 'use strict'; 3 | 4 | if ( $( '.wp_event_calendar_datepicker' ).length > 0 ) { 5 | $( '.wp_event_calendar_datepicker' ).datepicker( { 6 | dateFormat: 'mm/dd/yy' 7 | } ); 8 | } 9 | 10 | $( '#wp_event_calendar_all_day' ).click( function( e ) { 11 | var checked = $( this ).prop( 'checked' ); 12 | 13 | if ( true === checked ) { 14 | $( '.event-time' ).hide(); 15 | } else { 16 | $( '.event-time' ).show(); 17 | } 18 | } ); 19 | 20 | if ( $( '.ct_rental_datepicker' ).length > 0 ) { 21 | $( '.ct_rental_datepicker' ).datepicker( { 22 | dateFormat: 'mm/dd/yy' 23 | } ); 24 | } 25 | } ); 26 | -------------------------------------------------------------------------------- /wp-event-calendar/includes/events/user-dashboard.php: -------------------------------------------------------------------------------- 1 | 'events', 24 | 'slug' => 'events', 25 | 'url' => '', 26 | 'label' => esc_html__( 'Events', 'wp-user-alerts' ), 27 | 'show_in_menu' => true, 28 | 'order' => 20 29 | ); 30 | 31 | // Return sections 32 | return $sections; 33 | } 34 | -------------------------------------------------------------------------------- /wp-event-calendar/includes/events/post-statuses.php: -------------------------------------------------------------------------------- 1 | esc_html_x( 'Past', 'events', 'wp-event-calendar' ), 22 | 'label_count' => _nx_noop( 'Past (%s)', 'Past (%s)', 'events', 'wp-event-calendar' ), 23 | 'exclude_from_search' => get_post_type_object( 'event' )->exclude_from_search, 24 | 'public' => get_post_type_object( 'event' )->public, 25 | 'publicly_queryable' => get_post_type_object( 'event' )->publicly_queryable, 26 | 'show_in_admin_status_list' => true, 27 | 'show_in_admin_all_list' => true, 28 | ) ); 29 | } 30 | -------------------------------------------------------------------------------- /wp-event-calendar/includes/events/user-alerts.php: -------------------------------------------------------------------------------- 1 | 'OR', 34 | 35 | // Starts after midnight yesterday 36 | array( 37 | 'key' => 'wp_event_calendar_date_time', 38 | 'value' => $midnight_yesterday, 39 | 'type' => 'DATETIME', 40 | 'compare' => '>', 41 | ), 42 | 43 | // Ends after midnight today 44 | array( 45 | 'key' => 'wp_event_calendar_end_date_time', 46 | 'value' => $midnight_yesterday, 47 | 'type' => 'DATETIME', 48 | 'compare' => '>', 49 | ) 50 | ); 51 | 52 | return $r; 53 | } 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WP Event Calendar 2 | 3 | WP Event Calendar is the best way to keep track of events in WordPress. 4 | 5 | * Seamlessly integrates into WordPress's dashboard interface 6 | * Month, week, and day list-table views 7 | * Organize events by type, category, & tag 8 | * Easily & logically paginate through date ranges 9 | * Safe, secure, & efficient 10 | * Full of developer hooks for integration with other plugins & themes 11 | * Integrates with User Groups, Alerts, Activity, and Profiles 12 | 13 | # Installation 14 | 15 | * Download and install using the built in WordPress plugin installer. 16 | * Activate in the "Plugins" area of your admin by clicking the "Activate" link. 17 | * No further setup or configuration is necessary. 18 | 19 | # FAQ 20 | 21 | ### Does this create new database tables? 22 | 23 | No. It uses WordPress's custom post-type, custom taxonomy, and metadata APIs. 24 | 25 | ### Does this modify existing database tables? 26 | 27 | No. All of WordPress's core database tables remain untouched. 28 | 29 | ### Where can I get support? 30 | 31 | * Basic: https://wordpress.org/support/plugin/wp-event-calendar/ 32 | * Priority: https://chat.flox.io/support/channels/wp-event-calendar/ 33 | 34 | ### Can I contribute? 35 | 36 | Yes, please! The number of users needing events and calendars in WordPress is always growing. Having an easy-to-use API and powerful set of functions is critical to managing complex WordPress installations. If this is your thing, please help us out! 37 | -------------------------------------------------------------------------------- /wp-event-calendar/includes/events/editor.php: -------------------------------------------------------------------------------- 1 | 'ids', 51 | 'post_type' => wp_event_calendar_allowed_post_types(), 52 | 'post_status' => 'publish', 53 | 'posts_per_page' => -1, 54 | 'meta_query' => array( array( 55 | 'relation' => 'AND', 56 | array( 57 | 'key' => 'wp_event_calendar_date_time', 58 | 'value' => $eod, 59 | 'type' => 'DATETIME', 60 | 'compare' => '<', 61 | ), 62 | array( 63 | 'key' => 'wp_event_calendar_end_date_time', 64 | 'value' => $eod, 65 | 'type' => 'DATETIME', 66 | 'compare' => '<', 67 | ) 68 | ) ) 69 | ) ); 70 | 71 | // Bail if no posts 72 | if ( empty( $old_events->posts ) ) { 73 | return; 74 | } 75 | 76 | // Loop through posts and update status 77 | foreach ( $old_events->posts as $post_id ) { 78 | wp_update_post( array( 79 | 'ID' => $post_id, 80 | 'post_status' => 'passed' 81 | ) ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /wp-event-calendar/includes/events/hooks.php: -------------------------------------------------------------------------------- 1 | _x( 'Events', 'post type general name', 'wp-event-calendar' ), 25 | 'singular_name' => _x( 'Event', 'post type singular name', 'wp-event-calendar' ), 26 | 'add_new' => _x( 'Add New', 'event', 'wp-event-calendar' ), 27 | 'add_new_item' => __( 'Add New Event', 'wp-event-calendar' ), 28 | 'edit_item' => __( 'Edit Event', 'wp-event-calendar' ), 29 | 'new_item' => __( 'New Event', 'wp-event-calendar' ), 30 | 'view_item' => __( 'View Event', 'wp-event-calendar' ), 31 | 'search_items' => __( 'Search Events', 'wp-event-calendar' ), 32 | 'not_found' => __( 'No events found.', 'wp-event-calendar' ), 33 | 'not_found_in_trash' => __( 'No events found in trash.', 'wp-event-calendar' ), 34 | 'parent_item_colon' => __( 'Parent Event:', 'wp-event-calendar' ), 35 | 'all_items' => __( 'All Events', 'wp-event-calendar' ), 36 | 'featured_image' => __( 'Featured Image', 'wp-event-calendar' ), 37 | 'set_featured_image' => __( 'Set featured image', 'wp-event-calendar' ), 38 | 'remove_featured_image' => __( 'Remove featured image', 'wp-event-calendar' ), 39 | 'use_featured_image' => __( 'Use as featured image', 'wp-event-calendar' ), 40 | ); 41 | 42 | // Supports 43 | $supports = array( 44 | 'title', 45 | 'thumbnail', 46 | 'revisions', 47 | 'events' 48 | ); 49 | 50 | // Capability types 51 | $cap_types = array( 52 | 'event', 53 | 'events' 54 | ); 55 | 56 | // Capabilities 57 | $caps = array( 58 | 59 | // Meta caps 60 | 'edit_post' => 'edit_event', 61 | 'read_post' => 'read_event', 62 | 'delete_post' => 'delete_event', 63 | 64 | // Primitive/meta caps 65 | 'read' => 'read', 66 | 'create_posts' => 'create_events', 67 | 68 | // Primitive caps (used outside of map_meta_cap) 69 | 'edit_posts' => 'edit_events', 70 | 'edit_others_posts' => 'edit_others_events', 71 | 'publish_posts' => 'publish_events', 72 | 'read_private_posts' => 'read_private_events', 73 | 74 | // Primitive caps (used inside of map_meta_cap) 75 | 'delete_posts' => 'delete_events', 76 | 'delete_private_posts' => 'delete_private_events', 77 | 'delete_published_posts' => 'delete_published_events', 78 | 'delete_others_posts' => 'delete_others_events', 79 | 'edit_private_posts' => 'edit_private_events', 80 | 'edit_published_posts' => 'edit_published_events' 81 | ); 82 | 83 | // Rewrite 84 | $rewrite = array( 85 | 'slug' => 'event', 86 | 'with_front' => false 87 | ); 88 | 89 | // Post type arguments 90 | $args = array( 91 | 'labels' => $labels, 92 | 'supports' => $supports, 93 | 'description' => '', 94 | 'public' => true, 95 | 'hierarchical' => true, 96 | 'exclude_from_search' => true, 97 | 'publicly_queryable' => false, 98 | 'show_ui' => true, 99 | 'show_in_menu' => true, 100 | 'show_in_nav_menus' => false, 101 | 'archive_in_nav_menus' => false, 102 | 'show_in_admin_bar' => true, 103 | 'menu_position' => 44, 104 | 'menu_icon' => 'dashicons-calendar-alt', 105 | 'capabilities' => $caps, 106 | 'capability_type' => $cap_types, 107 | 'register_meta_box_cb' => null, 108 | 'taxonomies' => array(), 109 | 'has_archive' => false, 110 | 'rewrite' => $rewrite, 111 | 'query_var' => true, 112 | 'can_export' => true, 113 | 'delete_with_user' => false, 114 | ); 115 | 116 | // Register the event type 117 | register_post_type( 'event', $args ); 118 | } 119 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === WP Event Calendar === 2 | Contributors: johnjamesjacoby, stuttter 3 | Tags: event, calendar, session, appointment, month, week, day, category, tag, term, type 4 | Requires at least: 4.7 5 | Tested up to: 4.8 6 | Stable tag: 1.1.0 7 | License: GPLv2 or later 8 | License URI: https://www.gnu.org/licenses/gpl-2.0.html 9 | Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9Q4F4EL5YJ62J 10 | 11 | == Description == 12 | 13 | WP Event Calendar is the best way to manage events in WordPress! 14 | 15 | > We know you have your choice of calendar & events plugins. We appreciate you giving this one a try, and we hope you really love it! 16 | 17 | = Details = 18 | 19 | * Seamlessly integrates into WordPress's dashboard interface 20 | * Month, week, and day list-table views 21 | * Organize events by type, category, & tag 22 | * Easily & logically paginate through date ranges 23 | * Safe, secure, & efficient 24 | * Full of developer hooks for integration with other plugins & themes 25 | * Integrates with User Groups, Alerts, Activity, and Profiles 26 | 27 | = Works great with = 28 | 29 | * [WP Chosen](https://wordpress.org/plugins/wp-chosen/ "Make long, unwieldy select boxes much more user-friendly.") 30 | * [WP Pretty Filters](https://wordpress.org/plugins/wp-pretty-filters/ "Makes post filters better match what's already in Media & Attachments.") 31 | * [WP Media Categories](https://wordpress.org/plugins/wp-media-categories/ "Add categories to media & attachments.") 32 | * [WP Term Order](https://wordpress.org/plugins/wp-term-order/ "Sort taxonomy terms, your way.") 33 | * [WP Term Authors](https://wordpress.org/plugins/wp-term-authors/ "Authors for categories, tags, and other taxonomy terms.") 34 | * [WP Term Colors](https://wordpress.org/plugins/wp-term-colors/ "Pretty colors for categories, tags, and other taxonomy terms.") 35 | * [WP Term Families](https://wordpress.org/plugins/wp-term-families/ "Families of taxonomies for taxonomy terms.") 36 | * [WP Term Icons](https://wordpress.org/plugins/wp-term-icons/ "Pretty icons for categories, tags, and other taxonomy terms.") 37 | * [WP Term Images](https://wordpress.org/plugins/wp-term-images/ "Pretty images for categories, tags, and other taxonomy terms.") 38 | * [WP Term Visibility](https://wordpress.org/plugins/wp-term-visibility/ "Visibilities for categories, tags, and other taxonomy terms.") 39 | * [WP User Activity](https://wordpress.org/plugins/wp-user-activity/ "The best way to log activity in WordPress.") 40 | * [WP User Alerts](https://wordpress.org/plugins/wp-user-alerts/ "Alert registered users when new content is published.") 41 | * [WP User Avatars](https://wordpress.org/plugins/wp-user-avatars/ "Allow users to upload avatars or choose them from your media library.") 42 | * [WP User Groups](https://wordpress.org/plugins/wp-user-groups/ "Group users together with taxonomies & terms.") 43 | * [WP User Parents](https://wordpress.org/plugins/wp-user-parents/ "Allow parent users to manage their direct decendants.") 44 | * [WP User Profiles](https://wordpress.org/plugins/wp-user-profiles/ "A sophisticated way to edit users in WordPress.") 45 | 46 | == Screenshots == 47 | 48 | 1. Month View 49 | 2. Week View 50 | 3. Metabox 51 | 4. Date Picker 52 | 53 | == Installation == 54 | 55 | * Download and install using the built in WordPress plugin installer. 56 | * Activate in the "Plugins" area of your admin by clicking the "Activate" link. 57 | * No further setup or configuration is necessary. 58 | 59 | == Frequently Asked Questions == 60 | 61 | = Does this create new database tables? = 62 | 63 | No. It uses WordPress's custom post-type, custom taxonomy, and metadata APIs. 64 | 65 | = Does this modify existing database tables? = 66 | 67 | No. All of WordPress's core database tables remain untouched. 68 | 69 | = Where can I get support? = 70 | 71 | * Basic: https://wordpress.org/support/plugin/wp-event-calendar/ 72 | * Priority: https://chat.flox.io/support/channels/wp-event-calendar/ 73 | 74 | = Where can I find documentation? = 75 | 76 | http://github.com/stuttter/wp-event-calendar/ 77 | 78 | == Changelog == 79 | 80 | = [1.1.0] - 2016-12-09 = 81 | * Improved editor metabox experience 82 | * Fix bug caused when relocating the "Details" metabox 83 | 84 | = [1.0.0] - 2016-09-07 = 85 | * Improved support for mu-plugins location 86 | * Improved performance of all ranged calendar queries 87 | * Updated plugin file organization structure 88 | 89 | = [0.4.0] - 2016-05-12 = 90 | * More improvements to capability mappings 91 | * Support for additional post types 92 | * Helper functions for iCal integration 93 | * Query functions 94 | 95 | = [0.3.1] - 2016-05-03 = 96 | * Fix capability regression in 0.3.1 97 | * Add contextual help to Calendar page 98 | 99 | = [0.3.0] - 2016-03-30 = 100 | * Improve capability mapping 101 | * Improve TinyMCE compatibility 102 | * Update calendar styling 103 | 104 | = [0.2.4] - 2016-02-05 = 105 | * Dropdown select elements for hours & minutes 106 | 107 | = [0.2.3] - 2016-02-01 = 108 | * Rename metabox titles 109 | * Make "Location" optional 110 | * Add revision support to events 111 | * General code & styling clean-up 112 | 113 | = [0.2.2] - 2016-01-10 = 114 | * Improve support for all-day & multi-day events 115 | * Improve month, week, and day views 116 | * Improve contrast of current & active items 117 | 118 | = [0.2.1] - 2015-12-30 = 119 | * Improve taxonomy & status filtering 120 | 121 | = [0.2.0] - 2015-11-30 = 122 | * Add "Day" view 123 | * Improve "Month" view 124 | 125 | = [0.1.10] - 2015-11-30 = 126 | * Filter events by registered taxonomy 127 | 128 | = [0.1.9] - 2015-11-16 = 129 | * Move events to "Passed" status twice daily 130 | 131 | = [0.1.8] - 2015-9-10 = 132 | * Added week view 133 | * Added persistent pagination for mode, status, & search 134 | * Added styling for a few event types 135 | * Added help text 136 | * Added legend in help text area 137 | 138 | = [0.1.7] - 2015-9-8 = 139 | * Updated date-picker styling 140 | 141 | = [0.1.6] - 2015-9-7 = 142 | * Sort by start, end, and repetition 143 | 144 | = [0.1.5] - 2015-9-6 = 145 | * All-day events 146 | 147 | = [0.1.4] - 2015-9-5 = 148 | * More flexible mapped capabilities 149 | 150 | = [0.1.3] - 2015-9-4 = 151 | * Revert role & capability changes 152 | 153 | = [0.1.2] - 2015-9-3 = 154 | * Remove dependency 155 | * Start, end, repeat, & expiring events 156 | * Date picker 157 | * Cleanup 158 | 159 | = [0.1.1] - 2015-9-2 = 160 | * Show pointers on event clicks 161 | * Add flexible methods for pointers output 162 | * Add actions & filters 163 | * Rename main class to be for month-only view 164 | * Use new icon for private posts 165 | 166 | = [0.1.0] - 2015-9-1 = 167 | * Initial release 168 | -------------------------------------------------------------------------------- /wp-event-calendar/assets/css/calendar.css: -------------------------------------------------------------------------------- 1 | /** Editor ********************************************************************/ 2 | 3 | body.post-type-event #post-body-content { 4 | margin-bottom: 0; 5 | } 6 | 7 | .wp-event-calendar-editor #wp-content-editor-tools { 8 | background-color: #fff; 9 | padding-top: 0; 10 | } 11 | 12 | #above_event_editor-sortables { 13 | margin-top: 20px; 14 | } 15 | 16 | .event-time { 17 | display: inline-block; 18 | } 19 | .wp_event_calendar_datepicker { 20 | max-width: 125px; 21 | height: 28px; 22 | margin: 2px 0 3px; 23 | } 24 | .event-time input { 25 | max-width: 50px; 26 | } 27 | 28 | /** Edit & Rows ***************************************************************/ 29 | 30 | .widefat .column-start, 31 | .widefat .column-end, 32 | .widefat .column-duration { 33 | width: 15% 34 | } 35 | 36 | .widefat .column-event-types, 37 | .widefat .column-event-categories { 38 | width: 10%; 39 | } 40 | 41 | .widefat .column-repeat { 42 | width: 10%; 43 | } 44 | 45 | /** Common ********************************************************************/ 46 | 47 | .view-switch .view-month:before { 48 | content: '\f509'; 49 | } 50 | 51 | .view-switch .view-week:before { 52 | content: '\f164'; 53 | } 54 | 55 | .view-switch .view-day:before { 56 | content: '\f163'; 57 | } 58 | 59 | table.calendar { 60 | table-layout: fixed; 61 | } 62 | 63 | table.calendar thead th, 64 | table.calendar tfoot th { 65 | text-align: center; 66 | } 67 | 68 | table.calendar tbody td { 69 | padding: 0; 70 | position: relative; 71 | text-align: left; 72 | border-bottom: 1px solid #e1e1e1; 73 | border-right: 1px solid #e1e1e1; 74 | } 75 | 76 | table.calendar a { 77 | display: block; 78 | -o-text-overflow: ellipsis; 79 | text-overflow: ellipsis; 80 | overflow-x: hidden; 81 | white-space: nowrap; 82 | margin: 0; 83 | padding: 0 5px; 84 | } 85 | 86 | table.calendar:not(.day) tbody td.saturday, 87 | table.calendar:not(.day) tbody td.sunday { 88 | background-color: #f1f1f1; 89 | } 90 | 91 | table.calendar a:before { 92 | font: normal 15px/20px 'dashicons'; 93 | vertical-align: top; 94 | speak: none; 95 | -webkit-font-smoothing: antialiased; 96 | -moz-osx-font-smoothing: grayscale; 97 | width: 20px; 98 | height: 20px; 99 | margin-right: 4px; 100 | } 101 | 102 | 103 | .wp-pointer-content h3.type-event:before, 104 | table.calendar a.type-event:before { 105 | content: '\f145'; 106 | } 107 | 108 | .wp-pointer-content h3.type-post:before, 109 | table.calendar a.type-post:before { 110 | content: '\f109'; 111 | } 112 | 113 | .wp-pointer-content h3.type-page:before, 114 | table.calendar a.type-page:before { 115 | content: '\f105'; 116 | } 117 | 118 | .wp-pointer-content h3.status-trash:before, 119 | table.calendar a.status-trash:before { 120 | content: '\f182'; 121 | } 122 | 123 | .wp-pointer-content h3.status-private:before, 124 | table.calendar a.status-private:before { 125 | content: '\f530'; 126 | } 127 | 128 | .wp-pointer-content h3.post-password-required:before, 129 | table.calendar a.post-password-required:before { 130 | content: '\f315'; 131 | } 132 | 133 | .wp-pointer-content h3.has-location:before, 134 | table.calendar a.has-location:before { 135 | content: "\f230"; 136 | } 137 | 138 | .wp-pointer-content h3.all-day:before, 139 | table.calendar a.all-day:before { 140 | content: '\f469'; 141 | } 142 | 143 | table.calendar a.status-draft { 144 | opacity: 0.5; 145 | } 146 | 147 | /** Month *********************************************************************/ 148 | 149 | table.month tbody td a.day-number { 150 | position: absolute; 151 | top: 0px; 152 | left: 0px; 153 | height: 26px; 154 | width: 26px; 155 | text-align: center; 156 | line-height: 1.9em; 157 | padding: 0; 158 | border-right: 1px solid #e1e1e1; 159 | border-bottom: 1px solid #e1e1e1; 160 | background-color: rgba(255, 255, 255, 1); 161 | color: #aaa; 162 | font-weight: 600; 163 | } 164 | 165 | table.month tbody td.today a.day-number { 166 | background: #ffe; 167 | } 168 | table.month tbody a.add-event-for-day:hover, 169 | table.month tbody td a.day-number:hover { 170 | background: rgba(235, 235, 235, 1); 171 | } 172 | 173 | table.month tbody a.add-event-for-day { 174 | visibility: hidden; 175 | position: absolute; 176 | right: 0; 177 | top: 0; 178 | height: 26px; 179 | width: 26px; 180 | text-align: center; 181 | border-left: 1px solid #e1e1e1; 182 | border-bottom: 1px solid #e1e1e1; 183 | background-color: rgba(255, 255, 255, 1); 184 | padding: 0; 185 | margin: 0; 186 | } 187 | 188 | table.month tbody a i { 189 | line-height: 1.9em; 190 | font-size: 15px; 191 | overflow: hidden; 192 | } 193 | 194 | table.month tbody td:hover a.add-event-for-day { 195 | visibility: visible; 196 | } 197 | 198 | table.month tbody td:hover { 199 | background-color: #ffe; 200 | } 201 | 202 | table.month tbody td { 203 | min-height: 110px; 204 | } 205 | 206 | table.month tbody th { 207 | height: 80px; 208 | background-color: #fff; 209 | border-bottom: 1px solid #e1e1e1; 210 | } 211 | 212 | table.month tbody tr th:last-of-type { 213 | border-right: 1px solid #e1e1e1; 214 | } 215 | 216 | table.month tbody td:last-of-type:not(.position-6) { 217 | border-right: 1px solid #e1e1e1; 218 | } 219 | 220 | table.month tbody tr td:last-of-type { 221 | border-right: none; 222 | } 223 | 224 | table.month div.events-for-cell { 225 | margin: 30px 0 5px; 226 | min-height: 80px 227 | } 228 | 229 | .wp-pointer-content h3 a { 230 | color: #fff; 231 | text-decoration: none; 232 | } 233 | 234 | .wp-pointer-content h3 a:hover { 235 | color: #eee; 236 | } 237 | 238 | input#year { 239 | width: 90px; 240 | } 241 | 242 | /** Week **********************************************************************/ 243 | 244 | table.week th, 245 | table.day th { 246 | width: 30px; 247 | background-color: #fff; 248 | color: #333; 249 | border-bottom: 1px solid #e1e1e1; 250 | border-right: 1px solid #e1e1e1; 251 | text-align: center; 252 | cursor: default; 253 | } 254 | 255 | table.week tbody tr.this-hour td, 256 | table.day tbody tr.this-hour td { 257 | background: #ffe; 258 | } 259 | 260 | table.day tr.multi-day, 261 | table.day tr.all-day, 262 | table.week tr.multi-day, 263 | table.week tr.all-day { 264 | background-color: #efe; 265 | } 266 | 267 | table.week tr.multi-day th, 268 | table.week tr.multi-day td, 269 | table.day tr.multi-day th, 270 | table.day tr.multi-day td { 271 | border-bottom: 2px solid #e1e1e1; 272 | } 273 | 274 | table.week th.column-hour, 275 | table.day th.column-hour { 276 | width: 100px; 277 | } 278 | 279 | table.week th.column-monday, 280 | table.week th.column-tuesday, 281 | table.week th.column-wednesday, 282 | table.week th.column-thursday, 283 | table.week th.column-friday, 284 | table.week th.column-saturday, 285 | table.week th.column-sunday, 286 | table.day th.column-monday, 287 | table.day th.column-tuesday, 288 | table.day th.column-wednesday, 289 | table.day th.column-thursday, 290 | table.day th.column-friday, 291 | table.day th.column-saturday, 292 | table.day th.column-sunday { 293 | width: 100%; 294 | } -------------------------------------------------------------------------------- /wp-event-calendar/includes/common/list-table-month.php: -------------------------------------------------------------------------------- 1 | view_start = "{$this->year}-{$this->month}-01 00:00:00"; 41 | 42 | $mon = mysql2date( 'U', $this->view_start ); 43 | $eom = strtotime( '+1 month', $mon ); 44 | 45 | $this->view_end = date_i18n( 'Y-m-d H:i:s', $eom ); 46 | } 47 | 48 | /** 49 | * Setup the list-table's columns 50 | * 51 | * @see WP_List_Table::single_row_columns() 52 | * 53 | * @return array An associative array containing column information 54 | */ 55 | public function get_columns() { 56 | return array( 57 | 'sunday' => $GLOBALS['wp_locale']->get_weekday( 0 ), 58 | 'monday' => $GLOBALS['wp_locale']->get_weekday( 1 ), 59 | 'tuesday' => $GLOBALS['wp_locale']->get_weekday( 2 ), 60 | 'wednesday' => $GLOBALS['wp_locale']->get_weekday( 3 ), 61 | 'thursday' => $GLOBALS['wp_locale']->get_weekday( 4 ), 62 | 'friday' => $GLOBALS['wp_locale']->get_weekday( 5 ), 63 | 'saturday' => $GLOBALS['wp_locale']->get_weekday( 6 ) 64 | ); 65 | } 66 | 67 | /** 68 | * Get a list of CSS classes for the list table table tag. 69 | * 70 | * @since 0.1.0 71 | * @access protected 72 | * 73 | * @return array List of CSS classes for the table tag. 74 | */ 75 | protected function get_table_classes() { 76 | return array( 'widefat', 'fixed', 'striped', 'calendar', 'month', $this->_args['plural'] ); 77 | } 78 | 79 | /** 80 | * Add a post to the item array, keyed by day 81 | * 82 | * @todo Repeat & expire 83 | * 84 | * @since 0.1.1 85 | * 86 | * @param object $post 87 | * @param int $max 88 | */ 89 | protected function setup_item( $post = false, $max = 10 ) { 90 | 91 | // Bail if there is no post 92 | if ( empty( $post ) ) { 93 | return; 94 | } 95 | 96 | // Start day 97 | $time = $this->item_start; 98 | $type = 'items'; 99 | $max_int = absint( $max ); 100 | 101 | // Loop through days and setup the item 102 | for ( $i = 0, $j = $this->item_days; $i < $j; ++$i ) { 103 | $day = (int) date_i18n( 'j', $time ); 104 | $month = (int) date_i18n( 'n', $time ); 105 | $year = (int) date_i18n( 'Y', $time ); 106 | 107 | // Skip if not this month 108 | if ( ( $month === (int) $this->month ) && ( $year === (int) $this->year ) ) { 109 | 110 | // Setup the pointer for each day 111 | $this->setup_pointer( $post, $day ); 112 | 113 | // Get count for day 114 | $count = empty( $this->{$type}[ $day ] ) 115 | ? 0 116 | : count( $this->{$type}[ $day ] ); 117 | 118 | // Add post to item types for each day in it's duration 119 | if ( $count < $max_int ) { 120 | $this->{$type}[ $day ][ $post->ID ] = $post; 121 | } 122 | } 123 | 124 | // Bump the time 1 day 125 | $time += DAY_IN_SECONDS; 126 | } 127 | } 128 | 129 | /** 130 | * Start the week with a table row 131 | * 132 | * @since 0.1.0 133 | */ 134 | protected function get_row_start() { 135 | ?> 136 | 137 | 138 | 139 | 149 | 150 | 151 | 152 | 162 | 163 | 164 | 165 | 'day', 181 | 'cy' => $this->year, 182 | 'cm' => $this->month, 183 | 'cd' => $day_of_month 184 | ), $this->get_base_url() ); 185 | 186 | // Link to add new event on this day 187 | $add_event_for_day = add_query_arg( array( 188 | 'post_type' => wp_event_calendar_get_admin_post_type(), 189 | 'start_day' => strtotime( "{$this->month}/{$day_of_month}/{$this->year}" ) 190 | ), admin_url( 'post-new.php' ) ); ?> 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 |
202 | get_posts_for_cell( $day_of_month ); ?> 203 |
204 | 205 | 206 | month, 1, $this->year ); 218 | $max_day = date_i18n( 't', $timestamp ); 219 | $this_month = getdate( $timestamp ); 220 | $start_day = $this_month['wday']; 221 | 222 | // Loop through days of the month 223 | for ( $i = 0; $i < ( $max_day + $start_day ); $i++ ) { 224 | 225 | // New row 226 | if ( ( $i % 7 ) === 0 ) { 227 | $this->get_row_start(); 228 | } 229 | 230 | // Pad day 231 | if ( $i < $start_day ) { 232 | $this->get_row_pad( $i, $start_day ); 233 | 234 | // Month day 235 | } else { 236 | $this->get_row_cell( $i, $start_day ); 237 | } 238 | 239 | if ( ( $i % 7 ) === 6 ) { 240 | $this->get_row_end(); 241 | } 242 | } 243 | } 244 | 245 | /** 246 | * Paginate through months & years 247 | * 248 | * @since 0.1.0 249 | * 250 | * @param array $args 251 | */ 252 | protected function pagination( $args = array() ) { 253 | 254 | // Parse args 255 | $r = wp_parse_args( $args, array( 256 | 'small' => '1 month', 257 | 'large' => '1 year', 258 | 'labels' => array( 259 | 'next_small' => esc_html__( 'Next Month', 'wp-event-calendar' ), 260 | 'next_large' => esc_html__( 'Next Year', 'wp-event-calendar' ), 261 | 'prev_small' => esc_html__( 'Previous Month', 'wp-event-calendar' ), 262 | 'prev_large' => esc_html__( 'Previous Year', 'wp-event-calendar' ) 263 | ) 264 | ) ); 265 | 266 | // Return pagination 267 | return parent::pagination( $r ); 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /wp-event-calendar/includes/events/capabilities.php: -------------------------------------------------------------------------------- 1 | post_type ) { 40 | $post = get_post( $post->post_parent ); 41 | if ( ! $post ) { 42 | $caps[] = 'do_not_allow'; 43 | break; 44 | } 45 | } 46 | 47 | // If the post author is set and the user is the author... 48 | if ( (int) $user_id === $post->post_author ) { 49 | 50 | // If the post is published or scheduled... 51 | if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) { 52 | $caps = array( 'delete_published_posts' ); 53 | 54 | } elseif ( 'trash' === $post->post_status ) { 55 | $status = get_post_meta( $post->ID, '_wp_trash_meta_status', true ); 56 | 57 | if ( in_array( $status, array( 'publish', 'future' ), true ) ) { 58 | $caps = array( 'delete_published_posts' ); 59 | } else { 60 | $caps = array( 'delete_posts' ); 61 | } 62 | 63 | // If the post is draft... 64 | } else { 65 | $caps = array( 'delete_posts' ); 66 | } 67 | 68 | // The user is trying to edit someone else's post. 69 | } else { 70 | $caps = array( 'delete_others_posts' ); 71 | 72 | // The post is published or scheduled, extra cap required. 73 | if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) { 74 | $caps = array( 'delete_published_posts' ); 75 | } elseif ( 'private' === $post->post_status ) { 76 | $caps = array( 'delete_private_posts' ); 77 | } 78 | } 79 | break; 80 | 81 | // Editing 82 | case 'edit_event' : 83 | $post = get_post( $args[0] ); 84 | if ( empty( $post ) ) { 85 | $caps = array( 'do_not_allow' ); 86 | break; 87 | } 88 | 89 | if ( 'revision' === $post->post_type ) { 90 | $post = get_post( $post->post_parent ); 91 | if ( empty( $post ) ) { 92 | $caps = array( 'do_not_allow' ); 93 | break; 94 | } 95 | } 96 | 97 | // If the post author is set and the user is the author... 98 | if ( (int) $user_id === (int) $post->post_author ) { 99 | 100 | // If the post is published or scheduled... 101 | if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) { 102 | $caps = array( 'edit_published_posts' ); 103 | 104 | } elseif ( 'trash' === $post->post_status ) { 105 | $status = get_post_meta( $post->ID, '_wp_trash_meta_status', true ); 106 | 107 | if ( in_array( $status, array( 'publish', 'future' ), true ) ) { 108 | $caps = array( 'edit_published_posts' ); 109 | } else { 110 | $caps = array( 'edit_posts' ); 111 | } 112 | 113 | // If the post is draft... 114 | } else { 115 | $caps = array( 'edit_posts' ); 116 | } 117 | 118 | // The user is trying to edit someone else's post. 119 | } else { 120 | $caps = array( 'edit_others_posts' ); 121 | 122 | // The post is published or scheduled, extra cap required. 123 | if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) { 124 | $caps = array( 'edit_published_posts' ); 125 | } elseif ( 'private' === $post->post_status ) { 126 | $caps = array( 'edit_private_posts' ); 127 | } 128 | } 129 | break; 130 | 131 | // Reading 132 | case 'read_event' : 133 | $post = get_post( $args[0] ); 134 | if ( empty( $post ) ) { 135 | $caps = array( 'do_not_allow' ); 136 | break; 137 | } 138 | 139 | if ( 'revision' == $post->post_type ) { 140 | $post = get_post( $post->post_parent ); 141 | if ( empty( $post ) ) { 142 | $caps = 'do_not_allow'; 143 | break; 144 | } 145 | } 146 | 147 | $status_obj = get_post_status_object( $post->post_status ); 148 | if ( true === $status_obj->public ) { 149 | $caps = array( 'read' ); 150 | break; 151 | } 152 | 153 | if ( (int) $user_id === $post->post_author ) { 154 | $caps = array( 'read' ); 155 | } elseif ( true === $status_obj->private ) { 156 | $caps = array( 'read_private_posts' ); 157 | } else { 158 | $caps = map_meta_cap( 'edit_post', $user_id, $post->ID ); 159 | } 160 | 161 | break; 162 | 163 | // Remap 164 | case 'delete_events' : 165 | $caps = array( 'delete_posts' ); 166 | break; 167 | 168 | case 'delete_others_events' : 169 | $caps = array( 'delete_others_events' ); 170 | break; 171 | 172 | case 'create_events' : 173 | case 'edit_events' : 174 | case 'read_calendar' : 175 | $caps = array( 'edit_posts' ); 176 | break; 177 | 178 | case 'edit_others_events' : 179 | $caps = array( 'edit_others_posts' ); 180 | break; 181 | 182 | case 'publish_events' : 183 | $caps = array( 'publish_posts' ); 184 | break; 185 | } 186 | 187 | return apply_filters( 'wp_event_calendar_meta_caps', $caps, $cap, $user_id, $args ); 188 | } 189 | 190 | /** 191 | * Maps event type capabilities 192 | * 193 | * @since 0.1.4 194 | * 195 | * @param array $caps Capabilities for meta capability 196 | * @param string $cap Capability name 197 | * @param int $user_id User id 198 | * @param array $args Arguments 199 | * 200 | * @return array Actual capabilities for meta capability 201 | */ 202 | function wp_event_calendar_type_meta_caps( $caps, $cap, $user_id, $args ) { 203 | 204 | // What capability is being checked? 205 | switch ( $cap ) { 206 | case 'manage_event_types' : 207 | case 'edit_event_types' : 208 | case 'delete_event_types' : 209 | case 'assign_event_types' : 210 | $caps = array( 'list_users' ); 211 | break; 212 | } 213 | 214 | return apply_filters( 'wp_event_calendar_type_meta_caps', $caps, $cap, $user_id, $args ); 215 | } 216 | 217 | /** 218 | * Maps event category capabilities 219 | * 220 | * @since 0.1.4 221 | * 222 | * @param array $caps Capabilities for meta capability 223 | * @param string $cap Capability name 224 | * @param int $user_id User id 225 | * @param array $args Arguments 226 | * 227 | * @return array Actual capabilities for meta capability 228 | */ 229 | function wp_event_calendar_category_meta_caps( $caps, $cap, $user_id, $args ) { 230 | 231 | // What capability is being checked? 232 | switch ( $cap ) { 233 | case 'manage_event_categories' : 234 | case 'edit_event_categories' : 235 | case 'delete_event_categories' : 236 | case 'assign_event_categories' : 237 | $caps = array( 'list_users' ); 238 | break; 239 | } 240 | 241 | return apply_filters( 'wp_event_calendar_category_meta_caps', $caps, $cap, $user_id, $args ); 242 | } 243 | 244 | /** 245 | * Maps event tag capabilities 246 | * 247 | * @since 0.1.4 248 | * 249 | * @param array $caps Capabilities for meta capability 250 | * @param string $cap Capability name 251 | * @param int $user_id User id 252 | * @param array $args Arguments 253 | * 254 | * @return array Actual capabilities for meta capability 255 | */ 256 | function wp_event_calendar_tag_meta_caps( $caps, $cap, $user_id, $args ) { 257 | 258 | // What capability is being checked? 259 | switch ( $cap ) { 260 | case 'manage_event_tags' : 261 | case 'edit_event_tags' : 262 | case 'delete_event_tags' : 263 | case 'assign_event_tags' : 264 | $caps = array( 'list_users' ); 265 | break; 266 | } 267 | 268 | return apply_filters( 'wp_event_calendar_tag_meta_caps', $caps, $cap, $user_id, $args ); 269 | } 270 | -------------------------------------------------------------------------------- /wp-event-calendar/assets/css/datepicker.css: -------------------------------------------------------------------------------- 1 | /* Date Picker Default Styles */ 2 | .ui-datepicker { 3 | padding: 0; 4 | margin: 0; 5 | -webkit-border-radius: 0; 6 | -moz-border-radius: 0; 7 | border-radius: 0; 8 | background-color: #fff; 9 | border: 1px solid #dfdfdf; 10 | border-top: none; 11 | -webkit-box-shadow: 0 3px 6px rgba(0, 0, 0, 0.075); 12 | box-shadow: 0 3px 6px rgba(0, 0, 0, 0.075); 13 | min-width: 17em; 14 | width: auto; 15 | z-index: 1000 !important; 16 | } 17 | 18 | .ui-datepicker * { 19 | padding: 0; 20 | font-family: "Open Sans", sans-serif; 21 | -webkit-border-radius: 0; 22 | -moz-border-radius: 0; 23 | border-radius: 0; 24 | } 25 | 26 | .ui-datepicker table { 27 | font-size: 13px; 28 | margin: 0; 29 | border: none; 30 | border-collapse: collapse; 31 | } 32 | 33 | .ui-datepicker .ui-widget-header, 34 | .ui-datepicker .ui-datepicker-header { 35 | background-image: none; 36 | border: none; 37 | color: #fff; 38 | font-weight: normal; 39 | } 40 | 41 | .ui-datepicker .ui-datepicker-header .ui-state-hover { 42 | background: transparent; 43 | border-color: transparent; 44 | cursor: pointer; 45 | } 46 | 47 | .ui-datepicker .ui-datepicker-title { 48 | margin: 0; 49 | padding: 10px 0; 50 | color: #fff; 51 | font-size: 14px; 52 | line-height: 14px; 53 | text-align: center; 54 | } 55 | 56 | .ui-datepicker .ui-datepicker-prev, 57 | .ui-datepicker .ui-datepicker-next { 58 | position: relative; 59 | top: 0; 60 | height: 34px; 61 | width: 34px; 62 | } 63 | 64 | .ui-datepicker .ui-state-hover.ui-datepicker-prev, 65 | .ui-datepicker .ui-state-hover.ui-datepicker-next { 66 | border: none; 67 | } 68 | 69 | .ui-datepicker .ui-datepicker-prev, 70 | .ui-datepicker .ui-datepicker-prev-hover { 71 | left: 0; 72 | } 73 | 74 | .ui-datepicker .ui-datepicker-next, 75 | .ui-datepicker .ui-datepicker-next-hover { 76 | right: 0; 77 | } 78 | 79 | .ui-datepicker .ui-datepicker-next span, 80 | .ui-datepicker .ui-datepicker-prev span { 81 | display: none; 82 | } 83 | 84 | .ui-datepicker .ui-datepicker-prev { 85 | float: left; 86 | } 87 | 88 | .ui-datepicker .ui-datepicker-next { 89 | float: right; 90 | } 91 | 92 | .ui-datepicker .ui-datepicker-prev:before, 93 | .ui-datepicker .ui-datepicker-next:before { 94 | font: normal 20px/34px 'dashicons'; 95 | padding-left: 7px; 96 | color: #fff; 97 | speak: none; 98 | -webkit-font-smoothing: antialiased; 99 | -moz-osx-font-smoothing: grayscale; 100 | width: 34px; 101 | height: 34px; 102 | } 103 | 104 | .ui-datepicker .ui-datepicker-prev:before { 105 | content: '\f341'; 106 | } 107 | 108 | .ui-datepicker .ui-datepicker-next:before { 109 | content: '\f345'; 110 | } 111 | 112 | .ui-datepicker .ui-datepicker-prev-hover:before, 113 | .ui-datepicker .ui-datepicker-next-hover:before { 114 | opacity: 0.7; 115 | } 116 | 117 | .ui-datepicker select.ui-datepicker-month, 118 | .ui-datepicker select.ui-datepicker-year { 119 | width: 33%; 120 | } 121 | 122 | .ui-datepicker thead { 123 | color: #fff; 124 | font-weight: 600; 125 | } 126 | 127 | .ui-datepicker th { 128 | padding: 10px; 129 | } 130 | 131 | .ui-datepicker td { 132 | padding: 0; 133 | border: 1px solid #f4f4f4; 134 | } 135 | 136 | .ui-datepicker td.ui-datepicker-other-month { 137 | border: transparent; 138 | } 139 | 140 | .ui-datepicker td.ui-datepicker-week-end { 141 | background-color: #f4f4f4; 142 | border: 1px solid #f4f4f4; 143 | } 144 | 145 | .ui-datepicker td.ui-datepicker-today { 146 | background-color: #f0f0c0; 147 | } 148 | 149 | .ui-datepicker td.ui-datepicker-current-day { 150 | background: #bbdd88; 151 | } 152 | 153 | .ui-datepicker td .ui-state-default { 154 | background: transparent; 155 | border: none; 156 | text-align: center; 157 | text-decoration: none; 158 | width: auto; 159 | display: block; 160 | padding: 5px 10px; 161 | font-weight: normal; 162 | color: #444; 163 | } 164 | 165 | .ui-datepicker td.ui-state-disabled .ui-state-default { 166 | opacity: 0.5; 167 | } 168 | 169 | /* Default Color Scheme */ 170 | .ui-datepicker .ui-widget-header, 171 | .ui-datepicker .ui-datepicker-header { 172 | background: #00a0d2; 173 | } 174 | 175 | .ui-datepicker thead { 176 | background: #32373c; 177 | } 178 | 179 | .ui-datepicker td .ui-state-hover { 180 | background: #0073aa; 181 | color: #fff; 182 | } 183 | 184 | /* WordPress Color Schemes */ 185 | 186 | /* Fresh */ 187 | .admin-color-fresh .ui-datepicker .ui-widget-header, 188 | .admin-color-fresh .ui-datepicker .ui-datepicker-header { 189 | background: #00a0d2; 190 | } 191 | 192 | .admin-color-fresh .ui-datepicker thead { 193 | background: #32373c; 194 | } 195 | 196 | .admin-color-fresh .ui-datepicker td .ui-state-hover { 197 | background: #0073aa; 198 | color: #fff; 199 | } 200 | 201 | /* Blue */ 202 | .admin-color-blue .ui-datepicker .ui-widget-header, 203 | .admin-color-blue .ui-datepicker .ui-datepicker-header { 204 | background: #52accc; 205 | } 206 | 207 | .admin-color-blue .ui-datepicker thead { 208 | background: #4796b3; 209 | } 210 | 211 | .admin-color-blue .ui-datepicker td .ui-state-hover { 212 | background: #096484; 213 | color: #fff; 214 | } 215 | 216 | /* Coffee */ 217 | .admin-color-coffee .ui-datepicker .ui-widget-header, 218 | .admin-color-coffee .ui-datepicker .ui-datepicker-header { 219 | background: #59524c; 220 | } 221 | 222 | .admin-color-coffee .ui-datepicker thead { 223 | background: #46403c; 224 | } 225 | 226 | .admin-color-coffee .ui-datepicker td .ui-state-hover { 227 | background: #c7a589; 228 | color: #fff; 229 | } 230 | 231 | /* Ectoplasm */ 232 | .admin-color-ectoplasm .ui-datepicker .ui-widget-header, 233 | .admin-color-ectoplasm .ui-datepicker .ui-datepicker-header { 234 | background: #523f6d; 235 | } 236 | 237 | .admin-color-ectoplasm .ui-datepicker thead { 238 | background: #413256; 239 | } 240 | 241 | .admin-color-ectoplasm .ui-datepicker td .ui-state-hover { 242 | background: #a3b745; 243 | color: #fff; 244 | } 245 | 246 | /* Midnight */ 247 | .admin-color-midnight .ui-datepicker .ui-widget-header, 248 | .admin-color-midnight .ui-datepicker .ui-datepicker-header { 249 | background: #363b3f; 250 | } 251 | 252 | .admin-color-midnight .ui-datepicker thead { 253 | background: #26292c; 254 | } 255 | 256 | .admin-color-midnight .ui-datepicker td .ui-state-hover { 257 | background: #e14d43; 258 | color: #fff; 259 | } 260 | 261 | /* Ocean */ 262 | .admin-color-ocean .ui-datepicker .ui-widget-header, 263 | .admin-color-ocean .ui-datepicker .ui-datepicker-header { 264 | background: #738e96; 265 | } 266 | 267 | .admin-color-ocean .ui-datepicker thead { 268 | background: #627c83; 269 | } 270 | 271 | .admin-color-ocean .ui-datepicker td .ui-state-hover { 272 | background: #9ebaa0; 273 | color: #fff; 274 | } 275 | 276 | /* Sunrise */ 277 | .admin-color-sunrise .ui-datepicker .ui-widget-header, 278 | .admin-color-sunrise .ui-datepicker .ui-datepicker-header, 279 | .admin-color-sunrise .ui-datepicker .ui-datepicker-header .ui-state-hover { 280 | background: #cf4944; 281 | } 282 | 283 | .admin-color-sunrise .ui-datepicker th { 284 | border-color: #be3631; 285 | background: #be3631; 286 | } 287 | 288 | .admin-color-sunrise .ui-datepicker td .ui-state-hover { 289 | background: #dd823b; 290 | color: #fff; 291 | } 292 | 293 | /* Light */ 294 | .admin-color-light .ui-datepicker .ui-widget-header, 295 | .admin-color-light .ui-datepicker .ui-datepicker-header { 296 | background: #e5e5e5; 297 | } 298 | 299 | .admin-color-light .ui-datepicker thead { 300 | background: #888; 301 | } 302 | 303 | .admin-color-light .ui-datepicker .ui-datepicker-title, 304 | .admin-color-light .ui-datepicker td .ui-state-default, 305 | .admin-color-light .ui-datepicker .ui-datepicker-prev:before, 306 | .admin-color-light .ui-datepicker .ui-datepicker-next:before { 307 | color: #555; 308 | } 309 | 310 | .admin-color-light .ui-datepicker td .ui-state-hover { 311 | background: #e5e5e5; 312 | } 313 | 314 | /* bbPress Color Schemes */ 315 | 316 | /* Evergreen */ 317 | .admin-color-bbp-evergreen .ui-datepicker .ui-widget-header, 318 | .admin-color-bbp-evergreen .ui-datepicker .ui-datepicker-header { 319 | background: #56b274; 320 | } 321 | 322 | .admin-color-bbp-evergreen .ui-datepicker thead { 323 | background: #36533f; 324 | } 325 | 326 | .admin-color-bbp-evergreen .ui-datepicker td .ui-state-hover { 327 | background: #446950; 328 | color: #fff; 329 | } 330 | 331 | /* Mint */ 332 | .admin-color-bbp-mint .ui-datepicker .ui-widget-header, 333 | .admin-color-bbp-mint .ui-datepicker .ui-datepicker-header { 334 | background: #4ca26a; 335 | } 336 | 337 | .admin-color-bbp-mint .ui-datepicker thead { 338 | background: #4f6d59; 339 | } 340 | 341 | .admin-color-bbp-mint .ui-datepicker td .ui-state-hover { 342 | background: #5fb37c; 343 | color: #fff; 344 | } -------------------------------------------------------------------------------- /wp-event-calendar/includes/events/taxonomies.php: -------------------------------------------------------------------------------- 1 | __( 'Types', 'wp-event-calendar' ), 22 | 'singular_name' => __( 'Type', 'wp-event-calendar' ), 23 | 'search_items' => __( 'Search Types', 'wp-event-calendar' ), 24 | 'popular_items' => __( 'Popular Types', 'wp-event-calendar' ), 25 | 'all_items' => __( 'All Types', 'wp-event-calendar' ), 26 | 'parent_item' => __( 'Parent Type', 'wp-event-calendar' ), 27 | 'parent_item_colon' => __( 'Parent Type:', 'wp-event-calendar' ), 28 | 'edit_item' => __( 'Edit Type', 'wp-event-calendar' ), 29 | 'view_item' => __( 'View Type', 'wp-event-calendar' ), 30 | 'update_item' => __( 'Update Type', 'wp-event-calendar' ), 31 | 'add_new_item' => __( 'Add New Type', 'wp-event-calendar' ), 32 | 'new_item_name' => __( 'New Type Name', 'wp-event-calendar' ), 33 | 'separate_items_with_commas' => __( 'Separate types with commas', 'wp-event-calendar' ), 34 | 'add_or_remove_items' => __( 'Add or remove types', 'wp-event-calendar' ), 35 | 'choose_from_most_used' => __( 'Choose from the most used types', 'wp-event-calendar' ), 36 | 'no_terms' => __( 'No types', 'wp-event-calendar' ), 37 | 'not_found' => __( 'No types found', 'wp-event-calendar' ), 38 | 'items_list_navigation' => __( 'Types list navigation', 'wp-event-calendar' ), 39 | 'items_list' => __( 'Types list', 'wp-event-calendar' ) 40 | ); 41 | 42 | // Rewrite rules 43 | $rewrite = array( 44 | 'slug' => 'events/type', 45 | 'with_front' => false 46 | ); 47 | 48 | // Capabilities 49 | $caps = array( 50 | 'manage_terms' => 'manage_event_types', 51 | 'edit_terms' => 'edit_event_types', 52 | 'delete_terms' => 'delete_event_types', 53 | 'assign_terms' => 'assign_event_types' 54 | ); 55 | 56 | // Arguments 57 | $args = array( 58 | 'labels' => $labels, 59 | 'rewrite' => $rewrite, 60 | 'capabilities' => $caps, 61 | 'update_count_callback' => '_update_post_term_count', 62 | 'query_var' => 'event-type', 63 | 'show_tagcloud' => true, 64 | 'hierarchical' => false, 65 | 'show_in_nav_menus' => false, 66 | 'public' => false, 67 | 'show_ui' => true 68 | ); 69 | 70 | // Register 71 | register_taxonomy( 'event-type', 'event', $args ); 72 | } 73 | 74 | /** 75 | * Event category taxonomy 76 | * 77 | * @since 0.1.2 78 | */ 79 | function wp_event_calendar_register_category_taxonomy() { 80 | 81 | // Labels 82 | $labels = array( 83 | 'name' => __( 'Categories', 'wp-event-calendar' ), 84 | 'singular_name' => __( 'Category', 'wp-event-calendar' ), 85 | 'search_items' => __( 'Search Categories', 'wp-event-calendar' ), 86 | 'popular_items' => __( 'Popular Categories', 'wp-event-calendar' ), 87 | 'all_items' => __( 'All Categories', 'wp-event-calendar' ), 88 | 'parent_item' => __( 'Parent Category', 'wp-event-calendar' ), 89 | 'parent_item_colon' => __( 'Parent Category:', 'wp-event-calendar' ), 90 | 'edit_item' => __( 'Edit Category', 'wp-event-calendar' ), 91 | 'view_item' => __( 'View Category', 'wp-event-calendar' ), 92 | 'update_item' => __( 'Update Category', 'wp-event-calendar' ), 93 | 'add_new_item' => __( 'Add New Category', 'wp-event-calendar' ), 94 | 'new_item_name' => __( 'New Category Name', 'wp-event-calendar' ), 95 | 'separate_items_with_commas' => __( 'Separate categories with commas', 'wp-event-calendar' ), 96 | 'add_or_remove_items' => __( 'Add or remove categories', 'wp-event-calendar' ), 97 | 'choose_from_most_used' => __( 'Choose from the most used categories', 'wp-event-calendar' ), 98 | 'no_terms' => __( 'No categories', 'wp-event-calendar' ), 99 | 'not_found' => __( 'No categories found', 'wp-event-calendar' ), 100 | 'items_list_navigation' => __( 'Categories list navigation', 'wp-event-calendar' ), 101 | 'items_list' => __( 'Categories list', 'wp-event-calendar' ) 102 | ); 103 | 104 | // Rewrite rules 105 | $rewrite = array( 106 | 'slug' => 'events/category', 107 | 'with_front' => false 108 | ); 109 | 110 | // Capabilities 111 | $caps = array( 112 | 'manage_terms' => 'manage_event_categories', 113 | 'edit_terms' => 'edit_event_categories', 114 | 'delete_terms' => 'delete_event_categories', 115 | 'assign_terms' => 'assign_event_categories' 116 | ); 117 | 118 | // Arguments 119 | $args = array( 120 | 'labels' => $labels, 121 | 'rewrite' => $rewrite, 122 | 'capabilities' => $caps, 123 | 'update_count_callback' => '_update_post_term_count', 124 | 'query_var' => 'event-category', 125 | 'show_tagcloud' => false, 126 | 'hierarchical' => true, 127 | 'show_in_nav_menus' => false, 128 | 'public' => false, 129 | 'show_ui' => true 130 | ); 131 | 132 | // Register 133 | register_taxonomy( 'event-category', 'event', $args ); 134 | } 135 | 136 | /** 137 | * Event tag taxonomy 138 | * 139 | * @since 0.1.2 140 | */ 141 | function wp_event_calendar_register_tag_taxonomy() { 142 | 143 | // Labels 144 | $labels = array( 145 | 'name' => __( 'Tags', 'wp-event-calendar' ), 146 | 'singular_name' => __( 'Tag', 'wp-event-calendar' ), 147 | 'search_items' => __( 'Search Tags', 'wp-event-calendar' ), 148 | 'popular_items' => __( 'Popular Tags', 'wp-event-calendar' ), 149 | 'all_items' => __( 'All Tags', 'wp-event-calendar' ), 150 | 'parent_item' => __( 'Parent Tag', 'wp-event-calendar' ), 151 | 'parent_item_colon' => __( 'Parent Tag:', 'wp-event-calendar' ), 152 | 'edit_item' => __( 'Edit Tag', 'wp-event-calendar' ), 153 | 'view_item' => __( 'View Tag', 'wp-event-calendar' ), 154 | 'update_item' => __( 'Update Tag', 'wp-event-calendar' ), 155 | 'add_new_item' => __( 'Add New Tag', 'wp-event-calendar' ), 156 | 'new_item_name' => __( 'New Tag Name', 'wp-event-calendar' ), 157 | 'separate_items_with_commas' => __( 'Separate tags with commas', 'wp-event-calendar' ), 158 | 'add_or_remove_items' => __( 'Add or remove tags', 'wp-event-calendar' ), 159 | 'choose_from_most_used' => __( 'Choose from the most used tags', 'wp-event-calendar' ), 160 | 'no_terms' => __( 'No tags', 'wp-event-calendar' ), 161 | 'not_found' => __( 'No tags found', 'wp-event-calendar' ), 162 | 'items_list_navigation' => __( 'Tags list navigation', 'wp-event-calendar' ), 163 | 'items_list' => __( 'Tags list', 'wp-event-calendar' ) 164 | ); 165 | 166 | // Rewrite rules 167 | $rewrite = array( 168 | 'slug' => 'events/tag', 169 | 'with_front' => false 170 | ); 171 | 172 | // Capabilities 173 | $caps = array( 174 | 'manage_terms' => 'manage_event_tags', 175 | 'edit_terms' => 'edit_event_tags', 176 | 'delete_terms' => 'delete_event_tags', 177 | 'assign_terms' => 'assign_event_tags' 178 | ); 179 | 180 | // Arguments 181 | $args = array( 182 | 'labels' => $labels, 183 | 'rewrite' => $rewrite, 184 | 'capabilities' => $caps, 185 | 'update_count_callback' => '_update_post_term_count', 186 | 'query_var' => 'event-tag', 187 | 'show_tagcloud' => false, 188 | 'hierarchical' => false, 189 | 'show_in_nav_menus' => false, 190 | 'public' => false, 191 | 'show_ui' => true 192 | ); 193 | 194 | // Register 195 | register_taxonomy( 'event-tag', 'event', $args ); 196 | } 197 | -------------------------------------------------------------------------------- /wp-event-calendar/includes/common/event.php: -------------------------------------------------------------------------------- 1 | 'CONFIRMED', 64 | 'EventCancelled' => 'CANCELLED', 65 | 'EventRescheduled' => 'CONFIRMED', 66 | 'EventPostponed' => 'TENTATIVE', 67 | ); 68 | 69 | /** 70 | * @var string Where the event is happening 71 | * 72 | * @link http://schema.org/location 73 | */ 74 | protected $location; 75 | 76 | /** 77 | * WP_Event_Calendar_Event constructor handles validating the data and 78 | * getting a handle on what the event is doing. This way we can echo the 79 | * object and have it render an ICS file 80 | * 81 | * @todo Can I do PHP primitive type checks here or object checks? 82 | * @todo Use PhysicalLocation to align with Schema.org - check if this is possible 83 | * 84 | * @param DateTime $start_date Start date in ISO-8601 date format 85 | * @param DateTime $end_date End date in ISO-8601 date format. If NULL it will assume an all day event 86 | * @param string $name Name of the event 87 | * @param string $description Description of the event possibly with event details 88 | * @param string $location Simple string of text describing the event location 89 | * @param int $repeat Repetition of the event 90 | * @param string $event_status Defaults to EventScheduled 91 | */ 92 | public function __construct( $start_date = null, $end_date = null, $name = '', $description = '', $location = '', $repeat = 0, $event_status = 'EventScheduled' ) { 93 | 94 | // Check if start_date is instance of DateTime object 95 | if ( $start_date instanceof DateTime ) { 96 | $this->start_date = $start_date; 97 | 98 | // Assumes we are at least passing some date through 99 | } elseif ( ! empty( $start_date ) ) { 100 | $this->start_date = new DateTime( $start_date ); 101 | 102 | // Bail if event has no start_date 103 | } else { 104 | return; 105 | } 106 | 107 | // If the end_date is null, this is an all_day event 108 | if ( is_null( $end_date ) ) { 109 | $this->start_date->setTime( 0, 0, 0 ); // Set to beginning of day (all day event) 110 | $this->end_date = clone $this->start_date; 111 | $this->end_date->setTime( 23, 59, 59 ); // EOD 112 | $this->all_day = true; 113 | 114 | // End date exists 115 | } elseif ( $end_date instanceof DateTime ) { 116 | $this->end_date = $end_date; 117 | 118 | // End dates are not required - if empty we want to skip added business logic 119 | } elseif ( empty( $end_date ) ) { 120 | $this->end_date = ''; 121 | 122 | // Assumes we are passing some date through if getting here 123 | } else { 124 | $this->end_date = new DateTime( $end_date ); 125 | } 126 | 127 | // Not doing anything with the rest of the properties for now 128 | $this->name = $name; 129 | $this->description = $description; 130 | $this->event_status = $event_status; 131 | $this->location = $location; 132 | $this->repeat = $repeat; 133 | } 134 | 135 | /** 136 | * This converts the Schema.org enumeration to the RFC spec 137 | * 138 | * @see https://tools.ietf.org/html/rfc5545#section-3.8.1.11 139 | * @see http://schema.org/EventStatusType 140 | * 141 | * @return string Event Status according to RFC5545 142 | */ 143 | protected function event_status_to_rfc() { 144 | 145 | // Make sure the EventStatus is to spec 146 | if ( ! empty( $this->event_status_mapping[ $this->event_status ] ) ) { 147 | return $this->event_status_mapping[ $this->event_status ]; 148 | } 149 | 150 | // Returning EventScheduled/CONFIRMED if spec does not match 151 | return $this->event_status_mapping[ 'EventScheduled' ]; 152 | } 153 | 154 | /** 155 | * Formats the event date according to how the RFC specifies 156 | * 157 | * @see https://tools.ietf.org/html/rfc5545#section-3.3.5 158 | * @param $date DateTime Date to be formatted 159 | * 160 | * @return string date 161 | */ 162 | protected function event_date_to_rfc( $date = '' ) { 163 | 164 | // @todo check WordPress UTC functions 165 | // @todo look at moving this conversion towards the constructor 166 | if ( empty( $date ) || ! ( $date instanceof DateTime ) ) { 167 | return ''; 168 | } 169 | 170 | // 20160119T070000 - not sure about returning Z 171 | return $date->format( 'Ymd\THis' ); 172 | } 173 | 174 | /** 175 | * Returns the event recurrence in accordance with the RFC 176 | * 177 | * @see https://tools.ietf.org/html/rfc5545#section-3.3.10 178 | * 179 | * @return string Frequency of repeated event 180 | */ 181 | protected function event_recurrance_to_rfc() { 182 | 183 | // No recurrance by default 184 | $frequency = ''; 185 | 186 | // Check how our event repeats 187 | switch ( $this->repeat ) { 188 | case 10: 189 | $frequency = 'FREQ=WEEKLY'; 190 | break; 191 | case 100: 192 | $frequency = 'FREQ=MONTHLY'; 193 | break; 194 | case 1000: 195 | $frequency = 'FREQ=YEARLY'; 196 | break; 197 | } 198 | 199 | // Get the UNTIL portion if the frequency and end_date are not empty 200 | if ( ! empty( $frequency ) && ! empty( $this->end_date ) ) { 201 | $frequency .= ';UNTIL=' . $this->event_date_to_rfc( $this->end_date ); 202 | } 203 | 204 | return $frequency; 205 | } 206 | 207 | /** 208 | * All of the array keys are VEVENT properties as part of the RFC. If 209 | * the property is empty() it will just be ignored in the end. They are 210 | * here to be filled in eventually and as a reminder that we can expand 211 | * our implementation of the spec. 212 | * 213 | * Note: Keeping them in the order of the RFC spec for now 214 | * 215 | * @return array Array of key/value pairs to match the RFC spec 216 | */ 217 | protected function event_to_rfc() { 218 | return array( 219 | 220 | // Required 221 | 'dtstamp' => '', // required if using 'METHOD' - which we aren't 222 | 'dtstart' => $this->event_date_to_rfc( $this->start_date ), // the only real required property 223 | 224 | // Optional 225 | 'class' => '', 226 | 'created' => '', 227 | 'description' => $this->description, 228 | 'geo' => '', 229 | 'last-mod' => '', 230 | 'location' => $this->location, 231 | 'organizer' => '', 232 | 'priority' => '', 233 | 'seq' => '', 234 | 'status' => $this->event_status_to_rfc(), 235 | 'summary' => $this->name, // title of the event? 236 | 'transp' => '', 237 | 'url' => '', 238 | 'recurid' => '', 239 | 'rrule' => $this->event_recurrance_to_rfc(), 240 | 'dtend' => $this->event_date_to_rfc( $this->end_date ), 241 | 'duration' => '', 242 | 'attach' => '', 243 | 'attendee' => '', 244 | 'categories' => '', 245 | 'comment' => '', 246 | 'contact' => '', 247 | 'exdate' => '', 248 | 'rstatus' => '', 249 | 'related' => '', 250 | 'resources' => '', 251 | 'rdate' => '', 252 | 'x-prop' => '', 253 | 'iana-prop' => '' 254 | ); 255 | } 256 | 257 | /** 258 | * The object will return an ICS VEVENT for use in an iCal. This formats the 259 | * object according to RFC5545. 260 | * 261 | * @see https://tools.ietf.org/html/rfc5545#section-3.6.1 262 | * 263 | * @return string ICS Formatted VEVENT 264 | */ 265 | public function __toString() { 266 | 267 | // Get the event as an array of keys & values to match the RFC 268 | $vevent = $this->event_to_rfc(); 269 | 270 | // Start going through the array and building the event 271 | $event = 'BEGIN:VEVENT' . PHP_EOL; 272 | 273 | foreach ( $vevent as $key => $value ) { 274 | if ( ! empty( $value ) ) { 275 | $event .= strtoupper( $key ) . ':' . $value . PHP_EOL; 276 | } 277 | } 278 | 279 | $event .= 'END:VEVENT' . PHP_EOL; 280 | 281 | return apply_filters( 'wp_event_calendar_event', $event, $vevent ); 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /wp-event-calendar/includes/common/list-table-day.php: -------------------------------------------------------------------------------- 1 | day_start = strtotime( 'midnight', $this->today ); 61 | $this->day_end = strtotime( 'tomorrow', $this->day_start ); 62 | 63 | // Setup the day ranges 64 | $this->view_start = date_i18n( 'Y-m-d H:i:s', $this->day_start ); 65 | $this->view_end = date_i18n( 'Y-m-d H:i:s', $this->day_end ); 66 | } 67 | 68 | /** 69 | * Setup the list-table's columns 70 | * 71 | * @see WP_List_Table::single_row_columns() 72 | * 73 | * @return array An associative array containing column information 74 | */ 75 | public function get_columns() { 76 | 77 | // Lowercase day, for column key 78 | $day = strtolower( date( 'l', $this->day_start ) ); 79 | 80 | // Return Week & Day 81 | return array( 82 | 'hour' => sprintf( esc_html__( 'Wk. %s', 'wp-event-calendar' ), date_i18n( 'W', $this->today ) ), 83 | $day => date_i18n( 'l, F j, Y', $this->day_start ), 84 | ); 85 | } 86 | 87 | /** 88 | * Get a list of CSS classes for the list table table tag. 89 | * 90 | * @since 0.1.8 91 | * @access protected 92 | * 93 | * @return array List of CSS classes for the table tag. 94 | */ 95 | protected function get_table_classes() { 96 | return array( 'widefat', 'fixed', 'striped', 'calendar', 'day', $this->_args['plural'] ); 97 | } 98 | 99 | /** 100 | * Add a post to the items array, keyed by day 101 | * 102 | * @todo Repeat & expire 103 | * 104 | * @since 0.1.8 105 | * 106 | * @param object $post 107 | * @param int $max 108 | */ 109 | protected function setup_item( $post = false, $max = 10 ) { 110 | 111 | // Calculate start day 112 | if ( ! empty( $this->item_start ) ) { 113 | $start_day = date_i18n( 'j', $this->item_start ); 114 | $start_hour = date_i18n( 'G', $this->item_start ); 115 | } else { 116 | $start_day = 0; 117 | $start_hour = 0; 118 | } 119 | 120 | // Calculate end day 121 | if ( ! empty( $this->item_end ) ) { 122 | $end_day = date_i18n( 'j', $this->item_end ); 123 | $end_hour = date_i18n( 'G', $this->item_end ); 124 | } else { 125 | $end_day = $start_day; 126 | $end_hour = $start_hour; 127 | } 128 | 129 | // All day events (and days that overlap multiple days) 130 | if ( ( true === $this->item_all_day ) || ( $this->item_days > 1 ) || ( $start_day !== $end_day ) ) { 131 | 132 | // What type of item is this? 133 | $type = ( true === $this->item_all_day ) 134 | ? 'all_day_items' 135 | : 'multi_day_items'; 136 | 137 | // Math the heck out of this 138 | $interval = 1; 139 | $offset = 0; 140 | $cell = $offset; 141 | $end_cell = ( $end_day * $start_day ) + $offset; 142 | 143 | // Regular single-day events 144 | } else { 145 | $type = 'items'; 146 | $interval = 1; 147 | 148 | // Calculate the cell offset 149 | $offset = intval( ( $this->item_start - $this->day_start ) / DAY_IN_SECONDS ); 150 | $cell = ( $start_hour * $interval ) + $offset; 151 | $end_cell = ( $end_hour * $interval ) + $offset; 152 | } 153 | 154 | // Loop through days 155 | while ( $cell <= $end_cell ) { 156 | 157 | // Setup the pointer for each day 158 | $this->setup_pointer( $post, $cell ); 159 | 160 | // Add post to items for each day in it's duration 161 | if ( empty( $this->{$type}[ $cell ] ) || ( $max > count( $this->{$type}[ $cell ] ) ) ) { 162 | $this->{$type}[ $cell ][ $post->ID ] = $post; 163 | } 164 | 165 | // Bump the hour 166 | $cell = intval( $cell + $interval ); 167 | } 168 | } 169 | 170 | /** 171 | * Paginate through days & weeks 172 | * 173 | * @since 0.1.8 174 | * 175 | * @param array $args 176 | */ 177 | protected function pagination( $args = array() ) { 178 | 179 | // Parse args 180 | $r = wp_parse_args( $args, array( 181 | 'small' => '1 day', 182 | 'large' => '1 week', 183 | 'labels' => array( 184 | 'next_small' => esc_html__( 'Tomorrow', 'wp-event-calendar' ), 185 | 'next_large' => esc_html__( 'Next Week', 'wp-event-calendar' ), 186 | 'prev_small' => esc_html__( 'Yerterday', 'wp-event-calendar' ), 187 | 'prev_large' => esc_html__( 'Previous Week', 'wp-event-calendar' ) 188 | ) 189 | ) ); 190 | 191 | // Return pagination 192 | return parent::pagination( $r ); 193 | } 194 | 195 | /** 196 | * Output a special row for "All Day" events 197 | * 198 | * @since 0.1.8 199 | */ 200 | protected function get_all_day_row() { 201 | 202 | // Start an output buffer 203 | ob_start(); ?> 204 | 205 | 206 | 207 | get_day_row_cell( 0, 'all_day_items' ); ?> 208 | 209 | 210 | 225 | 226 | 227 | 228 | get_day_row_cell( 0, 'multi_day_items' ); ?> 229 | 230 | 231 | 246 | 247 |
248 | get_posts_for_cell( $iterator, $type ); ?> 249 |
250 | 251 | 279 | 280 | 281 | 282 | 297 | 298 | 299 | 300 | 315 | 316 | 317 |
318 | get_posts_for_cell( $iterator ); ?> 319 |
320 | 321 | 322 | month, $this->day, $this->year ); 337 | $this_month = getdate( $timestamp ); 338 | $start_day = $this_month['wday']; 339 | 340 | // All day events 341 | echo $this->get_all_day_row(); 342 | 343 | // Multi day events 344 | echo $this->get_multi_day_row(); 345 | 346 | // Loop through days of the month 347 | for ( $i = 0; $i <= ( 24 - 1 ); $i++ ) { 348 | 349 | // Get timestamp & hour 350 | $timestamp = mktime( $i, 0, 0, $this->month, $this->day, $this->year ); 351 | 352 | // New row 353 | echo $this->get_row_start( $timestamp ); 354 | 355 | // Get this table cell 356 | echo $this->get_row_cell( $i, $start_day ); 357 | 358 | // Close row 359 | echo $this->get_row_end(); 360 | } 361 | } 362 | } 363 | -------------------------------------------------------------------------------- /wp-event-calendar/includes/common/list-table-week.php: -------------------------------------------------------------------------------- 1 | week_start = strtotime( $where_is_thumbkin, $this->today ); 66 | $this->week_end = strtotime( 'this Saturday midnight', $this->today ); 67 | 68 | // Setup the week ranges 69 | $this->view_start = date_i18n( 'Y-m-d H:i:s', $this->week_start ); 70 | $this->view_end = date_i18n( 'Y-m-d H:i:s', $this->week_end ); 71 | } 72 | 73 | /** 74 | * Setup the list-table's columns 75 | * 76 | * @see WP_List_Table::single_row_columns() 77 | * 78 | * @return array An associative array containing column information 79 | */ 80 | public function get_columns() { 81 | return array( 82 | 'hour' => sprintf( esc_html__( 'Wk. %s', 'wp-event-calendar' ), date_i18n( 'W', $this->today ) ), 83 | 'sunday' => date_i18n( 'D, M. j', $this->week_start ), 84 | 'monday' => date_i18n( 'D, M. j', $this->week_start + ( DAY_IN_SECONDS * 1 ) ), 85 | 'tuesday' => date_i18n( 'D, M. j', $this->week_start + ( DAY_IN_SECONDS * 2 ) ), 86 | 'wednesday' => date_i18n( 'D, M. j', $this->week_start + ( DAY_IN_SECONDS * 3 ) ), 87 | 'thursday' => date_i18n( 'D, M. j', $this->week_start + ( DAY_IN_SECONDS * 4 ) ), 88 | 'friday' => date_i18n( 'D, M. j', $this->week_start + ( DAY_IN_SECONDS * 5 ) ), 89 | 'saturday' => date_i18n( 'D, M. j', $this->week_end ) 90 | ); 91 | } 92 | 93 | /** 94 | * Get a list of CSS classes for the list table table tag. 95 | * 96 | * @since 0.1.8 97 | * @access protected 98 | * 99 | * @return array List of CSS classes for the table tag. 100 | */ 101 | protected function get_table_classes() { 102 | return array( 'widefat', 'fixed', 'striped', 'calendar', 'week', $this->_args['plural'] ); 103 | } 104 | 105 | /** 106 | * Add a post to the items array, keyed by day 107 | * 108 | * @todo Repeat & expire 109 | * 110 | * @since 0.1.8 111 | * 112 | * @param object $post 113 | * @param int $max 114 | */ 115 | protected function setup_item( $post = false, $max = 10 ) { 116 | 117 | // Calculate start day 118 | if ( ! empty( $this->item_start ) ) { 119 | $start_day = date_i18n( 'j', $this->item_start ); 120 | $start_hour = date_i18n( 'G', $this->item_start ); 121 | } else { 122 | $start_day = 0; 123 | $start_hour = 0; 124 | } 125 | 126 | // Calculate end day 127 | if ( ! empty( $this->item_end ) ) { 128 | $end_day = date_i18n( 'j', $this->item_end ); 129 | $end_hour = date_i18n( 'G', $this->item_end ); 130 | } else { 131 | $end_day = $start_day; 132 | $end_hour = $start_hour; 133 | } 134 | 135 | // All day events (and days that overlap multiple days) 136 | if ( ( true === $this->item_all_day ) || ( $this->item_days > 1 ) || ( $start_day !== $end_day ) ) { 137 | 138 | // What type of item is this? 139 | $type = ( true === $this->item_all_day ) 140 | ? 'all_day_items' 141 | : 'multi_day_items'; 142 | 143 | // Math the heck out of this 144 | $interval = 1; 145 | $offset = - ceil( ( $this->week_start - $this->item_start ) / DAY_IN_SECONDS ); 146 | $cell = $offset; 147 | $end_cell = ( $end_day - $start_day ) + $offset; 148 | 149 | // Regular single-day events 150 | } else { 151 | $type = 'items'; 152 | $interval = 7; 153 | 154 | // Calculate the cell offset 155 | $offset = intval( ( $this->item_start - $this->week_start ) / DAY_IN_SECONDS ); 156 | 157 | // Start the days loop with the start day 158 | $cell = ( $start_hour * $interval ) + $offset; 159 | 160 | // Maybe don't include the end hour 161 | $end_cell = ( $end_hour * $interval ) + $offset; 162 | } 163 | 164 | // Loop through days 165 | while ( $cell <= $end_cell ) { 166 | 167 | // Setup the pointer for each day 168 | $this->setup_pointer( $post, $cell ); 169 | 170 | // Add post to items for each day in it's duration 171 | if ( empty( $this->{$type}[ $cell ] ) || ( $max > count( $this->{$type}[ $cell ] ) ) ) { 172 | $this->{$type}[ $cell ][ $post->ID ] = $post; 173 | } 174 | 175 | // Bump the hour 176 | $cell = intval( $cell + $interval ); 177 | } 178 | } 179 | 180 | /** 181 | * Paginate through months & years 182 | * 183 | * @since 0.1.8 184 | * 185 | * @param array $args 186 | */ 187 | protected function pagination( $args = array() ) { 188 | 189 | // Parse args 190 | $r = wp_parse_args( $args, array( 191 | 'small' => '1 week', 192 | 'large' => '1 month', 193 | 'labels' => array( 194 | 'next_small' => esc_html__( 'Next Week', 'wp-event-calendar' ), 195 | 'next_large' => esc_html__( 'Next Month', 'wp-event-calendar' ), 196 | 'prev_small' => esc_html__( 'Previous Week', 'wp-event-calendar' ), 197 | 'prev_large' => esc_html__( 'Previous Month', 'wp-event-calendar' ) 198 | ) 199 | ) ); 200 | 201 | // Return pagination 202 | return parent::pagination( $r ); 203 | } 204 | 205 | /** 206 | * Start the week with a table row, and a th to show the time 207 | * 208 | * @since 0.1.8 209 | */ 210 | protected function get_all_day_row() { 211 | 212 | // Items that last an entire day, maybe for multiple 213 | $type = 'all_day_items'; 214 | 215 | // Start an output buffer 216 | ob_start(); ?> 217 | 218 | 219 | 220 | get_day_row_cell( 0, $type ); ?> 221 | get_day_row_cell( 1, $type ); ?> 222 | get_day_row_cell( 2, $type ); ?> 223 | get_day_row_cell( 3, $type ); ?> 224 | get_day_row_cell( 4, $type ); ?> 225 | get_day_row_cell( 5, $type ); ?> 226 | get_day_row_cell( 6, $type ); ?> 227 | 228 | 229 | 247 | 248 | 249 | 250 | get_day_row_cell( 0, $type ); ?> 251 | get_day_row_cell( 1, $type ); ?> 252 | get_day_row_cell( 2, $type ); ?> 253 | get_day_row_cell( 3, $type ); ?> 254 | get_day_row_cell( 4, $type ); ?> 255 | get_day_row_cell( 5, $type ); ?> 256 | get_day_row_cell( 6, $type ); ?> 257 | 258 | 259 | 274 | 275 |
276 | get_posts_for_cell( $iterator, $type ); ?> 277 |
278 | 279 | 307 | 308 | 309 | 310 | 325 | 326 | 327 | 328 | 343 | 344 | 345 |
346 | get_posts_for_cell( $iterator ); ?> 347 |
348 | 349 | 350 | month, $this->day, $this->year ); 365 | $this_month = getdate( $timestamp ); 366 | $start_day = $this_month['wday']; 367 | 368 | // All day events 369 | echo $this->get_all_day_row(); 370 | 371 | // Multi day events 372 | echo $this->get_multi_day_row(); 373 | 374 | // Loop through hours in days of week 375 | for ( $i = 0; $i <= ( 7 * 24 ) - 1; $i++ ) { 376 | 377 | // Get timestamp & hour 378 | $timestamp = mktime( ( $i / 7 ), 0, 0, $this->month, $this->day, $this->year ); 379 | 380 | // New row 381 | if ( ( $i % 7 ) === 0 ) { 382 | echo $this->get_row_start( $timestamp ); 383 | } 384 | 385 | // Get this table cell 386 | echo $this->get_row_cell( $i, $start_day ); 387 | 388 | // Close row 389 | if ( ( $i % 7 ) === 6 ) { 390 | echo $this->get_row_end(); 391 | } 392 | } 393 | } 394 | } 395 | -------------------------------------------------------------------------------- /wp-event-calendar/includes/events/admin.php: -------------------------------------------------------------------------------- 1 | post_type ) { 26 | $title = esc_html__( 'Name this event', 'wp-event-calendar' ); 27 | } 28 | 29 | // Return possibly modified title 30 | return $title; 31 | } 32 | 33 | /** 34 | * Disable months dropdown 35 | * 36 | * @since 0.1.2 37 | */ 38 | function wp_event_calendar_disable_months_dropdown( $disabled = false, $post_type = 'post' ) { 39 | 40 | // Disable dropdown for events 41 | if ( 'event' === $post_type ) { 42 | $disabled = true; 43 | } 44 | 45 | // Return maybe modified value 46 | return $disabled; 47 | } 48 | 49 | /** 50 | * Output dropdowns & filters 51 | * 52 | * @since 0.1.2 53 | */ 54 | function wp_event_calendar_add_dropdown_filters( $post_type = '' ) { 55 | 56 | // Bail if not the event post type 57 | if ( 'event' !== $post_type ) { 58 | return; 59 | } 60 | 61 | // Bail if event type taxonomy was unregistered 62 | if ( ! is_object_in_taxonomy( 'event', 'event-type' ) ) { 63 | return; 64 | } 65 | 66 | // Get registered taxonomies 67 | $taxonomies = get_object_taxonomies( 'event', 'objects' ); 68 | 69 | // Loop through query vars 70 | foreach ( $taxonomies as $taxonomy ) { 71 | 72 | // Is this taxonomy being queried? 73 | $selected = isset( $_GET[ $taxonomy->query_var ] ) 74 | ? sanitize_key( $_GET[ $taxonomy->query_var ] ) 75 | : ''; 76 | 77 | // Output label 78 | echo ''; 79 | 80 | // Output dropdown 81 | wp_dropdown_categories( array( 82 | 'show_option_none' => $taxonomy->labels->all_items, 83 | 'option_none_value' => 0, 84 | 'hide_empty' => false, 85 | 'hierarchical' => false, 86 | 'taxonomy' => $taxonomy->name, 87 | 'show_count' => 0, 88 | 'orderby' => 'name', 89 | 'value_field' => 'slug', 90 | 'name' => $taxonomy->query_var, 91 | 'selected' => $selected 92 | ) ); 93 | } 94 | } 95 | 96 | /** 97 | * Filter events posts list-table columns 98 | * 99 | * @since 0.1.2 100 | * 101 | * @param array $old_columns 102 | * @return array 103 | */ 104 | function wp_event_calendar_manage_posts_columns( $old_columns = array() ) { 105 | 106 | // New columns 107 | $new_columns = array( 108 | 'cb' => '', 109 | 'title' => esc_html__( 'Event', 'wp-event-calendar' ), 110 | 'start' => esc_html__( 'Starts', 'wp-event-calendar' ), 111 | 'end' => esc_html__( 'Ends', 'wp-event-calendar' ), 112 | 'duration' => esc_html__( 'Duration', 'wp-event-calendar' ), 113 | 'repeat' => esc_html__( 'Repeats', 'wp-event-calendar' ), 114 | 'event-categories' => esc_html__( 'Categories', 'wp-event-calendar' ), 115 | 'event-types' => esc_html__( 'Types', 'wp-event-calendar' ), 116 | ); 117 | 118 | // Filter & return 119 | return apply_filters( 'wp_event_calendar_manage_posts_columns', $new_columns, $old_columns ); 120 | } 121 | 122 | /** 123 | * Sortable event columns 124 | * 125 | * @since 0.1.2 126 | * 127 | * @param array $columns 128 | * 129 | * @return array 130 | */ 131 | function wp_event_calendar_sortable_columns( $columns = array() ) { 132 | 133 | // Override columns 134 | $columns = array( 135 | 'title' => 'title', 136 | 'start' => 'start_date', 137 | 'end' => 'end_date', 138 | 'repeat' => 'repeat' 139 | ); 140 | 141 | return $columns; 142 | } 143 | 144 | /** 145 | * Set the relevant query vars for sorting posts by our front-end sortables. 146 | * 147 | * @since 0.1.6 148 | * 149 | * @param WP_Query $wp_query The current WP_Query object. 150 | */ 151 | function wp_event_calendar_maybe_sort_by_fields( WP_Query $wp_query ) { 152 | 153 | // Bail if no post_type 154 | if ( empty( $wp_query->query['post_type'] ) ) { 155 | return; 156 | } 157 | 158 | // Bail if several post_type's 159 | if ( is_array( $wp_query->query['post_type'] ) ) { 160 | return; 161 | } 162 | 163 | // Bail if any post type 164 | if ( 'any' === $wp_query->query['post_type'] ) { 165 | return; 166 | } 167 | 168 | // Bail if single post type does not support events 169 | if ( ! post_type_supports( $wp_query->query['post_type'], 'events' ) ) { 170 | return; 171 | } 172 | 173 | // Bail in AJAX for now 174 | if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { 175 | return; 176 | } 177 | 178 | // Some default order values 179 | $order = ! empty( $wp_query->query['order'] ) 180 | ? strtoupper( $wp_query->query['order'] ) 181 | : 'DESC'; 182 | 183 | // Bail if no orderby 184 | if ( empty( $wp_query->query['orderby'] ) ) { 185 | return; 186 | } 187 | 188 | // Set by 'orderby' 189 | switch ( $wp_query->query['orderby'] ) { 190 | 191 | // Skip if title 192 | case 'title' : 193 | break; 194 | 195 | // End 196 | case 'repeat' : 197 | $wp_query->set( 'order', $order ); 198 | $wp_query->set( 'orderby', 'meta_value' ); 199 | $wp_query->set( 'meta_key', 'wp_event_calendar_repeat' ); 200 | $wp_query->set( 'meta_type', 'NUMERIC' ); 201 | break; 202 | 203 | // End 204 | case 'end_date' : 205 | $wp_query->set( 'order', $order ); 206 | $wp_query->set( 'orderby', 'meta_value' ); 207 | //$wp_query->set( 'meta_key', 'wp_event_calendar_end_date_time' ); 208 | //$wp_query->set( 'meta_type', 'DATETIME' ); 209 | break; 210 | 211 | // Start (default) 212 | case 'start_date' : 213 | default : 214 | $_GET['orderby'] = 'start_date'; 215 | $wp_query->set( 'order', $order ); 216 | $wp_query->set( 'orderby', 'meta_value' ); 217 | //$wp_query->set( 'meta_key', 'wp_event_calendar_date_time' ); 218 | //$wp_query->set( 'meta_type', 'DATETIME' ); 219 | break; 220 | } 221 | } 222 | 223 | /** 224 | * Set the relevant query vars for filtering posts by our front-end filters. 225 | * 226 | * @since 0.1.0 227 | * 228 | * @param WP_Query $wp_query The current WP_Query object. 229 | */ 230 | function wp_event_calendar_maybe_filter_by_fields( WP_Query $wp_query ) { 231 | 232 | // Bail if not 'event' post type 233 | if ( empty( $wp_query->query['post_type'] ) || ! in_array( 'event', (array) $wp_query->query['post_type'], true ) ) { 234 | return; 235 | } 236 | 237 | // Event statuses 238 | if ( ! empty( $_GET['post_status'] ) ) { 239 | $wp_query->post_status = sanitize_key( $_GET['post_status'] ); 240 | } 241 | 242 | // Get taxonomies 243 | $taxonomies = get_object_taxonomies( 'event', 'objects' ); 244 | $tax_query = array(); 245 | 246 | // Loop through query vars 247 | foreach ( $taxonomies as $taxonomy ) { 248 | 249 | // Skip if not set 250 | if ( empty( $_GET[ $taxonomy->query_var ] ) ) { 251 | continue; 252 | } 253 | 254 | // Add to taxonomy query 255 | $tax_query[] = array( 256 | 'taxonomy' => $taxonomy->name, 257 | 'field' => 'slug', 258 | 'terms' => sanitize_key( $_GET[ $taxonomy->query_var ] ) 259 | ); 260 | } 261 | 262 | // Maybe set tax_query 263 | if ( ! empty( $tax_query ) ) { 264 | $wp_query->set( 'tax_query', $tax_query ); 265 | } 266 | } 267 | 268 | /** 269 | * Output content for each event column 270 | * 271 | * @since 0.1.2 272 | * 273 | * @param string $column 274 | * @param int $post_id 275 | */ 276 | function wp_event_calendar_manage_custom_column_data( $column = '', $post_id = 0 ) { 277 | 278 | // Get post & metadata 279 | $post = get_post( $post_id ); 280 | 281 | // Custom column IDs 282 | switch ( $column ) { 283 | 284 | // Type 285 | case 'event-types' : 286 | echo wp_get_event_taxonomy_column_data( $post, 'event-type' ); 287 | break; 288 | 289 | // Category 290 | case 'event-categories' : 291 | echo wp_get_event_taxonomy_column_data( $post, 'event-category' ); 292 | break; 293 | 294 | // Starts 295 | case 'start' : 296 | echo wp_get_event_start_date_time( $post ); 297 | break; 298 | 299 | // Ends 300 | case 'end' : 301 | echo wp_get_event_end_date_time( $post ); 302 | break; 303 | 304 | // Duration 305 | case 'duration' : 306 | echo wp_get_event_duration( $post ); 307 | break; 308 | 309 | // Repeat 310 | case 'repeat' : 311 | $repeat = get_post_meta( $post->ID, 'wp_event_calendar_repeat', true ); 312 | switch( $repeat ) { 313 | case '1000' : 314 | esc_html_e( 'Yearly', 'wp-event-calendar' ); 315 | break; 316 | case '100' : 317 | esc_html_e( 'Monthly', 'wp-event-calendar' ); 318 | break; 319 | case '10' : 320 | esc_html_e( 'Weekly', 'wp-event-calendar' ); 321 | break; 322 | case '0' : 323 | default : 324 | esc_html_e( 'Never', 'wp-event-calendar' ); 325 | break; 326 | } 327 | break; 328 | } 329 | } 330 | 331 | /** 332 | * Helper function for getting column data for event taxonomies 333 | * 334 | * @since 0.1.5 335 | * 336 | * @param int $post 337 | * @param string $taxonomy 338 | * @return string 339 | */ 340 | function wp_get_event_taxonomy_column_data( $post = false, $taxonomy = '' ) { 341 | 342 | // Get post & taxonomy 343 | $post = get_post( $post ); 344 | $taxonomy_object = get_taxonomy( $taxonomy ); 345 | $terms = get_the_terms( $post->ID, $taxonomy ); 346 | 347 | // Event statuses 348 | $post_status = ! empty( $_GET['post_status'] ) 349 | ? array( 'post_status' => sanitize_key( $_GET['post_status'] ) ) 350 | : array( 'post_status' => 'all' ); 351 | 352 | // Has terms 353 | if ( is_array( $terms ) ) { 354 | $out = array(); 355 | 356 | // Loop through terms and create links 357 | foreach ( $terms as $t ) { 358 | 359 | // Reset default query variables 360 | $posts_in_term_qv = $post_status; 361 | 362 | // Set the post type 363 | if ( 'post' !== $post->post_type ) { 364 | $posts_in_term_qv['post_type'] = $post->post_type; 365 | } 366 | 367 | // Set the query variables 368 | if ( $taxonomy_object->query_var ) { 369 | $posts_in_term_qv[ $taxonomy_object->query_var ] = $t->slug; 370 | } else { 371 | $posts_in_term_qv['taxonomy'] = $taxonomy; 372 | $posts_in_term_qv['term'] = $t->slug; 373 | } 374 | 375 | // Add the term to array of links 376 | $out[] = sprintf( '%s', 377 | esc_url( add_query_arg( $posts_in_term_qv, 'edit.php' ) ), 378 | esc_html( sanitize_term_field( 'name', $t->name, $t->term_id, $taxonomy, 'display' ) ) 379 | ); 380 | } 381 | 382 | /* translators: used between list items, there is a space after the comma */ 383 | $retval = join( __( ', ', 'wp-event-calendar' ), $out ); 384 | 385 | // No terms 386 | } else { 387 | $retval = '' . esc_html( $taxonomy_object->labels->no_terms ) . ''; 388 | } 389 | 390 | return apply_filters( 'wp_event_calendar_taxonomy_column_data', $retval, $post, $taxonomy ); 391 | } 392 | 393 | /** 394 | * Enqueue scripts 395 | * 396 | * @since 0.1.0 397 | */ 398 | function wp_event_calendar_admin_event_assets() { 399 | 400 | // Bail if not an event post type 401 | if ( ! post_type_supports( get_post_type(), 'events' ) ) { 402 | return; 403 | } 404 | 405 | // Enqueue the date picker 406 | wp_enqueue_script( 'jquery-ui-datepicker' ); 407 | 408 | $url = wp_event_calendar_get_plugin_url(); 409 | $ver = wp_event_calendar_get_asset_version(); 410 | 411 | // Date picker CSS (for jQuery UI calendar) 412 | wp_enqueue_style( 'wp_event_calendar_datepicker', $url . 'assets/css/datepicker.css', false, $ver, false ); 413 | 414 | // Datepicker & event JS 415 | wp_enqueue_script( 'wp_event_calendar_all_event', $url . 'assets/js/event.js', array( 'jquery' ), $ver, true ); 416 | } 417 | 418 | /** 419 | * Hides the inline-edit-group from the admin form 420 | * 421 | * @since 0.4.1 422 | */ 423 | function wp_event_calendar_hide_quick_bulk_edit() { 424 | 425 | // Bail if not an event post type 426 | if ( ! post_type_supports( get_post_type(), 'events' ) ) { 427 | return; 428 | } 429 | 430 | ?> 431 | 439 | esc_html__( 'Close', 'wp-event-calendar' ), 59 | ) ); 60 | } 61 | 62 | /** 63 | * This tells WordPress to highlight the Events > Calendar submenu. 64 | * 65 | * @since 0.1.0 66 | * 67 | * @global string $plugin_page 68 | * @global array $submenu 69 | */ 70 | function wp_event_calendar_admin_submenu_highlight() { 71 | global $plugin_page, $submenu_file; 72 | 73 | // Highlight both, since they're the same thing. 74 | if ( in_array( $plugin_page, array( 'event-calendar', 'event_page_calendar' ) ) ) { 75 | $submenu_file = $plugin_page; 76 | } 77 | } 78 | 79 | /** 80 | * Admin screen options 81 | * 82 | * @since 0.1.0 83 | */ 84 | function wp_event_calendar_admin_add_screen_options() { 85 | 86 | // columns screen option 87 | add_screen_option( 'layout_type', array( 88 | 'label' => _x( 'Layout', 'Month, Week, or Day (screen options)', 'wp-event-calendar' ), 89 | 'default' => 'month', 90 | 'option' => 'calendar_layout' 91 | ) ); 92 | 93 | // Events per day 94 | add_screen_option( 'per_page', array( 95 | 'label' => _x( 'Events per day', 'Events per day (screen options)', 'wp-event-calendar' ), 96 | 'default' => 10, 97 | 'option' => 'edit_calendar_per_day' 98 | ) ); 99 | } 100 | 101 | /** 102 | * Admin help tabs 103 | * 104 | * @since 0.1.0 105 | */ 106 | function wp_event_calendar_admin_add_help_tabs() { 107 | 108 | // Bail if not an Event type screen 109 | if ( 'event' !== get_current_screen()->post_type ) { 110 | return; 111 | } 112 | 113 | // Bail if viewing a taxonomy 114 | if ( get_current_screen()->taxonomy ) { 115 | return; 116 | } 117 | 118 | // Calendar 119 | get_current_screen()->add_help_tab( array( 120 | 'id' => 'calendar', 121 | 'title' => esc_html__( 'Calendar', 'wp-event-calendar' ), 122 | 'content' => 123 | '

' . esc_html__( 'This is a calendar that lays out your content chronologically.', 'wp-event-calendar' ) . '

' . 127 | 128 | '

' . esc_html__( 'Events', 'wp-event-calendar' ) . '

' ) ); 133 | 134 | // Month View 135 | get_current_screen()->add_help_tab( array( 136 | 'id' => 'month', 137 | 'title' => esc_html__( '— Month', 'wp-event-calendar' ), 138 | 'content' => 139 | '

' . esc_html__( 'This is a traditional monthly calendar view.', 'wp-event-calendar' ) . '

' . 143 | 144 | '

' . esc_html__( 'Navigation', 'wp-event-calendar' ) . '

' 149 | ) ); 150 | 151 | // Month View 152 | get_current_screen()->add_help_tab( array( 153 | 'id' => 'week', 154 | 'title' => __( '— Week', 'wp-event-calendar' ), 155 | 'content' => 156 | '

' . esc_html__( 'This is a traditional weekly calendar view.', 'wp-event-calendar' ) . '

' . 160 | 161 | '

' . esc_html__( 'Navigation', 'wp-event-calendar' ) . '

' 166 | ) ); 167 | 168 | // Month View 169 | get_current_screen()->add_help_tab( array( 170 | 'id' => 'day', 171 | 'title' => __( '— Day', 'wp-event-calendar' ), 172 | 'content' => 173 | '

' . esc_html__( 'This is a traditional daily calendar view.', 'wp-event-calendar' ) . '

' . 177 | 178 | '

' . esc_html__( 'Navigation', 'wp-event-calendar' ) . '

' 183 | ) ); 184 | 185 | // Help Sidebar 186 | get_current_screen()->set_help_sidebar( 187 | '

' . esc_html__( 'Regular Event', 'wp-event-calendar' ) . '

' . 188 | '

' . esc_html__( 'Has Location', 'wp-event-calendar' ) . '

' . 189 | '

' . esc_html__( 'All Day', 'wp-event-calendar' ) . '

' . 190 | '

' . esc_html__( 'Recurring', 'wp-event-calendar' ) . '

' . 191 | '

' . esc_html__( 'Trashed', 'wp-event-calendar' ) . '

' . 192 | '

' . esc_html__( 'Private', 'wp-event-calendar' ) . '

' . 193 | '

' . esc_html__( 'Protected', 'wp-event-calendar' ) . '

' 194 | ); 195 | } 196 | 197 | /** 198 | * Reorder "Events" submenu items to reprioritize "Calendar" 199 | * 200 | * @since 0.1.0 201 | * 202 | * @param array $menu_order 203 | * 204 | * @return array 205 | */ 206 | function wp_event_calendar_change_menu_order( $menu_order = array() ) { 207 | global $submenu; 208 | 209 | // Setup a bunch of empty arrays 210 | $neighbors = $calendar_menus = $calendar_submenus = array(); 211 | 212 | // Get allowed post types 213 | $allowed_types = wp_event_calendar_allowed_post_types(); 214 | 215 | // Loop through allowed types 216 | foreach ( $allowed_types as $type ) { 217 | 218 | // Find "All" links 219 | if ( 'post' === $type ) { 220 | $pt = 'edit.php'; 221 | } else { 222 | $pt = 'edit.php?post_type=' . $type; 223 | } 224 | 225 | $neighbors[] = $pt; 226 | 227 | // Custom menus 228 | $calendar_menus[] = $type . '-calendar'; 229 | 230 | // The calendar submenu arrays 231 | $calendar_submenus[ $pt ] = array( 232 | esc_html__( 'Calendar', 'wp-event-calendar' ), 233 | 'read_calendar', 234 | $type . '-calendar', 235 | esc_html__( 'Calendar', 'wp-event-calendar' ), 236 | ); 237 | } 238 | 239 | // Loop through menu order and do some rearranging 240 | foreach ( $submenu as $parent => $children ) { 241 | 242 | // Skip if no neighborly match 243 | if ( in_array( $parent, $neighbors ) ) { 244 | foreach ( $children as $child => $menu ) { 245 | 246 | // Found a neighbor 247 | if ( in_array( $menu[2], $neighbors ) ) { 248 | unset( $submenu[ $parent ][ $child ] ); 249 | 250 | $submenu[ $parent ][ $child ] = $menu; 251 | $submenu[ $parent ][9] = $calendar_submenus[ $parent ]; 252 | } elseif ( in_array( $menu[2], $calendar_menus ) ) { 253 | unset( $submenu[ $parent ][ $child ] ); 254 | } 255 | } 256 | 257 | ksort( $submenu[ $parent ] ); 258 | } 259 | } 260 | 261 | // Always return the menu order 262 | return $menu_order; 263 | } 264 | 265 | /** 266 | * Output the admin calendar 267 | * 268 | * @since 0.1.0 269 | */ 270 | function wp_event_calendar_show_admin_calendar() { 271 | 272 | // Get the post type for easy caps checking 273 | $post_type = wp_event_calendar_get_admin_post_type(); 274 | $post_type_object = get_post_type_object( $post_type ); 275 | $user_id = get_current_user_id(); 276 | 277 | // Check for new mode 278 | if ( ! empty( $_REQUEST['mode'] ) ) { 279 | if ( in_array( $_REQUEST['mode'], array( 'month', 'week', 'day' ) ) ) { 280 | $mode = $_REQUEST['mode']; 281 | } 282 | update_user_option( $user_id, 'event_calendar_mode', $mode ); 283 | 284 | // Use existing mode 285 | } else { 286 | $mode = get_user_option( 'event_calendar_mode', $user_id ); 287 | } 288 | 289 | // Load the list table based on the mode 290 | switch ( $mode ) { 291 | case 'day' : 292 | $wp_list_table = new WP_Event_Calendar_Day_Table(); 293 | break; 294 | case 'week' : 295 | $wp_list_table = new WP_Event_Calendar_Week_Table(); 296 | break; 297 | case 'month' : 298 | default : 299 | $wp_list_table = new WP_Event_Calendar_Month_Table(); 300 | break; 301 | } 302 | 303 | // Query for calendar content 304 | $wp_list_table->prepare_items(); 305 | 306 | // Set the help tabs 307 | $wp_list_table->set_help_tabs(); ?> 308 | 309 |
310 |

311 | cap->create_posts ) ) : ?> 312 | 313 | 314 |

315 | 316 | views(); ?> 317 | 318 |
319 | 320 | search_box( $post_type_object->labels->search_items, $post_type ); ?> 321 | 322 | 323 | 324 | 325 | display(); ?> 326 | 327 |
328 | 329 |
330 |
331 |
332 | 333 | true ) ); 27 | $types = array_keys( $supports ); 28 | 29 | // Filter & return 30 | return apply_filters( 'wp_event_calendar_allowed_post_types', $types, $supports ); 31 | } 32 | 33 | /** 34 | * Get the current admin post type 35 | * 36 | * @since 0.1.0 37 | * 38 | * @return string 39 | */ 40 | function wp_event_calendar_get_admin_post_type() { 41 | 42 | // Use $typenow global if it's not empty 43 | if ( ! empty( $GLOBALS['typenow'] ) ) { 44 | return $GLOBALS['typenow']; 45 | } 46 | 47 | // Use $GET post_type if it's not empty 48 | if ( ! empty( $_GET['post_type'] ) ) { 49 | return wp_unslash( $_GET['post_type'] ); 50 | } 51 | 52 | // Use get parameter 53 | return 'post'; 54 | } 55 | 56 | 57 | /** 58 | * Get a human readable representation of the time elapsed since a given date. 59 | * 60 | * Based on function created by Dunstan Orchard - http://1976design.com 61 | * 62 | * This function will return a read representation of the time elapsed 63 | * since a given date. 64 | * eg: 2 hours and 50 minutes 65 | * eg: 4 days 66 | * eg: 4 weeks and 6 days 67 | * 68 | * Note that fractions of minutes are not represented in the return string. So 69 | * an interval of 3 minutes will be represented by "3 minutes", as will an 70 | * interval of 3 minutes 59 seconds. 71 | * 72 | * @param int|string $older_date The earlier time from which you're calculating 73 | * the time elapsed. Enter either as an integer Unix timestamp, 74 | * or as a date string of the format 'Y-m-d h:i:s'. 75 | * @param int|bool $newer_date Optional. Unix timestamp of date to compare older 76 | * date to. Default: false (current time). 77 | * 78 | * @return string String representing the time since the older date, eg 79 | * "2 hours and 50 minutes". 80 | */ 81 | function wp_event_calendar_human_diff_time( $older_date, $newer_date = false ) { 82 | 83 | // Format 84 | if ( ! is_numeric( $older_date ) ) { 85 | $older_date = strtotime( $older_date ); 86 | } 87 | 88 | if ( ! is_numeric( $newer_date ) ) { 89 | $newer_date = strtotime( $newer_date ); 90 | } 91 | 92 | // Catch issues with flipped old vs. new dates 93 | $flipped = false; 94 | 95 | // array of time period chunks 96 | $chunks = array( 97 | YEAR_IN_SECONDS, 98 | 30 * DAY_IN_SECONDS, 99 | WEEK_IN_SECONDS, 100 | DAY_IN_SECONDS, 101 | HOUR_IN_SECONDS, 102 | MINUTE_IN_SECONDS, 103 | 1 104 | ); 105 | 106 | if ( ! empty( $older_date ) && ! is_numeric( $older_date ) ) { 107 | $time_chunks = explode( ':', str_replace( ' ', ':', $older_date ) ); 108 | $date_chunks = explode( '-', str_replace( ' ', '-', $older_date ) ); 109 | $older_date = gmmktime( (int) $time_chunks[1], (int) $time_chunks[2], (int) $time_chunks[3], (int) $date_chunks[1], (int) $date_chunks[2], (int) $date_chunks[0] ); 110 | } 111 | 112 | /** 113 | * $newer_date will equal false if we want to know the time elapsed between 114 | * a date and the current time. $newer_date will have a value if we want to 115 | * work out time elapsed between two known dates. 116 | */ 117 | $newer_date = empty( $newer_date ) 118 | ? current_time( 'timestamp' ) 119 | : $newer_date; 120 | 121 | // Difference in seconds 122 | $since = $newer_date - $older_date; 123 | 124 | // Flipped 125 | if ( $since < 0 ) { 126 | $flipped = true; 127 | $since = $older_date - $newer_date; 128 | } 129 | 130 | // Step one: the first chunk 131 | for ( $i = 0, $j = count( $chunks ); $i < $j; ++$i ) { 132 | $seconds = $chunks[$i]; 133 | 134 | // Finding the biggest chunk (if the chunk fits, break) 135 | $count = floor( $since / $seconds ); 136 | if ( 0 != $count ) { 137 | break; 138 | } 139 | } 140 | 141 | // Set output var 142 | switch ( $seconds ) { 143 | case YEAR_IN_SECONDS : 144 | $output = sprintf( _n( '%s year', '%s years', $count, 'wp-event-calendar' ), $count ); 145 | break; 146 | case 30 * DAY_IN_SECONDS : 147 | $output = sprintf( _n( '%s month', '%s months', $count, 'wp-event-calendar' ), $count ); 148 | break; 149 | case WEEK_IN_SECONDS : 150 | $output = sprintf( _n( '%s week', '%s weeks', $count, 'wp-event-calendar' ), $count ); 151 | break; 152 | case DAY_IN_SECONDS : 153 | $output = sprintf( _n( '%s day', '%s days', $count, 'wp-event-calendar' ), $count ); 154 | break; 155 | case HOUR_IN_SECONDS : 156 | $output = sprintf( _n( '%s hour', '%s hours', $count, 'wp-event-calendar' ), $count ); 157 | break; 158 | case MINUTE_IN_SECONDS : 159 | $output = sprintf( _n( '%s minute', '%s minutes', $count, 'wp-event-calendar' ), $count ); 160 | break; 161 | default: 162 | $output = sprintf( _n( '%s second', '%s seconds', $count, 'wp-event-calendar' ), $count ); 163 | } 164 | 165 | // Step two: the second chunk 166 | // A quirk in the implementation means that this 167 | // condition fails in the case of minutes and seconds. 168 | // We've left the quirk in place, since fractions of a 169 | // minute are not a useful piece of information for our 170 | // purposes 171 | if ( $i + 2 < $j ) { 172 | $seconds2 = $chunks[$i + 1]; 173 | $count2 = floor( ( $since - ( $seconds * $count ) ) / $seconds2 ); 174 | 175 | // Add to output var 176 | if ( 0 != $count2 ) { 177 | $output .= _x( ',', 'Separator in time since', 'wp-event-calendar' ) . ' '; 178 | 179 | switch ( $seconds2 ) { 180 | case 30 * DAY_IN_SECONDS : 181 | $output .= sprintf( _n( '%s month', '%s months', $count2, 'wp-event-calendar' ), $count2 ); 182 | break; 183 | case WEEK_IN_SECONDS : 184 | $output .= sprintf( _n( '%s week', '%s weeks', $count2, 'wp-event-calendar' ), $count2 ); 185 | break; 186 | case DAY_IN_SECONDS : 187 | $output .= sprintf( _n( '%s day', '%s days', $count2, 'wp-event-calendar' ), $count2 ); 188 | break; 189 | case HOUR_IN_SECONDS : 190 | $output .= sprintf( _n( '%s hour', '%s hours', $count2, 'wp-event-calendar' ), $count2 ); 191 | break; 192 | case MINUTE_IN_SECONDS : 193 | $output .= sprintf( _n( '%s minute', '%s minutes', $count2, 'wp-event-calendar' ), $count2 ); 194 | break; 195 | default: 196 | $output .= sprintf( _n( '%s second', '%s seconds', $count2, 'wp-event-calendar' ), $count2 ); 197 | } 198 | } 199 | } 200 | 201 | if ( true === $flipped ) { 202 | $output = '-' . $output; 203 | } 204 | 205 | /** 206 | * Filters the human readable representation of the time elapsed since a 207 | * given date. 208 | * 209 | * @since 0.1.2 210 | * 211 | * @param string $output Final string 212 | * @param string $older_date Earlier time from which we're calculating time elapsed 213 | * @param string $newer_date Unix timestamp of date to compare older time to 214 | */ 215 | return apply_filters( 'wp_event_calendar_human_diff_time', $output, $older_date, $newer_date ); 216 | } 217 | 218 | /** 219 | * Return the start date & time of an event 220 | * 221 | * @since 0.1.5 222 | * 223 | * @param mixed $post 224 | * 225 | * @return string 226 | */ 227 | function wp_get_event_start_date_time( $post = false ) { 228 | 229 | // Get the post object & start date 230 | $post = get_post( $post ); 231 | $date = get_post_meta( $post->ID, 'wp_event_calendar_date_time', true ); 232 | $all_day = get_post_meta( $post->ID, 'wp_event_calendar_all_day', true ); 233 | 234 | // Start an output buffer 235 | ob_start(); 236 | 237 | if ( ! empty( $date ) ) { 238 | $date = strtotime( $date ); 239 | $df = get_option( 'date_format' ); 240 | $tf = get_option( 'time_format' ); 241 | 242 | echo date_i18n( $df, $date ); 243 | 244 | // Time 245 | if ( empty( $all_day ) ) { 246 | echo '
'. date_i18n( $tf, $date ); 247 | } 248 | 249 | // No start date 250 | } else { 251 | echo '—'; 252 | } 253 | 254 | // Get the output buffer 255 | $retval = ob_get_clean(); 256 | 257 | // Filter & return 258 | return apply_filters( 'wp_get_event_start_date_time', $retval, $post, $date ); 259 | } 260 | 261 | /** 262 | * Return the end date & time of an event 263 | * 264 | * @since 0.1.5 265 | * 266 | * @param mixed $post 267 | * 268 | * @return string 269 | */ 270 | function wp_get_event_end_date_time( $post = false ) { 271 | 272 | // Get the post object & start date 273 | $post = get_post( $post ); 274 | $date = get_post_meta( $post->ID, 'wp_event_calendar_end_date_time', true ); 275 | $all_day = get_post_meta( $post->ID, 'wp_event_calendar_all_day', true ); 276 | 277 | // Start an output buffer 278 | ob_start(); 279 | 280 | if ( ! empty( $date ) ) { 281 | $date = strtotime( $date ); 282 | $df = get_option( 'date_format' ); 283 | $tf = get_option( 'time_format' ); 284 | 285 | echo date_i18n( $df, $date ); 286 | 287 | // Time 288 | if ( empty( $all_day ) ) { 289 | echo '
'. date_i18n( $tf, $date ); 290 | } 291 | 292 | } else { 293 | echo '—'; 294 | } 295 | 296 | // Get the output buffer 297 | $retval = ob_get_clean(); 298 | 299 | // Filter & return 300 | return apply_filters( 'wp_get_event_end_date_time', $retval, $post, $date ); 301 | } 302 | 303 | /** 304 | * Return the duration of an event 305 | * 306 | * @since 0.1.5 307 | * 308 | * @param mixed $post 309 | * 310 | * @return string 311 | */ 312 | function wp_get_event_duration( $post = false ) { 313 | 314 | // Get the post object & start date 315 | $post = get_post( $post ); 316 | $all_day = (bool) get_post_meta( $post->ID, 'wp_event_calendar_all_day', true ); 317 | $start_date = strtotime( get_post_meta( $post->ID, 'wp_event_calendar_date_time', true ) ); 318 | $end_date = strtotime( get_post_meta( $post->ID, 'wp_event_calendar_end_date_time', true ) ); 319 | 320 | // Start an output buffer 321 | ob_start(); 322 | 323 | // All day event 324 | if ( true === $all_day ) { 325 | 326 | // 1 day 327 | if ( date( 'd', $start_date ) === date( 'd', $end_date ) ) { 328 | esc_html_e( 'All Day', 'wp-event-calendar' ); 329 | 330 | // More than 1 day 331 | } else { 332 | echo wp_event_calendar_human_diff_time( 333 | strtotime( 'midnight', $start_date ), 334 | strtotime( 'midnight', $end_date ) 335 | ); 336 | } 337 | 338 | // Specific times 339 | } else { 340 | echo wp_event_calendar_human_diff_time( $start_date, $end_date ); 341 | } 342 | 343 | // Get the output buffer 344 | $retval = ob_get_clean(); 345 | 346 | // Filter & return 347 | return apply_filters( 'wp_get_event_end_date_time', $retval, $post, $all_day, $start_date, $end_date ); 348 | } 349 | 350 | /** 351 | * Return array of hours 352 | * 353 | * @since 0.2.4 354 | * 355 | * @return array 356 | */ 357 | function wp_event_calendar_get_hours() { 358 | return apply_filters( 'wp_event_calendar_get_hours', array( 359 | '01', 360 | '02', 361 | '03', 362 | '04', 363 | '05', 364 | '06', 365 | '07', 366 | '08', 367 | '09', 368 | '10', 369 | '11', 370 | '12' 371 | ) ); 372 | } 373 | 374 | /** 375 | * Return array of minutes 376 | * 377 | * @since 0.2.4 378 | * 379 | * @return array 380 | */ 381 | function wp_event_calendar_get_minutes() { 382 | return apply_filters( 'wp_event_calendar_get_minutes', array( 383 | '00', 384 | '05', 385 | '10', 386 | '15', 387 | '20', 388 | '25', 389 | '30', 390 | '35', 391 | '40', 392 | '45', 393 | '50', 394 | '55' 395 | ) ); 396 | } 397 | 398 | /** 399 | * Output a select dropdown for hours & minutes 400 | * 401 | * @since 0.2.4 402 | * 403 | * @param array $args 404 | */ 405 | function wp_event_calendar_time_dropdown( $args = array() ) { 406 | 407 | // Parse the arguments 408 | $r = wp_parse_args( $args, array( 409 | 'first' => esc_html( 'Select One', 'wp-event-calendar' ), 410 | 'placeholder' => ' ', 411 | 'id' => '', 412 | 'name' => '', 413 | 'class' => '', 414 | 'items' => array(), 415 | 'selected' => '', 416 | 'multi' => false, 417 | 'echo' => true, 418 | 'width' => 55 419 | ) ); 420 | 421 | // Is multi? 422 | $multi = ( true === $r['multi'] ) 423 | ? 'multi' 424 | : ''; 425 | 426 | // Start an output buffer 427 | ob_start(); 428 | 429 | // Start the select wrapper 430 | ?> wp_event_calendar_allowed_post_types(), 466 | 'post_status' => array( 'publish', 'future' ), 467 | 'posts_per_page' => -1, 468 | 'orderby' => 'meta_value', 469 | 'order' => 'ASC', 470 | 'hierarchical' => false, 471 | 'ignore_sticky_posts' => true, 472 | 'suppress_filters' => true, 473 | 'no_found_rows' => true, 474 | 'meta_query' => wp_event_calendar_get_meta_query() 475 | ) ); 476 | 477 | // Query for events 478 | $query = new WP_Query( $r ); 479 | 480 | // Return posts 481 | return $query->posts; 482 | } 483 | 484 | /** 485 | * Get meta_query argument for a WP_Query 486 | * 487 | * Okay; let's talk this out here in regular English, because otherwise it 488 | * barely makes any sense at all and you just get lost trying to remember: 489 | * 490 | * - Ranges are: day, week, month, custom 491 | * - Look for: 492 | * - Events starting & ending within the current range 493 | * - Events starting before and ending anytime after current range 494 | * - Events starting before and ending within the current range 495 | * - Events starting within and ending anytime after the current range 496 | * 497 | * @since 0.4.0 498 | * 499 | * @param array $args { 500 | * @type string $mode month|week|day 501 | * @type string $start Start time in mysql format 502 | * @type string $end End time in mysql format 503 | * } 504 | * 505 | * @return array 'meta_query' used for WP_Query 506 | */ 507 | function wp_event_calendar_get_meta_query( $args = array() ) { 508 | 509 | // Parse arguments 510 | $r = wp_parse_args( $args, array( 511 | 'mode' => 'month', 512 | 'start' => '', 513 | 'end' => '' 514 | ) ); 515 | 516 | // They're all the same now! 517 | $retval = array( 518 | 'wp_event_calendar_clause' => array( 519 | 'relation' => 'AND', 520 | 'within_range_clause' => array( 521 | 'relation' => 'AND', 522 | 'start_between_clause' => array( 523 | 'key' => 'wp_event_calendar_date_time', 524 | 'value' => $r['end'], 525 | 'type' => 'DATETIME', 526 | 'compare' => '<' 527 | ), 528 | 'end_between_clause' => array( 529 | 'key' => 'wp_event_calendar_end_date_time', 530 | 'value' => $r['start'], 531 | 'type' => 'DATETIME', 532 | 'compare' => '>' 533 | ) 534 | ) 535 | ) 536 | ); 537 | 538 | return apply_filters( 'wp_event_calendar_get_meta_query', $retval, $r, $args ); 539 | } 540 | 541 | /** 542 | * Get an array of WP_Event_Calendar_Event objects 543 | * 544 | * @since 0.4.0 545 | * 546 | * @param array $args 547 | */ 548 | function wp_event_calendar_get_events( $args = array() ) { 549 | 550 | // Get posts & define default 551 | $posts = wp_get_events( $args ); 552 | $events = array(); 553 | 554 | // Loop through events and create the object 555 | foreach ( $posts as $post ) { 556 | $events[] = wp_event_calendar_post_to_event( $post ); 557 | } 558 | 559 | return apply_filters( 'wp_event_calendar_get_events', $events, $posts, $args ); 560 | } 561 | 562 | /** 563 | * Convert a WP_Post object to a WP_Event_Calendar_Event object 564 | * 565 | * @since 0.4.0 566 | * 567 | * @param WP_Post $post 568 | * 569 | * @return \WP_Event_Calendar_Event 570 | */ 571 | function wp_event_calendar_post_to_event( WP_Post $post ) { 572 | 573 | // Core 574 | $title = $post->post_title; 575 | $content = $post->post_content; 576 | 577 | // Meta 578 | $start = get_post_meta( $post->ID, 'wp_event_calendar_date_time', true ); 579 | $end = get_post_meta( $post->ID, 'wp_event_calendar_end_date_time', true ); 580 | $location = get_post_meta( $post->ID, 'wp_event_calendar_location', true ); 581 | $repeat = get_post_meta( $post->ID, 'wp_event_calendar_repeat', true ); 582 | 583 | // Return object 584 | return new WP_Event_Calendar_Event( $start, $end, $title, $content, $location, $repeat ); 585 | } 586 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /wp-event-calendar/includes/events/metaboxes.php: -------------------------------------------------------------------------------- 1 | term_id, $args['popular_cats'] ) 49 | ? ' class="popular-category"' 50 | : ''; 51 | 52 | // Maybe use already selected categories 53 | $args['selected_cats'] = empty( $args['selected_cats'] ) 54 | ? array() 55 | : $args['selected_cats']; 56 | 57 | /** This filter is documented in wp-includes/category-template.php */ 58 | $output .= "\n
  • " . 59 | ''; 63 | } 64 | } 65 | 66 | /** 67 | * Event Types Metabox 68 | * Output radio buttons instead of the default WordPress mechanism 69 | * 70 | * @since 0.4.1 71 | * 72 | * @param array $args 73 | * @param string $taxonomy 74 | * 75 | * @return array 76 | */ 77 | function wp_event_calendar_taxonomy_args( $args = array(), $taxonomy = '' ) { 78 | if ( 'event-type' === $taxonomy ) { 79 | 80 | $r = apply_filters( 'wp_event_calendar_taxonomy_args', array( 81 | 'meta_box_cb' => 'post_categories_meta_box' 82 | ), $args ); 83 | 84 | $args = wp_parse_args( $args, $r ); 85 | } 86 | 87 | return $args; 88 | } 89 | 90 | /** 91 | * Use the custom walker for radio buttons 92 | * 93 | * @since 0.4.1 94 | * 95 | * @param array $args 96 | * 97 | * @return array 98 | */ 99 | function wp_event_calendar_checklist_args( $args = array() ) { 100 | if ( 'event-type' === $args['taxonomy'] ) { 101 | 102 | $r = apply_filters( 'wp_event_calendar_checklist_args', array( 103 | 'walker' => new WP_Event_Calendar_Walker_Category_Radio(), 104 | ), $args ); 105 | 106 | $args = wp_parse_args( $args, $r ); 107 | } 108 | 109 | return $args; 110 | } 111 | 112 | /** 113 | * Event Metabox 114 | * 115 | * @since 0.1.1 116 | */ 117 | function wp_event_calendar_add_metabox() { 118 | add_meta_box( 119 | 'wp_event_calendar_duration', 120 | __( 'Duration', 'wp-event-calendar' ), 121 | 'wp_event_calendar_duration_metabox', 122 | wp_event_calendar_allowed_post_types(), 123 | 'above_event_editor', 124 | 'default' 125 | ); 126 | 127 | add_meta_box( 128 | 'wp_event_calendar_details', 129 | __( 'Details', 'wp-event-calendar' ), 130 | 'wp_event_calendar_details_metabox', 131 | wp_event_calendar_allowed_post_types(), 132 | 'above_event_editor', 133 | 'default' 134 | ); 135 | } 136 | 137 | /** 138 | * Output the event duration metabox 139 | * 140 | * @since 0.2.3 141 | * 142 | * @param WP_Post $post The post 143 | */ 144 | function wp_event_calendar_duration_metabox( $post = null ) { 145 | $meta = get_post_custom( $post->ID ); 146 | $date = $hour = $minute = $am_pm = ''; 147 | $end_date = $end_hour = $end_minute = $end_am_pm = ''; 148 | 149 | /** All Day ***************************************************************/ 150 | 151 | $all_day = ! empty( $meta['wp_event_calendar_all_day'][0] ) 152 | ? (bool) $meta['wp_event_calendar_all_day'][0] 153 | : false; 154 | $hidden = ( true === $all_day ) 155 | ? ' style="display: none;"' 156 | : ''; 157 | 158 | /** Location **************************************************************/ 159 | 160 | $location = ! empty( $meta['wp_event_calendar_location'][0] ) 161 | ? $meta['wp_event_calendar_location'][0] 162 | : ''; 163 | 164 | /** Ends ******************************************************************/ 165 | 166 | // Get date_time 167 | $end_date_time = ! empty( $meta['wp_event_calendar_end_date_time'][0] ) 168 | ? strtotime( $meta['wp_event_calendar_end_date_time'][0] ) 169 | : null; 170 | 171 | // Only if end isn't empty 172 | if ( ! empty( $end_date_time ) ) { 173 | 174 | // Date 175 | $end_date = date( 'm/d/Y', $end_date_time ); 176 | 177 | // Only if not all-day 178 | if ( empty( $all_day ) ) { 179 | 180 | // Hour 181 | $end_hour = date( 'h', $end_date_time ); 182 | if ( empty( $end_hour ) ) { 183 | $end_hour = ''; 184 | } 185 | 186 | // Minute 187 | $end_minute = date( 'i', $end_date_time ); 188 | if ( empty( $end_hour ) ) { 189 | $end_minute = ''; 190 | } 191 | 192 | // Day/night 193 | $end_am_pm = date( 'a', $end_date_time ); 194 | } 195 | } 196 | 197 | /** Starts ****************************************************************/ 198 | 199 | // Get date_time 200 | if ( ! empty( $_GET['start_day'] ) ) { 201 | $date_time = (int) $_GET['start_day']; 202 | } else { 203 | $date_time = ! empty( $meta['wp_event_calendar_date_time'][0] ) 204 | ? strtotime( $meta['wp_event_calendar_date_time'][0] ) 205 | : null; 206 | } 207 | 208 | // Date 209 | if ( ! empty( $date_time ) ) { 210 | $date = date( 'm/d/Y', $date_time ); 211 | 212 | // Only if not all-day 213 | if ( empty( $all_day ) ) { 214 | 215 | // Hour 216 | $hour = date( 'h', $date_time ); 217 | if ( empty( $end_hour ) || empty( $hour ) ) { 218 | $hour = ''; 219 | } 220 | 221 | // Minute 222 | $minute = date( 'i', $date_time ); 223 | if ( empty( $hour ) && empty( $end_minute ) ) { 224 | $minute = ''; 225 | } 226 | 227 | // Day/night 228 | $am_pm = date( 'a', $date_time ); 229 | } 230 | } 231 | 232 | /** Repeat ****************************************************************/ 233 | 234 | // Interval 235 | $interval = ! empty( $meta['wp_event_calendar_repeat'][0] ) 236 | ? $meta['wp_event_calendar_repeat'][0] 237 | : ''; 238 | 239 | // Filter the intervals 240 | $options = apply_filters( 'wp_event_calendar_intervals', array( 241 | '0' => __( 'Never', 'wp-event-calendar' ), 242 | '10' => __( 'Weekly', 'wp-event-calendar' ), 243 | '100' => __( 'Monthly', 'wp-event-calendar' ), 244 | '1000' => __( 'Yearly', 'wp-event-calendar' ) 245 | ) ); 246 | 247 | /** Let's Go! *************************************************************/ 248 | 249 | // Start an output buffer 250 | ob_start(); ?> 251 | 252 | 253 | 254 | 255 | 256 | 259 | 260 | 266 | 267 | 268 | 269 | 272 | 273 | 298 | 299 | 300 | 301 | 302 | 305 | 306 | 331 | 332 | 333 | 334 | 335 | 336 | 339 | 340 | 345 | 346 | 347 | 348 | 349 | 350 | 353 | 354 | 371 | 372 | 373 |
    257 | 258 | 261 | 265 |
    270 | 271 | 274 | 275 |
    > 276 | 277 | '', 279 | 'id' => 'wp_event_calendar_time_hour', 280 | 'name' => 'wp_event_calendar_time_hour', 281 | 'items' => wp_event_calendar_get_hours(), 282 | 'selected' => $hour 283 | ) ); ?> 284 | : 285 | '', 287 | 'id' => 'wp_event_calendar_time_minute', 288 | 'name' => 'wp_event_calendar_time_minute', 289 | 'items' => wp_event_calendar_get_minutes(), 290 | 'selected' => $minute 291 | ) ); ?> 292 | 296 |
    297 |
    303 | 304 | 307 | 308 |
    > 309 | 310 | '', 312 | 'id' => 'wp_event_calendar_end_time_hour', 313 | 'name' => 'wp_event_calendar_end_time_hour', 314 | 'items' => wp_event_calendar_get_hours(), 315 | 'selected' => $end_hour 316 | ) ); ?> 317 | : 318 | '', 320 | 'id' => 'wp_event_calendar_end_time_minute', 321 | 'name' => 'wp_event_calendar_end_time_minute', 322 | 'items' => wp_event_calendar_get_minutes(), 323 | 'selected' => $end_minute 324 | ) ); ?> 325 | 329 |
    330 |
    337 | 338 | 341 | 344 |
    351 | 352 | 355 | 364 | 365 | 367 | 368 | 369 | */ ?> 370 |
    374 | 375 | post_content, 'post_content' ); 390 | } 391 | 392 | /** 393 | * Offset hour based on meridem 394 | * 395 | * @since 0.2.2 396 | * 397 | * @param int $hour 398 | * @param string $meridiem 399 | * 400 | * @return int 401 | */ 402 | function wp_event_calendar_adjust_hour_for_meridiem( $hour = 0, $meridiem = 'am' ) { 403 | 404 | // Store new hour 405 | $new_hour = $hour; 406 | 407 | // Bump by 12 hours 408 | if ( 'pm' === $meridiem && ( $new_hour < 12 ) ) { 409 | $new_hour += 12; 410 | 411 | // Decrease by 12 hours 412 | } elseif ( 'am' === $meridiem && ( $new_hour >= 12 ) ) { 413 | $new_hour -= 12; 414 | } 415 | 416 | // Filter & return 417 | return (int) apply_filters( 'wp_event_calendar_adjust_hour_for_meridiem', $new_hour, $hour, $meridiem ); 418 | } 419 | 420 | /** 421 | * Metabox save 422 | * 423 | * @since 0.1.1 424 | * 425 | * @return int|void 426 | */ 427 | function wp_event_calendar_metabox_save( $post_id = 0 ) { 428 | 429 | // Bail if no nonce or nonce check fails 430 | if ( empty( $_POST['wp_event_calendar_metabox_nonce'] ) || ! wp_verify_nonce( $_POST['wp_event_calendar_metabox_nonce'], 'wp_event_calendar' ) ) { 431 | return $post_id; 432 | } 433 | 434 | // Bail on autosave, ajax, or bulk 435 | if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || ( defined( 'DOING_AJAX') && DOING_AJAX ) || isset( $_REQUEST['bulk_edit'] ) ) { 436 | return $post_id; 437 | } 438 | 439 | // Get the post type 440 | $post_type = get_post_type( $post_id ); 441 | 442 | // Only save event metadata to supported post types 443 | if ( ! post_type_supports( $post_type, 'events' ) ) { 444 | return $post_id; 445 | } 446 | 447 | // Bail if revision 448 | if ( wp_is_post_revision( $post_id ) ) { 449 | return $post_id; 450 | } 451 | 452 | // Get post type object 453 | $post_type_object = get_post_type_object( $post_type ); 454 | 455 | // Bail if user cannot edit this event 456 | if ( ! current_user_can( $post_type_object->cap->edit_post, $post_id ) ) { 457 | return $post_id; 458 | } 459 | 460 | /** Location **************************************************************/ 461 | 462 | // Get event location 463 | $location = ! empty( $_POST['wp_event_calendar_location'] ) 464 | ? wp_kses( $_POST['wp_event_calendar_location'], array() ) 465 | : ''; 466 | 467 | /** Starts ****************************************************************/ 468 | 469 | // Get calendar date 470 | $date = ! empty( $_POST['wp_event_calendar_date'] ) 471 | ? strtotime( sanitize_text_field( $_POST['wp_event_calendar_date'] ) ) 472 | : current_time( 'timestamp' ); 473 | 474 | // Hour 475 | $hour = ! empty( $_POST['wp_event_calendar_time_hour'] ) 476 | ? sanitize_text_field( $_POST['wp_event_calendar_time_hour'] ) 477 | : 0; 478 | 479 | // Minutes 480 | $minutes = ! empty( $_POST['wp_event_calendar_time_minute'] ) 481 | ? sanitize_text_field( $_POST['wp_event_calendar_time_minute'] ) 482 | : 0; 483 | 484 | // Day/night 485 | $am_pm = ! empty( $_POST['wp_event_calendar_time_am_pm'] ) 486 | ? sanitize_text_field( $_POST['wp_event_calendar_time_am_pm'] ) 487 | : 'am'; 488 | 489 | /** Ends ******************************************************************/ 490 | 491 | // Calendar date is set 492 | $end_date = ! empty( $_POST['wp_event_calendar_end_date'] ) 493 | ? strtotime( sanitize_text_field( $_POST['wp_event_calendar_end_date'] ) ) 494 | : null; 495 | 496 | // Hour 497 | $end_hour = ! empty( $_POST['wp_event_calendar_end_time_hour'] ) 498 | ? sanitize_text_field( $_POST['wp_event_calendar_end_time_hour'] ) 499 | : 0; 500 | 501 | // Minutes 502 | $end_minutes = ! empty( $_POST['wp_event_calendar_end_time_minute'] ) 503 | ? sanitize_text_field( $_POST['wp_event_calendar_end_time_minute'] ) 504 | : 0; 505 | 506 | // Day/night 507 | $end_am_pm = ! empty( $_POST['wp_event_calendar_end_time_am_pm'] ) 508 | ? sanitize_text_field( $_POST['wp_event_calendar_end_time_am_pm'] ) 509 | : 'am'; 510 | 511 | /** Repeat ****************************************************************/ 512 | 513 | // Repeat 514 | $repeat = ! empty( $_POST['wp_event_calendar_repeat'] ) 515 | ? sanitize_key( $_POST['wp_event_calendar_repeat'] ) 516 | : ''; 517 | 518 | // Expire 519 | $expire = ! empty( $_POST['wp_event_calendar_expire'] ) 520 | ? sanitize_text_field( $_POST['wp_event_calendar_expire'] ) 521 | : ''; 522 | 523 | /** All Day ***************************************************************/ 524 | 525 | // Get all-day status 526 | $all_day = ! empty( $_POST['wp_event_calendar_all_day'] ) 527 | ? (bool) $_POST['wp_event_calendar_all_day'] 528 | : false; 529 | 530 | // Set all day if no end date 531 | if ( ( false === $all_day ) && ( empty( $minutes ) && empty( $hour ) && empty( $end_minutes ) && empty( $end_hour ) ) ) { 532 | if ( empty( $end_date ) || ( $date === $end_date ) ) { 533 | 534 | // Make all-day event 535 | $all_day = true; 536 | 537 | // Make single-day event 538 | if ( empty( $end_date ) ) { 539 | $end_date = $date; 540 | } 541 | } 542 | } 543 | 544 | /** Combine ***************************************************************/ 545 | 546 | // Maybe tweak hours 547 | $hour = wp_event_calendar_adjust_hour_for_meridiem( $hour, $am_pm ); 548 | $end_hour = wp_event_calendar_adjust_hour_for_meridiem( $end_hour, $end_am_pm ); 549 | 550 | // Make timestamps from pieces 551 | $date = mktime( intval( $hour ), intval( $minutes ), 0, date( 'm', $date ), date( 'd', $date ), date( 'Y', $date ) ); 552 | $end_date = mktime( intval( $end_hour ), intval( $end_minutes ), 0, date( 'm', $end_date ), date( 'd', $end_date ), date( 'Y', $end_date ) ); 553 | 554 | // End dates can't be before start dates 555 | if ( $end_date <= $date ) { 556 | $end_date = strtotime( '+30 minutes', $date ); 557 | } 558 | 559 | /** Save ******************************************************************/ 560 | 561 | // Save the start date & time 562 | ! empty( $date ) 563 | ? update_post_meta( $post_id, 'wp_event_calendar_date_time', gmdate( 'Y-m-d H:i:s', $date ) ) 564 | : delete_post_meta( $post_id, 'wp_event_calendar_date_time' ); 565 | 566 | // Save the end date & time 567 | ! empty( $end_date ) 568 | ? update_post_meta( $post_id, 'wp_event_calendar_end_date_time', gmdate( 'Y-m-d H:i:s', $end_date ) ) 569 | : delete_post_meta( $post_id, 'wp_event_calendar_end_date_time' ); 570 | 571 | // Save location 572 | ! empty( $location ) 573 | ? update_post_meta( $post_id, 'wp_event_calendar_location', $location ) 574 | : delete_post_meta( $post_id, 'wp_event_calendar_location' ); 575 | 576 | // Save all-day 577 | ! empty( $all_day ) 578 | ? update_post_meta( $post_id, 'wp_event_calendar_all_day', 1 ) 579 | : delete_post_meta( $post_id, 'wp_event_calendar_all_day' ); 580 | 581 | // Save if repeating 582 | ! empty( $repeat ) 583 | ? update_post_meta( $post_id, 'wp_event_calendar_repeat', $repeat ) 584 | : delete_post_meta( $post_id, 'wp_event_calendar_repeat' ); 585 | 586 | // Save only if repeating 587 | ! empty( $expire ) && ( 'never' !== $repeat ) 588 | ? update_post_meta( $post_id, 'wp_event_calendar_expire', strtotime( $expire ) ) 589 | : delete_post_meta( $post_id, 'wp_event_calendar_expire' ); 590 | } 591 | -------------------------------------------------------------------------------- /wp-event-calendar/includes/common/list-table-base.php: -------------------------------------------------------------------------------- 1 | year = $this->get_year(); 183 | $this->month = $this->get_month(); 184 | $this->day = $this->get_day(); 185 | 186 | // Set "today" based on current request 187 | $this->today = strtotime( "{$this->year}/{$this->month}/{$this->day}" ); 188 | 189 | // Set modes 190 | $this->modes = $this->get_modes(); 191 | 192 | // Setup arguments 193 | $r = wp_parse_args( $args, array( 194 | 'singular' => esc_html__( 'Event', 'wp-event-calendar' ), 195 | 'plural' => esc_html__( 'Events', 'wp-event-calendar' ) 196 | ) ); 197 | 198 | // Pass arguments into parent 199 | parent::__construct( $r ); 200 | } 201 | 202 | /** 203 | * Get the possible list table modes 204 | * 205 | * @since 0.1.8 206 | * 207 | * @return array 208 | */ 209 | protected function get_modes() { 210 | return apply_filters( 'wp_event_calendar_list_table_modes', array( 211 | 'month' => __( 'Month', 'wp-event-calendar' ), 212 | 'week' => __( 'Week', 'wp-event-calendar' ), 213 | 'day' => __( 'Day', 'wp-event-calendar' ) 214 | ) ); 215 | } 216 | 217 | /** 218 | * Return the post type of the current screen 219 | * 220 | * @since 0.1.0 221 | * 222 | * @return string 223 | */ 224 | protected function get_screen_post_type() { 225 | 226 | // Special handling for "post" post type 227 | if ( ! empty( $this->screen->post_type ) ) { 228 | $post_type = $this->screen->post_type; 229 | } else { 230 | $post_type = 'post'; 231 | } 232 | 233 | return $post_type; 234 | } 235 | 236 | /** 237 | * Return the base URL 238 | * 239 | * @since 0.1.0 240 | * 241 | * @return string 242 | */ 243 | protected function get_base_url() { 244 | 245 | // Define local variables 246 | $args = array(); 247 | $post_type = $this->get_screen_post_type(); 248 | 249 | // "post" post type needs special handling 250 | if ( 'post' !== $post_type ) { 251 | $args['post_type'] = $post_type; 252 | } 253 | 254 | // Persistent `post_status` 255 | if ( isset( $_GET['post_status'] ) ) { 256 | $args['post_status'] = sanitize_key( $_GET['post_status'] ); 257 | } 258 | 259 | // Persistent searches 260 | if ( isset( $_GET['s'] ) ) { 261 | $args['s'] = urlencode( $_GET['s'] ); 262 | } 263 | 264 | // Setup `page` & `mode` arguments 265 | $args['page'] = $post_type . '-calendar'; 266 | $args['mode'] = $this->mode; 267 | 268 | // Add args & return 269 | return add_query_arg( $args, admin_url( 'edit.php' ) ); 270 | } 271 | 272 | /** 273 | * Setup the list-table's columns 274 | * 275 | * @see WP_List_Table::single_row_columns() 276 | * 277 | * @return array An associative array containing column information 278 | */ 279 | public function get_columns() { 280 | return array( 281 | 'sunday' => $GLOBALS['wp_locale']->get_weekday( 0 ), 282 | 'monday' => $GLOBALS['wp_locale']->get_weekday( 1 ), 283 | 'tuesday' => $GLOBALS['wp_locale']->get_weekday( 2 ), 284 | 'wednesday' => $GLOBALS['wp_locale']->get_weekday( 3 ), 285 | 'thursday' => $GLOBALS['wp_locale']->get_weekday( 4 ), 286 | 'friday' => $GLOBALS['wp_locale']->get_weekday( 5 ), 287 | 'saturday' => $GLOBALS['wp_locale']->get_weekday( 6 ) 288 | ); 289 | } 290 | 291 | /** 292 | * Allow columns to be sortable 293 | * 294 | * @return array An associative array containing the sortable columns 295 | */ 296 | protected function get_sortable_columns() { 297 | return array(); 298 | } 299 | 300 | /** 301 | * Setup the bulk actions 302 | * 303 | * @return array An associative array containing all the bulk actions 304 | */ 305 | public function get_bulk_actions() { 306 | return array(); 307 | } 308 | 309 | /** 310 | * Get the current month 311 | * 312 | * @since 0.1.0 313 | * 314 | * @access public 315 | * 316 | * @return int 317 | */ 318 | protected function get_month() { 319 | return (int) isset( $_REQUEST['cm'] ) 320 | ? (int) $_REQUEST['cm'] 321 | : date_i18n( 'n' ); 322 | } 323 | 324 | /** 325 | * Get the current day 326 | * 327 | * @since 0.1.0 328 | * 329 | * @access public 330 | * 331 | * @return int 332 | */ 333 | protected function get_day() { 334 | return (int) isset( $_REQUEST['cd'] ) 335 | ? (int) $_REQUEST['cd'] 336 | : date_i18n( 'j' ); 337 | } 338 | 339 | /** 340 | * Get the current year 341 | * 342 | * @since 0.1.0 343 | * 344 | * @access public 345 | * 346 | * @return int 347 | */ 348 | protected function get_year() { 349 | return (int) isset( $_REQUEST['cy'] ) 350 | ? (int) $_REQUEST['cy'] 351 | : date_i18n( 'Y' ); 352 | } 353 | 354 | /** 355 | * Get the current page number 356 | * 357 | * @since 0.1.0 358 | * 359 | * @access public 360 | * 361 | * @return int 362 | */ 363 | protected function get_post_status() { 364 | return isset( $_REQUEST['post_status'] ) 365 | ? sanitize_key( $_REQUEST['post_status'] ) 366 | : $this->get_available_post_stati(); 367 | } 368 | 369 | /** 370 | * Get the current page number 371 | * 372 | * @since 0.1.0 373 | * 374 | * @access public 375 | * 376 | * @return int 377 | */ 378 | protected function get_available_post_stati() { 379 | return array( 380 | 'publish', 381 | 'future', 382 | 'draft', 383 | 'pending', 384 | 'private', 385 | 'hidden', 386 | 'passed' 387 | ); 388 | } 389 | 390 | /** 391 | * Get the current page number 392 | * 393 | * @since 0.1.0 394 | * 395 | * @access public 396 | * 397 | * @return int 398 | */ 399 | protected function get_orderby() { 400 | return isset( $_REQUEST['orderby'] ) 401 | ? sanitize_key( $_REQUEST['orderby'] ) 402 | : 'date'; 403 | } 404 | 405 | /** 406 | * Get the current page number 407 | * 408 | * @since 0.1.0 409 | * 410 | * @access public 411 | * 412 | * @return int 413 | */ 414 | protected function get_order() { 415 | return isset( $_REQUEST['order'] ) 416 | ? ucwords( $_REQUEST['order'] ) 417 | : 'ASC'; 418 | } 419 | 420 | /** 421 | * Get the current page number 422 | * 423 | * @since 0.1.0 424 | * 425 | * @access public 426 | * 427 | * @return int 428 | */ 429 | protected function get_search() { 430 | return isset( $_REQUEST['s'] ) 431 | ? wp_unslash( $_REQUEST['s'] ) 432 | : ''; 433 | } 434 | 435 | /** 436 | * Get the meta_query for the current mode 437 | * 438 | * @since 0.3.2 439 | * 440 | * @return array 441 | */ 442 | protected function get_meta_query() { 443 | return wp_event_calendar_get_meta_query( array( 444 | 'mode' => $this->mode, 445 | 'start' => $this->view_start, 446 | 'end' => $this->view_end 447 | ) ); 448 | } 449 | 450 | /** 451 | * Get the current page number 452 | * 453 | * @since 0.1.0 454 | * 455 | * @access public 456 | * 457 | * @return int 458 | */ 459 | protected function get_max() { 460 | $user_option = get_user_option( get_current_screen()->base . 'per_day' ); 461 | 462 | if ( empty( $user_option ) ) { 463 | $user_option = 10; 464 | } 465 | 466 | return (int) $user_option; 467 | } 468 | 469 | /** 470 | * Get a list of CSS classes for the list table table tag. 471 | * 472 | * @since 0.1.0 473 | * 474 | * @return array List of CSS classes for the table tag. 475 | */ 476 | protected function get_table_classes() { 477 | return array( 'widefat', 'fixed', 'striped', 'calendar', $this->_args['plural'] ); 478 | } 479 | 480 | /** 481 | * Determine if the current view is the "All" view. 482 | * 483 | * @since 0.1.0 484 | * 485 | * @return bool Whether the current view is the "All" view. 486 | */ 487 | protected function is_base_request() { 488 | if ( empty( $_GET ) ) { 489 | return true; 490 | } elseif ( empty( $_GET['post_status'] ) ) { 491 | return true; 492 | } elseif ( 2 === count( $_GET ) && ! empty( $_GET['post_type'] ) ) { 493 | return ( $this->screen->post_type === $_GET['post_type'] ); 494 | } 495 | return false; 496 | } 497 | 498 | /** 499 | * Get the calendar views 500 | * 501 | * @since 0.1.0 502 | * 503 | * @access public 504 | * 505 | * @return int 506 | */ 507 | protected function get_views() { 508 | 509 | // Screen 510 | $base_url = $this->get_base_url(); 511 | 512 | // Posts 513 | $post_type = $this->get_screen_post_type(); 514 | $num_posts = wp_count_posts( $post_type, 'readable' ); 515 | $total_posts = array_sum( (array) $num_posts ); 516 | $class = $allposts = ''; 517 | 518 | // Post type statuses 519 | $avail_post_stati = $this->get_available_post_stati(); 520 | $status_links = array(); 521 | $post_statuses = get_post_stati( array( 'show_in_admin_all_list' => false ) ); 522 | 523 | // Subtract post statuses that are not included in the admin all list. 524 | foreach ( $post_statuses as $state ) { 525 | $total_posts -= $num_posts->$state; 526 | } 527 | 528 | // "All" link 529 | if ( empty( $class ) && ( $this->is_base_request() || isset( $_REQUEST['all_posts'] ) ) ) { 530 | $class = 'current'; 531 | } 532 | 533 | $all_inner_html = sprintf( 534 | _nx( 535 | 'All (%s)', 536 | 'All (%s)', 537 | $total_posts, 538 | 'posts' 539 | ), 540 | number_format_i18n( $total_posts ) 541 | ); 542 | 543 | $status_links['all'] = '' . $all_inner_html . ''; 544 | 545 | // Other links 546 | $post_statuses = get_post_stati( array( 'show_in_admin_status_list' => true ), 'objects' ); 547 | 548 | // Loop through statuses and compile array of available ones 549 | foreach ( $post_statuses as $status ) { 550 | 551 | // Set variable to trick PHP 552 | $status_name = $status->name; 553 | 554 | // "Passed" status is irrelevant in calendar view 555 | if ( 'passed' === $status_name ) { 556 | continue; 557 | } 558 | 559 | // Skip if not available status 560 | if ( ! in_array( $status_name, $avail_post_stati ) ) { 561 | continue; 562 | } 563 | 564 | // Skip if no post count 565 | if ( empty( $num_posts->$status_name ) ) { 566 | continue; 567 | } 568 | 569 | // Set the class value 570 | if ( isset( $_REQUEST['post_status'] ) && ( $status_name === $_REQUEST['post_status'] ) ) { 571 | $class = 'current'; 572 | } else { 573 | $class = ''; 574 | } 575 | 576 | // Calculate the status text 577 | $status_text = sprintf( translate_nooped_plural( $status->label_count, $num_posts->$status_name ), number_format_i18n( $num_posts->$status_name ) ); 578 | $status_url = add_query_arg( array( 'post_status' => $status_name ), $base_url ); 579 | 580 | // Add link to array 581 | $status_links[ $status_name ] = '' . $status_text . ''; 582 | } 583 | 584 | return $status_links; 585 | } 586 | 587 | /** 588 | * Prepare items for list-table display 589 | * 590 | * @since 0.1.0 591 | * 592 | * @uses $this->_column_headers 593 | * @uses $this->get_columns() 594 | * @uses $this->get_orderby() 595 | * @uses $this->get_order() 596 | */ 597 | public function prepare_items() { 598 | 599 | // Set the mode 600 | $this->set_mode(); 601 | 602 | // Set column headers 603 | $this->_column_headers = array( 604 | $this->get_columns(), 605 | array(), 606 | array() 607 | ); 608 | 609 | // Query for posts for this month only 610 | $this->query = new WP_Query( $this->main_query_args() ); 611 | 612 | // Max per day 613 | $max_per = $this->get_max(); 614 | 615 | // Rearrange posts into an array keyed by day of the month 616 | foreach ( $this->query->posts as $post ) { 617 | 618 | // Get start & end 619 | $this->item_all_day = (bool) get_post_meta( $post->ID, 'wp_event_calendar_all_day', true ); 620 | $this->item_start = get_post_meta( $post->ID, 'wp_event_calendar_date_time', true ); 621 | $this->item_end = get_post_meta( $post->ID, 'wp_event_calendar_end_date_time', true ); 622 | 623 | // Format start 624 | if ( ! empty( $this->item_start ) ) { 625 | $this->item_start = strtotime( $this->item_start ); 626 | } 627 | 628 | // Format end 629 | if ( ! empty( $this->item_end ) ) { 630 | $this->item_end = strtotime( $this->item_end ); 631 | } 632 | 633 | if ( empty( $this->item_start ) ) { 634 | continue; 635 | } 636 | 637 | // Convert dates to timestamps that exclude the timestamp 638 | $start_date_timestamp = strtotime( date( 'F j, Y', $this->item_start ) ); 639 | $end_date_timestamp = strtotime( date( 'F j, Y', $this->item_end ) ); 640 | 641 | // Get the number of seconds between each timestamp's date 642 | $diff = abs( $end_date_timestamp - $start_date_timestamp ); 643 | 644 | // Calculate full days spanned 645 | $this->item_days = ceil( $diff / DAY_IN_SECONDS ) + 1; 646 | 647 | // Prepare pointer & item 648 | $this->setup_item( $post, $max_per ); 649 | } 650 | } 651 | 652 | /** 653 | * 654 | * @return type 655 | */ 656 | protected function main_query_args( $args = array() ) { 657 | 658 | // Events 659 | if ( post_type_supports( $this->screen->post_type, 'events' ) ) { 660 | $defaults = array( 661 | 'post_type' => $this->screen->post_type, 662 | 'post_status' => $this->get_post_status(), 663 | 'posts_per_page' => -1, 664 | 'orderby' => 'meta_value', 665 | 'order' => $this->get_order(), 666 | 'hierarchical' => false, 667 | 'ignore_sticky_posts' => true, 668 | 's' => $this->get_search(), 669 | 'meta_query' => $this->get_meta_query() 670 | ); 671 | 672 | // All others 673 | } else { 674 | $defaults = array( 675 | 'post_type' => $this->screen->post_type, 676 | 'post_status' => $this->get_post_status(), 677 | 'monthnum' => $this->month, 678 | 'year' => $this->year, 679 | 'day' => null, 680 | 'posts_per_page' => -1, 681 | 'orderby' => $this->get_orderby(), 682 | 'order' => $this->get_order(), 683 | 'hierarchical' => false, 684 | 'ignore_sticky_posts' => true, 685 | 's' => $this->get_search() 686 | ); 687 | } 688 | 689 | // Parse the arguments 690 | $r = wp_parse_args( $args, $defaults ); 691 | 692 | return apply_filters( 'wp_event_calendar_month_query', $r, $args, $defaults ); 693 | } 694 | 695 | /** 696 | * Handle bulk action requests 697 | */ 698 | public function process_bulk_action() { 699 | // No bulk actions 700 | } 701 | 702 | /** 703 | * Always have items 704 | * 705 | * This method forces WordPress to always show our calendar, and never to 706 | * trigger the `no_items()` method. 707 | * 708 | * @since 0.1.0 709 | * 710 | * @return boolean 711 | */ 712 | public function has_items() { 713 | return true; 714 | } 715 | 716 | /** 717 | * Add a post to the item array, keyed by day 718 | * 719 | * @todo Repeat & expire 720 | * 721 | * @since 0.1.1 722 | * 723 | * @param object $post 724 | * @param int $max 725 | */ 726 | protected function setup_item( $post = false, $max = 10 ) { 727 | 728 | // Calculate start day 729 | if ( ! empty( $this->item_start ) ) { 730 | $start_day = date_i18n( 'j', $this->item_start ); 731 | } else { 732 | $start_day = 0; 733 | } 734 | 735 | // Calculate end day 736 | if ( ! empty( $this->item_end ) ) { 737 | $end_day = date_i18n( 'j', $this->item_end ); 738 | } else { 739 | $end_day = $start_day; 740 | } 741 | 742 | // Start the days loop with the start day 743 | $day = (int) $start_day; 744 | $type = 'items'; 745 | 746 | // Loop through days 747 | while ( $day <= $end_day ) { 748 | 749 | // Setup the pointer for each day 750 | $this->setup_pointer( $post, $day ); 751 | 752 | // Add post to item types for each day in it's duration 753 | if ( empty( $this->{$type}[ $day ] ) || ( $max > count( $this->{$type}[ $day ] ) ) ) { 754 | $this->{$type}[ $day ][ $post->ID ] = $post; 755 | } 756 | 757 | // Bump the day 758 | ++$day; 759 | } 760 | } 761 | 762 | /** 763 | * Get the already queried posts for a given day 764 | * 765 | * @since 0.1.8 766 | * 767 | * @param int $iterator 768 | * 769 | * @return array 770 | */ 771 | protected function get_queried_items( $iterator = 1, $type = 'items' ) { 772 | return isset( $this->{$type}[ $iterator ] ) 773 | ? $this->{$type}[ $iterator ] 774 | : array(); 775 | } 776 | 777 | /** 778 | * Get posts for a given cell 779 | * 780 | * @since 0.1.0 781 | * 782 | * @param int $iterator 783 | * 784 | * @return string 785 | */ 786 | protected function get_posts_for_cell( $iterator = 1, $type = 'items' ) { 787 | 788 | // Get posts and bail if none 789 | $posts = $this->get_queried_items( $iterator, $type ); 790 | if ( empty( $posts ) ) { 791 | return ''; 792 | } 793 | 794 | // Start an output buffer 795 | ob_start(); 796 | 797 | // Loop through today's posts 798 | foreach ( $posts as $post ) : 799 | 800 | // Setup the pointer ID 801 | $ponter_id = "{$post->ID}-{$iterator}"; 802 | 803 | // Get the post link 804 | $post_link = get_edit_post_link( $post->ID ); 805 | 806 | // Handle empty titles 807 | $post_title = get_the_title( $post->ID ); 808 | if ( empty( $post_title ) ) { 809 | $post_title = esc_html__( '(No title)', 'wp-event-calendar' ); 810 | } ?> 811 | 812 | 813 | 814 | get_event_classes( $post->ID ) . '">' . $this->get_pointer_title( $post ) . ''; 870 | $pointer_content[] = '

    ' . implode( '
    ', $this->get_pointer_text( $post ) ) . '

    '; 871 | 872 | // Filter pointer content specifically 873 | $pointer_content = apply_filters( 'wp_event_calendar_pointer_content', $pointer_content, $post ); 874 | 875 | // Filter the entire pointer array 876 | $pointer = apply_filters( 'wp_event_calendar_pointer', array( 877 | 'content' => implode( '', $pointer_content ), 878 | 'anchor_id' => "#event-pointer-{$post->ID}-{$day}", 879 | 'edge' => 'top', 880 | 'align' => 'left' 881 | ), $post ); 882 | 883 | // Add pointer to pointers array 884 | $this->pointers[] = $pointer; 885 | } 886 | 887 | /** 888 | * Return the pointer title text 889 | * 890 | * @since 0.1.1 891 | * 892 | * @param object $post 893 | * @return string 894 | */ 895 | protected function get_pointer_title( $post = false ) { 896 | 897 | // Get post type object 898 | $post_type_object = get_post_type_object( $post->post_type ); 899 | 900 | // Handle empty titles 901 | $title = ! empty( $post->post_title ) 902 | ? $post->post_title 903 | : esc_html__( '(No title)', 'wp-event-calendar' ); 904 | 905 | // Title links to edit 906 | if ( current_user_can( $post_type_object->cap->edit_post, $post->ID ) ) { 907 | $retval = '' . esc_js( $title ) . ''; 908 | 909 | // No title link 910 | } else { 911 | $retval = esc_js( $title ); 912 | } 913 | 914 | // Filter & return the pointer title 915 | return apply_filters( 'wp_event_calendar_pointer_title', $retval, $post ); 916 | } 917 | 918 | /** 919 | * Return the pointer title text 920 | * 921 | * @since 0.1.1 922 | * 923 | * @param object $post 924 | * 925 | * @return string 926 | */ 927 | protected function get_pointer_text( $post = false ) { 928 | $pointer_text = $this->get_pointer_metadata( $post ); 929 | 930 | // Append with new-line if metadata exists 931 | $new_line = ! empty( $pointer_text ) 932 | ? '
    ' 933 | : ''; 934 | 935 | // Special case for password protected posts 936 | if ( ! empty( $post->post_password ) ) { 937 | $pointer_text[] = $new_line . esc_html__( 'Password required.', 'wp-event-calendar' ); 938 | 939 | // Post is not protected 940 | } else { 941 | 942 | // No content 943 | if ( empty( $post->post_content ) ) { 944 | $pointer_text[] = $new_line . esc_html__( 'No description.', 'wp-event-calendar' ); 945 | 946 | // Attempt to sanitize content 947 | } else { 948 | 949 | // Strip new lines & reduce to allowed tags 950 | $the_content = wptexturize( $post->post_content ); 951 | $the_content = wpautop( $the_content, true ); 952 | $the_content = wp_kses( $the_content, $this->get_allowed_pointer_tags() ); 953 | $the_content = preg_replace( '#\r|\n#', '
    ', $the_content ); 954 | 955 | // Texturize 956 | $pointer_text[] = $new_line . $the_content; 957 | } 958 | } 959 | 960 | // Filter & return the pointer title 961 | return apply_filters( 'wp_event_calendar_pointer_text', $pointer_text, $post ); 962 | } 963 | 964 | /** 965 | * Get event metadata for display in a pointer 966 | * 967 | * @since 0.1.0 968 | * 969 | * @param object $post 970 | * 971 | * @return array 972 | */ 973 | protected function get_pointer_metadata( $post = false ) { 974 | $pointer_metadata = array(); 975 | 976 | // All day event 977 | if ( ( true === $this->item_all_day ) ) { 978 | $pointer_metadata[] = '' . esc_html__( 'All Day', 'wp-event-calendar' ) . ''; 979 | 980 | } else { 981 | 982 | // Date & Time 983 | if ( ! empty( $this->item_start ) ) { 984 | $pointer_metadata[] = '' . esc_html__( 'Start', 'wp-event-calendar' ) . ''; 985 | $pointer_metadata[] = sprintf( esc_html__( '%s on %s', 'wp-event-calendar' ), $this->get_event_time( $post, $this->item_start ), $this->get_event_date( $post, $this->item_start ) ); 986 | } 987 | 988 | // Date & Time 989 | if ( ! empty( $this->item_end ) ) { 990 | 991 | // Extra padding 992 | if ( ! empty( $this->item_start ) ) { 993 | $pointer_metadata[] = ''; 994 | } 995 | 996 | $pointer_metadata[] = '' . esc_html__( 'End', 'wp-event-calendar' ) . ''; 997 | $pointer_metadata[] = sprintf( esc_html__( '%s on %s', 'wp-event-calendar' ), $this->get_event_time( $post, $this->item_end ), $this->get_event_date( $post, $this->item_end ) ); 998 | } 999 | } 1000 | 1001 | // Filter & return the pointer title 1002 | return apply_filters( 'wp_event_calendar_pointer_metadata', $pointer_metadata, $post ); 1003 | } 1004 | 1005 | /** 1006 | * Return array of allowed HTML tags to use in admin pointers 1007 | * 1008 | * @since 0.1.1 1009 | * 1010 | * @return allay Allowed HTML tags 1011 | */ 1012 | protected function get_allowed_pointer_tags() { 1013 | return apply_filters( 'wp_event_calendar_get_allowed_pointer_tags', array( 1014 | 'a' => array(), 1015 | 'strong' => array(), 1016 | 'em' => array(), 1017 | 'img' => array() 1018 | ) ); 1019 | } 1020 | 1021 | /** 1022 | * Output the pointers for each event 1023 | * 1024 | * This is a pretty horrible way to accomplish this, but it's currently the 1025 | * way WordPress's pointer API expects to work, so be it. 1026 | * 1027 | * @since 0.1.1 1028 | */ 1029 | public function admin_pointers_footer() { 1030 | ?> 1031 | 1032 | 1033 | 1058 | 1059 | 1060 | display_tablenav( 'top' ); ?> 1079 | 1080 | 1081 | 1082 | 1083 | print_column_headers(); ?> 1084 | 1085 | 1086 | 1087 | '> 1088 | display_mode(); ?> 1089 | 1090 | 1091 | 1092 | 1093 | print_column_headers( false ); ?> 1094 | 1095 | 1096 |
    1097 | 1098 | display_tablenav( 'bottom' ); 1102 | 1103 | // End and flush the buffer 1104 | ob_end_flush(); 1105 | } 1106 | 1107 | /** 1108 | * Message to be displayed when there are no items 1109 | */ 1110 | public function no_items() { 1111 | // Do nothing; calendars always have rows 1112 | } 1113 | 1114 | /** 1115 | * Get classes for post in day 1116 | * 1117 | * @since 0.1.0 1118 | * 1119 | * @param int $post_id 1120 | */ 1121 | protected function get_event_classes( $post_id = 0 ) { 1122 | 1123 | // Empty classes array 1124 | $classes = array(); 1125 | 1126 | // Is the event all day? 1127 | $classes[] = get_post_meta( $post_id, 'wp_event_calendar_all_day' ) 1128 | ? 'all-day' 1129 | : ''; 1130 | 1131 | // Is the event all day? 1132 | $classes[] = get_post_meta( $post_id, 'wp_event_calendar_location' ) 1133 | ? 'has-location' 1134 | : ''; 1135 | 1136 | // Get event terms 1137 | $terms = wp_get_object_terms( $post_id, get_object_taxonomies( 'event' ) ); 1138 | foreach ( $terms as $term ) { 1139 | $classes[] = "tax-{$term->taxonomy}"; 1140 | $classes[] = "term-{$term->slug}"; 1141 | } 1142 | 1143 | // Remove any empties 1144 | $classes = get_post_class( $classes, $post_id ); 1145 | 1146 | // Join & return 1147 | return join( ' ', $classes ); 1148 | } 1149 | 1150 | /** 1151 | * Is the current calendar view today 1152 | * 1153 | * @since 0.1.0 1154 | * 1155 | * @return bool 1156 | */ 1157 | protected function is_today( $month, $day, $year ) { 1158 | $_month = (bool) ( $month == date_i18n( 'n' ) ); 1159 | $_day = (bool) ( $day == date_i18n( 'j' ) ); 1160 | $_year = (bool) ( $year == date_i18n( 'Y' ) ); 1161 | 1162 | return (bool) ( true === $_month && true === $_day && true === $_year ); 1163 | } 1164 | 1165 | /** 1166 | * Get classes for table cell 1167 | * 1168 | * @since 0.1.0 1169 | * 1170 | * @param type $iterator 1171 | * @param type $start_day 1172 | * 1173 | * @return type 1174 | */ 1175 | protected function get_day_classes( $iterator = 1, $start_day = 1 ) { 1176 | $dow = ( $iterator % 7 ); 1177 | $day_key = sanitize_key( $GLOBALS['wp_locale']->get_weekday( $dow ) ); 1178 | $offset = ( $iterator - $start_day ) + 1; 1179 | 1180 | // Position & day info 1181 | $position = "position-{$dow}"; 1182 | $day_number = "day-{$offset}"; 1183 | $month_number = "month-{$this->month}"; 1184 | $year_number = "year-{$this->year}"; 1185 | 1186 | $is_today = $this->is_today( $this->month, $offset, $this->year ) 1187 | ? 'today' 1188 | : ''; 1189 | 1190 | // Assemble classes 1191 | $classes = array( 1192 | $day_key, 1193 | $is_today, 1194 | $position, 1195 | $day_number, 1196 | $month_number, 1197 | $year_number 1198 | ); 1199 | 1200 | return implode( ' ', $classes ); 1201 | } 1202 | 1203 | /** 1204 | * Display a calendar by month and year 1205 | * 1206 | * @since 0.1.0 1207 | */ 1208 | protected function display_mode() { 1209 | // Performed by subclass 1210 | } 1211 | 1212 | /** 1213 | * Generate the table navigation above or below the table 1214 | * 1215 | * @since 0.1.0 1216 | * 1217 | * @access protected 1218 | * @param string $which 1219 | */ 1220 | protected function display_tablenav( $which ) { 1221 | ?> 1222 | 1223 |
    1224 | 1225 | extra_tablenav( $which ); 1229 | 1230 | // Output year/month pagination 1231 | echo $this->pagination( $which ); 1232 | 1233 | // Output month/week/day switcher 1234 | echo $this->view_switcher( $which ); ?> 1235 | 1236 |
    1237 |
    1238 | 1239 | 1258 | 1259 | 1260 | 1269 | 1270 | 1271 | 1272 | 1273 | 'doaction' ) ); 1280 | 1281 | // Filter & return 1282 | return apply_filters( 'wp_event_calendar_get_extra_tablenav', ob_get_clean() ); 1283 | } 1284 | 1285 | /** 1286 | * Paginate through months & years 1287 | * 1288 | * @since 0.1.0 1289 | * 1290 | * @param array $args 1291 | */ 1292 | protected function pagination( $args = array() ) { 1293 | 1294 | // Parse args 1295 | $r = wp_parse_args( $args, array( 1296 | 'which' => 'top', 1297 | 'small' => '1 month', 1298 | 'large' => '1 year', 1299 | 'labels' => array( 1300 | 'today' => esc_html__( 'Today', 'wp-event-calendar' ), 1301 | 'next_small' => esc_html__( 'Next', 'wp-event-calendar' ), 1302 | 'next_large' => esc_html__( 'Next', 'wp-event-calendar' ), 1303 | 'prev_small' => esc_html__( 'Previous', 'wp-event-calendar' ), 1304 | 'prev_large' => esc_html__( 'Previous', 'wp-event-calendar' ) 1305 | ) 1306 | ) ); 1307 | 1308 | // No botton pagination 1309 | if ( 'top' !== $r['which'] ) { 1310 | return; 1311 | } 1312 | 1313 | // Base URLs 1314 | $today = $this->get_base_url(); 1315 | 1316 | // Calculate previous & next weeks & months 1317 | $prev_small = strtotime( "-{$r['small']}", $this->today ); 1318 | $next_small = strtotime( "+{$r['small']}", $this->today ); 1319 | $prev_large = strtotime( "-{$r['large']}", $this->today ); 1320 | $next_large = strtotime( "+{$r['large']}", $this->today ); 1321 | 1322 | // Week 1323 | $prev_small_d = date_i18n( 'j', $prev_small ); 1324 | $prev_small_m = date_i18n( 'n', $prev_small ); 1325 | $prev_small_y = date_i18n( 'Y', $prev_small ); 1326 | $next_small_d = date_i18n( 'j', $next_small ); 1327 | $next_small_m = date_i18n( 'n', $next_small ); 1328 | $next_small_y = date_i18n( 'Y', $next_small ); 1329 | 1330 | // Month 1331 | $prev_large_d = date_i18n( 'j', $prev_large ); 1332 | $prev_large_m = date_i18n( 'n', $prev_large ); 1333 | $prev_large_y = date_i18n( 'Y', $prev_large ); 1334 | $next_large_d = date_i18n( 'j', $next_large ); 1335 | $next_large_m = date_i18n( 'n', $next_large ); 1336 | $next_large_y = date_i18n( 'Y', $next_large ); 1337 | 1338 | // Setup month args 1339 | $prev_small_args = array( 'cy' => $prev_small_y, 'cm' => $prev_small_m, 'cd' => $prev_small_d ); 1340 | $prev_large_args = array( 'cy' => $prev_large_y, 'cm' => $prev_large_m, 'cd' => $prev_large_d ); 1341 | $next_small_args = array( 'cy' => $next_small_y, 'cm' => $next_small_m, 'cd' => $next_small_d ); 1342 | $next_large_args = array( 'cy' => $next_large_y, 'cm' => $next_large_m, 'cd' => $next_large_d ); 1343 | 1344 | // Setup links 1345 | $prev_small_link = add_query_arg( $prev_small_args, $today ); 1346 | $next_small_link = add_query_arg( $next_small_args, $today ); 1347 | $prev_large_link = add_query_arg( $prev_large_args, $today ); 1348 | $next_large_link = add_query_arg( $next_large_args, $today ); 1349 | 1350 | // Start an output buffer 1351 | ob_start(); ?> 1352 | 1353 | 1378 | 1379 | 1401 | 1402 |
    1403 | 1404 | 1405 | modes as $mode => $title ) : 1409 | 1410 | $url = add_query_arg( 'mode', $mode ); 1411 | 1412 | // Setup classes 1413 | $classes = array( 'view-' . $mode ); 1414 | if ( $this->mode === $mode ) { 1415 | $classes[] = 'current'; 1416 | } ?> 1417 | 1418 | 1419 | 1420 | 1421 | 1422 | 1423 | 1424 | 1425 |
    1426 | 1427 |