Completed
Push — master ( 72dfec...61887c )
by
unknown
06:50
created

Event_Builder::get_when()   D

Complexity

Conditions 9
Paths 72

Size

Total Lines 44
Code Lines 21

Duplication

Lines 9
Ratio 20.45 %

Importance

Changes 0
Metric Value
cc 9
eloc 21
nc 72
nop 1
dl 9
loc 44
rs 4.909
c 0
b 0
f 0
1
<?php
2
/**
3
 * Event Builder
4
 *
5
 * @package SimpleCalendar/Events
6
 */
7
namespace SimpleCalendar\Events;
8
9
use Carbon\Carbon;
10
use SimpleCalendar\Abstracts\Calendar;
11
12
if ( ! defined( 'ABSPATH' ) ) {
13
	exit;
14
}
15
16
/**
17
 * The Event Builder.
18
 *
19
 * Parses event templates from a feed post type for printing on calendar views.
20
 *
21
 * @since 3.0.0
22
 */
23
class Event_Builder {
24
25
	/**
26
	 * Event.
27
	 *
28
	 * @access public
29
	 * @var Event
30
	 */
31
	public $event = null;
32
33
	/**
34
	 * Calendar.
35
	 *
36
	 * @access public
37
	 * @var Calendar
38
	 */
39
	public $calendar = null;
40
41
	/**
42
	 * Tags.
43
	 *
44
	 * @access public
45
	 * @var array
46
	 */
47
	public $tags = array();
48
49
	/**
50
	 * Constructor.
51
	 *
52
	 * @since 3.0.0
53
	 *
54
	 * @param Event    $event
55
	 * @param Calendar $calendar
56
	 */
57
	public function __construct( Event $event, Calendar $calendar ) {
58
		$this->event    = $event;
59
		$this->calendar = $calendar;
60
		$this->tags     = $this->get_content_tags();
61
	}
62
63
	/**
64
	 * Get content tags.
65
	 *
66
	 * @since  3.0.0
67
	 *
68
	 * @return array
69
	 */
70
	public function get_content_tags() {
71
		return array_merge( array(
72
73
			/* ============ *
74
			 * Content Tags *
75
			 * ============ */
76
77
			'title',
78
			// The event title.
79
			'event-title',
80
			// @deprecated An alias for 'title' tag.
81
			'description',
82
			// The event description.
83
84
			'when',
85
			// Date and time of the event.
86
			'start-time',
87
			// Start time of the event.
88
			'start-date',
89
			// Start date of the event.
90
			'start-custom',
91
			// @deprecated Start time in a user defined format (set by tag 'format' attribute).
92
			'start-human',
93
			// Start time in a human friendly format.
94
			'end-time',
95
			// End time of the event.
96
			'end-date',
97
			// End date of the event.
98
			'end-custom',
99
			// @deprecated End date-time in a user defined format (set by tag 'format' attribute).
100
			'end-human',
101
			// End date-time in a human friendly format.
102
103
			'duration',
104
			// How long the events lasts, in a human-readable format.
105
			'length',
106
			// @deprecated An alias of 'duration' tag.
107
108
			'location',
109
			// Alias of start-location.
110
			'start-location',
111
			// Location name where the event starts.
112
			'maps-link',
113
			// @deprecated An alias for 'start-location-link' tag.
114
			'start-location-link',
115
			// Link to Google Maps querying the event start location address.
116
			'end-location',
117
			// Location name where the event ends.
118
			'end-location-link',
119
			// Link to Google Maps querying the event end location address.
120
121
			'link',
122
			// An HTML link to the event URL.
123
			'url',
124
			// A string with the raw event link URL.
125
			'add-to-gcal-link',
126
			// Link for viewers to add to their GCals.
127
128
			'calendar',
129
			// The title of the source calendar.
130
			'feed-title',
131
			// @deprecated An alias of 'calendar'.
132
133
			'id',
134
			// The event unique ID.
135
			'uid',
136
			// An alias of ID.
137
			'ical-id',
138
			// iCal ID.
139
			'event-id',
140
			// @deprecated An alias for 'id' tag.
141
			'calendar-id',
142
			// The calendar ID.
143
			'feed-id',
144
			// @deprecated An alias for 'calendar-id' tag.
145
			'cal-id',
146
			// @deprecated An alias for 'calendar-id' tag.
147
148
			/* ========= *
149
			 * Meta Tags *
150
			 * ========= */
151
152
			'attachments',
153
			// List of attachments.
154
			'attendees',
155
			// List of attendees.
156
			'organizer',
157
			// Creator info.
158
159
			/* ================ *
160
			 * Conditional Tags *
161
			 * ================ */
162
163
			'if-title',
164
			// If the event has a title.
165
			'if-description',
166
			// If the event has a description.
167
168
			'if-now',
169
			// If the event is taking place now.
170
			'if-not-now',
171
			// If the event is not taking place now (may have ended or just not started yet).
172
			'if-started',
173
			// If the event has started (and may as well as ended).
174
			'if-not-started',
175
			// If the event has NOT started yet (event could be any time in the future).
176
			'if-ended',
177
			// If the event has ended (event could be any time in the past).
178
			'if-not-ended',
179
			// If the event has NOT ended (may as well as not started yet).
180
181
			'if-whole-day',
182
			// If the event lasts the whole day.
183
			'if-all-day',
184
			// @deprecated Alias for 'if-whole-day'.
185
			'if-not-whole-day',
186
			// If the event does NOT last the whole day.
187
			'if-not-all-day',
188
			// @deprecated Alias for 'if-not-whole-day'.
189
			'if-end-time',
190
			// If the event has a set end time.
191
			'if-no-end-time',
192
			// If the event has NOT a set end time.
193
194
			'if-multi-day',
195
			// If the event spans multiple days.
196
			'if-single-day',
197
			// If the event does not span multiple days.
198
199
			'if-recurring',
200
			// If the event is a recurring event.
201
			'if-not-recurring',
202
			// If the event is NOT a recurring event.
203
204
			'if-location',
205
			// @deprecated Alias for 'if-start-location'.
206
			'if-start-location',
207
			// Does the event has a start location?
208
			'if-end-location',
209
			// Does the event has an end location?
210
			'if-not-location',
211
			// @deprecated Alias for 'if-not-start-location'.
212
			'if-not-start-location',
213
			// Does the event has NOT a start location?
214
			'if-not-end-location',
215
			// Does the event has NOT an end location?
216
217
		), (array) $this->add_custom_event_tags() );
218
	}
219
220
	/**
221
	 * Get event content.
222
	 *
223
	 * @since  3.0.0
224
	 *
225
	 * @param  string $template_tags
226
	 *
227
	 * @return string
228
	 */
229
	public function parse_event_template_tags( $template_tags = '' ) {
230
231
		// Process tags.
232
		$result = preg_replace_callback( $this->get_regex(), array( $this, 'process_event_content' ), $template_tags );
233
234
		// Removes extra consecutive <br> tags.
235
		// TODO: Doesn't seem to work but going to remove it to allow multiple <br> tags in the editor
236
		/*return preg_replace( '#(<br *//*?>\s*)+#i', '<br />', trim( $result ) );*/
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
237
238
		return do_shortcode( trim( $result ) );
239
	}
240
241
	/**
242
	 * Process event content.
243
	 *
244
	 * @since  3.0.0
245
	 *
246
	 * @param  string $match
247
	 *
248
	 * @return string
249
	 */
250
	public function process_event_content( $match ) {
251
252
		if ( $match[1] == '[' && $match[6] == ']' ) {
253
			return substr( $match[0], 1, -1 );
254
		}
255
256
		$tag     = $match[2]; // Tag name without square brackets.
257
		$before  = $match[1]; // Before tag.
258
		$partial = $match[5]; // HTML content between tags.
259
		$after   = $match[6]; // After tag.
260
		$attr    = $match[3]; // Tag attributes in quotes.
261
262
		$calendar = $this->calendar;
263
		$event    = $this->event;
264
265
		if ( ( $calendar instanceof Calendar ) && ( $event instanceof Event ) ) {
266
267
			switch ( $tag ) {
268
269
				/* ============ *
270
				 * Content Tags *
271
				 * ============ */
272
273
				case 'title' :
274
				case 'event-title' :
275
					return $this->get_title( $event->title, $attr );
276
277
				case 'description' :
278
					return $this->get_description( $event->description, $attr );
279
280
				case 'when' :
281
					return $this->get_when( $event );
282
283
				case 'end-date' :
284
				case 'end-custom' :
285
				case 'end-human' :
286
				case 'end-time' :
287
				case 'start-custom' :
288
				case 'start-date' :
289
				case 'start-human' :
290
				case 'start-time' :
291
					return $this->get_dt( $tag, $event, $attr );
292
293
				case 'length' :
294
				case 'duration' :
295
					if ( false !== $event->end ) {
296
						$duration = $event->start - $event->end;
297
						$value    = human_time_diff( $event->start, $event->end );
298
					} else {
299
						$duration = '-1';
300
						$value    = __( 'No end time', 'google-calendar-events' );
301
					}
302
303
					return ' <span class="simcal-event-duration" data-event-duration="' . $duration . '">' . $value . '</span>';
304
305
				case 'location' :
306
				case 'start-location' :
307
				case 'end-location' :
308
					$location       = ( 'end-location' == $tag ) ? $event->end_location['address'] : $event->start_location['address'];
309
					$location_class = ( 'end-location' == $tag ) ? 'end' : 'start';
310
311
					// location, location.name, location.address (type PostalAddress) all required for schema data.
312
					// Need to use event name where location data doesn't exist.
313
					// Since we have 1 location field, use it as the name and address.
314
					// If the location is blank, use the event title as the name and address.
315
					// Wrap with wp_strip_all_tags().
316
					$meta_location_name_and_address = empty( $location ) ? wp_strip_all_tags( $event->title ) : wp_strip_all_tags( $location );
317
318
					return ' <span class="simcal-event-address simcal-event-' . $location_class . '-location" itemprop="location" itemscope itemtype="http://schema.org/Place">' . '<meta itemprop="name" content="' . $meta_location_name_and_address . '" />' . '<meta itemprop="address" content="' . $meta_location_name_and_address . '" />' . wp_strip_all_tags( $location ) . '</span>';
319
320
				case 'start-location-link':
321
				case 'end-location-link' :
322
				case 'maps-link' :
323
					$location = ( 'end-location-link' == $tag ) ? $event->end_location['address'] : $event->start_location['address'];
324
					if ( ! empty( $location ) ) {
325
						$url = '//maps.google.com?q=' . urlencode( $location );
326
327
						return $this->make_link( $tag, $url, $calendar->get_event_html( $event, $partial ), $attr );
328
					}
329
					break;
330
331
				case 'link' :
332
				case 'url' :
333
					$content = ( 'link' == $tag ) ? $calendar->get_event_html( $event, $partial ) : '';
334
335
					return $this->make_link( $tag, $event->link, $content, $attr );
336
337
				case 'add-to-gcal-link';
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
338
					$content = ( 'add-to-gcal-link' == $tag ) ? $calendar->get_event_html( $event, $partial ) : '';
339
					if ( ! empty( $content ) ) {
340
						$url = $calendar->get_add_to_gcal_url( $event );
341
342
						return $this->make_link( $tag, $url, $content, $attr );
343
					}
344
					break;
345
346
				case 'calendar' :
347
				case 'feed-title' :
348
					return $event->source;
349
350
				case 'id' :
351
				case 'uid' :
352
				case 'event-id' :
353
					return $event->uid;
354
355
				case 'ical-id' :
356
					return $event->ical_id;
357
358
				case 'calendar-id' :
359
				case 'cal-id' :
360
				case 'feed-id' :
361
					return $event->calendar;
362
363
				/* ========= *
364
				 * Meta Tags *
365
				 * ========= */
366
367
				case 'attachments' :
368
					$attachments = $event->get_attachments();
369
					if ( ! empty( $attachments ) ) {
370
						return $this->get_attachments( $attachments );
371
					}
372
					break;
373
374
				case 'attendees' :
375
					$attendees = $event->get_attendees();
376
					if ( ! empty( $attendees ) ) {
377
						return $this->get_attendees( $attendees, $attr );
378
					}
379
					break;
380
381
				case 'organizer' :
382
					$organizer = $event->get_organizer();
383
					if ( ! empty( $organizer ) ) {
384
						return $this->get_organizer( $organizer, $attr );
385
					}
386
					break;
387
388
				/* ================ *
389
				 * Conditional Tags *
390
				 * ================ */
391
392
				case 'if-title':
393
					if ( ! empty( $event->title ) ) {
394
						return $calendar->get_event_html( $event, $partial );
395
					}
396
					break;
397
398
				case 'if-description':
399
					if ( ! empty( $event->description ) ) {
400
						return $calendar->get_event_html( $event, $partial );
401
					}
402
					break;
403
404
				case 'if-now' :
405
				case 'if-not-now' :
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
406
407
					$start_dt = $event->start_dt->setTimezone( $calendar->timezone );
408
					$start    = $start_dt->getTimestamp();
409
410
					if ( $event->end_dt instanceof Carbon ) {
411
						$end = $event->end_dt->setTimezone( $calendar->timezone )->getTimestamp();
412
					} else {
413
						return '';
414
					}
415
416
					$now = ( $start <= $calendar->now ) && ( $end >= $calendar->now );
417
418
					if ( ( 'if-now' == $tag ) && $now ) {
419
						return $calendar->get_event_html( $event, $partial );
420
					} elseif ( ( 'if-not-now' == $tag ) && ( false == $now ) ) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
421
						return $calendar->get_event_html( $event, $partial );
422
					}
423
424
					break;
425
426
				case 'if-started' :
427 View Code Duplication
				case 'if-not-started' :
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
428
429
					$start = $event->start_dt->setTimezone( $calendar->timezone )->getTimestamp();
430
431
					if ( 'if-started' == $tag ) {
432
						if ( $start < $calendar->now ) {
433
							return $calendar->get_event_html( $event, $partial );
434
						}
435
					} elseif ( 'if-not-started' == $tag ) {
436
						if ( $start > $calendar->now ) {
437
							return $calendar->get_event_html( $event, $partial );
438
						}
439
					}
440
441
					break;
442
443
				case 'if-ended' :
444 View Code Duplication
				case 'if-not-ended' :
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
445
446
					if ( false !== $event->end ) {
447
448
						$end = $event->end_dt->setTimezone( $calendar->timezone )->getTimestamp();
449
450
						if ( 'if-ended' == $tag ) {
451
							if ( $end < $calendar->now ) {
452
								return $calendar->get_event_html( $event, $partial );
453
							}
454
						} elseif ( 'if-not-ended' == $tag ) {
455
							if ( $end > $calendar->now ) {
456
								return $calendar->get_event_html( $event, $partial );
457
							}
458
						}
459
460
					}
461
462
					break;
463
464
				case 'if-end-time' :
465
					if ( false !== $event->end ) {
466
						return $calendar->get_event_html( $event, $partial );
467
					}
468
					break;
469
470
				case 'if-no-end-time' :
471
					if ( false === $event->end ) {
472
						return $calendar->get_event_html( $event, $partial );
473
					}
474
					break;
475
476
				case 'if-all-day' :
477
				case 'if-whole-day' :
478
				case 'if-not-all-day' :
479
				case 'if-not-whole-day' :
480
					$bool = strstr( $tag, 'not' ) ? false : true;
481
					if ( $bool === $event->whole_day ) {
482
						return $calendar->get_event_html( $event, $partial );
483
					}
484
					break;
485
486
				case 'if-recurring' :
487
					if ( ! empty( $event->recurrence ) ) {
488
						return $calendar->get_event_html( $event, $partial );
489
					}
490
					break;
491
492
				case 'if-not-recurring' :
493
					if ( false === $event->recurrence ) {
494
						return $calendar->get_event_html( $event, $partial );
495
					}
496
					break;
497
498
				case 'if-multi-day' :
499
					if ( false !== $event->multiple_days ) {
500
						return $calendar->get_event_html( $event, $partial );
501
					}
502
					break;
503
504
				case 'if-single-day' :
505
					if ( false === $event->multiple_days ) {
506
						return $calendar->get_event_html( $event, $partial );
507
					}
508
					break;
509
510
				case 'if-location' :
511
				case 'if-start-location' :
512
					if ( ! empty( $event->start_location['address'] ) ) {
513
						return $calendar->get_event_html( $event, $partial );
514
					}
515
516
					return false;
517
518
				case 'if-not-location' :
519
				case 'if-not-start-location' :
520
					if ( empty( $event->start_location['address'] ) ) {
521
						return $calendar->get_event_html( $event, $partial );
522
					}
523
524
					return '';
525
526
				case 'if-not-end-location' :
527
					if ( empty( $event->end_location['address'] ) ) {
528
						return $calendar->get_event_html( $event, $partial );
529
					}
530
531
					return '';
532
533
				case 'if-end-location' :
534
					if ( ! empty( $event->end_location['address'] ) ) {
535
						return $calendar->get_event_html( $event, $partial );
536
					}
537
538
					return '';
539
540
				/* ======= *
541
				 * Custom Event Tags or Default *
542
				 * ======= */
543
544
				default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
545
					$resultCustom = $this->do_custom_event_tag( $tag, $partial, $attr, $event );
546
					if ( $resultCustom != "" ) {
547
						return $resultCustom;
548
					}
549
550
					return wp_kses_post( $before . $partial . $after );
551
			}
552
		}
553
554
		return '';
555
	}
