PluginProbe
Elementor Website Builder – more than just a page builder / 3.31.4
Elementor Website Builder – more than just a page builder v3.31.4
4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 4.0.7 All 451 releases
elementor / includes / widgets / heading.php

heading.php in Elementor Website Builder – more than just a page builder 3.31.4, at includes/widgets/heading.php

539 lines 13.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace Elementor;
3
4 if ( ! defined( 'ABSPATH' ) ) {
5 exit; // Exit if accessed directly.
6 }
7
8 use Elementor\Core\Kits\Documents\Tabs\Global_Colors;
9 use Elementor\Core\Kits\Documents\Tabs\Global_Typography;
10 use Elementor\Modules\ContentSanitizer\Interfaces\Sanitizable;
11 use Elementor\Core\Utils\Hints;
12 use Elementor\Core\Admin\Admin_Notices;
13 use Elementor\Modules\Promotions\Controls\Promotion_Control;
14
15 /**
16 * Elementor heading widget.
17 *
18 * Elementor widget that displays an eye-catching headlines.
19 *
20 * @since 1.0.0
21 */
22 class Widget_Heading extends Widget_Base implements Sanitizable {
23
24 /**
25 * Get widget name.
26 *
27 * Retrieve heading widget name.
28 *
29 * @since 1.0.0
30 * @access public
31 *
32 * @return string Widget name.
33 */
34 public function get_name() {
35 return 'heading';
36 }
37
38 /**
39 * Get widget title.
40 *
41 * Retrieve heading widget title.
42 *
43 * @since 1.0.0
44 * @access public
45 *
46 * @return string Widget title.
47 */
48 public function get_title() {
49 return esc_html__( 'Heading', 'elementor' );
50 }
51
52 /**
53 * Get widget icon.
54 *
55 * Retrieve heading widget icon.
56 *
57 * @since 1.0.0
58 * @access public
59 *
60 * @return string Widget icon.
61 */
62 public function get_icon() {
63 return 'eicon-t-letter';
64 }
65
66 /**
67 * Get widget categories.
68 *
69 * Retrieve the list of categories the heading widget belongs to.
70 *
71 * Used to determine where to display the widget in the editor.
72 *
73 * @since 2.0.0
74 * @access public
75 *
76 * @return array Widget categories.
77 */
78 public function get_categories() {
79 return [ 'basic' ];
80 }
81
82 /**
83 * Get widget keywords.
84 *
85 * Retrieve the list of keywords the widget belongs to.
86 *
87 * @since 2.1.0
88 * @access public
89 *
90 * @return array Widget keywords.
91 */
92 public function get_keywords() {
93 return [ 'heading', 'title', 'text' ];
94 }
95
96 protected function is_dynamic_content(): bool {
97 return false;
98 }
99
100 /**
101 * Get style dependencies.
102 *
103 * Retrieve the list of style dependencies the widget requires.
104 *
105 * @since 3.24.0
106 * @access public
107 *
108 * @return array Widget style dependencies.
109 */
110 public function get_style_depends(): array {
111 return [ 'widget-heading' ];
112 }
113
114 public function has_widget_inner_wrapper(): bool {
115 return ! Plugin::$instance->experiments->is_feature_active( 'e_optimized_markup' );
116 }
117
118 /**
119 * Remove data attributes from the html.
120 *
121 * @param string $content Heading title.
122 * @return string
123 */
124 public function sanitize( $content ): string {
125 $allowed_tags = wp_kses_allowed_html( 'post' );
126 $allowed_tags_for_heading = [];
127 $non_allowed_tags = [ 'img' ];
128
129 foreach ( $allowed_tags as $tag => $attributes ) {
130 if ( in_array( $tag, $non_allowed_tags, true ) ) {
131 continue;
132 }
133
134 $filtered_attributes = array_filter( $attributes, function( $attribute ) {
135 return ! substr( $attribute, 0, 5 ) === 'data-';
136 }, ARRAY_FILTER_USE_KEY );
137
138 $allowed_tags_for_heading[ $tag ] = $filtered_attributes;
139 }
140
141 return wp_kses( $content, $allowed_tags_for_heading );
142 }
143
144 /**
145 * Get widget upsale data.
146 *
147 * Retrieve the widget promotion data.
148 *
149 * @since 3.18.0
150 * @access protected
151 *
152 * @return array Widget promotion data.
153 */
154 protected function get_upsale_data() {
155 return [
156 'condition' => ! Utils::has_pro(),
157 'image' => esc_url( ELEMENTOR_ASSETS_URL . 'images/go-pro.svg' ),
158 'image_alt' => esc_attr__( 'Upgrade', 'elementor' ),
159 'description' => esc_html__( 'Create captivating headings that rotate with the Animated Headline Widget.', 'elementor' ),
160 'upgrade_url' => esc_url( 'https://go.elementor.com/go-pro-heading-widget/' ),
161 'upgrade_text' => esc_html__( 'Upgrade Now', 'elementor' ),
162 ];
163 }
164
165 /**
166 * Register heading widget controls.
167 *
168 * Adds different input fields to allow the user to change and customize the widget settings.
169 *
170 * @since 3.1.0
171 * @access protected
172 */
173 protected function register_controls() {
174 $this->start_controls_section(
175 'section_title',
176 [
177 'label' => esc_html__( 'Heading', 'elementor' ),
178 ]
179 );
180
181 $this->add_control(
182 'title',
183 [
184 'label' => esc_html__( 'Title', 'elementor' ),
185 'type' => Controls_Manager::TEXTAREA,
186 'ai' => [
187 'type' => 'text',
188 ],
189 'dynamic' => [
190 'active' => true,
191 ],
192 'placeholder' => esc_html__( 'Enter your title', 'elementor' ),
193 'default' => esc_html__( 'Add Your Heading Text Here', 'elementor' ),
194 ]
195 );
196
197 $this->add_control(
198 'link',
199 [
200 'label' => esc_html__( 'Link', 'elementor' ),
201 'type' => Controls_Manager::URL,
202 'dynamic' => [
203 'active' => true,
204 ],
205 'default' => [
206 'url' => '',
207 ],
208 ]
209 );
210
211 $this->add_control(
212 'size',
213 [
214 'label' => esc_html__( 'Size', 'elementor' ),
215 'type' => Controls_Manager::SELECT,
216 'options' => [
217 'default' => esc_html__( 'Default', 'elementor' ),
218 'small' => esc_html__( 'Small', 'elementor' ),
219 'medium' => esc_html__( 'Medium', 'elementor' ),
220 'large' => esc_html__( 'Large', 'elementor' ),
221 'xl' => esc_html__( 'XL', 'elementor' ),
222 'xxl' => esc_html__( 'XXL', 'elementor' ),
223 ],
224 'default' => 'default',
225 'condition' => [
226 'size!' => 'default', // a workaround to hide the control, unless it's in use (not default).
227 ],
228 ]
229 );
230
231 $this->add_control(
232 'header_size',
233 [
234 'label' => esc_html__( 'HTML Tag', 'elementor' ),
235 'type' => Controls_Manager::SELECT,
236 'options' => [
237 'h1' => 'H1',
238 'h2' => 'H2',
239 'h3' => 'H3',
240 'h4' => 'H4',
241 'h5' => 'H5',
242 'h6' => 'H6',
243 'div' => 'div',
244 'span' => 'span',
245 'p' => 'p',
246 ],
247 'default' => 'h2',
248 ]
249 );
250
251 $this->maybe_add_ally_heading_hint();
252
253 $this->end_controls_section();
254
255 $this->start_controls_section(
256 'section_title_style',
257 [
258 'label' => esc_html__( 'Heading', 'elementor' ),
259 'tab' => Controls_Manager::TAB_STYLE,
260 ]
261 );
262
263 $this->add_responsive_control(
264 'align',
265 [
266 'label' => esc_html__( 'Alignment', 'elementor' ),
267 'type' => Controls_Manager::CHOOSE,
268 'options' => [
269 'left' => [
270 'title' => esc_html__( 'Left', 'elementor' ),
271 'icon' => 'eicon-text-align-left',
272 ],
273 'center' => [
274 'title' => esc_html__( 'Center', 'elementor' ),
275 'icon' => 'eicon-text-align-center',
276 ],
277 'right' => [
278 'title' => esc_html__( 'Right', 'elementor' ),
279 'icon' => 'eicon-text-align-right',
280 ],
281 'justify' => [
282 'title' => esc_html__( 'Justified', 'elementor' ),
283 'icon' => 'eicon-text-align-justify',
284 ],
285 ],
286 'default' => '',
287 'selectors' => [
288 '{{WRAPPER}}' => 'text-align: {{VALUE}};',
289 ],
290 'separator' => 'after',
291 ]
292 );
293
294 $this->add_group_control(
295 Group_Control_Typography::get_type(),
296 [
297 'name' => 'typography',
298 'global' => [
299 'default' => Global_Typography::TYPOGRAPHY_PRIMARY,
300 ],
301 'selector' => '{{WRAPPER}} .elementor-heading-title',
302 ]
303 );
304
305 $this->add_group_control(
306 Group_Control_Text_Stroke::get_type(),
307 [
308 'name' => 'text_stroke',
309 'selector' => '{{WRAPPER}} .elementor-heading-title',
310 ]
311 );
312
313 $this->add_group_control(
314 Group_Control_Text_Shadow::get_type(),
315 [
316 'name' => 'text_shadow',
317 'selector' => '{{WRAPPER}} .elementor-heading-title',
318 ]
319 );
320
321 $this->add_control(
322 'blend_mode',
323 [
324 'label' => esc_html__( 'Blend Mode', 'elementor' ),
325 'type' => Controls_Manager::SELECT,
326 'options' => [
327 '' => esc_html__( 'Normal', 'elementor' ),
328 'multiply' => esc_html__( 'Multiply', 'elementor' ),
329 'screen' => esc_html__( 'Screen', 'elementor' ),
330 'overlay' => esc_html__( 'Overlay', 'elementor' ),
331 'darken' => esc_html__( 'Darken', 'elementor' ),
332 'lighten' => esc_html__( 'Lighten', 'elementor' ),
333 'color-dodge' => esc_html__( 'Color Dodge', 'elementor' ),
334 'saturation' => esc_html__( 'Saturation', 'elementor' ),
335 'color' => esc_html__( 'Color', 'elementor' ),
336 'difference' => esc_html__( 'Difference', 'elementor' ),
337 'exclusion' => esc_html__( 'Exclusion', 'elementor' ),
338 'hue' => esc_html__( 'Hue', 'elementor' ),
339 'luminosity' => esc_html__( 'Luminosity', 'elementor' ),
340 ],
341 'selectors' => [
342 '{{WRAPPER}} .elementor-heading-title' => 'mix-blend-mode: {{VALUE}}',
343 ],
344 ]
345 );
346
347 $this->add_control(
348 'separator',
349 [
350 'type' => Controls_Manager::DIVIDER,
351 ]
352 );
353
354 $this->start_controls_tabs( 'title_colors' );
355
356 $this->start_controls_tab(
357 'title_colors_normal',
358 [
359 'label' => esc_html__( 'Normal', 'elementor' ),
360 ]
361 );
362
363 $this->add_control(
364 'title_color',
365 [
366 'label' => esc_html__( 'Text Color', 'elementor' ),
367 'type' => Controls_Manager::COLOR,
368 'global' => [
369 'default' => Global_Colors::COLOR_PRIMARY,
370 ],
371 'selectors' => [
372 '{{WRAPPER}} .elementor-heading-title' => 'color: {{VALUE}};',
373 ],
374 ]
375 );
376
377 $this->end_controls_tab();
378
379 $this->start_controls_tab(
380 'title_colors_hover',
381 [
382 'label' => esc_html__( 'Hover', 'elementor' ),
383 ]
384 );
385
386 $this->add_control(
387 'title_hover_color',
388 [
389 'label' => esc_html__( 'Link Color', 'elementor' ),
390 'type' => Controls_Manager::COLOR,
391 'selectors' => [
392 '{{WRAPPER}} .elementor-heading-title a:hover, {{WRAPPER}} .elementor-heading-title a:focus' => 'color: {{VALUE}};',
393 ],
394 ]
395 );
396
397 $this->add_control(
398 'title_hover_color_transition_duration',
399 [
400 'label' => esc_html__( 'Transition Duration', 'elementor' ),
401 'type' => Controls_Manager::SLIDER,
402 'size_units' => [ 's', 'ms', 'custom' ],
403 'default' => [
404 'unit' => 's',
405 ],
406 'selectors' => [
407 '{{WRAPPER}} .elementor-heading-title a' => 'transition-duration: {{SIZE}}{{UNIT}};',
408 ],
409 ]
410 );
411
412 $this->end_controls_tab();
413
414 $this->end_controls_tabs();
415
416 $this->end_controls_section();
417 }
418
419 /**
420 * Render heading widget output on the frontend.
421 *
422 * Written in PHP and used to generate the final HTML.
423 *
424 * @since 1.0.0
425 * @access protected
426 */
427 protected function render() {
428 $settings = $this->get_settings_for_display();
429
430 if ( '' === $settings['title'] ) {
431 return;
432 }
433
434 $this->add_render_attribute( 'title', 'class', 'elementor-heading-title' );
435
436 if ( ! empty( $settings['size'] ) ) {
437 $this->add_render_attribute( 'title', 'class', 'elementor-size-' . $settings['size'] );
438 } else {
439 $this->add_render_attribute( 'title', 'class', 'elementor-size-default' );
440 }
441
442 $this->add_inline_editing_attributes( 'title' );
443
444 $title = wp_kses_post( $settings['title'] );
445
446 if ( ! empty( $settings['link']['url'] ) ) {
447 $this->add_link_attributes( 'url', $settings['link'] );
448
449 $title = sprintf( '<a %1$s>%2$s</a>', $this->get_render_attribute_string( 'url' ), $title );
450 }
451
452 $title_html = sprintf( '<%1$s %2$s>%3$s</%1$s>', Utils::validate_html_tag( $settings['header_size'] ), $this->get_render_attribute_string( 'title' ), $title );
453
454 // PHPCS - the variable $title_html holds safe data.
455 echo $title_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
456 }
457
458 public function maybe_add_ally_heading_hint() {
459 $notice_id = 'ally_heading_notice';
460 $plugin_slug = 'pojo-accessibility';
461 if ( ! Hints::should_display_hint( $notice_id ) ) {
462 return;
463 }
464 $notice_content = esc_html__( 'Make sure your page is structured with accessibility in mind. Ally helps detect and fix common issues across your site.', 'elementor' );
465
466 $campaign_data = [
467 'name' => 'elementor_ea11y_campaign',
468 'campaign' => 'acc-scanner-plg-heading',
469 'source' => 'editor-heading-widget',
470 'medium' => 'editor',
471 ];
472
473 $button_text = __( 'Install Plugin', 'elementor' );
474 $action_url = Admin_Notices::add_plg_campaign_data( Hints::get_plugin_action_url( $plugin_slug ), $campaign_data );
475
476 if ( Hints::is_plugin_installed( $plugin_slug ) && ! Hints::is_plugin_active( $plugin_slug ) ) {
477 $button_text = __( 'Activate Plugin', 'elementor' );
478 } else if ( Hints::is_plugin_active( $plugin_slug ) && empty( get_option( 'ea11y_access_token' ) ) ) {
479 $button_text = __( 'Connect to Ally', 'elementor' );
480 $action_url = admin_url( 'admin.php?page=accessibility-settings' );
481 }
482
483 $this->add_control(
484 $notice_id,
485 [
486 'type' => Controls_Manager::RAW_HTML,
487 'raw' => Hints::get_notice_template( [
488 'display' => ! Hints::is_dismissed( $notice_id ),
489 'heading' => esc_html__( 'Accessible structure matters', 'elementor' ),
490 'type' => 'info',
491 'content' => $notice_content,
492 'icon' => true,
493 'dismissible' => $notice_id,
494 'button_text' => $button_text,
495 'button_event' => $notice_id,
496 'button_data' => [
497 'action_url' => $action_url,
498 ],
499 ], true ),
500 ]
501 );
502 }
503
504 /**
505 * Render heading widget output in the editor.
506 *
507 * Written as a Backbone JavaScript template and used to generate the live preview.
508 *
509 * @since 2.9.0
510 * @access protected
511 */
512 protected function content_template() {
513 ?>
514 <#
515 let title = elementor.helpers.sanitize( settings.title, { ALLOW_DATA_ATTR: false } );
516
517 if ( '' !== settings.link.url ) {
518 title = '<a href="' + elementor.helpers.sanitizeUrl( settings.link.url ) + '">' + title + '</a>';
519 }
520
521 view.addRenderAttribute( 'title', 'class', [ 'elementor-heading-title' ] );
522
523 if ( '' !== settings.size ) {
524 view.addRenderAttribute( 'title', 'class', [ 'elementor-size-' + settings.size ] );
525 } else {
526 view.addRenderAttribute( 'title', 'class', [ 'elementor-size-default' ] );
527 }
528
529 view.addInlineEditingAttributes( 'title' );
530
531 var headerSizeTag = elementor.helpers.validateHTMLTag( settings.header_size ),
532 title_html = '<' + headerSizeTag + ' ' + view.getRenderAttributeString( 'title' ) + '>' + title + '</' + headerSizeTag + '>';
533
534 print( title_html );
535 #>
536 <?php
537 }
538 }
539