1 <?php
2 /**
3 * Handles individual events.
4 *
5 * An event can have one or more event dates.
6 *
7 * ##Usage
8 *
9 * <code>
10 * // Output an event as HTML.
11 * $event = new Theater_Event( 123 );
12 * echo $event;
13 * </code>
14 * <code>
15 * // Output an event as HTML with a custom template:
16 * $event = new Theater_Event( 123, '{{title}}{{dates}}{{location}}' );
17 * echo $event;
18 * </code>
19 * <code>
20 * // Get the value of an event field:
21 * $event = new Theater_Event( 123 );
22 * $dates = $event->dates(); // Eg. '05-06-2017'.
23 * $prices = $event->prices(); // An array of all ticket prices for this date.
24 * $title = $event->title(); // Eg. 'Sound of Music'.
25 * </code>
26 * <code>
27 * // Output the value of an event date field as HTML:
28 * $event = new Theater_Event( 123 );
29 * echo $event->dates;
30 * echo $event->prices;
31 * echo $event->title;
32 * </code>
33 *
34 * ## Fields
35 *
36 * Events have the following fields:
37 *
38 * | field | description |
39 * |---|---|
40 * | `categories` | The categories of the event.
41 * | `cities` | A summary of all the cities that the event takes place.
42 * | `content` | The post content of the event.
43 * | `dates` | A summary of all the dates of the event.
44 * | `excerpt` | The excerpt of the event.
45 * | `summary` | A summary of the event.
46 * | `permalink` | The permalink of the event.
47 * | `title` | The title of the event.
48 * | `thumbnail` | The thumbnail image of the event.
49 *
50 * ## HTML template
51 *
52 * The default template for the HTML output of an event date is:
53 * `{{thumbnail|permalink}} {{title|permalink}} {{dates}} {{cities}}`
54 *
55 * @package Theater/Events
56 * @since 0.16
57 *
58 */
59 class Theater_Event extends Theater_Item {
60
61 const name = 'event';
62 const post_type_name = 'wp_theatre_prod';
63
64 function get_fields() {
65
66 $fields = array(
67 'categories',
68 'cities',
69 'content',
70 'dates',
71 'dates_summary',
72 'event_dates',
73 'excerpt',
74 'prices',
75 'summary',
76 );
77
78 return $fields;
79 }
80
81 /**
82 * Gets the categories of an event.
83 *
84 * @since 0.x
85 * @todo The results from wp_get_post_categories() aren’t cached which will result in a db call beign made
86 * every time this function is called. Use this function with care. For performance, functions like
87 * get_the_category() should be used to return categories attached to a post.
88 * @internal
89 * @return WP_Category[] The categories of an event
90 */
91 function get_categories() {
92 $categories = wp_get_post_categories( $this->ID );
93 return $categories;
94 }
95
96 /**
97 * Gets the HTML for the categories of an event.
98 *
99 * @since 0.16
100 * @uses Theater_Item::get_field() to get the list of categories for an event.
101 * @internal
102 * @return string The HTML for the categories of an event.
103 */
104 function get_categories_html() {
105
106 $categories = $this->get_field('categories');
107
108 if (empty($categories)) {
109 return '';
110 }
111
112 ob_start();
113 ?><ul class="wpt_production_categories"><?php
114 foreach ( $categories as $category_id ) {
115 $category = get_category( $category_id );
116 ?><li class="wpt_production_category wpt_production_category_<?php echo $category->slug; ?>"><?php
117 echo $category->name;
118 ?></li><?php
119 }
120 ?></ul><?php
121 $html = ob_get_clean();
122
123 return $html;
124 }
125
126 /**
127 * Gets the cities of an event.
128 *
129 * @since 0.4
130 * @uses Theater_Event::get_event_dates() to get a list of upcoming dates for an event.
131 * @uses Theater_Item::get_field() to get the city of an event date.
132 * @internal
133 * @return string The cities of an event.
134 */
135 function get_cities() {
136
137 $cities = array();
138
139 foreach( $this->get_event_dates(array( 'start' => 'now' ) ) as $date) {
140 $city = $date->get_field('city');
141 $city = trim( ucwords( $city ) );
142 if (!empty($city)) {
143 $cities[] = $city;
144 }
145 }
146
147 $cities = array_unique( array_values($cities) );
148
149 $cities_text = '';
150
151 switch ( count( array_slice( $cities,0,3 ) ) ) {
152 case 1:
153 $cities_text .= $cities[0];
154 break;
155 case 2:
156 $cities_text .= $cities[0].' '.__( 'and','theatre' ).' '.$cities[1];
157 break;
158 case 3:
159 $cities_text .= $cities[0].', '.$cities[1].' '.__( 'and','theatre' ).' '.$cities[2];
160 break;
161 }
162
163 if ( count( $cities ) > 3 ) {
164 $cities_text = __( 'ao','theatre' ).' '.$cities_text;
165 }
166
167 return $cities_text;
168 }
169
170 /**
171 * Gets the post content of an event.
172 *
173 * @since 0.x
174 * @uses Theater_Item::get_post() to get the post object of an event.
175 * @internal
176 * @return string The post content of an event.
177 */
178 function get_content() {
179 $content = $this->get_post()->post_content;
180 return $content;
181 }
182
183 /**
184 * Gets the HTML for the post content of an event.
185 *
186 * @since 0.16
187 * @uses Theater_Item::get_post_type() to add the post type to the classes of the HTML output.
188 * @internal
189 * @return string The HTML for the post content of an event.
190 */
191 function get_content_html() {
192 global $wp_theatre;
193 /*
194 * Temporarily unhook other Theater filters that hook into `the_content`
195 * to avoid loops.
196 */
197
198 remove_filter( 'the_content', array( $wp_theatre->frontend, 'the_content' ) );
199 remove_filter( 'the_content', array( $wp_theatre->listing_page, 'the_content' ) );
200
201 ob_start();
202 ?><div class="<?php echo $this->get_post_type(); ?>_content"><?php
203 echo apply_filters( 'the_content',$this->get_field('content'));
204 ?></div><?php
205
206 $html = ob_get_clean();
207
208 add_filter( 'the_content', array( $wp_theatre->frontend, 'the_content' ) );
209 add_filter( 'the_content', array( $wp_theatre->listing_page, 'the_content' ) );
210
211 return $html;
212
213 }
214
215 /**
216 * Gets the upcoming event dates.
217 *
218 * @since 0.4
219 * @since 0.15.3 Moved HTML output to seperate method.
220 * @see WPT_Production::dates_html();
221 * Now returns an array instead of a summary (string).
222 * @since 0.15.7 Make sure that the keys are reset of the returned array.
223 * Fixes #199.
224 * @uses Theater_Event::get_event_dates() to get a list of upcoming dates for an event.
225 * @uses Theater_Item::get_field() to get the startdate of an event date.
226 * @internal
227 * @return array The upcoming production dates.
228 */
229 function get_dates() {
230
231 $dates = array();
232
233 foreach ( $this->get_event_dates( array( 'start' => 'now' ) ) as $date ) {
234 $dates[] = $date->get_field('startdate');
235 }
236
237 // Remove duplicate dates _without_ preserving keys.
238 $dates = array_values( array_unique( $dates ) );
239
240 return $dates;
241 }
242
243 /**
244 * Gets the HTML for the upcoming event dates.
245 *
246 * @since 0.15.3
247 * @uses Theater_Item::get_post_type() to add the post type to the classes of the HTML output.
248 * @uses Theater_Item::get_field() to get the dates summary of an event date.
249 * @param WPT_Template_Placeholder_Filter[] $filters An array of filters to apply to the value if the field.
250 * @internal
251 * @return string The HTML for the upcoming event dates.
252 */
253 function get_dates_html( $filters = array() ) {
254
255 ob_start();
256
257 ?><div class="<?php echo $this->get_post_type(); ?>_dates"><?php echo $this->apply_template_filters( $this->get_field('dates_summary'), $filters ); ?></div><?php
258
259 $html = ob_get_clean();
260
261 return $html;
262 }
263
264 /**
265 * Gets the summary of the upcoming event dates of an event.
266 *
267 * @since 0.15.3
268 * @since 0.15.7 Fix: A production with both historic and multiple upcoming events
269 * showed the first upcoming event as the enddate.
270 * Fixes #200.
271 * @uses Theater_Item::get_field() to get all upcoming event dates of an event.
272 * @uses Theater_Event::get_event_dates() to get all past event dates of an event.
273 * @internal
274 * @return string The summary for the upcoming event dates.
275 */
276 function get_dates_summary() {
277
278 $dates = $this->get_field('dates');
279
280 if ( empty( $dates ) ) {
281 return '';
282 }
283
284 $old_dates_args = array(
285 'end' => 'now',
286 'production' => $this->ID,
287 );
288 $old_events = $this->get_event_dates($old_dates_args);
289
290 if ( empty( $old_events ) ) {
291 if ( 1 == count( $dates ) ) {
292 $dates_summary = $dates[0];
293 } else {
294 /* translators: a date range, eg. April 10, 2016 to April 12, 2016 */
295 $dates_summary = sprintf( _x( '%s to %s', 'production dates', 'theatre' ), $dates[0], $dates[ count( $dates ) -1 ] );
296 }
297 } else {
298 /* translators: enddate of a running event, eg. until April 12, 2016 */
299 $dates_summary = sprintf( _x( 'until %s', 'production dates', 'theatre' ), $dates[count( $dates ) -1] );
300 }
301
302 return $dates_summary;
303 }
304
305 /**
306 * Gets the event dates of an event.
307 *
308 * @since 0.16
309 * @uses Theater_Event_Date_List::get() to get the event dates of an event.
310 * @param array $filters An array of filter arguments. Optional.
311 * See [Theater_Event_Date_List::get()](class-Theater_Event_Date_List.html#_get) for possible filter arguments.
312 * @return Theater_Event_Date[] The event dates of an event.
313 */
314 function get_event_dates( $filters = array() ) {
315
316 $defaults = array(
317 'event' => $this->ID,
318 'status' => $this->post()->post_status,
319 );
320
321 $filters = wp_parse_args( $filters, $defaults );
322
323 $dates = new Theater_Event_Date_List($filters);
324 return $dates->get();
325
326 }
327
328 /**
329 * Gets the event excerpt.
330 *
331 * Returns an excerpt of the production page as plain text or as an HTML element.
332 *
333 * @since 0.4
334 *
335 * @param array $args {
336 * @type bool $html Return HTML? Default <false>.
337 * }
338 * @internal
339 * @return string The event excerpt.
340 */
341 function excerpt( $args = array() ) {
342
343 $defaults = array(
344 'words' => 15,
345 );
346
347 $args = wp_parse_args( $args, $defaults );
348
349 $excerpt = $this->post()->post_excerpt;
350 if ( empty( $excerpt ) ) {
351 $excerpt = wp_trim_words( strip_shortcodes( $this->post()->post_content ), $args['words'] );
352 }
353 $excerpt = apply_filters( 'wpt_production_excerpt',$excerpt, $this );
354
355 return $excerpt;
356 }
357
358 /**
359 * Gets the HTML for the excerpt of an event.
360 *
361 * @since 0.16
362 * @param array $filters (default: array())
363 * @internal
364 * @return void
365 */
366 function get_excerpt_html( $filters = array() ) {
367 $value = $this->excerpt();
368 ob_start();
369 ?><p class="<?php echo $this->get_post_type(); ?>_excerpt"><?php
370 echo $this->apply_template_filters( $this->excerpt(), $filters );
371 ?></p><?php
372 $html= ob_get_clean();
373
374 return $html;
375 }
376
377 /**
378 * Production permalink.
379 *
380 * Returns a link to the production page as a URL or as an HTML element.
381 *
382 * @since 0.4
383 *
384 * @param array $args {
385 * @type bool $html Return HTML? Default <false>.
386 * @type string $text Display text for HTML version. Defaults to the title of the production.
387 * @type bool $inside Try to place de link inside the surrounding div. Default <false>.
388 * }
389 * @internal
390 * @return string URL or HTML.
391 */
392 function permalink( $args = array() ) {
393 $defaults = array(
394 'html' => false,
395 'text' => $this->title(),
396 'inside' => false,
397 );
398
399 $args = wp_parse_args( $args, $defaults );
400
401 if ( ! isset( $this->permalink ) ) {
402 $this->permalink = apply_filters( 'wpt_production_permalink',get_permalink( $this->ID ), $this );
403 }
404
405 if ( $args['html'] ) {
406 $html = '';
407
408 if ( $args['inside'] ) {
409 $text_sanitized = trim( $args['text'] );
410
411 $before = '';
412 $after = '';
413 $text = $args['text'];
414
415 $elements = array( 'div','figure' );
416 foreach ( $elements as $element ) {
417 if (
418 $args['inside'] &&
419 strpos( $text_sanitized, '<'.$element ) === 0 &&
420 strrpos( $text_sanitized, '</'.$element ) === strlen( $text_sanitized ) - strlen( $element ) - 3
421 ) {
422 $before = substr( $args['text'], 0, strpos( $args['text'], '>' ) + 1 );
423 $after = '</'.$element.'>';
424 $text = substr( $args['text'], strpos( $args['text'], '>' ) + 1, strrpos( $args['text'],'<' ) - strpos( $args['text'], '>' ) - 1 );
425 continue;
426 }
427 }
428 $inside_args = array(
429 'html' => true,
430 'text' => $text,
431 );
432 return $before.$this->permalink( $inside_args ).$after;
433 } else {
434 $html .= '<a href="'.get_permalink( $this->ID ).'">';
435 $html .= $args['text'];
436 $html .= '</a>';
437 }
438 return apply_filters( 'wpt_production_permalink_html', $html, $this );
439 } else {
440 return $this->permalink;
441 }
442 }
443
444 /**
445 * Gets the prices for the production.
446 *
447 * @since 0.15.3
448 * @internal
449 * @return array The prices for the production.
450 */
451 function get_prices() {
452
453 $prices = array();
454
455 foreach ( $this->event_dates() as $date ) {
456 $date_prices = $date->get_field('prices');
457 foreach ( $date_prices as $price ) {
458 $prices[] = $price;
459 }
460 }
461 $prices = array_unique( $prices );
462 sort( $prices );
463
464 /**
465 * Filter the prices of the production.
466 *
467 * @since 0.15.3
468 * @param array $prices The current prices.
469 * @param WPT_Production $production The production.
470 */
471 $prices = apply_filters( 'wpt/production/prices', $prices, $this );
472
473 return $prices;
474 }
475
476 /**
477 * Gets the HTML of the prices for the production.
478 *
479 * @since 0.15.3
480 * @internal
481 * @param array $filters The template filters to apply.
482 * @return array The HTML of the prices for the production.
483 */
484 function get_prices_html( $filters = array() ) {
485 $html = '';
486
487 $prices_summary_html = $this->get_field_html('prices_summary');
488
489 if ( ! empty( $prices_summary_html ) ) {
490 ob_start();
491 ?><div class="<?php echo $this->get_post_type(); ?>_prices"><?php echo $this->apply_template_filters( $prices_summary_html, $filters ); ?></div><?php
492 $html = ob_get_clean();
493 }
494
495 /**
496 * Filter the HTML of the prices for the production.
497 *
498 * @since 0.15.3
499 * @param string $html The current html.
500 * @param WPT_Production $production The production.
501 */
502 $html = apply_filters( 'wpt/production/prices/html', $html, $this );
503
504 return $html;
505 }
506
507 /**
508 * Gets a summary of the prices for the production.
509 *
510 * @since 0.15.3
511 * @internal
512 * @see WPT_Production::prices()
513 * @return string A summary of the prices for the production.
514 */
515 function get_prices_summary() {
516
517 global $wp_theatre;
518
519 $prices = $this->get_field('prices');
520
521 $prices_summary = '';
522
523 if ( count( $prices ) ) {
524 if ( count( $prices ) > 1 ) {
525 $prices_summary .= __( 'from','theatre' ).' ';
526 }
527 if ( ! empty( $wp_theatre->wpt_tickets_options['currencysymbol'] ) ) {
528 $prices_summary .= $wp_theatre->wpt_tickets_options['currencysymbol'].' ';
529 }
530 $prices_summary .= number_format_i18n( (float) min( $prices ), 2 );
531 }
532
533 return $prices_summary;
534 }
535
536 /**
537 * Gets the HTML for the summary of the prices for the production.
538 *
539 * @since 0.15.3
540 * @internal
541 * @see WPT_Production::prices_summary()
542 * @return string The HTML for the summary of the prices for the production.
543 */
544 public function get_prices_summary_html() {
545
546 $html = $this->get_field('prices_summary');
547 $html = esc_html( $html );
548 $html = str_replace( ' ', ' ', $html );
549
550 /**
551 * Filter the HTML for the summary of the prices for the production.
552 *
553 * @since 0.15.3
554 * @param string $html The current html.
555 * @param WPT_Production $production The production.
556 */
557 $html = apply_filters( 'wpt/production/prices/summary/html', $html, $this );
558
559 return $html;
560 }
561
562 /**
563 * Production season.
564 *
565 * @since 0.4
566 * @internal
567 *
568 * @return object WPT_Season.
569 */
570 function get_season() {
571 $season_id = get_post_meta( $this->ID,'wp_theatre_season',true );
572 if ( ! empty( $season_id ) ) {
573 $season = new WPT_Season( $season_id );
574 } else {
575 $season = false;
576 }
577
578 return $season;
579 }
580
581 /**
582 * Production summary.
583 *
584 * Returns a summary of the production page containing dates, cities and excerpt as plain text or as an HTML element.
585 *
586 * @todo Add prices.
587 *
588 * @since 0.4
589 * @internal
590 *
591 * @return string URL or HTML.
592 */
593 function get_summary() {
594
595 $summary = '';
596
597 $dates = $this->get_field('dates');
598
599 if (!empty($dates)) {
600 $short = $this->get_field('dates_summary');
601
602 $cities = $this->get_field('cities');
603 if ( !empty($cities) ) {
604 $short .= ' '.sprintf(__( 'in %s ','theatre' ), $cities);
605 }
606 $short .= '. ';
607 $summary .= ucfirst( $short );
608 }
609 $summary .= $this->get_field('excerpt');
610
611 return $summary;
612 }
613
614 /**
615 * Gets the production thumbnail ID.
616 *
617 * @since 0.4
618 * @since 0.12.5 Deprecated the HTML output.
619 * Use @see WPT_Production::thumbnail_html() instead.
620 * @internal
621 *
622 * @return int ID of the thumbnail.
623 */
624 function thumbnail( $deprecated = array() ) {
625
626 if ( ! empty( $deprecated['html'] ) ) {
627 $defaults = array(
628 'size' => 'thumbnail',
629 'filters' => array(),
630 );
631 $deprecated = wp_parse_args( $deprecated, $defaults );
632 return $this->thumbnail_html( $deprecated['size'], $deprecated['filters'] );
633 }
634
635 $thumbnail = get_post_thumbnail_id( $this->ID );
636
637 /**
638 * Filter the production thumbnail ID.
639 *
640 * @since 0.12.5
641 * @param int ID The production thumbnail ID.
642 * @param WPT_Production $production The production.
643 */
644 $thumbnail = apply_filters( 'wpt/production/thumbnail', $thumbnail, $this );
645
646 return $thumbnail;
647 }
648
649 /**
650 * Get the production thumbnail HTML.
651 *
652 * @since 0.12.5
653 * @internal
654 * @param string $size The thumbnail size. Default: 'thumbnail'.
655 * @param array $filters The template filters to apply.
656 * @return string The production thumbnail HTML.
657 */
658 function thumbnail_html( $size = 'thumbnail', $filters = array() ) {
659
660 $html = '';
661 $thumbnail = get_the_post_thumbnail( $this->ID,$size );
662 if ( ! empty( $thumbnail ) ) {
663 $html .= '<figure>';
664 $html .= $this->apply_template_filters( $thumbnail, $filters );
665 $html .= '</figure>';
666 }
667
668 /**
669 * Filter the production thumbnail HTML.
670 *
671 * @since 0.12.5
672 * @param string $html The production thumbnail HTML.
673 * @param string $size The thumbnail size.
674 * @param array $filters The template filters to apply.
675 * @param WPT_Production $production The production.
676 */
677 $html = apply_filters( 'wpt/production/thumbnail/html/size='.$size, $html, $filters, $this );
678 $html = apply_filters( 'wpt/production/thumbnail/html', $html, $size, $filters, $this );
679
680 /**
681 * @deprecated 0.12.5
682 */
683 $html = apply_filters( 'wpt_production_thumbnail_html', $html, $this );
684
685 return $html;
686 }
687
688 /**
689 * Production title.
690 *
691 * Returns the production title as plain text or as an HTML element.
692 *
693 * @since 0.4
694 * @internal
695 *
696 * @param array $args {
697 * @type bool $html Return HTML? Default <false>.
698 * }
699 * @return string text or HTML.
700 */
701 function get_title( $args = array() ) {
702 $title = get_the_title( $this->ID );
703 return $title;
704 }
705
706 /**
707 * Gets the HTML for an event.
708 *
709 * @since 0.4
710 * @since 0.10.8 Added a filter to the default template.
711 * @since 0.14.7 Added the $args parameter.
712 * @since 0.15.2 Removed the $args parameter.
713 *
714 * @param string $template The template for the event HTML.
715 * @return string The HTML for an event.
716 */
717 function get_html( $template = '' ) {
718 $classes = array();
719 $classes[] = $this->get_post_type();
720
721 $template = new WPT_Production_Template( $this, $this->template );
722 $html = $template->get_merged();
723
724 /**
725 * Filter the HTML output for a production.
726 *
727 * @since 0.14.7
728 * @param string $html The HTML output for a production.
729 * @param WPT_Event_Template $template The production template.
730 * @param array $args The listing args (if the production is part of a listing).
731 * @param WPT_Production $production The production.
732 */
733 $html = apply_filters( 'wpt/production/html',$html, $template, $this );
734
735 /**
736 * @deprecated 0.14.7
737 */
738 $html = apply_filters( 'wpt_production_html',$html, $this );
739
740 /**
741 * Filter the classes for a production.
742 *
743 * @since 0.?
744 * @param array $classes The classes for a production.
745 * @param WPT_Event $event The production.
746 */
747 $classes = apply_filters( 'wpt_production_classes',$classes, $this );
748
749 // Wrapper
750 $html = '<div class="'.implode( ' ',$classes ).'">'.$html.'</div>';
751
752 return $html;
753 }
754
755 /**
756 * @deprecated 0.x
757 * @internal
758 */
759 function render() {
760 return $this->html();
761 }
762
763 /**
764 * @deprecated 0.16
765 * @internal
766 */
767 function events( $filters = array() ) {
768 return $this->get_event_dates( $filters );
769 }
770
771 /**
772 * @deprecated 0.16
773 * @internal
774 */
775 function past() {
776 global $wp_theatre;
777 if ( ! isset( $this->past ) ) {
778 $filters = array(
779 'production' => $this->ID,
780 'past' => true,
781 );
782 $this->past = $wp_theatre->events->get( $filters );
783 }
784 return $this->past;
785 }
786
787 /**
788 * @deprecated 0.16
789 * @internal
790 */
791 function upcoming() {
792 global $wp_theatre;
793 if ( ! isset( $this->upcoming ) ) {
794 $filters = array(
795 'upcoming' => true,
796 );
797 $this->upcoming = $this->events( $filters );
798 }
799 return $this->upcoming;
800 }
801
802 }
803
804 ?>
805