556
557
	/**
558
	 * Limit words in text string.
559
	 *
560
	 * @since  3.0.0
561
	 * @access private
562
	 *
563
	 * @param  string $text
564
	 * @param  int    $limit
565
	 *
566
	 * @return string
567
	 */
568
	private function limit_words( $text, $limit ) {
569
570
		$limit = max( absint( $limit ), 0 );
571
572
		if ( $limit > 0 && ( str_word_count( $text, 0 ) > $limit ) ) {
573
			$words = str_word_count( $text, 2 );
574
			$pos   = array_keys( $words );
575
			$text  = trim( substr( $text, 0, $pos[ $limit ] ) ) . '&hellip;';
576
		}
577
578
		return $text;
579
	}
580
581
	/**
582
	 * Get event title.
583
	 *
584
	 * @since  3.0.0
585
	 * @access private
586
	 *
587
	 * @param  $title
588
	 * @param  $attr
589
	 *
590
	 * @return string
591
	 */
592
	private function get_title( $title, $attr ) {
593
594
		if ( empty( $title ) ) {
595
			return '';
596
		}
597
598
		$attr = array_merge( array(
599
			'html'  => '',  // Parse HTML
600
			'limit' => 0,   // Trim length to amount of words
601
		), (array) shortcode_parse_atts( $attr ) );
602
603
		if ( ! empty( $attr['html'] ) ) {
604
			$title = wp_kses_post( $title );
605
			$tag   = 'div';
606
		} else {
607
			$title = $this->limit_words( $title, $attr['limit'] );
608
			$tag   = 'span';
609
		}
610
611
		return '<' . $tag . ' class="simcal-event-title" itemprop="name">' . $title . '</' . $tag . '>';
612
	}
