Add upstream plugins

Signed-off-by: Adrian Nöthlich <git@promasu.tech>
This commit is contained in:
2019-10-25 22:42:20 +02:00
parent 5d3c2ec184
commit 290736650a
1186 changed files with 302577 additions and 0 deletions

View File

@@ -0,0 +1,405 @@
<?php
/**
* This class renders the html for the event avatar.
*
* @author Time.ly Network Inc.
* @since 2.0
*
* @package AI1EC
* @subpackage AI1EC.View.Event
*/
class Ai1ec_View_Event_Avatar extends Ai1ec_Base {
/**
* Get HTML markup for the post's "avatar" image according conditional
* fallback model.
*
* Accepts an ordered array of named avatar $fallbacks. Also accepts a string
* of space-separated classes to add to the default classes.
* @param Ai1ec_Event $event The event to get the avatar for
* @param array|null $fallback_order Order of fallback in searching for
* images, or null to use default
* @param string $classes A space-separated list of CSS classes
* to apply to the outer <div> element.
* @param boolean $wrap_permalink Whether to wrap the element in a link
* to the event details page.
*
* @return string String of HTML if image is found
*/
public function get_event_avatar(
Ai1ec_Event $event,
$fallback_order = null,
$classes = '',
$wrap_permalink = true
) {
$source = $size = null;
$url = $this->get_event_avatar_url(
$event,
$fallback_order,
$source,
$size
);
if ( empty( $url ) ) {
return '';
}
$url = esc_attr( $url );
$classes = esc_attr( $classes );
// Set the alt tag (helpful for SEO).
$alt = $event->get( 'post' )->post_title;
$location = $this->_registry->get( 'view.event.location' )->get_short_location( $event );
if ( ! empty( $location ) ) {
$alt .= ' @ ' . $location;
}
$alt = esc_attr( $alt );
$size_attr = $size[0] ? "width=\"$size[0]\" height=\"$size[1]\"" : "";
$html = '<img src="' . $url . '" alt="' . $alt . '" ' .
$size_attr . ' />';
if ( $wrap_permalink ) {
$permalink = add_query_arg(
'instance_id',
$event->get( 'instance_id' ),
get_permalink( $event->get( 'post_id' ) )
);
$html = '<a href="' . $permalink . '">' . $html . '</a>';
}
$classes .= ' ai1ec-' . $source;
$classes .= ( $size[0] > $size[1] )
? ' ai1ec-landscape'
: ' ai1ec-portrait';
$html = '<div class="ai1ec-event-avatar timely ' . $classes . '">' .
$html . '</div>';
return $html;
}
/**
* Get the post's "avatar" image url according conditional fallback model.
*
* Accepts an ordered array of named methods for $fallback order. Returns
* image URL or null if no image found. Also returns matching fallback in the
* $source reference.
*
* @param array|null $fallback_order Order of fallbacks in search for images
* @param null $source Fallback that returned matching image,
* returned format is string
* @param null $size (width, height) array of returned image
*
* @return string|null
*/
public function get_event_avatar_url(
Ai1ec_Event $event,
$fallback_order = null,
&$source = null,
&$size = null
) {
if ( empty( $fallback_order ) ) {
$fallback_order = array(
'post_thumbnail',
'content_img',
'category_avatar',
'default_avatar',
);
}
$valid_fallbacks = $this->_get_valid_fallbacks();
foreach ( $fallback_order as $fallback ) {
if ( ! isset( $valid_fallbacks[$fallback] ) ) {
continue;
}
$function = $valid_fallbacks[$fallback];
$url = null;
if (
! is_array( $function ) &&
method_exists( $this, $function )
) {
$url = $this->$function( $event, $size );
} else if ( is_callable( $function ) ) {
$url = call_user_func_array( $function, array( $event, &$size ) );
}
if ( null !== $url ) {
$source = $fallback;
break;
}
}
if ( empty( $url ) ) {
return null;
}
return $url;
}
/**
* Read post meta for post-thumbnail and return its URL as a string.
*
* @param Ai1ec_Event $event Event object.
* @param null $size (width, height) array of returned image.
*
* @return string|null
*/
public function get_post_thumbnail_url( Ai1ec_Event $event, &$size = null ) {
return $this->_get_post_attachment_url(
$event,
array(
'medium',
'large',
'full',
),
$size
);
}
/**
* Read post meta for post-image and return its URL as a string.
*
* @param Ai1ec_Event $event Event object.
* @param null $size (width, height) array of returned image.
*
* @return string|null
*/
public function get_post_image_url( Ai1ec_Event $event, &$size = null ) {
return $this->_get_post_attachment_url(
$event,
array(
'full',
'large',
'medium'
),
$size
);
}
/**
* Read post meta for featured image and return its URL as a string.
*
* @param Ai1ec_Event $event Event object.
* @param null $size (width, height) array of returned image.
*
* @return string|null
*/
public function get_featured_image_url( Ai1ec_Event $event, &$size = null ) {
$featured_image = get_post_meta( $event->get( 'post_id' ) , '_featured_image', true );
if ( empty( $featured_image ) ) {
return $this->_get_post_attachment_url(
$event,
array(
'full',
'large',
'medium'
),
$size
);
} else {
$priority_order = array( 'large', 'full', 'medium', 'thumbnail' );
$url = null;
foreach ( $priority_order as $priority ) {
foreach ( $featured_image as $values_arr ) {
if ( $values_arr[0] === $priority ) {
$url = $values_arr[1];
$size = array( $values_arr[2], $values_arr[3] );
break;
}
}
if ( null !== $url ) {
break;
}
}
return $url;
}
}
/**
* Remove the avatar url from the event content
*/
public function remove_avatar_url( $content ) {
return preg_replace( '/<div[^<>]+class=[\'"]?ai1ec-event-avatar[^<>]*[\'"]?[^<>]+>.+<\/div>[.\s]*/'
, ''
, $content );
}
/**
* Simple regex-parse of post_content for matches of <img src="foo" />; if
* one is found, return its URL.
*
* @param Ai1ec_Event $event
* @param null $size (width, height) array of returned image
*
* @return string|null
*/
public function get_content_img_url( Ai1ec_Event $event, &$size = null ) {
$matches = $this->get_image_from_content(
$event->get( 'post' )->post_content
);
// Check if we have a result, otherwise a notice is issued.
if ( empty( $matches ) ) {
return null;
}
$url = $matches[2];
$size = array( 0, 0 );
// Try to detect width and height.
$attrs = $matches[1] . $matches[3];
$matches = null;
preg_match_all(
'/(width|height)=["\']?(\d+)/i',
$attrs,
$matches,
PREG_SET_ORDER
);
// Check if we have a result, otherwise a notice is issued.
if ( ! empty( $matches ) ) {
foreach ( $matches as $match ) {
$size[ $match[1] === 'width' ? 0 : 1 ] = $match[2];
}
}
return $url;
}
/**
* Get an image tag from an html string
*
* @param string $content
*
* @return array
*/
public function get_image_from_content( $content ) {
preg_match(
'/<img([^>]+)src=["\']?([^"\'\ >]+)([^>]*)>/i',
$content,
$matches
);
return $matches;
}
/**
* Returns default avatar image (normally when no other ones are available).
*
* @param null $size (width, height) array of returned image
*
* @return string|null
*/
public function get_default_avatar_url( &$size = null ) {
$loader = $this->_registry->get( 'theme.loader' );
$file = $loader->get_file( 'default-event-avatar.png', array(), false );
$size = array( 256, 256 );
return $file->get_url();
}
/**
* Returns avatar image for event's deepest category, if any.
*
* @param Ai1ec_Event $event Avatar requester.
* @param void $size Unused argument.
*
* @return string|null Avatar's HTML or null if none.
*/
public function get_category_avatar_url( Ai1ec_Event $event, &$size = null ) {
$db = $this->_registry->get( 'dbi.dbi' );
$terms = $this->_registry->get( 'model.taxonomy' )->get_post_categories(
$event->get( 'post_id' )
);
if ( empty( $terms ) ) {
return null;
}
$terms_by_id = array();
// Key $terms by term_id rather than arbitrary int.
foreach ( $terms as $term ) {
$terms_by_id[$term->term_id] = $term;
}
// Array to store term depths, sorted later.
$term_depths = array();
foreach ( $terms_by_id as $term ) {
$depth = 0;
$ancestor = $term;
while ( ! empty( $ancestor->parent ) ) {
$depth++;
if ( ! isset( $terms_by_id[$ancestor->parent] ) ) {
break;
}
$ancestor = $terms_by_id[$ancestor->parent];
}
// Store negative depths for asort() to order from deepest to shallowest.
$term_depths[$term->term_id] = -$depth;
}
// Order term IDs by depth.
asort( $term_depths );
$url = '';
$model = $this->_registry->get( 'model.taxonomy' );
// Starting at deepest depth, find the first category that has an avatar.
foreach ( $term_depths as $term_id => $depth ) {
$term_image = $model->get_category_image( $term_id );
if ( $term_image ) {
$url = $term_image;
break;
}
}
return empty( $url ) ? null : $url;
}
/**
* Read post meta for post-attachment and return its URL as a string.
*
* @param Ai1ec_Event $event Event object.
* @param array $ordered_img_sizes Image sizes order.
* @param null $size (width, height) array of returned
* image.
*
* @return string|null
*/
protected function _get_post_attachment_url(
Ai1ec_Event $event,
array $ordered_img_sizes,
&$size = null
) {
// Since WP does will return null if the wrong size is targeted,
// we iterate over an array of sizes, breaking if a URL is found.
foreach ( $ordered_img_sizes as $size ) {
$attributes = wp_get_attachment_image_src(
get_post_thumbnail_id( $event->get( 'post_id' ) ), $size
);
if ( $attributes ) {
$url = array_shift( $attributes );
$size = $attributes;
break;
}
}
return empty( $url ) ? null : $url;
}
/**
* Returns list of valid fallbacks.
*
* @return array List of valid fallbacks.
*/
protected function _get_valid_fallbacks() {
static $fallbacks;
if ( null === $fallbacks ) {
$fallbacks = apply_filters(
'ai1ec_avatar_valid_callbacks',
array(
'post_image' => 'get_post_image_url',
'post_thumbnail' => 'get_post_thumbnail_url',
'content_img' => 'get_content_img_url',
'category_avatar' => 'get_category_avatar_url',
'default_avatar' => 'get_default_avatar_url',
'featured_image' => 'get_featured_image_url'
)
);
}
return $fallbacks;
}
}

