PluginProbe
Cool Timeline (Horizontal & Vertical Timeline) / trunk
Cool Timeline (Horizontal & Vertical Timeline) vtrunk
3.4.0 3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 2.2.3 2.3.3 2.4 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.6.1 2.7.1 2.8.3 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 2.9.8 2.9.9 3.0.0 All 79 releases
cool-timeline / includes / shortcodes / class-ctl-loop-helpers.php

class-ctl-loop-helpers.php in Cool Timeline (Horizontal & Vertical Timeline) trunk, at includes/shortcodes/class-ctl-loop-helpers.php

484 lines 14.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals -- Legacy CTL_* class name kept for compatibility.
3 /**
4 * CTL Loop Helper.
5 *
6 * @package CTL
7 */
8
9 /**
10 * Do not access the page directly
11 */
12 if ( ! defined( 'ABSPATH' ) ) {
13 exit;
14 }
15
16 /**
17 * Cool timeline loop helper class
18 */
19
20 if ( ! class_exists( 'CTL_Loop_Helpers' ) ) {
21
22 /**
23 * Class Loop Helpers.
24 */
25 class CTL_Loop_Helpers {
26
27 /**
28 * Member Variable
29 *
30 * @var attributes
31 */
32 public $attributes;
33
34 /**
35 * Member Variable
36 *
37 * @var settings
38 */
39 public $settings;
40
41 /**
42 * Used To stop duplicate year label
43 *
44 * @var active_year
45 */
46 public $active_year = '';
47
48 /**
49 * Define Story Type
50 *
51 * @var tm_type
52 */
53 public $tm_type = 'Story';
54
55
56 /**
57 * Constructor
58 *
59 * @param object $attributes shortcode attributes.
60 * @param object $settings timeline settings.
61 */
62 public function __construct( $attributes, $settings ) {
63 // Plugin initialization.
64 $this->attributes = $attributes;
65 $this->settings = $settings;
66 $this->active_year = '';
67 }
68
69 /**
70 * Renders the timeline Single Story.
71 *
72 * @param int $index Index value of current post.
73 * @param object $post Current Post object.
74 *
75 * @since 0.0.1
76 */
77 public function ctl_render( $index, $post ) {
78
79 $output = '';
80 $post_id = absint( is_object( $post ) && isset( $post->ID ) ? $post->ID : get_the_ID() );
81 if ( $post_id <= 0 ) {
82 return '';
83 }
84 $classes = array( 'ctl-story' );
85 $attributes = $this->attributes;
86 $layout = $attributes['layout'];
87 $animation = 'horizontal' === $layout ? 'none' : $attributes['config']['animation'];
88 $icons_disabled = isset( $attributes['icons'] ) && 'no' === strtolower( $attributes['icons'] );
89 if ( $icons_disabled ) {
90 $classes[] = 'ctl-story-dot-icon';
91 } else {
92 $classes[] = 'ctl-story-icon';
93 };
94 if ( 'horizontal' === $layout ) {
95 $classes[] = 'swiper-slide';
96 $classes[] = 0 === $index % 2 ? 'even' : 'odd';
97 } else {
98 // dynamic alternate class.
99 $condition = 0 === $index % 2;
100
101 $classes[] = $condition ? 'even' : 'odd';
102
103 if ( isset( $this->settings['first_story_position'] ) && 'left' === $this->settings['first_story_position'] ) {
104 $condition = 0 !== $index % 2;
105 }
106
107 if ( $condition ) {
108 $classes[] = 'one-side' !== $this->attributes['layout'] ? 'ctl-story-left' : 'ctl-story-right';
109 } else {
110 $classes[] = 'ctl-story-right';
111 }
112 }
113
114 // Get Year label.
115 if ( $this->settings['year_label'] && ( 'one-side' === $layout || 'default' === $layout ) ) {
116 $output .= $this->ctl_get_year_label( $post_id );
117 }
118 $output .= '<!-- Timeline Content --><div id="ctl-story-' . esc_attr( $post_id ) . '" class="' . esc_attr( implode( ' ', $classes ) ) . '" data-aos="' . esc_attr( $animation ) . '" data-story-index="' . esc_attr( $index ) . '" role="article">';
119
120 if ( 'compact' !== $layout ) {
121 $output .= $this->ctl_get_date( $post_id, $attributes['date-format'] );
122 }
123
124 if ( $icons_disabled ) {
125 $output .= '<!-- ' . esc_html( $this->tm_type ) . ' IconDot --><div class="ctl-icondot"></div>';
126 } else {
127 $output .= '<!-- ' . esc_html( $this->tm_type ) . ' Icon -->' . $this->ctl_get_icon( $post_id );
128 }
129
130 if ( 'compact' === $layout || 'horizontal' === $layout || 'clean' !== $attributes['skin'] ) {
131 $output .= '<!-- ' . $this->tm_type . ' Arrow --><div class="ctl-arrow"></div>';
132 }
133 $output .= '<!-- ' . $this->tm_type . ' Content --><div class="ctl-content">';
134 $output .= $this->ctl_get_title( $post_id );
135
136 // Grabing story content based upon content type.
137 if ( 'horizontal' !== $layout ) {
138 $output .= $this->ctl_get_featured_image( $post_id );
139 }
140
141 if ( 'compact' === $layout ) {
142 $output .= $this->ctl_get_date( $post_id, $attributes['date-format'] );
143 }
144
145 if ( 'horizontal' !== $layout ) {
146 $output .= $this->ctl_get_content();
147 }
148
149 $output .= '</div>';
150
151 if ( 'horizontal' === $layout ) {
152 $output .= $this->ctl_minimal_content();
153 }
154
155 $output .= '</div>';
156
157 return $output;
158 }
159
160 /**
161 * Get Story Title
162 *
163 * @param int $post_id Current Post ID.
164 */
165 public function ctl_get_title( $post_id ) {
166 $output = '';
167 if ( ! empty( get_the_title() ) ) {
168 $layout = $this->attributes['layout'];
169 $title_class = 'story-' . $post_id;
170 $re_more = ( ( isset( $this->settings['display_readmore'] ) && 'yes' === $this->settings['display_readmore'] ) || 'horizontal' === $layout );
171 $output .= '<!-- ' . $this->tm_type . ' Title --><div class="ctl-title ' . esc_attr( $title_class ) . '" aria-label="2">';
172 $output .= $re_more ? $this->get_story_link( $post_id ) : '';
173 $output .= esc_html( get_the_title( $post_id ) );
174 $output .= $re_more ? '</a>' : '';
175
176 if ( ( isset( $this->settings['display_readmore'] ) && 'yes' === $this->settings['display_readmore'] ) && 'horizontal' === $layout ) {
177 $output .= $re_more ? $this->get_story_link( $post_id ) : '';
178 $output .= '<p class="ctl_read_more">' . __( 'Read More', 'cool-timeline' ) . '</p>';
179 $output .= $re_more ? '</a>' : '';
180 }
181
182 $output .= '</div>';
183 }
184 return $output;
185 }
186
187 /**
188 * Get Timeline Story links
189 *
190 * @param int $post_id Current Post ID.
191 */
192 public function get_story_link( $post_id ) {
193 $attributes = $this->attributes;
194 $story_link = '';
195 $url = '';
196 // Get custom link settings.
197 if ( 'horizontal' === $attributes['layout'] ) {
198 $story_link = '<a class="minimal_glightbox" data-popup-id="#ctl-popup-' . esc_attr( $post_id ) . '">';
199 } else {
200 $target = ! empty( $this->settings['story_link_target'] ) ? $this->settings['story_link_target'] : '_self';
201 $url = get_the_permalink( $post_id );
202
203 $story_link = '<a target="' . esc_attr( $target ) . '" title="' . esc_attr( get_the_title( $post_id ) ) . '" href="' . esc_url( $url ) . '" class="story-link">';
204 }
205
206 return $story_link;
207 }
208
209 /**
210 * Get Story Date
211 *
212 * @param int $post_id Current Post ID.
213 * @param string $date_formats Timeline date format.
214 * @return string Formatted date HTML output.
215 */
216 public function ctl_get_date( $post_id, $date_formats ) {
217 $ctl_story_type = get_post_meta( $post_id, 'story_type', true );
218
219 $ctl_story_date = '';
220
221 if ( is_array( $ctl_story_type ) && isset( $ctl_story_type['ctl_story_date'] ) ) {
222 $ctl_story_date = $ctl_story_type['ctl_story_date'];
223 }
224
225 $layout = $this->attributes['layout'];
226 $re_more = ( ( isset( $this->settings['display_readmore'] ) && 'yes' === $this->settings['display_readmore'] ) && 'horizontal' === $layout );
227 $output = '';
228 $posted_date = '';
229 if ( $ctl_story_date ) {
230 if ( strtotime( $ctl_story_date ) !== false ) {
231 $posted_date = date_i18n( $date_formats, strtotime( $ctl_story_date ) );
232 } else {
233 $ctl_story_date = trim( str_ireplace( array( 'am', 'pm' ), '', $ctl_story_date ) );
234 $dateobj = DateTime::createFromFormat( 'm/d/Y H:i', $ctl_story_date, wp_timezone() );
235 if ( $dateobj ) {
236 $posted_date = $dateobj->format( $date_formats );
237 }
238 }
239 if ( ! empty( $posted_date ) ) {
240 $output .= '<!-- ' . $this->tm_type . ' Date --><div class="ctl-labels">';
241 $output .= '<div class="ctl-label-big story-date">';
242 $output .= $re_more ? $this->get_story_link( $post_id ) : '';
243 $output .= esc_html( $posted_date ); // Escape date output
244 $output .= $re_more ? '</a>' : '';
245 $output .= '</div>';
246 $output .= '</div>';
247 }
248 }
249
250 return $output;
251 }
252
253 /**
254 * Get stories content
255 */
256 public function ctl_get_content() {
257 $attributes = $this->attributes;
258 $output = '';
259 $content = '';
260 if ( 'full' === $attributes['story-content'] ) {
261 global $post;
262 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
263 $content .= apply_filters( 'the_content', $post->post_content );
264
265 } else {
266 $content .= CTL_Helpers::ctl_get_excerpt( $this->settings );
267
268 }
269
270 if ( ! empty( $content ) ) {
271
272 // Remove non-http/https iframe src URLs.
273 $content = preg_replace_callback(
274 '#<iframe[^>]+src=["\']([^"\']+)["\']#i',
275 function( $matches ) {
276
277 $src = esc_url_raw( $matches[1], array( 'http', 'https' ) );
278
279 if ( empty( $src ) ) {
280 return '';
281 }
282
283 return str_replace( $matches[1], esc_url( $src ), $matches[0] );
284 },
285 $content
286 );
287
288 $allowed_tags = wp_kses_allowed_html( 'post' );
289 $allowed_tags['iframe'] = array(
290 'src' => true,
291 'width' => true,
292 'height' => true,
293 'frameborder' => true,
294 'allow' => true,
295 'allowfullscreen' => true,
296 'loading' => true,
297 'referrerpolicy' => true,
298 );
299 $output .= '<!-- ' . esc_html( $this->tm_type ) . ' Description -->';
300 $output .= '<div class="ctl-description">' . wp_kses( $content, $allowed_tags ) . '</div>';
301 }
302
303 return $output;
304 }
305
306 /**
307 * Get Story Featured Image
308 *
309 * @param int $post_id Current Post ID.
310 */
311 public function ctl_get_featured_image( $post_id ) {
312 $attributes = $this->attributes;
313 global $post;
314 $post_id = ! empty( $post_id ) ? absint( $post_id ) : ( isset( $post->ID ) ? absint( $post->ID ) : 0 );
315
316 if ( ! $post_id ) {
317 return;
318 }
319
320 if ( ! get_the_post_thumbnail_url( $post_id ) ) {
321 return;
322 }
323
324 // Get story media and image container size.
325 $ctl_story_media = get_post_meta( $post_id, 'story_media', true );
326 $img_cont_size = isset( $ctl_story_media['img_cont_size'] ) ? $ctl_story_media['img_cont_size'] : '';
327 $img_html = '';
328
329 // Get alternative text for the image.
330 $img_alt = get_post_meta( get_post_thumbnail_id( $post_id ), '_wp_attachment_image_alt', true );
331 $alt_text = $img_alt ? $img_alt : get_the_title( $post_id );
332
333 // Generate image link based on stories images link setting.
334 $story_img_link = '';
335 $img_f_url = wp_get_attachment_url( get_post_thumbnail_id( $post_id ) );
336 $story_img_link = '<a data-glightbox="' . esc_attr( get_the_title( $post_id ) ) . '" href="' . esc_url( $img_f_url ) . '" class="ctl_glightbox">';
337
338 $image_size = '';
339 $image_wrapper_cls = '';
340 if ( 'small' === $img_cont_size ) {
341 $image_size = 'medium';
342 $image_wrapper_cls = 'small';
343 } else {
344 $image_size = 'large';
345 $image_wrapper_cls = 'full';
346 }
347
348 if ( 'horizontal' === $attributes['layout'] ) {
349 $img_html .= '<div class="ctl_popup_img" data-popup-image="' . esc_url( $img_f_url ) . '"></div>';
350 } else {
351 // Generate image HTML based on image container size.
352 if ( get_the_post_thumbnail_url( $post_id ) ) {
353 $img_html .= '<!-- ' . $this->tm_type . ' Media --><div class="ctl-media ' . esc_attr( $image_wrapper_cls ) . '">';
354 $img_html .= $story_img_link;
355 $img_html .= get_the_post_thumbnail(
356 $post_id,
357 apply_filters( 'cool_timeline_story_img_size', $image_size ),
358 array(
359 'class' => 'story-img',
360 'alt' => esc_html( $alt_text ),
361 )
362 );
363 $img_html .= '</a>';
364 $img_html .= '</div>';
365 }
366 }
367
368 return $img_html;
369 }
370
371 /**
372 * Get Story Icon
373 *
374 * @param int $post_id Current Post ID.
375 * @param string $default_icon default icon.
376 */
377 public function ctl_get_icon( $post_id ) {
378 $output = '';
379 $icon = '';
380 $extra_class = '';
381 if ( get_post_type( $post_id ) === 'cool_timeline' ) {
382 // Story Icon.
383 $story_icon = get_post_meta( $post_id, 'story_icon', true );
384 $ctl_fontawesome_icon = isset( $story_icon['fa_field_icon'] ) ? $story_icon['fa_field_icon'] : '';
385 $extra_class = '';
386 if ( '' !== $ctl_fontawesome_icon ) {
387 if ( strpos( $ctl_fontawesome_icon, 'fab' ) === false ) {
388 $extra_class = 'fa';
389 }
390 $icon = '<i class= "' . esc_attr( $extra_class ) . ' ' . esc_attr( $ctl_fontawesome_icon ) . '" aria-hidden ="true"></i>';
391 } else {
392 $icon = '<i class="fa fa-clock-o" aria-hidden="true"></i>';
393 }
394 }
395 $output .= '<div class="ctl-icon">' . $icon . '</div> ';
396 return $output;
397 }
398
399 /**
400 * Get Story Year and create highlighted Year section in timeline
401 *
402 * @param int $post_id Current Post ID.
403 * @return string Output HTML.
404 */
405 public function ctl_get_year_label( $post_id ) {
406 $attributes = $this->attributes;
407 $output = '';
408 $wrp_cls = 'vertical';
409 $design = 'light';
410
411 // Get story date.
412 $ctl_story_type = get_post_meta( $post_id, 'story_type', true );
413
414 $ctl_story_date = ( is_array( $ctl_story_type ) && isset( $ctl_story_type['ctl_story_date'] ) )
415 ? sanitize_text_field( $ctl_story_type['ctl_story_date'] )
416 : '';
417
418 $pattern = "/\b\d{4}\b/"; // Regular expression pattern to match a four-digit number.
419 preg_match( $pattern, $ctl_story_date, $matches );
420
421 // Get Year value.
422 if ( isset( $matches[0] ) ) {
423 $story_year = $matches[0];
424 // Display the year label if it is different from the previous year.
425 if ( $story_year !== $this->active_year ) {
426
427 $story_year_label = sprintf( '<div class="ctl-year-label ctl-year-text"><span>%s</span></div>', esc_html( $story_year ) );
428
429 $this->active_year = $story_year;
430 if ( 'compact' === $attributes['layout'] ) {
431 $output .= '<span class="scrollable-section ctl-year-container" data-section-title="' . esc_attr( $story_year ) . '"></span>';
432 } else {
433 $output .= sprintf(
434 '<!-- ' . $this->tm_type . ' Year Section --><div data-cls="sc-nv-%s %s" class="timeline-year scrollable-section ctl-year ctl-year-container %s-year" data-section-title="%s" id="year-%s">%s</div>',
435 esc_attr( $design ),
436 esc_attr( $wrp_cls ),
437 esc_attr( $design ),
438 esc_attr( $story_year ),
439 esc_attr( $story_year ),
440 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
441 apply_filters( 'ctl_story_year', $story_year_label )
442 );
443 }
444 }
445 }
446
447 return $output;
448 }
449
450 /**
451 * Minimal Popup Content
452 */
453 public function ctl_minimal_content() {
454 $post_id = get_the_ID();
455 $output = '';
456 $attributes = $this->attributes;
457
458 $output .= '<div id="ctl-popup-' . esc_attr( $post_id ) . '" class="ctl_popup_hide">
459 <div class="ctl_popup_container">
460 <div class="ctl_popup_date">';
461 $output .= $this->ctl_get_date( $post_id, $attributes['date-format'] );
462 $output .= '</div>
463 <h2 class="ctl_popup_title">';
464 $output .= esc_html( get_the_title() );
465 $output .= '</h2>
466 <div class="ctl_popup_media">';
467 $output .= $this->ctl_get_featured_image( $post_id );
468 $output .= '</div>
469 <div class="ctl_popup_desc">';
470 $output .= $this->ctl_get_content();
471 $output .= '</div>
472 </div>
473 </div>';
474
475 return $output;
476 }
477
478 }
479
480
481
482 }
483
484