PluginProbe ʕ •ᴥ•ʔ
FrontBlocks for Gutenberg/GeneratePress / trunk
FrontBlocks for Gutenberg/GeneratePress vtrunk
trunk 0.2.0 0.2.1 0.2.2 0.2.3 0.2.4 0.2.5 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.1.0 1.2.0 1.2.1 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 ci-artifacts
frontblocks / includes / Frontend / DownloadButton.php
frontblocks / includes / Frontend Last commit date
Animations.php 1 month ago BackButton.php 7 months ago BeforeAfter.php 1 month ago BlockPatterns.php 4 months ago Carousel.php 1 week ago ColumnsSameHeight.php 1 week ago ContainerEdgeAlignment.php 4 weeks ago Counter.php 1 month ago DownloadButton.php 1 week ago Events.php 4 weeks ago FaqSchema.php 1 week ago FluidTypography.php 4 weeks ago Gallery.php 8 months ago GravityFormsInline.php 1 month ago Headline.php 4 weeks ago InsertPost.php 1 month ago ProductCategories.php 4 weeks ago ReadingProgress.php 7 months ago ReadingTime.php 8 months ago ShapeAnimations.php 4 weeks ago StackedImages.php 4 months ago StickyColumn.php 4 weeks ago SvgUpload.php 4 weeks ago Testimonials.php 8 months ago TextAnimation.php 1 month ago UserText.php 4 weeks ago
DownloadButton.php
179 lines
1 <?php
2 /**
3 * Download Button module for FrontBlocks (native button block download option).
4 *
5 * @package FrontBlocks
6 * @author Alex castellón <castellon@close.technology>
7 * @copyright 2026 Closemarketing
8 * @version 1.0.0
9 */
10
11 namespace FrontBlocks\Frontend;
12
13 defined( 'ABSPATH' ) || exit;
14
15 /**
16 * DownloadButton class.
17 *
18 * Adds a download toggle to the native core/button block. When enabled,
19 * the button links to an uploaded file and forces the browser to download
20 * it instead of navigating to a URL.
21 *
22 * @since 1.3.7
23 */
24 class DownloadButton {
25 /**
26 * Constructor.
27 */
28 public function __construct() {
29 add_action( 'init', array( $this, 'register_assets' ) );
30 add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_editor_assets' ) );
31 add_filter( 'register_block_type_args', array( $this, 'register_native_block_attributes' ), 10, 2 );
32 add_filter( 'render_block_core/button', array( $this, 'apply_download_to_button' ), 10, 2 );
33 }
34
35 /**
36 * Register block assets for backend editor.
37 *
38 * @return void
39 */
40 public function register_assets() {
41 wp_register_script(
42 'frontblocks-download-button-editor',
43 FRBL_PLUGIN_URL . 'assets/download-button/frontblocks-download-button-option.js',
44 array(
45 'wp-blocks',
46 'wp-element',
47 'wp-block-editor',
48 'wp-components',
49 'wp-compose',
50 'wp-hooks',
51 'wp-i18n',
52 ),
53 FRBL_VERSION,
54 true
55 );
56 }
57
58 /**
59 * Editor assets.
60 *
61 * @return void
62 */
63 public function enqueue_editor_assets() {
64 wp_enqueue_script( 'frontblocks-download-button-editor' );
65
66 // Set script translations for JavaScript.
67 wp_set_script_translations(
68 'frontblocks-download-button-editor',
69 'frontblocks'
70 );
71 }
72
73 /**
74 * Register download attributes server-side for the native button block.
75 *
76 * @param array $args Block type args.
77 * @param string $block_type Block type name.
78 * @return array
79 */
80 public function register_native_block_attributes( $args, $block_type ) {
81 if ( 'core/button' !== $block_type ) {
82 return $args;
83 }
84
85 if ( ! isset( $args['attributes'] ) ) {
86 $args['attributes'] = array();
87 }
88
89 $args['attributes']['frblDownloadEnabled'] = array(
90 'type' => 'boolean',
91 'default' => false,
92 );
93
94 $args['attributes']['frblDownloadFileId'] = array(
95 'type' => 'number',
96 'default' => 0,
97 );
98
99 $args['attributes']['frblDownloadFileUrl'] = array(
100 'type' => 'string',
101 'default' => '',
102 );
103
104 $args['attributes']['frblDownloadFileName'] = array(
105 'type' => 'string',
106 'default' => '',
107 );
108
109 return $args;
110 }
111
112 /**
113 * Force the button anchor to download the selected file.
114 *
115 * @param string $block_content Block HTML.
116 * @param array $block Block data.
117 * @return string
118 */
119 public function apply_download_to_button( $block_content, $block ) {
120 $attrs = $block['attrs'] ?? array();
121
122 if ( empty( $attrs['frblDownloadEnabled'] ) ) {
123 return $block_content;
124 }
125
126 $file_url = $this->resolve_file_url( $attrs );
127
128 if ( empty( $file_url ) ) {
129 return $block_content;
130 }
131
132 $file_name = ! empty( $attrs['frblDownloadFileName'] ) ? sanitize_file_name( $attrs['frblDownloadFileName'] ) : '';
133
134 // WP 6.2+: rewrite the anchor with the HTML API.
135 if ( class_exists( 'WP_HTML_Tag_Processor' ) ) {
136 $processor = new \WP_HTML_Tag_Processor( $block_content );
137
138 if ( $processor->next_tag( 'a' ) ) {
139 $processor->set_attribute( 'href', esc_url( $file_url ) );
140 $processor->set_attribute( 'download', $file_name ? $file_name : true );
141 $processor->remove_attribute( 'target' );
142
143 return $processor->get_updated_html();
144 }
145
146 return $block_content;
147 }
148
149 // Fallback for older WordPress: inject attributes into the first anchor.
150 $download_attr = $file_name ? ' download="' . esc_attr( $file_name ) . '"' : ' download';
151
152 return preg_replace(
153 '/<a\s([^>]*?)href="[^"]*"([^>]*)>/i',
154 '<a $1href="' . esc_url( $file_url ) . '"' . $download_attr . '$2>',
155 $block_content,
156 1
157 );
158 }
159
160 /**
161 * Resolve the download file URL, preferring a fresh attachment URL.
162 *
163 * @param array $attrs Block attributes.
164 * @return string
165 */
166 private function resolve_file_url( $attrs ) {
167 // Prefer the attachment ID so the URL survives file moves or renames.
168 if ( ! empty( $attrs['frblDownloadFileId'] ) ) {
169 $attachment_url = wp_get_attachment_url( absint( $attrs['frblDownloadFileId'] ) );
170
171 if ( $attachment_url ) {
172 return $attachment_url;
173 }
174 }
175
176 return ! empty( $attrs['frblDownloadFileUrl'] ) ? esc_url_raw( $attrs['frblDownloadFileUrl'] ) : '';
177 }
178 }
179