View File

@@ -0,0 +1,105 @@
<?php
/**
* This class renders the html for the event colors.
*
* @author Time.ly Network Inc.
* @since 2.0
*
* @package AI1EC
* @subpackage AI1EC.View.Event
*/
class Ai1ec_View_Event_Color extends Ai1ec_Base {
/**
* Faded version of event category color
*/
protected function _get_color( Ai1ec_Event $event, $type ) {
static $categories_cache = array(
'rgba' => array(),
'faded' => array(),
);
$methods = array(
'rgba' => 'get_event_category_rgba_color',
'faded' => 'get_event_category_faded_color',
);
$categories = $this->_registry->get( 'model.taxonomy' )
->get_post_categories( $event->get( 'post_id' ) );
if ( ! empty( $categories ) ) {
if (
! isset( $categories_cache[$type][$categories[0]->term_id] )
) {
$method = $methods[$type];
$categories_cache[$type][$categories[0]->term_id] = $this
->$method( $categories[0]->term_id );
}
return $categories_cache[$type][$categories[0]->term_id];
}
return '';
}
public function get_faded_color( Ai1ec_Event $event ) {
return $this->_get_color( $event, 'faded' );
}
/**
* rgba() format of faded category color.
*
* @return string
*/
public function get_rgba_color( Ai1ec_Event $event ) {
return $this->_get_color( $event, 'rgba' );
}
/**
* Returns a faded version of the event's category color in hex format.
*
* @param int $term_id The Event Category's term ID
*
* @return string
*/
public function get_event_category_faded_color( $term_id ) {
$taxonomy = $this->_registry->get( 'model.taxonomy' );
$color = $taxonomy->get_category_color( $term_id );
if( ! is_null( $color ) && ! empty( $color ) ) {
$color1 = substr( $color, 1 );
$color2 = 'ffffff';
$c1_p1 = hexdec( substr( $color1, 0, 2 ) );
$c1_p2 = hexdec( substr( $color1, 2, 2 ) );
$c1_p3 = hexdec( substr( $color1, 4, 2 ) );
$c2_p1 = hexdec( substr( $color2, 0, 2 ) );
$c2_p2 = hexdec( substr( $color2, 2, 2 ) );
$c2_p3 = hexdec( substr( $color2, 4, 2 ) );
$m_p1 = dechex( round( $c1_p1 * 0.5 + $c2_p1 * 0.5 ) );
$m_p2 = dechex( round( $c1_p2 * 0.5 + $c2_p2 * 0.5 ) );
$m_p3 = dechex( round( $c1_p3 * 0.5 + $c2_p3 * 0.5 ) );
return '#' . $m_p1 . $m_p2 . $m_p3;
}
return '';
}
/**
* Returns the rgba() format of the event's category color, with '%s' in place
* of the opacity (to be substituted by sprintf).
*
* @param int $term_id The Event Category's term ID
*
* @return string
*/
public function get_event_category_rgba_color( $term_id ) {
$taxonomy = $this->_registry->get( 'model.taxonomy' );
$color = $taxonomy->get_category_color( $term_id );
if ( ! is_null( $color ) && ! empty( $color ) ) {
$p1 = hexdec( substr( $color, 1, 2 ) );
$p2 = hexdec( substr( $color, 3, 2 ) );
$p3 = hexdec( substr( $color, 5, 2 ) );
return "rgba($p1, $p2, $p3, %s)";
}
return '';
}
}

View File