613
614
	/**
615
	 * Get event description.
616
	 *
617
	 * @since  3.0.0
618
	 * @access private
619
	 *
620
	 * @param  string $description
621
	 * @param  string $attr
622
	 *
623
	 * @return string
624
	 */
625
	private function get_description( $description, $attr ) {
626
627
		if ( empty( $description ) ) {
628
			return '';
629
		}
630
631
		$attr = array_merge( array(
632
			'limit'    => 0,       // Trim length to number of words
633
			'html'     => 'no',    // Parse HTML content
634
			'markdown' => 'no',    // Parse Markdown content
635
			'autolink' => 'no',    // Automatically convert plaintext URIs to anchors
636
		), (array) shortcode_parse_atts( $attr ) );
637
638
		$allow_html = 'no' != $attr['html'] ? true : false;
639
		$allow_md   = 'no' != $attr['markdown'] ? true : false;
640
641
		$html = '<div class="simcal-event-description" itemprop="description">';
642
643
		// Markdown and HTML don't play well together, use one or the other in the same tag.
644
		if ( $allow_html || $allow_md ) {
645
			if ( $allow_html ) {
646
				$description = wp_kses_post( $description );
647
			} elseif ( $allow_md ) {
648
				$markdown    = new \Parsedown();
649
				$description = $markdown->text( wp_strip_all_tags( $description ) );
650
			}
651
		} else {
652
			$description = wpautop( $description );
653
		}
654
655
		$description = $this->limit_words( $description, $attr['limit'] );
656
657
		$html .= $description . '</div>';
658
659
		if ( 'no' != $attr['autolink'] ) {
660
			$html = ' ' . make_clickable( $html );
661
		}
662
663
		return $html;
664
	}
