photos-6.0.x-dev/src/PhotosAlbum.php
src/PhotosAlbum.php
<?php
namespace Drupal\photos;
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Link;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\Core\Utility\Error;
use Drupal\image\Entity\ImageStyle;
/**
* Create an album object.
*/
class PhotosAlbum {
use StringTranslationTrait;
/**
* Album ID {node}.nid.
*
* @var int
*/
protected $albumId;
/**
* Constructs a PhotosAlbum object.
*
* @param int $nid
* Album ID {node}.nid.
*/
public function __construct($nid) {
$this->albumId = $nid;
}
/**
* Page and Teaser display settings.
*/
public function nodeView($node, $display, $view_mode) {
// @todo convert to field api. Preserve for legacy mode.
$album = [];
$default_style = 'medium';
if ($display != 0) {
$default_order = \Drupal::config('photos.settings')->get('photos_display_imageorder');
$order = explode('|', ($node->album['imageorder'] ?? $default_order));
$order = PhotosAlbum::orderValueChange($order[0], $order[1]);
$default_style = \Drupal::config('photos.settings')->get('photos_display_' . $view_mode . '_imagesize') ?: 'thumbnail';
$style_name = $node->album[$view_mode . '_imagesize'] ?? $default_style;
}
switch ($display) {
case 0:
// Display none.
break;
case 1:
// Display cover.
// @todo get photos_image id and load cover display.
// @todo add field setting option to link to album...
$render_photos_image = [];
if (isset($node->album['cover'])) {
$render_photos_image = $node->album['cover'];
}
else {
$db = \Drupal::database();
$cover_id = $db->query('SELECT cover_id FROM {photos_album} WHERE album_id = :nid', [
':nid' => $node->id(),
])->fetchField();
if ($cover_id) {
$photos_image = \Drupal::entityTypeManager()
->getStorage('photos_image')
->load($cover_id);
if ($photos_image) {
// @todo add setting to override cover view_mode?
$render_photos_image = \Drupal::entityTypeManager()
->getViewBuilder('photos_image')
->view($photos_image, 'cover');
}
}
}
return $render_photos_image;
case 2:
// Display thumbnails.
$get_field = \Drupal::request()->query->get('field');
$get_sort = \Drupal::request()->query->get('sort');
$column = $get_field ? Html::escape($get_field) : 0;
$sort = $get_sort ? Html::escape($get_sort) : 0;
$view_num = \Drupal::config('photos.settings')->get('photos_display_' . $view_mode . '_viewnum') ?: 10;
$limit = $node->album[$view_mode . '_viewnum'] ?? $view_num;
$term = PhotosAlbum::orderValue($column, $sort, $limit, $order);
$db = \Drupal::database();
$query = $db->select('file_managed', 'f');
// @note currently legacy mode requires default field_image.
$query->join('photos_image__field_image', 'i', 'i.field_image_target_id = f.fid');
$query->join('photos_image_field_data', 'p', 'p.revision_id = i.revision_id');
$query->fields('f', ['fid']);
$query->condition('p.album_id', $node->id());
$query->orderBy($term['order']['column'], $term['order']['sort']);
$query->range(0, $term['limit']);
$result = $query->execute();
$i = 0;
// Necessary when upgrading from D6 to D7.
$image_styles = image_style_options(FALSE);
if (!isset($image_styles[$style_name])) {
$style_name = \Drupal::config('photos.settings')->get('photos_display_teaser_imagesize');
}
// @todo this can use a teaser display mode, but we want to keep legacy
// support, so if fid can be found from a file or image field that will
// be a nice backup option.
$album = [];
// Thumbnails.
foreach ($result as $data) {
$photos_image = new PhotosImageFile($data->fid);
$variables = [
'href' => 'photos/image/' . $data->fid,
];
$album[] = $photos_image->view($style_name, $variables);
++$i;
}
break;
case 3:
// Get cover.
$cover = FALSE;
if (isset($node->album['cover']) && isset($node->album['cover']['uri'])) {
$image_render_array = [
'#theme' => 'image_style',
'#style_name' => $style_name,
'#uri' => $node->album['cover']['uri'],
'#title' => $node->getTitle(),
'#alt' => $node->getTitle(),
];
$cover = $image_render_array;
}
if ($cover) {
// Cover with colorbox gallery.
$get_field = \Drupal::request()->query->get('field');
$get_sort = \Drupal::request()->query->get('sort');
$column = $get_field ? Html::escape($get_field) : 0;
$sort = $get_sort ? Html::escape($get_sort) : 0;
$view_num = \Drupal::config('photos.settings')->get('photos_display_' . $view_mode . '_viewnum') ?: 10;
$limit = FALSE;
// Query all images in gallery.
$term = PhotosAlbum::orderValue($column, $sort, $limit, $order);
$db = \Drupal::database();
$query = $db->select('file_managed', 'f');
// @todo p.fid will fail.
$query->join('photos_image_field_data', 'p', 'p.fid = f.fid');
$query->join('users_field_data', 'ufd', 'ufd.uid = f.uid');
$query->fields('f', [
'uri',
'filemime',
'created',
'filename',
'filesize',
])
->fields('p')
->fields('ufd', ['uid', 'name']);
$query->condition('p.album_id', $node->id());
$query->orderBy($term['order']['column'], $term['order']['sort']);
$result = $query->execute();
$i = 0;
// Setup colorbox.
if (\Drupal::moduleHandler()->moduleExists('colorbox')) {
$style = \Drupal::config('colorbox.settings')->get('custom.style');
$album['#attached']['library'] = [
'colorbox/colorbox',
'colorbox/' . $style,
];
$colorbox_height = \Drupal::config('photos.settings')->get('photos_display_colorbox_max_height') ?: 100;
$colorbox_width = \Drupal::config('photos.settings')->get('photos_display_colorbox_max_width') ?: 50;
$js_settings = [
'maxWidth' => $colorbox_width . '%',
'maxHeight' => $colorbox_height . '%',
];
$album['#attached']['drupalSettings']['colorbox'] = $js_settings;
}
// Display cover and list colorbox image links.
foreach ($result as $data) {
$style_name = $node->album['view_imagesize'] ?? $style_name;
$style = ImageStyle::load($style_name);
$file_url = $style->buildUrl($data->uri);
$image = NULL;
if ($i == 0) {
$image = $cover;
}
$album[] = [
'#theme' => 'photos_image_colorbox_link',
'#image' => $image,
'#image_title' => $data->title,
'#image_url' => $file_url,
'#nid' => $node->id(),
];
++$i;
}
}
break;
}
return $album;
}
/**
* Get album cover.
*
* @param int $cover_id
* The photos_image entity id.
* @param bool $uri_only
* Return image URI only if available (used for photos album media).
*
* @return array
* Render array.
*/
public function getCover($cover_id = NULL, $uri_only = FALSE) {
$albumId = $this->albumId;
$cover = [];
if (!$cover_id) {
// Check album for cover fid.
$db = \Drupal::database();
$cover_id = $db->query("SELECT cover_id FROM {photos_album} WHERE album_id = :album_id", [
':album_id' => $albumId,
])->fetchField();
}
// If id is still empty.
if (empty($cover_id)) {
// Cover not set, select an image from the album.
$db = \Drupal::database();
$query = $db->select('photos_image_field_data', 'p');
$query->fields('p', ['id']);
$query->condition('p.album_id', $albumId);
$cover_id = $query->execute()->fetchField();
}
if ($cover_id) {
// Load image.
$photos_image = NULL;
try {
$photos_image = \Drupal::entityTypeManager()
->getStorage('photos_image')
->load($cover_id);
}
catch (InvalidPluginDefinitionException | PluginNotFoundException $e) {
Error::logException(\Drupal::logger('photos'), $e);
}
if ($photos_image) {
if ($uri_only) {
if ($photos_image->hasField('field_image') && $photos_image->field_image->entity) {
$cover = $photos_image->field_image->entity->getFileUri();
}
}
else {
$cover = \Drupal::entityTypeManager()
->getViewBuilder('photos_image')
->view($photos_image, 'cover');
}
}
}
return $cover;
}
/**
* Set album cover.
*/
public function setCover($cover_id = 0) {
$albumId = $this->albumId;
// Update cover.
$db = \Drupal::database();
$db->update('photos_album')
->fields([
'cover_id' => $cover_id,
])
->condition('album_id', $albumId)
->execute();
// Clear node and views cache.
Cache::invalidateTags([
'node:' . $albumId,
'photos:album:' . $albumId,
'photos_image_list',
]);
\Drupal::messenger()->addMessage($this->t('Cover successfully set.'));
}
/**
* Get album images.
*/
public function getImages($limit = 10) {
$images = [];
// Prepare query.
$get_field = \Drupal::request()->query->get('field');
$column = $get_field ? Html::escape($get_field) : '';
$get_sort = \Drupal::request()->query->get('sort');
$sort = $get_sort ? Html::escape($get_sort) : '';
$term = PhotosAlbum::orderValue($column, $sort, $limit, [
'column' => 'p.weight',
'sort' => 'asc',
]);
// Query images in this album.
$db = \Drupal::database();
$query = $db->select('photos_image_field_data', 'p')
->extend('Drupal\Core\Database\Query\PagerSelectExtender');
$query->join('users_field_data', 'u', 'p.uid = u.uid');
$query->join('node_field_data', 'n', 'n.nid = p.album_id');
$query->fields('p', ['id']);
$query->condition('p.album_id', $this->albumId);
$query->limit($term['limit']);
$query->orderBy($term['order']['column'], $term['order']['sort']);
if ($term['order']['column'] != 'f.fid') {
$query->orderBy('p.id', 'DESC');
}
// $query->addTag('node_access');
$results = $query->execute();
// Prepare images.
foreach ($results as $result) {
$photosImage = \Drupal::entityTypeManager()->getStorage('photos_image')->load($result->id);
$images[] = [
'photos_image' => $photosImage,
];
}
return $images;
}
/**
* Limit and sort by links.
*/
public static function orderLinks($arg, $count = 0, $link = 0, $limit = 0) {
// @todo move out? This is used in recent images, user images and album.
// @todo move count and order links out.
// Get current path.
$q = \Drupal::service('path.current')->getPath();
$field = [
'weight' => t('By weight'),
'title' => t('By title'),
'created' => t('By time'),
'comments' => t('By comments'),
'filesize' => t('By filesize'),
];
// Check count image views variable.
$photos_image_count = \Drupal::config('photos.settings')->get('photos_image_count');
if (!$photos_image_count) {
$field['visits'] = t('By visits');
}
if ($limit) {
$get_limit = \Drupal::request()->query->get('limit');
$query = PhotosAlbum::getPagerQuery();
$links['limit'] = '';
if (!is_array($limit)) {
$limit = [5, 10, 20, 30, 40, 50];
}
$limit_query = $query;
$limit_count = count($limit);
foreach ($limit as $key => $tt) {
$limit_query['limit'] = $tt;
$sort = [
'query' => $limit_query,
'attributes' => [
'class' => [
(isset($get_limit) && $get_limit == $tt) ? 'orderac' : NULL,
],
'rel' => 'nofollow',
],
];
$links['limit'] .= Link::fromTextAndUrl($tt, Url::fromUri('base:' . $q, $sort))->toString();
if ($limit_count != $key) {
$links['limit'] .= ' ';
}
}
}
$links['count'] = $count;
$links['link'] = $link ? $link : NULL;
$sort_links = Link::fromTextAndUrl(t('Default'), Url::fromUri('base:' . $arg, ['attributes' => ['rel' => 'nofollow']]))->toString() . ' ';
$sort_link_count = count($field);
$get_field = \Drupal::request()->query->get('field');
$get_limit = \Drupal::request()->query->get('limit');
$get_sort = \Drupal::request()->query->get('sort');
foreach ($field as $key => $t) {
if (empty($get_field) || $get_field != $key) {
$sort = 'desc';
$class = 'photos-order-desc';
}
elseif ($get_sort == 'desc') {
$sort = 'asc';
$class = 'photos-order-asc photos-active-sort';
}
else {
$sort = 'desc';
$class = 'photos-order-desc photos-active-sort';
}
$field_query = [
'sort' => $sort,
'field' => $key,
];
if ($get_limit) {
$field_query['limit'] = Html::escape($get_limit);
}
$sort_links .= Link::fromTextAndUrl($t, Url::fromUri('base:' . $q, [
'query' => $field_query,
'attributes' => [
'class' => [$class],
'rel' => 'nofollow',
],
]))->toString();
if ($key != $sort_link_count) {
$sort_links .= ' ';
}
}
if ($sort_links) {
$links['sort'] = [
'#markup' => $sort_links,
];
}
return [
'#theme' => 'photos_album_links',
'#links' => $links,
];
}
/**
* Returns array of query parameters.
*/
public static function getPagerQuery() {
$query_array = ['limit', 'q', 'page', 'destination'];
$request = \Drupal::requestStack()->getCurrentRequest()->request->all();
$cookies = \Drupal::requestStack()->getCurrentRequest()->cookies->all();
return UrlHelper::filterQueryParameters($request, array_merge($query_array, array_keys($cookies)));
}
/**
* Sort order labels.
*/
public static function orderLabels() {
return [
'weight|asc' => t('Weight - smallest first'),
'weight|desc' => t('Weight - largest first'),
'title|asc' => t('Title - A-Z'),
'title|desc' => t('Title - Z-A'),
'created|desc' => t('Upload Date - newest first'),
'created|asc' => t('Upload Date - oldest first'),
'comments|desc' => t('Comments - most first'),
'comments|asc' => t('Comments - least first'),
'filesize|desc' => t('Filesize - smallest first'),
'filesize|asc' => t('Filesize - largest first'),
'visits|desc' => t('Visits - most first'),
'visits|asc' => t('Visits - least first'),
];
}
/**
* Extends photos order value.
*/
public static function orderValueChange($field, $sort) {
// @note timestamp is deprecated, but may exist
// if albums are migrated from a previous version.
$array = [
'weight' => 'p.weight',
'title' => 'p.title',
'timestamp' => 'p.id',
'changed' => 'p.changed',
'created' => 'p.created',
'comments' => 'c.comment_count',
'visits' => 'v.value',
'filesize' => 'f.filesize',
];
$array1 = [
'desc' => 'desc',
'asc' => 'asc',
];
if (isset($array[$field]) && isset($array1[$sort])) {
return [
'column' => $array[$field],
'sort' => $array1[$sort],
];
}
else {
// Default if values not found.
return [
'column' => 'p.id',
'sort' => 'desc',
];
}
}
/**
* Query helper: sort order and limit.
*/
public static function orderValue($field, $sort, $limit, $default = 0) {
// @todo update default to check album default!
$default_order = ['column' => 'p.id', 'sort' => 'desc'];
if (!$field && !$sort) {
$t['order'] = !$default ? $default_order : $default;
}
else {
if (!$t['order'] = PhotosAlbum::orderValueChange($field, $sort)) {
$t['order'] = !$default ? $default_order : $default;
}
}
if ($limit) {
$get_limit = \Drupal::request()->query->get('limit');
if ($get_limit && !$show = intval($get_limit)) {
$get_destination = \Drupal::request()->query->get('destination');
if ($get_destination) {
$str = $get_destination;
if (preg_match('/.*limit=(\d*).*/i', $str, $mat)) {
$show = intval($mat[1]);
}
}
}
$t['limit'] = $show ?? $limit;
}
return $t;
}
/**
* Return number of albums or photos.
*/
public static function getCount($type, $id = 0) {
$db = \Drupal::database();
switch ($type) {
case 'user_album':
case 'user_image':
case 'site_album':
case 'site_image':
return $db->query("SELECT value FROM {photos_count} WHERE cid = :cid AND type = :type", [
':cid' => $id,
':type' => $type,
])->fetchField();
case 'node_album':
return $db->query("SELECT count FROM {photos_album} WHERE album_id = :album_id", [
':album_id' => $id,
])->fetchField();
}
}
/**
* Update count.
*
* @param bool $cron
* If this is being called from cron.
*/
public static function resetCount($cron = FALSE) {
PhotosAlbum::setCount('site_album');
PhotosAlbum::setCount('site_image');
$time = $cron ? 7200 : 0;
// @todo optimize. Check if new images since last count.
// @todo this only works if cron has not run in 2 hours?
$cron_last = \Drupal::state()->get('system.cron_last', 0);
if ((\Drupal::time()->getRequestTime() - $cron_last) > $time) {
$db = \Drupal::database();
$result = $db->query('SELECT uid FROM {users} WHERE uid != 0');
foreach ($result as $t) {
PhotosAlbum::setCount('user_image', $t->uid);
PhotosAlbum::setCount('user_album', $t->uid);
}
$result = $db->query('SELECT album_id FROM {photos_album}');
foreach ($result as $t) {
PhotosAlbum::setCount('node_album', $t->album_id);
}
}
}
/**
* Calculate number of $type.
*/
public static function setCount($type, $id = 0) {
$db = \Drupal::database();
$requestTime = \Drupal::time()->getRequestTime();
switch ($type) {
case 'user_image':
$count = $db->query('SELECT count(p.id) FROM {photos_image_field_data} p WHERE p.uid = :id', [
':id' => $id,
])->fetchField();
$query = $db->update('photos_count');
$query->fields([
'value' => $count,
'changed' => $requestTime,
]);
$query->condition('cid', $id);
$query->condition('type', $type);
$affected_rows = $query->execute();
if (!$affected_rows) {
$db->insert('photos_count')
->fields([
'cid' => $id,
'changed' => $requestTime,
'type' => $type,
'value' => $count,
])
->execute();
}
// Clear cache tags.
Cache::invalidateTags(['photos:image:user:' . $id]);
break;
case 'user_album':
$count = $db->query('SELECT count(p.album_id) FROM {photos_album} p INNER JOIN {node_field_data} n ON p.album_id = n.nid WHERE n.uid = :uid',
[':uid' => $id])->fetchField();
$query = $db->update('photos_count')
->fields([
'value' => $count,
'changed' => $requestTime,
])
->condition('cid', $id)
->condition('type', $type);
$affected_rows = $query->execute();
if (!$affected_rows) {
$db->insert('photos_count')
->fields([
'cid' => $id,
'changed' => $requestTime,
'type' => $type,
'value' => $count,
])
->execute();
}
// Clear cache tags.
Cache::invalidateTags(['photos:album:user:' . $id]);
break;
case 'site_album':
$count = $db->query('SELECT COUNT(album_id) FROM {photos_album}')->fetchField();
$query = $db->update('photos_count')
->fields([
'value' => $count,
'changed' => $requestTime,
])
->condition('cid', 0)
->condition('type', $type);
$affected_rows = $query->execute();
if (!$affected_rows) {
$db->insert('photos_count')
->fields([
'cid' => 0,
'changed' => $requestTime,
'type' => $type,
'value' => $count,
])
->execute();
}
break;
case 'site_image':
$count = $db->query('SELECT COUNT(id) FROM {photos_image_field_data}')->fetchField();
$query = $db->update('photos_count')
->fields([
'value' => $count,
'changed' => $requestTime,
])
->condition('cid', 0)
->condition('type', $type);
$affected_rows = $query->execute();
if (!$affected_rows) {
$db->insert('photos_count')
->fields([
'cid' => 0,
'changed' => $requestTime,
'type' => $type,
'value' => $count,
])
->execute();
}
// Clear cache tags.
Cache::invalidateTags(['photos:image:recent']);
break;
case 'node_album':
$count = $db->query("SELECT COUNT(id) FROM {photos_image_field_data} WHERE album_id = :album_id", [':album_id' => $id])->fetchField();
$db->update('photos_album')
->fields([
'count' => $count,
])
->condition('album_id', $id)
->execute();
break;
}
}
/**
* Tracks number of albums created and number of albums allowed.
*/
public static function userAlbumCount() {
$user = \Drupal::currentUser();
$user_roles = $user->getRoles();
$t['create'] = PhotosAlbum::getCount('user_album', $user->id());
// @todo upgrade path? Check D7 role id and convert pnum variables as needed.
$role_limit = 0;
$t['total'] = 20;
// Check highest role limit.
foreach ($user_roles as $role) {
if (\Drupal::config('photos.settings')->get('photos_pnum_' . $role)
&& \Drupal::config('photos.settings')->get('photos_pnum_' . $role) > $role_limit) {
$role_limit = \Drupal::config('photos.settings')->get('photos_pnum_' . $role);
}
}
if ($role_limit > 0) {
$t['total'] = $role_limit;
}
$t['remain'] = ($t['total'] - $t['create']);
if ($user->id() != 1 && $t['remain'] <= 0) {
$t['rest'] = 1;
}
return $t;
}
/**
* User albums.
*/
public static function userAlbumOptions($uid = 0, $current = 0) {
if (!$uid) {
$uid = \Drupal::currentUser()->id();
}
$output = [];
// Query user albums.
$db = \Drupal::database();
$query = $db->select('node_field_data', 'n');
$query->join('photos_album', 'a', 'a.album_id = n.nid');
$query->fields('n', ['nid', 'title']);
$query->condition('n.uid', $uid);
$query->orderBy('n.nid', 'DESC');
$result = $query->execute();
$true = FALSE;
foreach ($result as $a) {
$choice = new \stdClass();
$choice->option = [$a->nid => $a->title];
$output[$a->nid] = $choice;
$true = TRUE;
}
if ($current) {
$choice = new \stdClass();
$choice->option = [$current[0] => $current[1]];
$output[$a->nid] = $choice;
}
if (!$true) {
$output = [t('You do not have an album yet.')];
}
return $output;
}
}