@@ -0,0 +1,190 @@
<?php
/**
* This class process event content.
*
* @author Time.ly Network Inc.
* @since 2.0
*
* @package AI1EC
* @subpackage AI1EC.View.Event
*/
class Ai1ec_View_Event_Content extends Ai1ec_Base {
/**
* Render event excerpt header.
*
* @param Ai1ec_Event $event Event to render excerpt for.
*
* @return void Content is not returned, just rendered.
*/
public function excerpt_view( Ai1ec_Event $event ) {
$location = $this->_registry->get( 'view.event.location' );
$location = esc_html(
str_replace(
"\n",
', ',
rtrim( $location->get_location( $event ) )
)
);
$args = array(
'event' => $event,
'location' => $location,
'text_when' => __( 'When:', AI1EC_PLUGIN_NAME ),
'text_where' => __( 'Where:', AI1EC_PLUGIN_NAME ),
);
$loader = $this->_registry->get( 'theme.loader' );
echo $loader->get_file(
'event-excerpt.twig',
$args,
true
)->get_content();
}
/**
* Format events excerpt view.
*
* @param string $text Content to excerpt.
*
* @return string Formatted event excerpt.
*/
public function event_excerpt( $text ) {
if ( ! $this->_registry->get( 'acl.aco' )->is_our_post_type() ) {
return $text;
}
$event = $this->_registry->get( 'model.event', get_the_ID() );
$post = $this->_registry->get( 'view.event.post' );
$ob = $this->_registry->get( 'compatibility.ob' );
$ob->start();
$this->excerpt_view( $event );
// Re-apply any filters to the post content that normally would have
// been applied if it weren't for our interference (below).
echo shortcode_unautop( wpautop( $post->trim_excerpt( $event ) ) );
return $ob->get_clean();
}
/**
* Avoid re-adding `wpautop` for Ai1EC instances.
*
* @param string $content Processed content.
*
* @return string Paragraphs enclosed text.
*/
public function event_excerpt_noautop( $content ) {
if ( ! $this->_registry->get( 'acl.aco' )->is_our_post_type() ) {
return wpautop( $content );
}
return $content;
}
public function get_post_excerpt( Ai1ec_Event $event ) {
$content = strip_tags(
strip_shortcodes(
preg_replace(
'#<\s*script[^>]*>.+<\s*/\s*script\s*>#x',
'',
apply_filters(
'ai1ec_the_content',
apply_filters(
'the_content',
$event->get( 'post' )->post_content
)
)
)
)
);
$content = preg_replace( '/\s+/', ' ', $content );
$words = explode( ' ', $content );
if ( count( $words ) > 25 ) {
return implode(
' ',
array_slice( $words, 0, 25 )
) . ' [...]';
}
return $content;
}
/**
* Generate the html for the "Calendar" button for this event.
*
* @return string
*/
public function get_back_to_calendar_button_html() {
$class = '';
$data_type = '';
$href = '';
if ( isset( $_COOKIE['ai1ec_calendar_url'] ) ) {
$href = json_decode(
stripslashes( $_COOKIE['ai1ec_calendar_url'] )
);
setcookie( 'ai1ec_calendar_url', '', time() - 3600 );
} else {
$href = $this->_registry->get( 'html.element.href', array() );
$href = $href->generate_href();
}
$text = esc_attr( Ai1ec_I18n::__( 'Calendar' ) );
$tooltip = esc_attr( Ai1ec_I18n::__( 'View all events' ) );
$html = <<<HTML
<a class="ai1ec-calendar-link ai1ec-btn ai1ec-btn-default ai1ec-btn-sm
ai1ec-tooltip-trigger $class"
href="$href"
$data_type
data-placement="left"
title="$tooltip">
<i class="ai1ec-fa ai1ec-fa-calendar ai1ec-fa-fw"></i>
<span class="ai1ec-hidden-xs">$text</span>
</a>
HTML;
return apply_filters( 'ai1ec_get_back_to_calendar_html', $html, $href );
}
/**
* Simple regex-parse of post_content for matches of <img src="foo" />; if
* one is found, return its URL.
*
* @param null $size (width, height) array of returned image
*
* @return string|null
*/
public function get_content_img_url( Ai1ec_Event $event, &$size = null ) {
preg_match(
'/<img([^>]+)src=["\']?([^"\'\ >]+)([^>]*)>/i',
$event->get( 'post' )->post_content,
$matches
);
// Check if we have a result, otherwise a notice is issued.
if ( empty( $matches ) ) {
return null;
}
// Mark found image.
$event->get( 'post' )->post_content = str_replace(
'<img' . $matches[1],
'<img' . $matches[1] . ' data-ai1ec-hidden ',
$event->get( 'post' )->post_content
);
$url = $matches[2];
$size = array( 0, 0 );
// Try to detect width and height.
$attrs = $matches[1] . $matches[3];
$matches = null;
preg_match_all(
'/(width|height)=["\']?(\d+)/i',
$attrs,
$matches,
PREG_SET_ORDER
);
// Check if we have a result, otherwise a notice is issued.
if ( ! empty( $matches ) ) {
foreach ( $matches as $match ) {
$size[ $match[1] === 'width' ? 0 : 1 ] = $match[2];
}
}
return $url;
}
}

View File

@@ -0,0 +1,139 @@
<?php
/**
* This class renders the html for the event location.
*
* @author Time.ly Network Inc.
* @since 2.0
*
* @package AI1EC
* @subpackage AI1EC.View.Event
*/
class Ai1ec_View_Event_Location extends Ai1ec_Base {
/**
* Return location details in brief format, separated by | characters.
*
* @return $string Short location string
*/
public function get_short_location( Ai1ec_Event $event ) {
$location_items = array();
foreach ( array( 'venue', 'city', 'province', 'country' ) as $field ) {
if ( $event->get( $field ) !== '' ) {
$location_items[] = $event->get( $field );
}
}
return implode( ' | ', $location_items );
}
/*
* Return any available location details separated by newlines
*/
public function get_location( Ai1ec_Event $event ) {
$location = '';
$venue = $event->get( 'venue' );
if ( $venue ) {
$location .= $venue . "\n";
}
$address = $event->get( 'address' );
if ( $address ) {
$bits = explode( ',', $address );
$bits = array_map( 'trim', $bits );
// If more than three comma-separated values, treat first value as
// the street address, last value as the country, and everything
// in the middle as the city, state, etc.
if ( count( $bits ) >= 3 ) {
// Append the street address
$street_address = array_shift( $bits ) . "\n";
if ( $street_address ) {
$location .= $street_address;
}
// Save the country for the last line
$country = array_pop( $bits );
// Append the middle bit(s) (filtering out any zero-length strings)
$bits = array_filter( $bits, 'strval' );
if ( $bits ) {
$location .= join( ', ', $bits ) . "\n";
}
if ( $country ) {
$location .= $country . "\n";
}
} else {
// There are two or less comma-separated values, so just append
// them each on their own line (filtering out any zero-length strings)
$bits = array_filter( $bits, 'strval' );
$location .= join( "\n", $bits );
}
}
return $location;
}
/**
* get_map_view function
*
* Returns HTML markup displaying a Google map of the given event, if the event
* has show_map set to true. Returns a zero-length string otherwise.
*
* @return void
**/
function get_map_view( Ai1ec_Event $event ) {
$settings = $this->_registry->get( 'model.settings' );
$loader = $this->_registry->get( 'theme.loader' );
if( ! $event->get( 'show_map' ) ) {
return '';
}
$location = $this->get_latlng( $event );
if ( ! $location ) {
$location = $event->get( 'address' );
}
$args = array(
'address' => $location,
'gmap_url_link' => $this->get_gmap_url( $event, false ),
'hide_maps_until_clicked' => $settings->get( 'hide_maps_until_clicked' ),
'text_view_map' => __( 'Click to view map', AI1EC_PLUGIN_NAME ),
'text_full_map' => __( 'View Full-Size Map', AI1EC_PLUGIN_NAME ),
);
return $loader->get_file( 'event-map.twig', $args, false )->get_content();
}
/**
* Returns the latitude/longitude coordinates as a textual string
* parsable by the Geocoder API.
*
* @param Ai1ec_Event &$event The event to return data from
*
* @return string The latitude & longitude string, or null
*/
public function get_latlng( Ai1ec_Event $event ) {
// If the coordinates are set, use those, otherwise use the address.
$location = NULL;
// If the coordinates are set by hand use them.
if ( $event->get( 'show_coordinates' ) ) {
$longitude = floatval( $event->get( 'longitude' ) );
$latitude = floatval( $event->get( 'latitude' ) );
$location = $latitude . ',' . $longitude;
}
return $location;
}
/**
* Returns the URL to the Google Map for the given event object.
*
* @param Ai1ec_Event $event The event object to display a map for
*
* @return string
*/
public function get_gmap_url( Ai1ec_Event $event ) {
$lang = $this->_registry->get( 'p28n.wpml' )->get_language();
$location = $this->get_latlng( $event );
if ( ! $location ) {
$location = $event->get( 'address' );
}
return 'https://www.google.com/maps?f=q&hl=' . urlencode( $lang ) .
'&source=embed&q=' . urlencode( $location );
}
}