665
666
	/**
667
	 * Get event start and end date and time.
668
	 *
669
	 * @since  3.0.0
670
	 * @access private
671
	 *
672
	 * @param  Event $event
673
	 *
674
	 * @return string
675
	 */
676
	private function get_when( Event $event ) {
677
678
		$start = $event->start_dt;
679
		$end   = $event->end_dt;
680
681
		$time_start = '';
682
		$time_end   = '';
683
		$start_ts   = $start->timestamp;
684
		$end_ts     = ! is_null( $end ) ? $end->timestamp : null;
685
		$start_iso  = $start->toIso8601String();
686
		$end_iso    = ! is_null( $end ) ? $end->toIso8601String() : null;;
687
688
		if ( ! $event->whole_day ) {
689
690
			$time_start = $this->calendar->datetime_separator . ' <span class="simcal-event-start simcal-event-start-time" ' . 'data-event-start="' . $start_ts . '" ' . 'data-event-format="' . $this->calendar->time_format . '" ' . 'itemprop="startDate" content="' . $start_iso . '">' . date_i18n( $this->calendar->time_format, strtotime( $start->toDateTimeString() ) ) . '</span> ';
691
692 View Code Duplication
			if ( $end instanceof Carbon ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
693
694
				$time_end = ' <span class="simcal-event-end simcal-event-end-time" ' . 'data-event-end="' . $end_ts . '" ' . 'data-event-format="' . $this->calendar->time_format . '" ' . 'itemprop="endDate" content="' . $end_iso . '">' . date_i18n( $this->calendar->time_format, strtotime( $end->toDateTimeString() ) ) . '</span> ';
695
696
			}
697
698
		}
699
700
		if ( $event->multiple_days ) {
701
702
			$output = ' <span class="simcal-event-start simcal-event-start-date" ' . 'data-event-start="' . $start_ts . '" ' . 'data-event-format="' . $this->calendar->date_format . '" ' . 'itemprop="startDate" content="' . $start_iso . '">' . date_i18n( $this->calendar->date_format, strtotime( $start->toDateTimeString() ) ) . '</span> ' . $time_start;
703
704 View Code Duplication
			if ( $end instanceof Carbon ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
705
706
				$output .= '-' . ' <span class="simcal-event-start simcal-event-end-date" ' . 'data-event-start="' . $end_ts . '" ' . 'data-event-format="' . $this->calendar->date_format . '" ' . 'itemprop="endDate" content="' . $end_iso . '">' . date_i18n( $this->calendar->date_format, strtotime( $end->toDateTimeString() ) ) . '</span> ' . $time_end;
707
			}
708
709
		} else {
710
711
			$time_end = ! empty( $time_start ) && ! empty( $time_end ) ? ' - ' . $time_end : '';
712
713
			// All-day events also need startDate for schema data.
714
			$output = ' <span class="simcal-event-start simcal-event-start-date" ' . 'data-event-start="' . $start_ts . '" ' . 'data-event-format="' . $this->calendar->date_format . '" ' . 'itemprop="startDate" content="' . $start_iso . '">' . date_i18n( $this->calendar->date_format, strtotime( $start->toDateTimeString() ) ) . '</span> ' . $time_start . $time_end;
715
716
		}
717
718
		return trim( $output );
719
	}
