push_to_stack('Generate level 1 gallery');
$short_code = array_merge($this->common_parameters, $short_code);
$photos = $photo_list->photos;
$lightbox = self::get_lightbox();
$layout = $short_code['layout'];
$columns = sanitize_text_field(!empty($short_code['columns']) && ('random' !== $layout && 'mosaic' !== $layout) ? $short_code['columns'] : 'auto');
$more = !empty($short_code['more']) ? sanitize_text_field($short_code['more']) : '';
$more = (empty($more) && !empty($short_code['photo_more'])) ? sanitize_text_field($short_code['photo_more']) : $more;
$title_position = empty($short_code['title_position']) ? $photo_list->title_position : sanitize_text_field($short_code['title_position']);
$row_constraints = $photo_list->row_constraints;
$pagination = $photo_list->pagination;
$indent = $photo_list->indent;
$display = sanitize_text_field(!empty($short_code['display']) ? $short_code['display'] : 'local');
$panel = !empty($short_code['panel']) ? sanitize_text_field($short_code['panel']) : '';
$parent = $photo_list->parent;
list($container_start_id, $container_end_id) = $this->get_container_details($short_code, $module);
$non_standard = in_array($layout, ['random', 'masonry', 'masonry-horizontal', 'mosaic'], true);
$gallery_column_class = $this->get_gallery_column_class($non_standard, $columns, $row_constraints);
$effect = esc_attr($this->get_thumbnail_effect($short_code, $layout, $title_position));
$gallery_figure_classes = ['photonic-level-1', 'photonic-thumb'];
$lightbox_attributes = $lightbox->get_gallery_attributes($panel, $module); // Function returns escaped values
$anchor_classes = $lightbox_attributes['class'] ?: [];
$anchor_rel = $lightbox_attributes['rel'] ?: [];
$anchor_lightbox_data = $lightbox_attributes['specific'] ?: [];
$ret = '';
global $photonic_external_links_in_new_tab;
if (!empty($photonic_external_links_in_new_tab)) {
$target = " target='_blank' ";
$anchor_lightbox_data['target'] = '_blank';
}
else {
$target = '';
}
$counter = 0;
$layout_engine = $this->get_layout_engine($module, $short_code);
$all_sizes_present = strtolower($layout_engine) === 'css';
$grid_figures = [];
/** @var Photo $photo */
foreach ($photos as $idx => $photo) {
$counter++;
$figure_data = [];
if ('masonry-horizontal' === $layout) {
$figure_data['data-photonic-idx'] = $idx;
}
$additional_image_data = [];
$styles = [];
if ($non_standard && 'local' === $display) {
$thumb = $photo->tile_image ?: $photo->main_image;
if (!empty($photo->tile_size)) {
$inbuilt_sizes = $photo->tile_size;
}
elseif (!empty($photo->main_size)) {
$inbuilt_sizes = $photo->main_size;
}
}
else {
$thumb = $photo->thumbnail;
if (!empty($photo->thumb_size)) {
$inbuilt_sizes = $photo->thumb_size;
}
}
if (!empty($inbuilt_sizes)) {
if ('random' === $layout) {
$styles['--dw'] = $inbuilt_sizes['w'];
$styles['--dh'] = $inbuilt_sizes['h'];
}
}
else {
$all_sizes_present = false;
}
$deep_value = 'gallery[photonic-' . $module->provider . '-' . $parent . '-' . ($panel ?: $module->gallery_index) . ']/' . ($photo->id ?: $counter) . '/';
$additional_image_data['data-photonic-deep'] = esc_attr($deep_value);
if (!empty($photo->buy_link) && $module->show_buy_link) {
$additional_image_data['data-photonic-buy'] = esc_url($photo->buy_link);
}
$title = wp_kses_post($photo->title);
$description = wp_kses_post($photo->description);
$alt = wp_kses_post($photo->alt_title);
$titles = [
'title' => $title,
'desc' => $description,
'alt' => $alt,
];
$title = $this->get_title($title, $titles, $short_code);
$title_markup = $lightbox->get_lightbox_title($photo, $module, $title, $alt, $target);
$additional_image_data['data-title'] = $title_markup;
$shown_title = '';
if (in_array($title_position, ['below', 'hover-slideup-show', 'hover-slidedown-show', 'slideup-stick'], true) && !empty($title)) {
// Convoluted... we want to remove any funky markup from $title, so wp_filter_nohtml_kses is used. But that does an `addslashes`, which causes more funky markup. So we use stripslashes, but then we decode the special characters.
// $shown_title = '' . wp_specialchars_decode(stripslashes(wp_filter_nohtml_kses($title)), ENT_QUOTES) . '
';
$shown_title = '' . wp_specialchars_decode(stripslashes(wp_kses($title, Photonic::$safe_description_tags)), ENT_QUOTES) . '
';
}
$photo_data = ['title' => $title_markup, 'deep' => $deep_value, 'raw_title' => esc_attr($title)];
if (!empty($photo->download)) {
$photo_data['download'] = esc_url($photo->download);
}
if (!empty($photo->video)) {
$photo_data['video'] = esc_url($photo->video);
$photo_data['poster'] = esc_url($photo->main_image);
}
else {
$photo_data['image'] = esc_url($photo->main_image);
}
$photo_data['id'] = $photo->id;
$photo_data['thumbnail'] = $thumb;
if (!empty($photo->main_size)) {
$photo_data['width'] = $photo->main_size['w'];
$photo_data['height'] = $photo->main_size['h'];
}
$image_lightbox_data = $lightbox->get_photo_attributes($photo_data, $module);
if ('tooltip' === $title_position) {
$additional_image_data['data-photonic-tooltip'] = esc_attr($title);
}
$grid_image = new Grid_Image();
$grid_image->alt = esc_attr($alt ?: wp_kses_post($photo->title)); // alt and title are the same for everything except WP galleries. This has been put in for cases where users don't specify an alt on WP galleries.
$grid_image->src = $thumb;
$grid_image->classes = [sanitize_html_class($layout)];
$grid_image->dimensions = $inbuilt_sizes ?? [];
$grid_anchor = new Grid_Anchor();
$grid_anchor->href = $lightbox->get_grid_link($photo, $short_code, $module); // CANNOT esc_url $lightbox->get_grid_link(...), since it sometimes returns a URL, and sometimes a "#" location. Instead, within get_grid_link there is either esc_attr or esc_url
$grid_anchor->classes = $anchor_classes;
$grid_anchor->rel = $anchor_rel;
$grid_anchor->data = array_merge(
$anchor_lightbox_data,
$image_lightbox_data,
$additional_image_data
);
$grid_anchor->title = 'none' !== $title_position ? esc_attr($title) : '';
$grid_anchor->figcaption = $shown_title;
$grid_anchor->image = $grid_image;
$grid_anchor->indent = $indent;
$grid_figure = new Grid_Figure();
$grid_figure->classes = $gallery_figure_classes;
$grid_figure->styles = $styles;
$grid_figure->data = $figure_data;
$grid_figure->video_markup = $lightbox->get_video_markup($photo, $module, $indent);
$grid_figure->anchor = $grid_anchor;
$grid_figure->indent = $indent;
$grid_figures[] = $grid_figure;
}
if (!empty($grid_figures)) {
$this->set_lazy_loading_attributes($all_sizes_present, $grid_figures);
$container_class_list = $this->get_common_gallery_classes(1, $title_position, $non_standard, $layout, $effect, $all_sizes_present, $gallery_column_class, $display);
$container_class_list[] = $lightbox->get_container_classes();
$data_query = $this->get_updated_data_query($short_code, $pagination);
$container_data_attributes = [
'data-photonic-platform' => esc_attr($module->provider),
'data-photonic-gallery-columns' => esc_attr($columns),
'data-photonic-query' => esc_attr($data_query),
];
if ('masonry-horizontal' === $layout) {
$container_data_attributes['data-photonic-max-idx'] = count($photos);
}
$ret = $this->generate_container_markup($module, $pagination, $container_class_list, $container_data_attributes, $grid_figures, $container_start_id, $container_end_id, $more, 1, $indent);
}
$module->pop_from_stack();
return $ret;
}
/**
* Generates the HTML for a group of level-2 items, i.e. Photosets (Albums) and Galleries for Flickr, Albums for Google Photos,
* Albums for SmugMug, and Photosets (Galleries and Collections) for Zenfolio. No concept of albums
* exists in native WP and Instagram.
*
* @param Album_List $album_list
* @param array $short_code
* @param Base $module
* @return string
*/
public function generate_level_2_gallery(Album_List $album_list, array $short_code, Base $module): string {
$module->push_to_stack('Generate Level 2 Gallery');
$short_code = array_merge($this->common_parameters, $short_code);
$objects = $album_list->albums;
$layout = esc_attr($short_code['layout'] ?? 'square');
$columns = $short_code['columns'];
$more = !empty($short_code['more']) ? esc_attr($short_code['more']) : '';
$title_position = esc_attr(empty($short_code['title_position']) ? $album_list->title_position : $short_code['title_position']);
$row_constraints = $album_list->row_constraints;
$pagination = $album_list->pagination;
$indent = $album_list->indent;
$singular_type = esc_attr($album_list->singular_type);
$level_1_count_display = $album_list->level_1_count_display;
$provider = esc_attr($module->provider);
$gallery_data = array_merge(
[
'platform' => $provider,
'singular' => $singular_type,
'popup' => esc_attr($short_code['popup']),
],
$album_list->gallery_attributes
);
list($container_start_id, $container_end_id) = $this->get_container_details($short_code, $module);
$non_standard = in_array($layout, ['random', 'masonry', 'masonry-horizontal', 'mosaic'], true);
$gallery_column_class = $this->get_gallery_column_class($non_standard, $columns, $row_constraints);
$effect = esc_attr($this->get_thumbnail_effect($short_code, $layout, $title_position));
$gallery_figure_classes = ['photonic-level-2', 'photonic-thumb'];
$gallery_anchor_classes = ['photonic-level-2-thumb'];
if ($album_list->album_opens_gallery) {
$gallery_anchor_classes[] = 'gallery-page';
}
$layout_engine = $this->get_layout_engine($module, $short_code);
$all_sizes_present = strtolower($layout_engine) === 'css';
$ret = '';
$grid_figures = [];
/** @var Album $object */
foreach ($objects as $idx => $object) {
$data_attributes = $object->data_attributes ?: [];
$styles = [];
$figure_data = [];
if ('masonry-horizontal' === $layout) {
$figure_data['data-photonic-idx'] = $idx;
}
$anchor_data = [];
foreach ($data_attributes as $attr => $value) {
$anchor_data['data-photonic-' . $attr] = $value;
}
$id = empty($object->id) ? '' : $object->id . '-';
$id = esc_attr($id . $module->gallery_index);
$title = wp_kses_post($object->title);
$anchor_data['data-title'] = esc_attr($title);
if ('tooltip' === $title_position) {
$anchor_data['data-photonic-tooltip'] = esc_attr($title);
}
if ($non_standard && !empty($object->tile_image)) {
$img_src = $object->tile_image;
$inbuilt_sizes = $object->tile_size ?: [];
}
else {
$img_src = $object->thumbnail;
$inbuilt_sizes = $object->thumb_size ?: [];
}
if (!empty($inbuilt_sizes)) {
if ('random' === $layout) {
$styles['--dw'] = $inbuilt_sizes['w'];
$styles['--dh'] = $inbuilt_sizes['h'];
}
}
else {
$all_sizes_present = false;
}
$shown_title = '';
if (in_array($title_position, ['below', 'hover-slideup-show', 'hover-slidedown-show', 'slideup-stick'], true)) {
$shown_title = "\n$indent\t\t\t\n$indent\t\t\t\t" . wp_specialchars_decode(stripslashes(wp_filter_nohtml_kses($title)), ENT_QUOTES);
if (!$level_1_count_display && !empty($object->counter)) {
$shown_title .= '' . sprintf(esc_html__('%s photos', 'photonic'), $object->counter) . '';
}
$shown_title .= "
\n$indent\t\t\t";
}
$grid_image = new Grid_Image();
$grid_image->alt = esc_attr($title);
$grid_image->src = esc_url($img_src);
$grid_image->classes = [sanitize_html_class($layout)];
$grid_image->dimensions = $inbuilt_sizes ?? [];
$grid_anchor = new Grid_Anchor();
$grid_anchor->id = "photonic-$provider-$singular_type-thumb-$id";
$grid_anchor->href = esc_url($object->gallery_url ?? $object->main_page);
$grid_anchor->classes = array_merge($gallery_anchor_classes, $object->classes);
$grid_anchor->data = $anchor_data;
$grid_anchor->title = 'none' !== $title_position ? esc_attr($title) : '';
$grid_anchor->figcaption = $shown_title;
$grid_anchor->image = $grid_image;
$grid_anchor->indent = $indent;
$grid_figure = new Grid_Figure();
$grid_figure->id = "photonic-$provider-$singular_type-$id";
$grid_figure->classes = $gallery_figure_classes;
$grid_figure->styles = $styles;
$grid_figure->data = $figure_data;
$grid_figure->anchor = $grid_anchor;
$grid_figure->indent = $indent;
$grid_figure->prompter_markup = $this->get_password_prompter($object, $provider, $singular_type, $id);
$grid_figures[] = $grid_figure;
}
if (!empty($grid_figures)) {
$this->set_lazy_loading_attributes($all_sizes_present, $grid_figures);
$gallery_class_list = $this->get_common_gallery_classes(2, $title_position, $non_standard, $layout, $effect, $all_sizes_present, $gallery_column_class);
$data_query = $this->get_updated_data_query($short_code, $pagination);
$container_data_attributes = [
'data-photonic-platform' => $provider,
'data-photonic-gallery-columns' => esc_attr($columns),
'data-photonic-query' => esc_attr($data_query),
'data-photonic' => esc_attr(wp_json_encode($gallery_data)),
];
$ret = $this->generate_container_markup($module, $pagination, $gallery_class_list, $container_data_attributes, $grid_figures, $container_start_id, $container_end_id, $more, 2, $indent);
}
$module->pop_from_stack();
return $ret;
}
/**
* @param array $short_code
* @param Base $module
* @return array
*/
private function get_container_details(array $short_code, Base $module): array {
if ('modal' !== $short_code['display']) {
$start_id = "photonic-" . esc_attr($module->provider) . "-stream-" . esc_attr($module->gallery_index) . "-container";
$end_id = "photonic-" . esc_attr($module->provider) . "-stream-" . esc_attr($module->gallery_index) . "-container-end";
}
else {
$start_id = "photonic-" . esc_attr($module->provider) . "-panel-" . esc_attr(sanitize_text_field($short_code['panel'])) . "-container";
$end_id = "photonic-" . esc_attr($module->provider) . "-panel-" . esc_attr(sanitize_text_field($short_code['panel'])) . "-container-end";
}
return [$start_id, $end_id];
}
/**
* @param array $short_code
* @param Pagination $pagination
* @return string
*/
private function get_updated_data_query(array $short_code, Pagination $pagination): string {
$to_be_glued = '';
if (!empty($short_code)) {
$to_be_glued = [];
foreach ($short_code as $name => $value) {
if (is_scalar($value)) {
if ('next_token' !== $name) {
$to_be_glued[] = $name . '=' . $value;
}
}
}
if (!empty($pagination->next_token)) {
$to_be_glued[] = 'next_token=' . $pagination->next_token;
}
$to_be_glued = implode('&', $to_be_glued);
$to_be_glued = esc_attr($to_be_glued);
}
return $to_be_glued;
}
/**
* Returns the thumbnail effect that should be used for a gallery. Not all effects can be used by all types of layouts.
*
* @param $short_code
* @param $layout
* @param $title_position
* @return string
*/
private function get_thumbnail_effect($short_code, $layout, $title_position): string {
if (!empty($short_code['thumbnail_effect'])) {
$effect = $short_code['thumbnail_effect'];
}
else {
global $photonic_standard_thumbnail_effect, $photonic_justified_thumbnail_effect, $photonic_mosaic_thumbnail_effect, $photonic_masonry_thumbnail_effect;
$effect = 'mosaic' === $layout ? $photonic_mosaic_thumbnail_effect :
(('masonry' === $layout || 'masonry-horizontal' === $layout)? $photonic_masonry_thumbnail_effect :
('random' === $layout ? $photonic_justified_thumbnail_effect :
$photonic_standard_thumbnail_effect));
$effect = esc_attr($effect);
}
if ('circle' === $layout && 'opacity' !== $effect) { // "Zoom" doesn't work for circle
$thumbnail_effect = 'none';
}
elseif (('square' === $layout || 'launch' === $layout || 'masonry' === $layout) && 'below' === $title_position) { // For these combinations, Zoom doesn't work
$thumbnail_effect = 'none';
}
else {
$thumbnail_effect = $effect;
}
return apply_filters('photonic_thumbnail_effect', $thumbnail_effect, $short_code, $layout, $title_position);
}
/**
* Returns the layout engine to be used, i.e. js or css.
*
* @param Base $module
* @param $short_code
* @return string
*/
private function get_layout_engine(Base $module, $short_code): string {
$layout_engine = 'photonic_' . $module->provider . '_layout_engine';
global ${$layout_engine};
return $short_code['layout_engine'] ?? ${$layout_engine};
}
/**
* @param Album $object
* @param string $provider
* @param string $singular_type
* @param string $id
* @return string
*/
private function get_password_prompter(Album $object, string $provider, string $singular_type, string $id): string {
$password_prompt = '';
if (!empty($object->passworded)) {
$password_prompt = "
";
}
return $password_prompt;
}
/**
* @param bool $all_sizes_present
* @param array $grid_figures
*/
private function set_lazy_loading_attributes(bool $all_sizes_present, array $grid_figures): void {
if ($all_sizes_present) {
/** @var Grid_Figure $figure */
foreach ($grid_figures as $figure) {
$figure->anchor->image->lazy_load = 'lazy';
$figure->anchor->image->src_attr = 'data-src';
}
}
else {
/** @var Grid_Figure $figure */
foreach ($grid_figures as $figure) {
$figure->anchor->image->lazy_load = 'eager';
$figure->anchor->image->src_attr = 'src';
}
}
}
private function get_common_gallery_classes($level, $title_position, $non_standard, $layout, $thumbnail_effect, $all_sizes_present, $gallery_column_class, $display = null): array {
$classes = [
'title-display-' . $title_position,
'photonic-level-' . $level . '-container',
$all_sizes_present ? 'sizes-present' : 'sizes-missing',
$gallery_column_class,
];
if ('modal' === $display) {
$classes[] = 'modal-gallery';
}
else {
$classes[] = $non_standard ? 'photonic-' . $layout . '-layout' : 'photonic-standard-layout';
$classes[] = 'photonic-thumbnail-effect-' . $thumbnail_effect;
}
return $classes;
}
/**
* @param bool $non_standard
* @param string $columns
* @param array $row_constraints
* @return string
*/
private function get_gallery_column_class(bool $non_standard, string $columns, array $row_constraints): string {
$gallery_column_class = '';
if (!$non_standard) {
if (absint($columns)) {
$gallery_column_class = 'photonic-gallery-' . esc_attr($columns) . 'c';
}
elseif ('padding' === $row_constraints['constraint-type']) {
$gallery_column_class = 'photonic-auto-padded';
}
elseif (!empty($row_constraints['count'])) {
$gallery_column_class = 'photonic-gallery-' . esc_attr($row_constraints['count']) . 'c';
}
}
return $gallery_column_class;
}
private function glue_data_attributes(array $map): string {
$data_pieces = [];
if (!empty($map)) {
$data_pieces = array_map(
function (string $key, string $value): string {
return $key . '="' . $value . '"';
},
array_keys($map),
array_values($map)
);
}
return implode(' ', $data_pieces);
}
private function generate_container_markup(Base $module, Pagination $pagination, array $container_class_list, array $container_data_attributes, array $grid_figures, string $start_id, string $end_id, string $more, int $level, string $indent): string {
$ret = '';
if (!empty($grid_figures)) {
$container_class = str_replace(' ', ' ', trim(implode(' ', $container_class_list)));
$style = '';
if (in_array('photonic-random-layout', $container_class_list, true)) {
global $photonic_tile_min_height;
if (!is_numeric($photonic_tile_min_height)) {
$photonic_tile_min_height = 200;
}
$photonic_tile_min_height = esc_attr($photonic_tile_min_height);
$style = "style='--tile-min-height: {$photonic_tile_min_height}px'";
}
$container_data_string = $this->glue_data_attributes($container_data_attributes);
$ret = "\n$indent\n";
foreach ($grid_figures as $figure) {
$ret .= $figure->html($module, $this, false);
}
$ret .= "\n$indent
";
$ret .= "\n$indent";
if ($pagination->end > -1 && $pagination->total > -1 && $pagination->total > $pagination->end) {
$ret .= !empty($more) ? "\n$indent$more\n" : '';
}
}
return $ret;
}
}