View File

@@ -0,0 +1,116 @@
<?php
/**
* This class renders the html for the event colors.
*
* @author Time.ly Network Inc.
* @since 2.0
*
* @package AI1EC
* @subpackage AI1EC.View.Event
*/
class Ai1ec_View_Event_Post extends Ai1ec_Base {
/**
* Add event-specific messages to be used when one is modified in dashboard.
*
* @wp_hook post_updated_messages
*
* @param array $messages List of messages.
*
* @return array Modified list of messages.
*/
public function post_updated_messages( $messages ) {
global $post, $post_ID;
$messages[AI1EC_POST_TYPE] = array(
0 => '', // Unused. Messages start at index 1.
1 => sprintf(
Ai1ec_I18n::__( 'Event updated. <a href="%s">View event</a>' ),
esc_url( get_permalink( $post_ID ) )
),
2 => Ai1ec_I18n::__( 'Custom field updated.' ),
3 => Ai1ec_I18n::__( 'Custom field deleted.' ),
4 => Ai1ec_I18n::__( 'Event updated.' ),
/* translators: %s: date and time of the revision */
5 => isset( $_GET['revision'] )
? sprintf(
Ai1ec_I18n::__( 'Event restored to revision from %s' ),
wp_post_revision_title( (int) $_GET['revision'], false )
)
: false,
6 => sprintf(
Ai1ec_I18n::__( 'Event published. <a href="%s">View event</a>' ),
esc_url( get_permalink($post_ID) )
),
7 => Ai1ec_I18n::__( 'Event saved.' ),
8 => sprintf(
Ai1ec_I18n::__( 'Event submitted. <a target="_blank" href="%s">Preview event</a>' ),
esc_url( add_query_arg( 'preview', 'true', get_permalink( $post_ID ) ) )
),
9 => sprintf(
Ai1ec_I18n::__( 'Event scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview event</a>' ),
// translators: Publish box date format, see http://php.net/date
$this->_registry->get( 'date.time', $post->post_date )->format_i18n( Ai1ec_I18n::__( 'M j, Y @ G:i' ) ),
esc_url( get_permalink($post_ID) )
),
10 => sprintf(
Ai1ec_I18n::__( 'Event draft updated. <a target="_blank" href="%s">Preview event</a>' ),
esc_url( add_query_arg( 'preview', 'true', get_permalink( $post_ID ) ) )
),
);
return $messages;
}
/**
* Generates an excerpt from the given content string.
*
* Adapted from WordPress's `wp_trim_excerpt' function that is not useful
* for applying to custom content.
*
* @param string $text The content to trim.
*
* @return string The excerpt.
*/
public function trim_excerpt( Ai1ec_Event $event, $length = 35, $more = '[...]' ) {
global $post;
$original_post = $post;
$post = $event->get( 'post' );
$raw_excerpt = $event->get( 'post' )->post_content;
if ( ! isset( $raw_excerpt{0} ) ) {
$raw_excerpt = '&nbsp;';
}
$text = preg_replace(
'#<\s*script[^>]*>.+<\s*/\s*script\s*>#x',
'',
apply_filters(
'the_excerpt',
$raw_excerpt
)
);
$text = strip_shortcodes( $text );
$text = str_replace( ']]>', ']]&gt;', $text );
$text = strip_tags( $text );
$excerpt_length = apply_filters( 'excerpt_length', $length );
$excerpt_more = apply_filters( 'excerpt_more', $more );
$words = preg_split(
'/\s+/',
$text,
$excerpt_length + 1,
PREG_SPLIT_NO_EMPTY
);
if ( count( $words ) > $excerpt_length ) {
array_pop( $words );
$text = implode( ' ', $words );
$text = $text . $excerpt_more;
} else {
$text = implode( ' ', $words );
}
$post = $original_post;
return apply_filters( 'wp_trim_excerpt', $text, $raw_excerpt );
}
}

View File