720
721
	/**
722
	 * Get event date or time.
723
	 *
724
	 * @since  3.0.0
725
	 * @access private
726
	 *
727
	 * @param  string $tag
728
	 * @param  Event  $event
729
	 * @param  string $attr
730
	 *
731
	 * @return string
732
	 */
733
	private function get_dt( $tag, Event $event, $attr ) {
734
735
		$bound = 0 === strpos( $tag, 'end' ) ? 'end' : 'start';
736
737
		if ( ( 'end' == $bound ) && ( false === $event->end ) ) {
738
			return '';
739
		}
740
741
		$dt = $bound . '_dt';
742
743
		if ( ! $event->$dt instanceof Carbon ) {
744
			return '';
745
		}
746
747
		$event_dt = $event->$dt;
748
749
		$attr = array_merge( array(
750
			'format' => '',
751
		), (array) shortcode_parse_atts( $attr ) );
752
753
		$format    = ltrim( strstr( $tag, '-' ), '-' );
754
		$dt_format = '';
755
756
		if ( ! empty( $attr['format'] ) ) {
757
			$dt_format = esc_attr( wp_strip_all_tags( $attr['format'] ) );
758
		} elseif ( 'date' == $format ) {
759
			$dt_format = $this->calendar->date_format;
760
		} elseif ( 'time' == $format ) {
761
			$dt_format = $this->calendar->time_format;
762
		}
763
764
		$dt_ts = $event_dt->timestamp;
765
766
		if ( 'human' == $format ) {
767
			$value = human_time_diff( $dt_ts, Carbon::now( $event->timezone )->getTimestamp() );
768
769
			if ( $dt_ts < Carbon::now( $event->timezone )->getTimestamp() ) {
770
				$value .= ' ' . _x( 'ago', 'human date event builder code modifier', 'google-calendar-events' );
771
			} else {
772
				$value .= ' ' . _x( 'from now', 'human date event builder code modifier', 'google-calendar-events' );
773
			}
774
		} else {
775
			$value = date_i18n( $dt_format, strtotime( $event_dt->toDateTimeString() ) );
776
		}
777
778
		return '<span class="simcal-event-' . $bound . ' ' . 'simcal-event-' . $bound . '-' . $format . '" ' . 'data-event-' . $bound . '="' . $dt_ts . '" ' . 'data-event-format="' . $dt_format . '" ' . 'itemprop="' . $bound . 'Date" content="' . $event_dt->toIso8601String() . '">' . $value . '</span>';
779
	}
