ColorboxFormatter.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. <?php
  2. namespace Drupal\colorbox\Plugin\Field\FieldFormatter;
  3. use Drupal\Core\Entity\EntityStorageInterface;
  4. use Drupal\Core\Field\FieldItemListInterface;
  5. use Drupal\Core\Field\FieldDefinitionInterface;
  6. use Drupal\Core\Link;
  7. use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
  8. use Drupal\Core\Session\AccountInterface;
  9. use Drupal\Core\Url;
  10. use Drupal\image\Entity\ImageStyle;
  11. use Symfony\Component\DependencyInjection\ContainerInterface;
  12. use Drupal\Core\Form\FormStateInterface;
  13. use Drupal\Core\Cache\Cache;
  14. use Drupal\colorbox\ElementAttachmentInterface;
  15. use Drupal\image\Plugin\Field\FieldFormatter\ImageFormatterBase;
  16. /**
  17. * Plugin implementation of the 'colorbox' formatter.
  18. *
  19. * @FieldFormatter(
  20. * id = "colorbox",
  21. * module = "colorbox",
  22. * label = @Translation("Colorbox"),
  23. * field_types = {
  24. * "image"
  25. * }
  26. * )
  27. */
  28. class ColorboxFormatter extends ImageFormatterBase implements ContainerFactoryPluginInterface {
  29. /**
  30. * The current user.
  31. *
  32. * @var \Drupal\Core\Session\AccountInterface
  33. */
  34. protected $currentUser;
  35. /**
  36. * The image style entity storage.
  37. *
  38. * @var \Drupal\image\ImageStyleStorageInterface
  39. */
  40. protected $imageStyleStorage;
  41. /**
  42. * Constructs an ImageFormatter object.
  43. *
  44. * @param string $plugin_id
  45. * The plugin_id for the formatter.
  46. * @param mixed $plugin_definition
  47. * The plugin implementation definition.
  48. * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
  49. * The definition of the field to which the formatter is associated.
  50. * @param array $settings
  51. * The formatter settings.
  52. * @param string $label
  53. * The formatter label display setting.
  54. * @param string $view_mode
  55. * The view mode.
  56. * @param array $third_party_settings
  57. * Any third party settings settings.
  58. * @param \Drupal\Core\Session\AccountInterface $current_user
  59. * The current user.
  60. * @param \Drupal\colorbox\ElementAttachmentInterface $attachment
  61. * Allow the library to be attached to the page.
  62. */
  63. public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, AccountInterface $current_user, EntityStorageInterface $image_style_storage, ElementAttachmentInterface $attachment) {
  64. parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
  65. $this->currentUser = $current_user;
  66. $this->imageStyleStorage = $image_style_storage;
  67. $this->attachment = $attachment;
  68. }
  69. /**
  70. * {@inheritdoc}
  71. */
  72. public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
  73. return new static(
  74. $plugin_id,
  75. $plugin_definition,
  76. $configuration['field_definition'],
  77. $configuration['settings'],
  78. $configuration['label'],
  79. $configuration['view_mode'],
  80. $configuration['third_party_settings'],
  81. $container->get('current_user'),
  82. $container->get('entity.manager')->getStorage('image_style'),
  83. $container->get('colorbox.attachment')
  84. );
  85. }
  86. /**
  87. * {@inheritdoc}
  88. */
  89. public static function defaultSettings() {
  90. return [
  91. 'colorbox_node_style' => '',
  92. 'colorbox_node_style_first' => '',
  93. 'colorbox_image_style' => '',
  94. 'colorbox_gallery' => 'post',
  95. 'colorbox_gallery_custom' => '',
  96. 'colorbox_caption' => 'auto',
  97. 'colorbox_caption_custom' => '',
  98. ] + parent::defaultSettings();
  99. }
  100. /**
  101. * {@inheritdoc}
  102. */
  103. public function settingsForm(array $form, FormStateInterface $form_state) {
  104. $image_styles = image_style_options(FALSE);
  105. $image_styles_hide = $image_styles;
  106. $image_styles_hide['hide'] = $this->t('Hide (do not display image)');
  107. $description_link = Link::fromTextAndUrl(
  108. $this->t('Configure Image Styles'),
  109. Url::fromRoute('entity.image_style.collection')
  110. );
  111. $element['colorbox_node_style'] = [
  112. '#title' => $this->t('Image style for content'),
  113. '#type' => 'select',
  114. '#default_value' => $this->getSetting('colorbox_node_style'),
  115. '#empty_option' => $this->t('None (original image)'),
  116. '#options' => $image_styles_hide,
  117. '#description' => $description_link->toRenderable() + [
  118. '#access' => $this->currentUser->hasPermission('administer image styles'),
  119. ],
  120. ];
  121. $element['colorbox_node_style_first'] = [
  122. '#title' => $this->t('Image style for first image in content'),
  123. '#type' => 'select',
  124. '#default_value' => $this->getSetting('colorbox_node_style_first'),
  125. '#empty_option' => $this->t('No special style.'),
  126. '#options' => $image_styles,
  127. '#description' => $description_link->toRenderable() + [
  128. '#access' => $this->currentUser->hasPermission('administer image styles'),
  129. ],
  130. ];
  131. $element['colorbox_image_style'] = [
  132. '#title' => $this->t('Image style for Colorbox'),
  133. '#type' => 'select',
  134. '#default_value' => $this->getSetting('colorbox_image_style'),
  135. '#empty_option' => $this->t('None (original image)'),
  136. '#options' => $image_styles,
  137. '#description' => $description_link->toRenderable() + [
  138. '#access' => $this->currentUser->hasPermission('administer image styles'),
  139. ],
  140. ];
  141. $gallery = [
  142. 'post' => $this->t('Per post gallery'),
  143. 'page' => $this->t('Per page gallery'),
  144. 'field_post' => $this->t('Per field in post gallery'),
  145. 'field_page' => $this->t('Per field in page gallery'),
  146. 'custom' => $this->t('Custom (with tokens)'),
  147. 'none' => $this->t('No gallery'),
  148. ];
  149. $element['colorbox_gallery'] = [
  150. '#title' => $this->t('Gallery (image grouping)'),
  151. '#type' => 'select',
  152. '#default_value' => $this->getSetting('colorbox_gallery'),
  153. '#options' => $gallery,
  154. '#description' => $this->t('How Colorbox should group the image galleries.'),
  155. ];
  156. $element['colorbox_gallery_custom'] = [
  157. '#title' => $this->t('Custom gallery'),
  158. '#type' => 'textfield',
  159. '#default_value' => $this->getSetting('colorbox_gallery_custom'),
  160. '#description' => $this->t('All images on a page with the same gallery value (rel attribute) will be grouped together. It must only contain lowercase letters, numbers, and underscores.'),
  161. '#required' => FALSE,
  162. '#states' => [
  163. 'visible' => [
  164. ':input[name$="[settings_edit_form][settings][colorbox_gallery]"]' => ['value' => 'custom'],
  165. ],
  166. ],
  167. ];
  168. if (\Drupal::moduleHandler()->moduleExists('token')) {
  169. $element['colorbox_token_gallery'] = [
  170. '#type' => 'fieldset',
  171. '#title' => t('Replacement patterns'),
  172. '#theme' => 'token_tree_link',
  173. '#token_types' => [$form['#entity_type'], 'file'],
  174. '#states' => [
  175. 'visible' => [
  176. ':input[name$="[settings_edit_form][settings][colorbox_gallery]"]' => ['value' => 'custom'],
  177. ],
  178. ],
  179. ];
  180. }
  181. else {
  182. $element['colorbox_token_gallery'] = [
  183. '#type' => 'fieldset',
  184. '#title' => $this->t('Replacement patterns'),
  185. '#description' => '<strong class="error">' . $this->t('For token support the <a href="@token_url">token module</a> must be installed.', ['@token_url' => 'http://drupal.org/project/token']) . '</strong>',
  186. '#states' => [
  187. 'visible' => [
  188. ':input[name$="[settings_edit_form][settings][colorbox_gallery]"]' => ['value' => 'custom'],
  189. ],
  190. ],
  191. ];
  192. }
  193. $caption = [
  194. 'auto' => $this->t('Automatic'),
  195. 'title' => $this->t('Title text'),
  196. 'alt' => $this->t('Alt text'),
  197. 'entity_title' => $this->t('Content title'),
  198. 'custom' => $this->t('Custom (with tokens)'),
  199. 'none' => $this->t('None'),
  200. ];
  201. $element['colorbox_caption'] = [
  202. '#title' => $this->t('Caption'),
  203. '#type' => 'select',
  204. '#default_value' => $this->getSetting('colorbox_caption'),
  205. '#options' => $caption,
  206. '#description' => $this->t('Automatic will use the first non-empty value out of the title, the alt text and the content title.'),
  207. ];
  208. $element['colorbox_caption_custom'] = [
  209. '#title' => $this->t('Custom caption'),
  210. '#type' => 'textfield',
  211. '#default_value' => $this->getSetting('colorbox_caption_custom'),
  212. '#states' => [
  213. 'visible' => [
  214. ':input[name$="[settings_edit_form][settings][colorbox_caption]"]' => ['value' => 'custom'],
  215. ],
  216. ],
  217. ];
  218. if (\Drupal::moduleHandler()->moduleExists('token')) {
  219. $element['colorbox_token_caption'] = [
  220. '#type' => 'fieldset',
  221. '#title' => t('Replacement patterns'),
  222. '#theme' => 'token_tree_link',
  223. '#token_types' => [$form['#entity_type'], 'file'],
  224. '#states' => [
  225. 'visible' => [
  226. ':input[name$="[settings_edit_form][settings][colorbox_caption]"]' => ['value' => 'custom'],
  227. ],
  228. ],
  229. ];
  230. }
  231. else {
  232. $element['colorbox_token_caption'] = [
  233. '#type' => 'fieldset',
  234. '#title' => $this->t('Replacement patterns'),
  235. '#description' => '<strong class="error">' . $this->t('For token support the <a href="@token_url">token module</a> must be installed.', ['@token_url' => 'http://drupal.org/project/token']) . '</strong>',
  236. '#states' => [
  237. 'visible' => [
  238. ':input[name$="[settings_edit_form][settings][colorbox_caption]"]' => ['value' => 'custom'],
  239. ],
  240. ],
  241. ];
  242. }
  243. return $element;
  244. }
  245. /**
  246. * {@inheritdoc}
  247. */
  248. public function settingsSummary() {
  249. $summary = [];
  250. $image_styles = image_style_options(FALSE);
  251. // Unset possible 'No defined styles' option.
  252. unset($image_styles['']);
  253. // Styles could be lost because of enabled/disabled modules that defines
  254. // their styles in code.
  255. if (isset($image_styles[$this->getSetting('colorbox_node_style')])) {
  256. $summary[] = $this->t('Content image style: @style', ['@style' => $image_styles[$this->getSetting('colorbox_node_style')]]);
  257. }
  258. elseif ($this->getSetting('colorbox_node_style') == 'hide') {
  259. $summary[] = $this->t('Content image style: Hide');
  260. }
  261. else {
  262. $summary[] = $this->t('Content image style: Original image');
  263. }
  264. if (isset($image_styles[$this->getSetting('colorbox_node_style_first')])) {
  265. $summary[] = $this->t('Content image style of first image: @style', ['@style' => $image_styles[$this->getSetting('colorbox_node_style_first')]]);
  266. }
  267. if (isset($image_styles[$this->getSetting('colorbox_image_style')])) {
  268. $summary[] = $this->t('Colorbox image style: @style', ['@style' => $image_styles[$this->getSetting('colorbox_image_style')]]);
  269. }
  270. else {
  271. $summary[] = $this->t('Colorbox image style: Original image');
  272. }
  273. $gallery = [
  274. 'post' => $this->t('Per post gallery'),
  275. 'page' => $this->t('Per page gallery'),
  276. 'field_post' => $this->t('Per field in post gallery'),
  277. 'field_page' => $this->t('Per field in page gallery'),
  278. 'custom' => $this->t('Custom (with tokens)'),
  279. 'none' => $this->t('No gallery'),
  280. ];
  281. if ($this->getSetting('colorbox_gallery')) {
  282. $summary[] = $this->t('Colorbox gallery type: @type', ['@type' => $gallery[$this->getSetting('colorbox_gallery')]]) . ($this->getSetting('colorbox_gallery') == 'custom' ? ' (' . $this->getSetting('colorbox_gallery_custom') . ')' : '');
  283. }
  284. $caption = [
  285. 'auto' => $this->t('Automatic'),
  286. 'title' => $this->t('Title text'),
  287. 'alt' => $this->t('Alt text'),
  288. 'entity_title' => $this->t('Content title'),
  289. 'custom' => $this->t('Custom (with tokens)'),
  290. 'none' => $this->t('None'),
  291. ];
  292. if ($this->getSetting('colorbox_caption')) {
  293. $summary[] = $this->t('Colorbox caption: @type', ['@type' => $caption[$this->getSetting('colorbox_caption')]]);
  294. }
  295. return $summary;
  296. }
  297. /**
  298. * {@inheritdoc}
  299. */
  300. public function viewElements(FieldItemListInterface $items, $langcode) {
  301. $elements = [];
  302. $settings = $this->getSettings();
  303. $files = $this->getEntitiesToView($items, $langcode);
  304. // Early opt-out if the field is empty.
  305. if (empty($files)) {
  306. return $elements;
  307. }
  308. // Collect cache tags to be added for each item in the field.
  309. $cache_tags = [];
  310. if (!empty($settings['colorbox_node_style']) && $settings['colorbox_node_style'] != 'hide') {
  311. $image_style = $this->imageStyleStorage->load($settings['colorbox_node_style']);
  312. $cache_tags = $image_style->getCacheTags();
  313. }
  314. $cache_tags_first = [];
  315. if (!empty($settings['colorbox_node_style_first'])) {
  316. $image_style_first = $this->imageStyleStorage->load($settings['colorbox_node_style_first']);
  317. $cache_tags_first = $image_style_first->getCacheTags();
  318. }
  319. foreach ($files as $delta => $file) {
  320. // Check if first image should have separate image style.
  321. if ($delta == 0 && !empty($settings['colorbox_node_style_first'])) {
  322. $settings['style_first'] = TRUE;
  323. $settings['style_name'] = $settings['colorbox_node_style_first'];
  324. $cache_tags = Cache::mergeTags($cache_tags_first, $file->getCacheTags());
  325. }
  326. else {
  327. $settings['style_first'] = FALSE;
  328. $settings['style_name'] = $settings['colorbox_node_style'];
  329. $cache_tags = Cache::mergeTags($cache_tags, $file->getCacheTags());
  330. }
  331. // Extract field item attributes for the theme function, and unset them
  332. // from the $item so that the field template does not re-render them.
  333. $item = $file->_referringItem;
  334. $item_attributes = $item->_attributes;
  335. unset($item->_attributes);
  336. $elements[$delta] = [
  337. '#theme' => 'colorbox_formatter',
  338. '#item' => $item,
  339. '#item_attributes' => $item_attributes,
  340. '#entity' => $items->getEntity(),
  341. '#settings' => $settings,
  342. '#cache' => [
  343. 'tags' => $cache_tags,
  344. ],
  345. ];
  346. }
  347. // Attach the Colorbox JS and CSS.
  348. if ($this->attachment->isApplicable()) {
  349. $this->attachment->attach($elements);
  350. }
  351. return $elements;
  352. }
  353. /**
  354. * {@inheritdoc}
  355. */
  356. public function calculateDependencies() {
  357. $dependencies = parent::calculateDependencies();
  358. $style_ids = [];
  359. $style_ids[] = $this->getSetting('colorbox_node_style');
  360. if (!empty($this->getSetting('colorbox_node_style_first'))) {
  361. $style_ids[] = $this->getSetting('colorbox_node_style_first');
  362. }
  363. $style_ids[] = $this->getSetting('colorbox_image_style');
  364. /** @var \Drupal\image\ImageStyleInterface $style */
  365. foreach ($style_ids as $style_id) {
  366. if ($style_id && $style = ImageStyle::load($style_id)) {
  367. // If this formatter uses a valid image style to display the image, add
  368. // the image style configuration entity as dependency of this formatter.
  369. $dependencies[$style->getConfigDependencyKey()][] = $style->getConfigDependencyName();
  370. }
  371. }
  372. return $dependencies;
  373. }
  374. /**
  375. * {@inheritdoc}
  376. */
  377. public function onDependencyRemoval(array $dependencies) {
  378. $changed = parent::onDependencyRemoval($dependencies);
  379. $style_ids = [];
  380. $style_ids['colorbox_node_style'] = $this->getSetting('colorbox_node_style');
  381. if (!empty($this->getSetting('colorbox_node_style_first'))) {
  382. $style_ids['colorbox_node_style_first'] = $this->getSetting('colorbox_node_style_first');
  383. }
  384. $style_ids['colorbox_image_style'] = $this->getSetting('colorbox_image_style');
  385. /** @var \Drupal\image\ImageStyleInterface $style */
  386. foreach ($style_ids as $name => $style_id) {
  387. if ($style_id && $style = ImageStyle::load($style_id)) {
  388. if (!empty($dependencies[$style->getConfigDependencyKey()][$style->getConfigDependencyName()])) {
  389. $replacement_id = $this->imageStyleStorage->getReplacementId($style_id);
  390. // If a valid replacement has been provided in the storage,
  391. // replace the image style with the replacement and signal
  392. // that the formatter plugin.
  393. // Settings were updated.
  394. if ($replacement_id && ImageStyle::load($replacement_id)) {
  395. $this->setSetting($name, $replacement_id);
  396. $changed = TRUE;
  397. }
  398. }
  399. }
  400. }
  401. return $changed;
  402. }
  403. }