@@ -0,0 +1,283 @@
<?php
/**
* This class renders the html for the single event page.
*
* @author Time.ly Network Inc.
* @since 2.0
*
* @package AI1EC
* @subpackage AI1EC.View.Event
*/
class Ai1ec_View_Event_Single extends Ai1ec_Base {
/**
* Renders the html of the page and returns it.
*
* @param Ai1ec_Event $event
*
* @return string the html of the page
*/
public function get_content( Ai1ec_Event $event ) {
$settings = $this->_registry->get( 'model.settings' );
$rrule = $this->_registry->get( 'recurrence.rule' );
$taxonomy = $this->_registry->get( 'view.event.taxonomy' );
$location = $this->_registry->get( 'view.event.location' );
$ticket = $this->_registry->get( 'view.event.ticket' );
$content = $this->_registry->get( 'view.event.content' );
$time = $this->_registry->get( 'view.event.time' );
$subscribe_url = AI1EC_EXPORT_URL . '&ai1ec_post_ids=' .
$event->get( 'post_id' );
$event->set_runtime(
'tickets_url_label',
$ticket->get_tickets_url_label( $event, false )
);
$event->set_runtime(
'content_img_url',
$content->get_content_img_url( $event )
);
$extra_buttons = apply_filters(
'ai1ec_rendering_single_event_actions',
'',
$event
);
$venues_html = apply_filters(
'ai1ec_rendering_single_event_venues',
nl2br( $location->get_location( $event ) ),
$event
);
$default_tz = $this->_registry->get( 'date.timezone' )->get_default_timezone();
$timezone_info = array(
'show_timezone' => $this->_registry->get( 'model.settings' )->get( 'always_use_calendar_timezone' ),
'using_calendar_tz' => $this->_registry->get( 'model.settings' )->get( 'always_use_calendar_timezone' ),
'event_timezone' => str_replace( '_', ' ', $event->get( 'timezone_name' ) ) . ' ' . __( 'Timezone', AI1EC_PLUGIN_NAME ),
'calendar_timezone' => str_replace( '_', ' ', $default_tz ) . ' ' . __( 'Timezone', AI1EC_PLUGIN_NAME ),
);
$banner_image_meta = get_post_meta( $event->get( 'post_id' ), 'ai1ec_banner_image' );
$banner_image = $banner_image_meta ? $banner_image_meta[0] : '';
// objects are passed by reference so an action is ok
do_action( 'ai1ec_single_event_page_before_render', $event );
$filter_groups_html = apply_filters( 'ai1ec_get_filter_groups_html', $event );
$args = array(
'event' => $event,
'recurrence' => $rrule->rrule_to_text( $event->get( 'recurrence_rules' ) ),
'exclude' => $time->get_exclude_html( $event, $rrule ),
'categories' => $taxonomy->get_categories_html( $event ),
'tags' => $taxonomy->get_tags_html( $event ),
'location' => html_entity_decode( $venues_html ),
'filter_groups' => $filter_groups_html,
'map' => $location->get_map_view( $event ),
'contact' => $ticket->get_contact_html( $event ),
'back_to_calendar' => $content->get_back_to_calendar_button_html(),
'subscribe_url' => $subscribe_url,
'subscribe_url_no_html' => $subscribe_url . '&no_html=true',
'edit_instance_url' => null,
'edit_instance_text' => null,
'google_url' => 'https://www.google.com/calendar/render?cid=' . urlencode( $subscribe_url ),
'show_subscribe_buttons' => ! $settings->get( 'turn_off_subscription_buttons' ),
'hide_featured_image' => $settings->get( 'hide_featured_image' ),
'extra_buttons' => $extra_buttons,
'show_get_calendar' => ! $settings->get( 'disable_get_calendar_button' ),
'text_add_calendar' => __( 'Add to Calendar', AI1EC_PLUGIN_NAME ),
'subscribe_buttons_text' => $this->_registry
->get( 'view.calendar.subscribe-button' )
->get_labels(),
'text_get_calendar' => Ai1ec_I18n::__( 'Get a Timely Calendar' ),
'text_when' => __( 'When:', AI1EC_PLUGIN_NAME ),
'text_where' => __( 'Where:', AI1EC_PLUGIN_NAME ),
'text_cost' => __( 'Cost:', AI1EC_PLUGIN_NAME ),
'text_contact' => __( 'Contact:', AI1EC_PLUGIN_NAME ),
'text_tickets' => __( 'Tickets:', AI1EC_PLUGIN_NAME ),
'text_free' => __( 'Free', AI1EC_PLUGIN_NAME ),
'text_categories' => __( 'Categories', AI1EC_PLUGIN_NAME ),
'text_tags' => __( 'Tags', AI1EC_PLUGIN_NAME ),
'buy_tickets_text' => __( 'Buy Tickets', AI1EC_PLUGIN_NAME ),
'timezone_info' => $timezone_info,
'banner_image' => $banner_image,
'content_img_url' => $event->get_runtime( 'content_img_url' ),
'post_id' => $event->get( 'post_id' ),
'ticket_url' => $event->get( 'ticket_url' ),
'tickets_url_label' => $event->get_runtime( 'tickets_url_label' ),
'start' => $event->get( 'start' ),
'end' => $event->get( 'end' ),
'cost' => $event->get( 'cost' ),
'instance_id' => $event->get( 'instance_id' ),
);
if (
! empty( $args['recurrence'] ) &&
$event->get( 'instance_id' ) &&
current_user_can( 'edit_ai1ec_events' )
) {
$args['edit_instance_url'] = ai1ec_admin_url(
'post.php?post=' . $event->get( 'post_id' ) .
'&action=edit&instance=' . $event->get( 'instance_id' )
);
$args['edit_instance_text'] = sprintf(
Ai1ec_I18n::__( 'Edit this occurrence (%s)' ),
$event->get( 'start' )->format_i18n( 'M j' )
);
}
$loader = $this->_registry->get( 'theme.loader' );
$api = $this->_registry->get( 'model.api.api-ticketing' );
if ( false === ai1ec_is_blank( $event->get( 'ical_feed_url' ) ) ) {
$ticket_url = $api->get_api_event_buy_ticket_url( $event->get( 'post_id' ) );
if ( ! empty ( $ticket_url ) ) {
$args['ticket_url'] = $ticket_url;
}
} else {
$api_event_id = $api->get_api_event_id( $event->get( 'post_id' ) );
if ( $api_event_id ) {
$api = $this->_registry->get( 'model.api.api-ticketing' );
$ticket_types = json_decode( $api->get_ticket_types( $event->get( 'post_id' ), false ) );
$args['has_tickets'] = true;
$args['API_URL'] = AI1EC_API_URL;
$args['tickets_block'] = $loader->get_file(
'tickets.twig',
array(
'tickets_checkout_url' => $api->get_api_event_buy_ticket_url( $event->get( 'post_id' ) ),
'tickets' => $ticket_types->data,
'text_tickets' => $args['text_tickets'],
'buy_tickets_text' => $args['buy_tickets_text'],
'api_event_id' => $api_event_id
), false
)->get_content();
}
}
return $loader->get_file( 'event-single.twig', $args, false )
->get_content();
}
/**
* Add meta OG tags to the event details page
*/
public function add_meta_tags() {
// Add tags only on Event Details page
$aco = $this->_registry->get( 'acl.aco' );
if ( ! $aco->is_our_post_type() ) return;
// Get Event and process description
$instance_id = ( isset( $_GET[ 'instance_id' ] ) ) ? $_GET[ 'instance_id' ] : null;
if ( !is_null( $instance_id ) ) {
$instance_id = preg_replace( '/\D/', '', $instance_id );
}
$event = $this->_registry->get( 'model.event', get_the_ID(), $instance_id );
$avatar = $this->_registry->get( 'view.event.avatar' );
$content = $this->_registry->get( 'view.event.content' );
$desc = $event->get( 'post' )->post_content;
$desc = apply_filters( 'the_excerpt', $desc );
$desc = strip_shortcodes( $desc );
$desc = str_replace( ']]>', ']]&gt;', $desc );
$desc = strip_tags( $desc );
$desc = preg_replace( '/\n+/', ' ', $desc);
$desc = substr( $desc, 0, 300 );
// Get featured image
$image = $avatar->get_post_thumbnail_url( $event );
if ( ! $image ) {
$image = $content->get_content_img_url( $event );
}
$og = array(
'url' => home_url( esc_url( add_query_arg( null, null ) ) ),
'title' => htmlspecialchars(
$event->get( 'post' )->post_title .
' (' . substr( $event->get( 'start' ) , 0, 10 ) . ')'
),
'type' => 'article',
'description' => htmlspecialchars( $desc ),
'image' => $image,
);
foreach ( $og as $key => $val ) {
echo "<meta property=\"og:$key\" content=\"$val\" />\n";
}
// Twitter meta tags
$twitter = array(
'card' => 'summary',
'title' => htmlspecialchars(
$event->get( 'post' )->post_title .
' (' . substr( $event->get( 'start' ) , 0, 10 ) . ')'
),
'description' => htmlspecialchars( $desc ),
'image' => $image,
);
foreach ( $twitter as $key => $val ) {
if ( empty( $val ) && 'image' !== $key ) {
$val = Ai1ec_I18n::__( 'No data' );
}
echo "<meta name=\"twitter:$key\" content=\"$val\" />\n";
}
}
/**
* @param Ai1ec_Event $event
*
* @return The html of the footer
*/
public function get_footer( Ai1ec_Event $event ) {
$text_calendar_feed = null;
$feed_url = trim( strtolower( $event->get( 'ical_feed_url' ) ) );
if ( strpos( $feed_url, 'http' ) === 0 ) {
$text_calendar_feed = Ai1ec_I18n::__(
'This post was replicated from another site\'s <a href="%s" title="iCalendar feed"><i class="ai1ec-fa ai1ec-fa-calendar"></i> calendar feed</a>.'
);
} else {
$text_calendar_feed = Ai1ec_I18n::__(
'This post was imported from a CSV/ICS file.'
);
}
$loader = $this->_registry->get( 'theme.loader' );
$text_calendar_feed = sprintf(
$text_calendar_feed,
esc_attr( str_replace( 'http://', 'webcal://', $event->get( 'ical_feed_url' ) ) )
);
$args = array(
'event' => $event,
'text_calendar_feed' => $text_calendar_feed,
'text_view_post' => Ai1ec_I18n::__( 'View original' ),
);
return $loader->get_file( 'event-single-footer.twig', $args, false )
->get_content();
}
/**
* Render the full article for the event  title, content, and footer.
*
* @param Ai1ec_Event $event
* @param string $footer Footer HTML to append to event
*/
public function get_full_article( Ai1ec_Event $event, $footer = '' ) {
$title = apply_filters(
'the_title',
$event->get( 'post' )->post_title,
$event->get( 'post_id' )
);
$event_details = $this->get_content( $event );
$content = wpautop(
apply_filters(
'ai1ec_the_content',
apply_filters(
'the_content',
$event->get( 'post' )->post_content
)
)
);
$args = compact( 'title', 'event_details', 'content', 'footer' );
$loader = $this->_registry->get( 'theme.loader' );
return $loader->get_file( 'event-single-full.twig', $args, false )
->get_content();
}
}