780
781
	/**
782
	 * Make a link.
783
	 *
784
	 * @since  3.0.0
785
	 * @access private
786
	 *
787
	 * @param  string $tag
788
	 * @param  string $url
789
	 * @param  string $content
790
	 * @param  string $attr
791
	 *
792
	 * @return string
793
	 */
794
	private function make_link( $tag, $url, $content, $attr ) {
795
796
		if ( empty( $url ) ) {
797
			return '';
798
		}
799
800
		$text = empty( $content ) ? $url : $content;
801
802
		$attr = array_merge( array(
803
			'autolink'  => false,   // Convert url to link anchor
804
			'newwindow' => false,   // If autolink attribute is true, open link in new window
805
		), (array) shortcode_parse_atts( $attr ) );
806
807
		$anchor = $tag != 'url' ? 'yes' : $attr['autolink'];
808
		$target = $attr['newwindow'] !== false ? 'target="_blank"' : '';
809
810
		return $anchor !== false ? ' <a href="' . esc_url( $url ) . '" ' . $target . '>' . $text . '</a>' : ' ' . $text;
811
	}
812
813
	/**
814
	 * Get event attachments.
815
	 *
816
	 * @since  3.0.0
817
	 * @access private
818
	 *
819
	 * @param  array $attachments
820
	 *
821
	 * @return string
822
	 */
