image.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  1. <?php
  2. namespace Elementor;
  3. if ( ! defined( 'ABSPATH' ) ) {
  4. exit; // Exit if accessed directly.
  5. }
  6. /**
  7. * Elementor image widget.
  8. *
  9. * Elementor widget that displays an image into the page.
  10. *
  11. * @since 1.0.0
  12. */
  13. class Widget_Image extends Widget_Base {
  14. /**
  15. * Get widget name.
  16. *
  17. * Retrieve image widget name.
  18. *
  19. * @since 1.0.0
  20. * @access public
  21. *
  22. * @return string Widget name.
  23. */
  24. public function get_name() {
  25. return 'image';
  26. }
  27. /**
  28. * Get widget title.
  29. *
  30. * Retrieve image widget title.
  31. *
  32. * @since 1.0.0
  33. * @access public
  34. *
  35. * @return string Widget title.
  36. */
  37. public function get_title() {
  38. return __( 'Image', 'elementor' );
  39. }
  40. /**
  41. * Get widget icon.
  42. *
  43. * Retrieve image widget icon.
  44. *
  45. * @since 1.0.0
  46. * @access public
  47. *
  48. * @return string Widget icon.
  49. */
  50. public function get_icon() {
  51. return 'eicon-insert-image';
  52. }
  53. /**
  54. * Get widget categories.
  55. *
  56. * Retrieve the list of categories the image widget belongs to.
  57. *
  58. * Used to determine where to display the widget in the editor.
  59. *
  60. * @since 2.0.0
  61. * @access public
  62. *
  63. * @return array Widget categories.
  64. */
  65. public function get_categories() {
  66. return [ 'basic' ];
  67. }
  68. /**
  69. * Get widget keywords.
  70. *
  71. * Retrieve the list of keywords the widget belongs to.
  72. *
  73. * @since 2.1.0
  74. * @access public
  75. *
  76. * @return array Widget keywords.
  77. */
  78. public function get_keywords() {
  79. return [ 'image', 'photo', 'visual' ];
  80. }
  81. /**
  82. * Register image widget controls.
  83. *
  84. * Adds different input fields to allow the user to change and customize the widget settings.
  85. *
  86. * @since 1.0.0
  87. * @access protected
  88. */
  89. protected function _register_controls() {
  90. $this->start_controls_section(
  91. 'section_image',
  92. [
  93. 'label' => __( 'Image', 'elementor' ),
  94. ]
  95. );
  96. $this->add_control(
  97. 'image',
  98. [
  99. 'label' => __( 'Choose Image', 'elementor' ),
  100. 'type' => Controls_Manager::MEDIA,
  101. 'dynamic' => [
  102. 'active' => true,
  103. ],
  104. 'default' => [
  105. 'url' => Utils::get_placeholder_image_src(),
  106. ],
  107. ]
  108. );
  109. $this->add_group_control(
  110. Group_Control_Image_Size::get_type(),
  111. [
  112. 'name' => 'image', // Usage: `{name}_size` and `{name}_custom_dimension`, in this case `image_size` and `image_custom_dimension`.
  113. 'default' => 'large',
  114. 'separator' => 'none',
  115. ]
  116. );
  117. $this->add_responsive_control(
  118. 'align',
  119. [
  120. 'label' => __( 'Alignment', 'elementor' ),
  121. 'type' => Controls_Manager::CHOOSE,
  122. 'options' => [
  123. 'left' => [
  124. 'title' => __( 'Left', 'elementor' ),
  125. 'icon' => 'fa fa-align-left',
  126. ],
  127. 'center' => [
  128. 'title' => __( 'Center', 'elementor' ),
  129. 'icon' => 'fa fa-align-center',
  130. ],
  131. 'right' => [
  132. 'title' => __( 'Right', 'elementor' ),
  133. 'icon' => 'fa fa-align-right',
  134. ],
  135. ],
  136. 'selectors' => [
  137. '{{WRAPPER}}' => 'text-align: {{VALUE}};',
  138. ],
  139. ]
  140. );
  141. $this->add_control(
  142. 'caption',
  143. [
  144. 'label' => __( 'Caption', 'elementor' ),
  145. 'type' => Controls_Manager::TEXT,
  146. 'default' => '',
  147. 'placeholder' => __( 'Enter your image caption', 'elementor' ),
  148. ]
  149. );
  150. $this->add_control(
  151. 'link_to',
  152. [
  153. 'label' => __( 'Link to', 'elementor' ),
  154. 'type' => Controls_Manager::SELECT,
  155. 'default' => 'none',
  156. 'options' => [
  157. 'none' => __( 'None', 'elementor' ),
  158. 'file' => __( 'Media File', 'elementor' ),
  159. 'custom' => __( 'Custom URL', 'elementor' ),
  160. ],
  161. ]
  162. );
  163. $this->add_control(
  164. 'link',
  165. [
  166. 'label' => __( 'Link to', 'elementor' ),
  167. 'type' => Controls_Manager::URL,
  168. 'dynamic' => [
  169. 'active' => true,
  170. ],
  171. 'placeholder' => __( 'https://your-link.com', 'elementor' ),
  172. 'condition' => [
  173. 'link_to' => 'custom',
  174. ],
  175. 'show_label' => false,
  176. ]
  177. );
  178. $this->add_control(
  179. 'open_lightbox',
  180. [
  181. 'label' => __( 'Lightbox', 'elementor' ),
  182. 'type' => Controls_Manager::SELECT,
  183. 'default' => 'default',
  184. 'options' => [
  185. 'default' => __( 'Default', 'elementor' ),
  186. 'yes' => __( 'Yes', 'elementor' ),
  187. 'no' => __( 'No', 'elementor' ),
  188. ],
  189. 'condition' => [
  190. 'link_to' => 'file',
  191. ],
  192. ]
  193. );
  194. $this->add_control(
  195. 'view',
  196. [
  197. 'label' => __( 'View', 'elementor' ),
  198. 'type' => Controls_Manager::HIDDEN,
  199. 'default' => 'traditional',
  200. ]
  201. );
  202. $this->end_controls_section();
  203. $this->start_controls_section(
  204. 'section_style_image',
  205. [
  206. 'label' => __( 'Image', 'elementor' ),
  207. 'tab' => Controls_Manager::TAB_STYLE,
  208. ]
  209. );
  210. $this->add_responsive_control(
  211. 'width',
  212. [
  213. 'label' => __( 'Width', 'elementor' ),
  214. 'type' => Controls_Manager::SLIDER,
  215. 'default' => [
  216. 'unit' => '%',
  217. ],
  218. 'tablet_default' => [
  219. 'unit' => '%',
  220. ],
  221. 'mobile_default' => [
  222. 'unit' => '%',
  223. ],
  224. 'size_units' => [ '%', 'px', 'vw' ],
  225. 'range' => [
  226. '%' => [
  227. 'min' => 1,
  228. 'max' => 100,
  229. ],
  230. 'px' => [
  231. 'min' => 1,
  232. 'max' => 1000,
  233. ],
  234. 'vw' => [
  235. 'min' => 1,
  236. 'max' => 100,
  237. ],
  238. ],
  239. 'selectors' => [
  240. '{{WRAPPER}} .elementor-image img' => 'width: {{SIZE}}{{UNIT}};',
  241. ],
  242. ]
  243. );
  244. $this->add_responsive_control(
  245. 'space',
  246. [
  247. 'label' => __( 'Max Width', 'elementor' ) . ' (%)',
  248. 'type' => Controls_Manager::SLIDER,
  249. 'default' => [
  250. 'unit' => '%',
  251. ],
  252. 'tablet_default' => [
  253. 'unit' => '%',
  254. ],
  255. 'mobile_default' => [
  256. 'unit' => '%',
  257. ],
  258. 'size_units' => [ '%' ],
  259. 'range' => [
  260. '%' => [
  261. 'min' => 1,
  262. 'max' => 100,
  263. ],
  264. ],
  265. 'selectors' => [
  266. '{{WRAPPER}} .elementor-image img' => 'max-width: {{SIZE}}{{UNIT}};',
  267. ],
  268. ]
  269. );
  270. $this->add_control(
  271. 'separator_panel_style',
  272. [
  273. 'type' => Controls_Manager::DIVIDER,
  274. 'style' => 'thick',
  275. ]
  276. );
  277. $this->start_controls_tabs( 'image_effects' );
  278. $this->start_controls_tab( 'normal',
  279. [
  280. 'label' => __( 'Normal', 'elementor' ),
  281. ]
  282. );
  283. $this->add_control(
  284. 'opacity',
  285. [
  286. 'label' => __( 'Opacity', 'elementor' ),
  287. 'type' => Controls_Manager::SLIDER,
  288. 'range' => [
  289. 'px' => [
  290. 'max' => 1,
  291. 'min' => 0.10,
  292. 'step' => 0.01,
  293. ],
  294. ],
  295. 'selectors' => [
  296. '{{WRAPPER}} .elementor-image img' => 'opacity: {{SIZE}};',
  297. ],
  298. ]
  299. );
  300. $this->add_group_control(
  301. Group_Control_Css_Filter::get_type(),
  302. [
  303. 'name' => 'css_filters',
  304. 'selector' => '{{WRAPPER}} .elementor-image img',
  305. ]
  306. );
  307. $this->end_controls_tab();
  308. $this->start_controls_tab( 'hover',
  309. [
  310. 'label' => __( 'Hover', 'elementor' ),
  311. ]
  312. );
  313. $this->add_control(
  314. 'opacity_hover',
  315. [
  316. 'label' => __( 'Opacity', 'elementor' ),
  317. 'type' => Controls_Manager::SLIDER,
  318. 'range' => [
  319. 'px' => [
  320. 'max' => 1,
  321. 'min' => 0.10,
  322. 'step' => 0.01,
  323. ],
  324. ],
  325. 'selectors' => [
  326. '{{WRAPPER}} .elementor-image:hover img' => 'opacity: {{SIZE}};',
  327. ],
  328. ]
  329. );
  330. $this->add_group_control(
  331. Group_Control_Css_Filter::get_type(),
  332. [
  333. 'name' => 'css_filters_hover',
  334. 'selector' => '{{WRAPPER}} .elementor-image:hover img',
  335. ]
  336. );
  337. $this->add_control(
  338. 'background_hover_transition',
  339. [
  340. 'label' => __( 'Transition Duration', 'elementor' ),
  341. 'type' => Controls_Manager::SLIDER,
  342. 'range' => [
  343. 'px' => [
  344. 'max' => 3,
  345. 'step' => 0.1,
  346. ],
  347. ],
  348. 'selectors' => [
  349. '{{WRAPPER}} .elementor-image img' => 'transition-duration: {{SIZE}}s',
  350. ],
  351. ]
  352. );
  353. $this->add_control(
  354. 'hover_animation',
  355. [
  356. 'label' => __( 'Hover Animation', 'elementor' ),
  357. 'type' => Controls_Manager::HOVER_ANIMATION,
  358. ]
  359. );
  360. $this->end_controls_tab();
  361. $this->end_controls_tabs();
  362. $this->add_group_control(
  363. Group_Control_Border::get_type(),
  364. [
  365. 'name' => 'image_border',
  366. 'selector' => '{{WRAPPER}} .elementor-image img',
  367. 'separator' => 'before',
  368. ]
  369. );
  370. $this->add_responsive_control(
  371. 'image_border_radius',
  372. [
  373. 'label' => __( 'Border Radius', 'elementor' ),
  374. 'type' => Controls_Manager::DIMENSIONS,
  375. 'size_units' => [ 'px', '%' ],
  376. 'selectors' => [
  377. '{{WRAPPER}} .elementor-image img' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};',
  378. ],
  379. ]
  380. );
  381. $this->add_group_control(
  382. Group_Control_Box_Shadow::get_type(),
  383. [
  384. 'name' => 'image_box_shadow',
  385. 'exclude' => [
  386. 'box_shadow_position',
  387. ],
  388. 'selector' => '{{WRAPPER}} .elementor-image img',
  389. ]
  390. );
  391. $this->end_controls_section();
  392. $this->start_controls_section(
  393. 'section_style_caption',
  394. [
  395. 'label' => __( 'Caption', 'elementor' ),
  396. 'tab' => Controls_Manager::TAB_STYLE,
  397. 'condition' => [
  398. 'caption!' => '',
  399. ],
  400. ]
  401. );
  402. $this->add_control(
  403. 'caption_align',
  404. [
  405. 'label' => __( 'Alignment', 'elementor' ),
  406. 'type' => Controls_Manager::CHOOSE,
  407. 'options' => [
  408. 'left' => [
  409. 'title' => __( 'Left', 'elementor' ),
  410. 'icon' => 'fa fa-align-left',
  411. ],
  412. 'center' => [
  413. 'title' => __( 'Center', 'elementor' ),
  414. 'icon' => 'fa fa-align-center',
  415. ],
  416. 'right' => [
  417. 'title' => __( 'Right', 'elementor' ),
  418. 'icon' => 'fa fa-align-right',
  419. ],
  420. 'justify' => [
  421. 'title' => __( 'Justified', 'elementor' ),
  422. 'icon' => 'fa fa-align-justify',
  423. ],
  424. ],
  425. 'default' => '',
  426. 'selectors' => [
  427. '{{WRAPPER}} .widget-image-caption' => 'text-align: {{VALUE}};',
  428. ],
  429. ]
  430. );
  431. $this->add_control(
  432. 'text_color',
  433. [
  434. 'label' => __( 'Text Color', 'elementor' ),
  435. 'type' => Controls_Manager::COLOR,
  436. 'default' => '',
  437. 'selectors' => [
  438. '{{WRAPPER}} .widget-image-caption' => 'color: {{VALUE}};',
  439. ],
  440. 'scheme' => [
  441. 'type' => Scheme_Color::get_type(),
  442. 'value' => Scheme_Color::COLOR_3,
  443. ],
  444. ]
  445. );
  446. $this->add_group_control(
  447. Group_Control_Typography::get_type(),
  448. [
  449. 'name' => 'caption_typography',
  450. 'selector' => '{{WRAPPER}} .widget-image-caption',
  451. 'scheme' => Scheme_Typography::TYPOGRAPHY_3,
  452. ]
  453. );
  454. $this->add_responsive_control(
  455. 'caption_space',
  456. [
  457. 'label' => __( 'Spacing', 'elementor' ),
  458. 'type' => Controls_Manager::SLIDER,
  459. 'range' => [
  460. 'px' => [
  461. 'min' => 0,
  462. 'max' => 100,
  463. ],
  464. ],
  465. 'selectors' => [
  466. '{{WRAPPER}} .widget-image-caption' => 'margin-top: {{SIZE}}{{UNIT}};',
  467. ],
  468. ]
  469. );
  470. $this->end_controls_section();
  471. }
  472. /**
  473. * Render image widget output on the frontend.
  474. *
  475. * Written in PHP and used to generate the final HTML.
  476. *
  477. * @since 1.0.0
  478. * @access protected
  479. */
  480. protected function render() {
  481. $settings = $this->get_settings_for_display();
  482. if ( empty( $settings['image']['url'] ) ) {
  483. return;
  484. }
  485. $has_caption = ! empty( $settings['caption'] );
  486. $this->add_render_attribute( 'wrapper', 'class', 'elementor-image' );
  487. if ( ! empty( $settings['shape'] ) ) {
  488. $this->add_render_attribute( 'wrapper', 'class', 'elementor-image-shape-' . $settings['shape'] );
  489. }
  490. $link = $this->get_link_url( $settings );
  491. if ( $link ) {
  492. $this->add_render_attribute( 'link', [
  493. 'href' => $link['url'],
  494. 'data-elementor-open-lightbox' => $settings['open_lightbox'],
  495. ] );
  496. if ( Plugin::$instance->editor->is_edit_mode() ) {
  497. $this->add_render_attribute( 'link', [
  498. 'class' => 'elementor-clickable',
  499. ] );
  500. }
  501. if ( ! empty( $link['is_external'] ) ) {
  502. $this->add_render_attribute( 'link', 'target', '_blank' );
  503. }
  504. if ( ! empty( $link['nofollow'] ) ) {
  505. $this->add_render_attribute( 'link', 'rel', 'nofollow' );
  506. }
  507. } ?>
  508. <div <?php echo $this->get_render_attribute_string( 'wrapper' ); ?>>
  509. <?php if ( $has_caption ) : ?>
  510. <figure class="wp-caption">
  511. <?php endif; ?>
  512. <?php if ( $link ) : ?>
  513. <a <?php echo $this->get_render_attribute_string( 'link' ); ?>>
  514. <?php endif; ?>
  515. <?php echo Group_Control_Image_Size::get_attachment_image_html( $settings ); ?>
  516. <?php if ( $link ) : ?>
  517. </a>
  518. <?php endif; ?>
  519. <?php if ( $has_caption ) : ?>
  520. <figcaption class="widget-image-caption wp-caption-text"><?php echo $settings['caption']; ?></figcaption>
  521. <?php endif; ?>
  522. <?php if ( $has_caption ) : ?>
  523. </figure>
  524. <?php endif; ?>
  525. </div>
  526. <?php
  527. }
  528. /**
  529. * Render image widget output in the editor.
  530. *
  531. * Written as a Backbone JavaScript template and used to generate the live preview.
  532. *
  533. * @since 1.0.0
  534. * @access protected
  535. */
  536. protected function _content_template() {
  537. ?>
  538. <# if ( settings.image.url ) {
  539. var image = {
  540. id: settings.image.id,
  541. url: settings.image.url,
  542. size: settings.image_size,
  543. dimension: settings.image_custom_dimension,
  544. model: view.getEditModel()
  545. };
  546. var image_url = elementor.imagesManager.getImageUrl( image );
  547. if ( ! image_url ) {
  548. return;
  549. }
  550. var link_url;
  551. if ( 'custom' === settings.link_to ) {
  552. link_url = settings.link.url;
  553. }
  554. if ( 'file' === settings.link_to ) {
  555. link_url = settings.image.url;
  556. }
  557. #><div class="elementor-image{{ settings.shape ? ' elementor-image-shape-' + settings.shape : '' }}"><#
  558. var imgClass = '',
  559. hasCaption = '' !== settings.caption;
  560. if ( '' !== settings.hover_animation ) {
  561. imgClass = 'elementor-animation-' + settings.hover_animation;
  562. }
  563. if ( hasCaption ) {
  564. #><figure class="wp-caption"><#
  565. }
  566. if ( link_url ) {
  567. #><a class="elementor-clickable" data-elementor-open-lightbox="{{ settings.open_lightbox }}" href="{{ link_url }}"><#
  568. }
  569. #><img src="{{ image_url }}" class="{{ imgClass }}" /><#
  570. if ( link_url ) {
  571. #></a><#
  572. }
  573. if ( hasCaption ) {
  574. #><figcaption class="widget-image-caption wp-caption-text">{{{ settings.caption }}}</figcaption><#
  575. }
  576. if ( hasCaption ) {
  577. #></figure><#
  578. }
  579. #></div><#
  580. } #>
  581. <?php
  582. }
  583. /**
  584. * Retrieve image widget link URL.
  585. *
  586. * @since 1.0.0
  587. * @access private
  588. *
  589. * @param array $settings
  590. *
  591. * @return array|string|false An array/string containing the link URL, or false if no link.
  592. */
  593. private function get_link_url( $settings ) {
  594. if ( 'none' === $settings['link_to'] ) {
  595. return false;
  596. }
  597. if ( 'custom' === $settings['link_to'] ) {
  598. if ( empty( $settings['link']['url'] ) ) {
  599. return false;
  600. }
  601. return $settings['link'];
  602. }
  603. return [
  604. 'url' => $settings['image']['url'],
  605. ];
  606. }
  607. }