View File

@@ -0,0 +1,398 @@
<?php
/**
* This class renders the html for the event taxonomy.
*
* @author Time.ly Network Inc.
* @since 2.0
*
* @package AI1EC
* @subpackage AI1EC.View.Event
*/
class Ai1ec_View_Event_Taxonomy extends Ai1ec_Base {
/**
* @var Ai1ec_Taxonomy Taxonomy abstraction layer.
*/
protected $_taxonomy_model = null;
/**
* @var array Caches the color evaluated for each event.
*/
protected $_event_color_map = array();
/**
* @var array Caches the color squares HTML evaluated for each event.
*/
protected $_event_color_squares_map = array();
/**
* Returns style attribute for events rendered in Month, Week, or Day view.
*
* @param Ai1ec_Event $event Event object.
*
* @return string Color style attribute.
*/
public function get_color_style( Ai1ec_Event $event ) {
$color = $this->get_color_for_event( $event );
// Convert to style attribute.
if ( $color ) {
$color = $event->is_allday() || $event->is_multiday()
? 'background-color: ' . $color . ';'
: 'color: ' . $color . ' !important;';
} else {
$color = '';
}
return $color;
}
/**
* Returns HTML of category color swatches for this event.
*
* @param Ai1ec_Event $event Event object.
*
* @return string HTML of the event's category color swatches.
*/
public function get_category_colors( Ai1ec_Event $event ) {
$post_id = $event->get( 'post_id' );
if ( ! isset( $this->_event_color_squares_map[$post_id] ) ) {
$squares = '';
$categories = $this->_taxonomy_model->get_post_categories( $post_id );
if ( false !== $categories ) {
$squares = $this->get_event_category_colors( $categories );
}
// Allow add-ons to modify/add to category color swatch HTML.
$squares = apply_filters(
'ai1ec_event_color_squares',
$squares,
$event
);
$this->_event_color_squares_map[$post_id] = $squares;
}
return $this->_event_color_squares_map[$post_id];
}
/**
* Returns the HTML markup for the category color square.
*
* @param int $term_id The term ID of event category
*
* @return string
*/
public function get_category_color_square( $term_id ) {
$color = $this->_taxonomy_model->get_category_color( $term_id );
$event_taxonomy = $this->_registry->get( 'model.event.taxonomy' );
if ( null !== $color ) {
$taxonomy = $event_taxonomy->get_taxonomy_for_term_id( $term_id );
$cat = get_term( $term_id, $taxonomy->taxonomy );
return '<span class="ai1ec-color-swatch ai1ec-tooltip-trigger" ' .
'style="background:' . $color . '" title="' .
esc_attr( $cat->name ) . '"></span>';
}
return '';
}
/**
* Returns the HTML markup for the category image square.
*
* @param int $term_id The term ID of event category.
*
* @return string HTML snippet to use for category image.
*/
public function get_category_image_square( $term_id ) {
$image = $this->_taxonomy_model->get_category_image( $term_id );
if ( null !== $image ) {
return '<img src="' . $image . '" alt="' .
Ai1ec_I18n::__( 'Category image' ) .
'" class="ai1ec_category_small_image_preview" />';
}
return '';
}
/**
* Returns category color squares for the list of Event Category objects.
*
* @param array $cats The Event Category objects as returned by get_terms()
*
* @return string
*/
public function get_event_category_colors( array $cats ) {
$sqrs = '';
foreach ( $cats as $cat ) {
$tmp = $this->get_category_color_square( $cat->term_id );
if ( ! empty( $tmp ) ) {
$sqrs .= $tmp;
}
}
return $sqrs;
}
/**
* Style attribute for event background color.
*
* @param Ai1ec_Event $event Event object.
*
* @return string Color to assign to event background.
*/
public function get_category_bg_color( Ai1ec_Event $event ) {
$color = $this->get_color_for_event( $event );
// Convert to HTML attribute.
if ( $color ) {
$color = 'style="background-color: ' . $color . ';"';
} else {
$color = '';
}
return $color;
}
/**
* Style attribute for event multi-date divider color.
*
* @param Ai1ec_Event $event Event object.
*
* @return string Color to assign to event background.
*/
public function get_category_divider_color( Ai1ec_Event $event ) {
$color = $this->get_color_for_event( $event );
// Convert to HTML attribute.
if ( $color ) {
$color = 'style="border-color: ' . $color . ' transparent transparent transparent;"';
} else {
$color = '';
}
return $color;
}
/**
* Style attribute for event text color.
*
* @param Ai1ec_Event $event Event object.
*
* @return string Color to assign to event text (foreground).
*/
public function get_category_text_color( Ai1ec_Event $event ) {
$color = $this->get_color_for_event( $event );
// Convert to HTML attribute.
if ( $color ) {
$color = 'style="color: ' . $color . ';"';
} else {
$color = '';
}
return $color;
}
/**
* Caches color for event having the given post ID.
*
* @param int $post_id Event's post ID.
*
* @return string Color associated with event.
*/
public function get_color_for_event( $event ) {
$post_id = $event->get( 'post_id' );
// If color for this event is uncached, populate cache.
if ( ! isset( $this->_event_color_map[$post_id] ) ) {
// Find out if an add-on has provided its own color for the event.
$color = apply_filters( 'ai1ec_event_color', '', $event );
// If none provided, fall back to event categories.
if ( empty( $color ) ) {
$categories = $this->_taxonomy_model->get_post_categories( $post_id );
// Find the first category of this post that defines a color.
foreach ( $categories as $category ) {
$color = $this->_taxonomy_model->get_category_color(
$category->term_id
);
if ( $color ) {
break;
}
}
}
$this->_event_color_map[$post_id] = $color;
}
return $this->_event_color_map[$post_id];
}
/**
* Categories as HTML, either as blocks or inline.
*
* @param Ai1ec_Event $event Rendered Event.
* @param string $format Return 'blocks' or 'inline' formatted result.
*
* @return string String of HTML for category blocks.
*/
public function get_categories_html(
Ai1ec_Event $event,
$format = 'blocks'
) {
$categories = $this->_taxonomy_model->get_post_categories(
$event->get( 'post_id' )
);
foreach ( $categories as &$category ) {
$href = $this->_registry->get(
'html.element.href',
array( 'cat_ids' => $category->term_id )
);
$class = $data_type = $title = '';
if ( $category->description ) {
$title = 'title="' .
esc_attr( $category->description ) . '" ';
}
$html = '';
$class .= ' ai1ec-category';
$color_style = '';
if ( $format === 'inline' ) {
$taxonomy = $this->_registry->get( 'model.taxonomy' );
$color_style = $taxonomy->get_category_color(
$category->term_id
);
if ( $color_style !== '' ) {
$color_style = 'style="color: ' . $color_style . ';" ';
}
$class .= '-inline';
}
$html .= '<a ' . $data_type . ' class="' . $class .
' ai1ec-term-id-' . $category->term_id . ' p-category" ' .
$title . $color_style . 'href="' . $href->generate_href() . '">';
if ( $format === 'blocks' ) {
$html .= $this->get_category_color_square(
$category->term_id
) . ' ';
} else {
$html .=
'<i ' . $color_style .
'class="ai1ec-fa ai1ec-fa-folder-open"></i>';
}
$html .= esc_html( $category->name ) . '</a>';
$category = $html;
}
return implode( ' ', $categories );
}
/**
* Tags as HTML
*/
public function get_tags_html( Ai1ec_Event $event ) {
$tags = $this->_taxonomy_model->get_post_tags(
$event->get( 'post_id' )
);
if ( ! $tags ) {
$tags = array();
}
foreach ( $tags as &$tag ) {
$href = $this->_registry->get(
'html.element.href',
array( 'tag_ids' => $tag->term_id )
);
$class = '';
$data_type = '';
$title = '';
if ( $tag->description ) {
$title = 'title="' . esc_attr( $tag->description ) . '" ';
}
$tag = '<a ' . $data_type . ' class="ai1ec-tag ' . $class .
' ai1ec-term-id-' . $tag->term_id . '" ' . $title .
'href="' . $href->generate_href() . '">' .
'<i class="ai1ec-fa ai1ec-fa-tag"></i>' .
esc_html( $tag->name ) . '</a>';
}
return implode( ' ', $tags );
}
/**
* Filter Groups as HTML, either as blocks or inline.
*
* @param Ai1ec_Event $event Rendered Event.
* @param array $filter_group Filter Group (Option Model)
* @param string $format Return 'blocks' or 'inline' formatted result.
*
* @return string String of HTML for filter group blocks.
*/
public function get_filter_group_html(
Ai1ec_Event $event,
$filter_group,
$format = 'blocks'
) {
$filter_groups = $this->_taxonomy_model->get_post_taxonomy(
$event->get( 'post_id' ), $filter_group['taxonomy_name']
);
$icon_name = '';
if ( 'ai1eccfgi-null' !== $filter_group['icon'] ) {
$icon_name = $filter_group['icon'];
} else {
$icon_name = 'ai1ec-icon-timely';
}
foreach ( $filter_groups as &$group ) {
$href = $this->_registry->get(
'html.element.href',
array( $filter_group['taxonomy_name'] . '_ids' => $group->term_id )
);
$class = $data_type = $title = '';
if ( $group->description ) {
$title = 'title="' .
esc_attr( $group->description ) . '" ';
}
$html = '';
$class .= ' ai1ec-category';
$color_style = '';
if ( 'inline' === $format ) {
$taxonomy = $this->_registry->get( 'model.taxonomy' );
$color_style = $taxonomy->get_category_color(
$group->term_id
);
if ( $color_style !== '' ) {
$color_style = 'style="color: ' . $color_style . ';" ';
}
$class .= '-inline';
}
$html .= '<a ' . $data_type . ' class="' . $class .
' ai1ec-term-id-' . $group->term_id . ' p-category" ' .
$title . $color_style . 'href="' . $href->generate_href() . '">';
if ( 'blocks' === $format ) {
$html .= $this->get_category_color_square($group->term_id) . ' ';
} else {
$html = $html
. '<i ' . $color_style . ' class="ai1ec-fa '
. $icon_name . '"></i>';
}
$html .= esc_html( $group->name ) . '</a>';
$group = $html;
}
return implode( ' ', $filter_groups );
}
public function __construct( Ai1ec_Registry_Object $registry ) {
parent::__construct( $registry );
$this->_taxonomy_model = $this->_registry->get( 'model.taxonomy' );
}
}