823
	private function get_attachments( $attachments ) {
824
825
		$html = '<ul class="simcal-attachments">' . "\n\t";
826
827
		foreach ( $attachments as $attachment ) {
828
			$html .= '<li class="simcal-attachment">';
829
			$html .= '<a href="' . $attachment['url'] . '" target="_blank">';
830
			$html .= ! empty( $attachment['icon'] ) ? '<img src="' . $attachment['icon'] . '" />' : '';
831
			$html .= '<span>' . $attachment['name'] . '</span>';
832
			$html .= '</a>';
833
			$html .= '</li>' . "\n";
834
		}
835
836
		$html .= '</ul>' . "\n";
837
838
		return $html;
839
	}
840
841
	/**
842
	 * Get attendees.
843
	 *
844
	 * @since  3.0.0
845
	 * @access private
846
	 *
847
	 * @param  array  $attendees
848
	 * @param  string $attr
849
	 *
850
	 * @return string
851
	 */
852
	private function get_attendees( $attendees, $attr ) {
853
854
		$attr = array_merge( array(
855
			'photo'    => 'show',  // show/hide attendee photo
856
			'email'    => 'hide',  // show/hide attendee email address
857
			'rsvp'     => 'hide',  // show/hide rsvp response status
858
			'response' => '',      // filter attendees by rsvp response (yes/no/maybe)
859
		), (array) shortcode_parse_atts( $attr ) );
860
861
		$html = '<ul class="simcal-attendees" itemprop="attendees">' . "\n\t";
862
863
		$known   = 0;
864
		$unknown = 0;
865
866
		foreach ( $attendees as $attendee ) {
867
868
			if ( 'yes' == $attr['response'] && 'yes' != $attendee['response'] ) {
869
				continue;
870
			} elseif ( 'no' == $attr['response'] && 'no' != $attendee['response'] ) {
871
				continue;
872
			} elseif ( 'maybe' == $attr['response'] && ! in_array( $attendee['response'], array( 'yes', 'maybe' ) ) ) {
873
				continue;
874
			}
875
876
			if ( ! empty( $attendee['name'] ) ) {
877
878
				$photo    = 'hide' != $attr['photo'] ? '<img class="avatar avatar-128 photo" src="' . $attendee['photo'] . '" itemprop="image" />' : '';
879
				$response = 'hide' != $attr['rsvp'] ? $this->get_rsvp_response( $attendee['response'] ) : '';
880
				$guest    = $photo . '<span itemprop="name">' . $attendee['name'] . $response . '</span>';
881
882
				if ( ! empty( $attendee['email'] ) && ( 'show' == $attr['email'] ) ) {
883
					$guest = sprintf( '<a href="mailto:' . $attendee['email'] . '" itemprop="email">%s</a>', $guest );
884
				}
885
886
				$html .= '<li class="simcal-attendee" itemprop="attendee" itemscope itemtype="http://schema.org/Person">' . $guest . '</li>' . "\n";
887
888
				$known++;
889
890
			} else {
891
892
				$unknown++;
893
894
			}
895
		}
896
897
		if ( $unknown > 0 ) {
898
			if ( $known > 0 ) {
899
				/* translators: One more person attending the event. */
900
				$others = sprintf( _n( '1 more attendee', '%s more attendees', $unknown, 'google-calendar-events' ), $unknown );
901
			} else {
902
				/* translators: One or more persons attending the event whose name is unknown. */
903
				$others = sprintf( _n( '1 anonymous attendee', '%s anonymous attendees', $unknown, 'google-calendar-events' ), $unknown );
904
			}
905
			$photo = $attr['photo'] !== 'hide' ? get_avatar( '', 128 ) : '';
906
			$html .= '<li class="simcal-attendee simcal-attendee-anonymous">' . $photo . '<span>' . $others . '</span></li>' . "\n";
907
		} elseif ( $known === 0 ) {
908
			$html .= '<li class="simcal-attendee">' . _x( 'No one yet', 'No one yet rsvp to attend the event.', 'google-calendar-events' ) . '</li>' . "\n";
909
		}
910
911
		$html .= '</ul>' . "\n";
912
913
		return $html;
914
	}
915
916
	/**
917
	 * Format attendee rsvp response.
918
	 *
919
	 * @since  3.0.0
920
	 *
921
	 * @param  $response
922
	 *
923
	 * @return string
924
	 */
925
	private function get_rsvp_response( $response ) {
926
927
		if ( 'yes' == $response ) {
928
			/* translators: Someone replied with 'yes' to a rsvp request. */
929
			$rsvp = __( 'Attending', 'google-calendar-events' );
930
		} elseif ( 'no' == $response ) {
931
			/* translators: Someone replied with 'no' to a rsvp request. */
932
			$rsvp = __( 'Not attending', 'google-calendar-events' );
933
		} elseif ( 'maybe' == $response ) {
934
			/* translators: Someone replied with 'maybe' to a rsvp request. */
935
			$rsvp = __( 'Maybe attending', 'google-calendar-events' );
936
		} else {
937
			/* translators: Someone did not send yet a rsvp confirmation to join an event. */
938
			$rsvp = __( 'Response pending', 'google-calendar-events' );
939
		}
940
941
		return ' <small>(' . $rsvp . ')</small>';
942
	}