View File

@@ -0,0 +1,90 @@
<?php
/**
* This class renders the html for the event ticket.
*
* @author Time.ly Network Inc.
* @since 2.0
*
* @package AI1EC
* @subpackage AI1EC.View.Event
*/
class Ai1ec_View_Event_Ticket {
/**
* Create readable content for buy tickets/register link
*
* @param bool $long Set to false to use short message version
*
* @return string Message to be rendered on buy tickets link
*/
public function get_tickets_url_label( Ai1ec_Event $event, $long = true ) {
if ( $event->is_free() ) {
return ( $long )
? __( 'Register Now', AI1EC_PLUGIN_NAME )
: __( 'Register', AI1EC_PLUGIN_NAME );
}
$output = '';
if ( $long ) {
$output = apply_filters(
'ai1ec_buy_tickets_url_icon',
'<i class="ai1ec-fa ai1ec-fa-shopping-cart"></i>'
);
if ( ! empty( $output ) ) {
$output .= ' ';
}
}
$output .= ( $long )
? __( 'Buy Tickets', AI1EC_PLUGIN_NAME )
: __( 'Tickets', AI1EC_PLUGIN_NAME );
return $output;
}
/**
* Contact info as HTML
*/
public function get_contact_html( Ai1ec_Event $event ) {
$contact = '<div class="h-card">';
$has_contents = false;
if ( $event->get( 'contact_name' ) ) {
$contact .=
'<div class="ai1ec-contact-name p-name">' .
'<i class="ai1ec-fa ai1ec-fa-fw ai1ec-fa-user"></i> ' .
esc_html( $event->get( 'contact_name' ) ) .
'</div> ';
$has_contents = true;
}
if ( $event->get( 'contact_phone' ) ) {
$contact .=
'<div class="ai1ec-contact-phone p-tel">' .
'<i class="ai1ec-fa ai1ec-fa-fw ai1ec-fa-phone"></i> ' .
esc_html( $event->get( 'contact_phone' ) ) .
'</div> ';
$has_contents = true;
}
if ( $event->get( 'contact_email' ) ) {
$contact .=
'<div class="ai1ec-contact-email">' .
'<a class="u-email" href="mailto:' .
esc_attr( $event->get( 'contact_email' ) ) . '">' .
'<i class="ai1ec-fa ai1ec-fa-fw ai1ec-fa-envelope-o"></i> ' .
__( 'Email', AI1EC_PLUGIN_NAME ) . '</a></div> ';
$has_contents = true;
}
if ( $event->get( 'contact_url' ) ) {
$contact .=
'<div class="ai1ec-contact-url">' .
'<a class="u-url" target="_blank" href="' .
esc_attr( $event->get( 'contact_url' ) ) .
'"><i class="ai1ec-fa ai1ec-fa-fw ai1ec-fa-link"></i> ' .
apply_filters(
'ai1ec_contact_url',
__( 'Event website', AI1EC_PLUGIN_NAME )
) .
' <i class="ai1ec-fa ai1ec-fa-external-link"></i></a></div>';
$has_contents = true;
}
$contact .= '</div>';
return $has_contents ? $contact : '';
}
}

View File

@@ -0,0 +1,217 @@
<?php
/**
* This class renders the html for the event time.
*
* @author Time.ly Network Inc.
* @since 2.0
*
* @package AI1EC
* @subpackage AI1EC.View.Event
*/
class Ai1ec_View_Event_Time extends Ai1ec_Base {
/**
* Returns timespan expression for the event.
*
* Properly handles:
* - instantaneous events
* - all-day events
* - multi-day events
* Display of start date can be hidden (non-all-day events only) or full
* date. All-day status, if any, is enclosed in a span.ai1ec-allday-badge
* element.
*
* @param Ai1ec_Event $event Rendered event.
* @param string $start_date_display Can be one of 'hidden', 'short',
* or 'long'.
*
* @return string Formatted timespan HTML element.
*/
public function get_timespan_html(
Ai1ec_Event $event,
$start_date_display = 'long'
) {
// Makes no sense to hide start date for all-day events, so fix argument
if ( 'hidden' === $start_date_display && $event->is_allday() ) {
$start_date_display = 'short';
}
// Localize time.
$start = $this->_registry->get( 'date.time', $event->get( 'start' ) );
$end = $this->_registry->get( 'date.time', $event->get( 'end' ) );
// All-day events need to have their end time shifted by 1 second less
// to land on the correct day.
$end_offset = 0;
if ( $event->is_allday() ) {
$end->set_time(
$end->format( 'H' ),
$end->format( 'i' ),
$end->format( 's' ) - 1
);
}
// Get timestamps of start & end dates without time component.
$start_ts = $this->_registry->get( 'date.time', $start )
->set_time( 0, 0, 0 )
->format();
$end_ts = $this->_registry->get( 'date.time', $end )
->set_time( 0, 0, 0 )
->format();
$break_years = $start->format( 'Y' ) !== $end->format( 'Y' );
$output = '';
// Display start date, depending on $start_date_display.
switch ( $start_date_display ) {
case 'hidden':
break;
case 'short':
case 'long':
$property = $start_date_display . '_date';
$output .= $this->{'get_' . $property}( $start, $break_years );
break;
default:
$start_date_display = 'long';
}
// Output start time for non-all-day events.
if ( ! $event->is_allday() ) {
if ( 'hidden' !== $start_date_display ) {
$output .= apply_filters(
'ai1ec_get_timespan_html_time_separator',
Ai1ec_I18n::_x( ' @ ', 'Event time separator' )
);
}
$output .= $this->get_short_time( $start );
}
// Find out if we need to output the end time/date. Do not output it for
// instantaneous events and all-day events lasting only one day.
if (
! (
$event->is_instant() ||
( $event->is_allday() && $start_ts === $end_ts )
)
) {
$output .= apply_filters(
'ai1ec_get_timespan_html_date_separator',
Ai1ec_I18n::_x( ' ', 'Event start/end separator' )
);
// If event ends on a different day, output end date.
if ( $start_ts !== $end_ts ) {
// for short date, use short display type
if ( 'short' === $start_date_display ) {
$output .= $this->get_short_date( $end, $break_years );
} else {
$output .= $this->get_long_date( $end );
}
}
// Output end time for non-all-day events.
if ( ! $event->is_allday() ) {
if ( $start_ts !== $end_ts ) {
$output .= apply_filters(
'ai1ec_get_timespan_html_time_separator',
Ai1ec_I18n::_x( ' @ ', 'Event time separator' )
);
}
$output .= $this->get_short_time( $end );
}
}
$output = esc_html( $output );
// Add all-day label.
if ( $event->is_allday() ) {
$output .= apply_filters(
'ai1ec_get_timespan_html_allday_badge',
' <span class="ai1ec-allday-badge">' .
Ai1ec_I18n::__( 'all-day' ) .
'</span>'
);
}
return apply_filters(
'ai1ec_get_timespan_html',
$output,
$event,
$start_date_display
);
}
/**
* Get the html for the exclude dates and exception rules.
*
* @param Ai1ec_Event $event
* @param Ai1ec_Recurrence_Rule $rrule
* @return string
*/
public function get_exclude_html(
Ai1ec_Event $event,
Ai1ec_Recurrence_Rule $rrule
) {
$excludes = array();
$exception_rules = $event->get( 'exception_rules' );
$exception_dates = $event->get( 'exception_dates' );
if ( $exception_rules ) {
$excludes[] =
$rrule->rrule_to_text( $exception_rules );
}
if ( $exception_dates && 0 !== strpos( $exception_rules, 'EXDATE' ) ) {
$excludes[] =
$rrule->exdate_to_text( $exception_dates );
}
return implode( Ai1ec_I18n::__( ', and ' ), $excludes );
}
/**
* Get the short date
*
* @param Ai1ec_Date_Time $time
* @param bool $add_year Whether to add year or not.
*
* @return string
*/
public function get_short_date( Ai1ec_Date_Time $time, $add_year = false ) {
$months = apply_filters( 'ai1ec_i18n_months', array() );
$m = $time->format_i18n( 'M' );
$m = array_key_exists( $m, $months ) ? $months[$m] : $m;
if ( $add_year ) {
return $m . ' ' . $time->format_i18n( 'j Y' );
}
return $m . ' ' . $time->format_i18n( 'j' );
}
/**
* Format a long-length date for use in other views (e.g., single event).
*
* @param Ai1ec_Date_Time $time Object to format.
*
* @return string Formatted date time [default: `l, M j, Y`].
*/
public function get_long_date( Ai1ec_Date_Time $time ) {
$date_format = $this->_registry->get( 'model.option' )->get(
'date_format',
'l, M j, Y'
);
return $time->format_i18n( $date_format );
}
/**
* Format a short-form time for use in compressed (e.g. month) views.
*
* @param Ai1ec_Date_Time $time Object to format.
*
* @return string Formatted date time [default: `g:i a`].
*/
public function get_short_time( Ai1ec_Date_Time $time ) {
$time_format = $this->_registry->get( 'model.option' )->get(
'time_format',
'g:i a'
);
return $time->format_i18n( $time_format );
}
}