943
944
	/**
945
	 * Get event organizer.
946
	 *
947
	 * @since  3.0.0
948
	 * @access private
949
	 *
950
	 * @param  array  $organizer
951
	 * @param  string $attr
952
	 *
953
	 * @return string
954
	 */
955
	private function get_organizer( $organizer, $attr ) {
956
957
		$attr = array_merge( array(
958
			'photo' => 'show',  // show/hide attendee photo
959
			'email' => 'hide',  // show/hide attendee email address
960
		), (array) shortcode_parse_atts( $attr ) );
961
962
		$photo          = 'hide' != $attr['photo'] ? '<img class="avatar avatar-128 photo" src="' . $organizer['photo'] . '" itemprop="image"  />' : '';
963
		$organizer_html = $photo . '<span itemprop="name">' . $organizer['name'] . '</span>';
964
965
		if ( ! empty( $organizer['email'] ) && ( 'show' == $attr['email'] ) ) {
966
			$organizer_html = sprintf( '<a href="mailto:' . $organizer['email'] . '" itemprop="email">%s</a>', $organizer_html );
967
		}
968
969
		return '<div class="simcal-organizer" itemprop="organizer" itemscope itemtype="https://schema.org/Person">' . $organizer_html . '</div>';
970
	}
971
972
	/**
973
	 * Retrieve the event builder tag regular expression for searching.
974
	 *
975
	 * Combines the event builder tags in the regular expression in a regex class.
976
	 * The regular expression contains 6 different sub matches to help with parsing:
977
	 *
978
	 *  1 - An extra [ to allow for escaping tags with double square brackets [[]]
979
	 *  2 - The tag name
980
	 *  3 - The tag argument list
981
	 *  4 - The self closing /
982
	 *  5 - The content of a tag when it wraps some content.
983
	 *  6 - An extra ] to allow for escaping tags with double square brackets [[]]
984
	 *
985
	 * @since  3.0.0
986
	 *
987
	 * @return string The tag search regular expression result
988
	 */
989
	private function get_regex() {
990
991
		// This is largely borrowed on get_shortcode_regex() from WordPress Core.
992
		// @see /wp-includes/shortcodes.php (with some modification)
993
994
		$tagregexp = implode( '|', array_values( $this->tags ) );
995
996
		return '/' . '\\['                              // Opening bracket
997
		       . '(\\[?)'                           // 1: Optional second opening bracket for escaping tags: [[tag]]
998
		       . "($tagregexp)"                     // 2: Tag name
999
		       . '(?![\\w-])'                       // Not followed by word character or hyphen
1000
		       . '('                                // 3: Unroll the loop: Inside the opening tag
1001
		       . '[^\\]\\/]*'                   // Not a closing bracket or forward slash
1002
		       . '(?:' . '\\/(?!\\])'               // A forward slash not followed by a closing bracket
1003
		       . '[^\\]\\/]*'               // Not a closing bracket or forward slash
1004
		       . ')*?' . ')' . '(?:' . '(\\/)'                        // 4: Self closing tag ...
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1005
		       . '\\]'                          // ... and closing bracket
1006
		       . '|' . '\\]'                          // Closing bracket
1007
		       . '(?:' . '('                        // 5: Unroll the loop: Optionally, anything between the opening and closing tags
1008
		       . '[^\\[]*+'             // Not an opening bracket
1009
		       . '(?:' . '\\[(?!\\/\\2\\])' // An opening bracket not followed by the closing tag
1010
		       . '[^\\[]*+'         // Not an opening bracket
1011
		       . ')*+' . ')' . '\\[\\/\\2\\]'             // Closing tag
1012
		       . ')?' . ')' . '(\\]?)'                           // 6: Optional second closing bracket for escaping tags: [[tag]]
1013
		       . '/s';
1014
	}
1015
1016
	//allow other plugins to register own event tags
1017
	private function add_custom_event_tags() {
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
1018
		$array = apply_filters( 'simcal_event_tags_add_custom', array() );
1019
1020
		return $array;
1021
	}
1022
1023
	//allow other plugins to replace own (registered) event tags with their value
1024
	private function do_custom_event_tag( $tag, $partial, $attr, $event ) {
1025
		$returnvalue = apply_filters( 'simcal_event_tags_do_custom', "", $tag, $partial, $attr, $event );
1026
1027
		return $returnvalue;
1028
	}
1029
}
1030