PluginProbe ʕ •ᴥ•ʔ
VK Blocks / 1.119.2
VK Blocks v1.119.2
1.126.4 1.126.3 1.126.1 1.126.2 1.126.0 1.125.0 1.124.0 1.123.0 1.122.0 1.121.0 1.120.0 1.119.2 1.119.1 1.119.0 1.118.7 1.82.0.0 1.82.0.1 1.83.0.0 1.83.0.1 1.84.0.0 1.84.0.1 1.85.0.2 1.85.0.3 1.85.1.0 1.85.1.1 1.86.0.0 1.86.0.1 1.86.1.0 1.87.0.0 1.87.0.1 1.88.0.1 1.88.0.2 1.89.0.0 1.9.2 1.90.0.0 1.90.0.1 1.90.1.1 1.91.0.0 1.91.1.1 1.92.0.0 1.92.0.1 1.92.1.0 1.92.1.1 1.93.0.0 trunk 1.93.0.1 0.1.1 1.93.1.0 0.10.3 1.93.1.1 0.11.0 1.94.0.0 0.12.6 1.94.0.1 0.15.1 1.94.1.0 0.16.2 1.94.2.1 0.17.6 1.94.2.2 0.2.2 1.95.0.3 0.22.4 1.95.0.4 0.26.7 1.96.2.0 0.3.2 1.96.2.1 0.31.0 1.97.0.1 0.35.5 1.97.0.2 0.37.5 1.99.0.0 0.38.8 1.99.0.1 0.39.4 0.4.7 0.41.0 0.42.0 0.44.13 0.45.2 0.46.1 0.47.0 0.48.0 0.49.8 0.5.2 0.50.3 0.51.0 0.52.2 0.53.2 0.54.0 0.55.0 0.56.2 0.56.3 0.57.6 0.58.10 0.59.0 0.6.0 0.60.1 0.61.2 0.7.1 0.8.2 1.0.16 1.10.0 1.100.0.0 1.100.0.1 1.102.0.0 1.102.0.1 1.103.0.0 1.104.0.0 1.104.0.1 1.105.1.0 1.105.1.1 1.106.0.0 1.106.0.1 1.107.0.0 1.107.0.1 1.107.0.2 1.108.0.0 1.108.0.1 1.109.0.0 1.109.0.1 1.11.4 1.110.0.0 1.110.0.1 1.111.0.0 1.111.0.1 1.111.0.2 1.112.0.0 1.112.0.1 1.113.0.0 1.113.0.1 1.114.0.0 1.114.0.1 1.114.2.0 1.114.2.1 1.115.0.0 1.115.0.1 1.115.1.0 1.115.1.1 1.115.2.0 1.115.2.1 1.116.0.0 1.116.0.1 1.116.1.0 1.116.1.1 1.116.2.0 1.117.0.0 1.117.0.1 1.117.1.0 1.118.2 1.118.5 1.12.0 1.13.2 1.14.1 1.15.1 1.16.11 1.17.0 1.18.6 1.19.0 1.19.1 1.2.3 1.20.7 1.21.0 1.22.4 1.23.0 1.24.5 1.25.1 1.26.2 1.27.7.2 1.28.0.1 1.29.2.0 1.3.9 1.30.0.1 1.31.0.1 1.32.0.2 1.33.2.1 1.36.1.5 1.37.0.0 1.39.2.1 1.4.6 1.40.0.1 1.40.1.0 1.40.1.1 1.41.0.0 1.41.0.1 1.41.2.2 1.41.2.3 1.43.0.0 1.43.0.1 1.43.0.2 1.44.0.0 1.44.0.1 1.45.0.0 1.45.0.1 1.46.0.0 1.46.0.1 1.47.0.0 1.47.0.1 1.47.1.0 1.48.0.0 1.48.0.1 1.48.0.2 1.48.1.0 1.48.1.1 1.5.0 1.50.0.0 1.50.0.1 1.51.0.0 1.51.0.1 1.52.0.0 1.52.0.1 1.53.0.0 1.53.0.1 1.54.0.0 1.54.0.1 1.55.0.0 1.55.0.1 1.56.0.0 1.56.0.1 1.57.0.0 1.57.0.2 1.57.0.3 1.57.0.4 1.57.0.5 1.57.1.0 1.57.1.1 1.57.1.2 1.58.0.0 1.58.0.1 1.59.0.0 1.59.0.1 1.6.0 1.60.0.0 1.60.0.1 1.63.0.0 1.63.0.1 1.64.0.0 1.64.0.1 1.64.1.0 1.64.1.2 1.67.0.0 1.67.0.1 1.68.0.0 1.68.0.1 1.69.0.0 1.69.0.1 1.69.1.1 1.69.1.2 1.7.1 1.70.0.0 1.70.0.1 1.71.0.0 1.71.0.1 1.72.0.0 1.72.1.0 1.72.1.1 1.73.0.0 1.73.0.1 1.74.0.0 1.74.0.1 1.75.0.0 1.75.1.0 1.75.1.1 1.76.0.0 1.76.0.1 1.76.1.0 1.76.1.1 1.76.2.0 1.76.2.1 1.77.0.0 1.77.0.1 1.78.0.0 1.78.0.1 1.79.0.0 1.79.0.1 1.79.0.2 1.79.0.3 1.79.1.0 1.79.1.1 1.8.2 1.80.1.0 1.80.1.1 1.80.1.2 1.81.0.1 1.81.0.2
vk-blocks / src / blocks / visual-embed / edit.js
vk-blocks / src / blocks / visual-embed Last commit date
utils 3 months ago block.json 4 months ago edit.js 3 months ago icon.svg 4 months ago index.js 4 months ago index.php 4 months ago save.js 4 months ago style.scss 4 months ago
edit.js
296 lines
1 import { __ } from '@wordpress/i18n';
2 import {
3 InspectorControls,
4 useBlockProps,
5 BlockControls,
6 } from '@wordpress/block-editor';
7
8 import {
9 PanelBody,
10 TextareaControl,
11 TextControl,
12 Notice,
13 } from '@wordpress/components';
14 import { useEffect, useRef, useState } from '@wordpress/element';
15
16 import { isAllowedSrc as isAllowedSrcByPatterns } from './utils/match-url-pattern';
17
18 const allowedUrlPatterns =
19 typeof vkBlocksVisualEmbed !== 'undefined' && // eslint-disable-line no-undef
20 vkBlocksVisualEmbed.allowedUrlPatterns // eslint-disable-line no-undef
21 ? vkBlocksVisualEmbed.allowedUrlPatterns // eslint-disable-line no-undef
22 : [];
23
24 // 許可するURLパターンの�
25 �列
26 const ALLOWED_URL_PATTERNS = [
27 'https://*.google.com/*',
28 'https://*.youtube.com/embed/*',
29 'https://www.openstreetmap.org/export/*',
30 'https://player.vimeo.com/*',
31 ];
32
33 // フィルターフックを使用してURLパターンを変更
34 const filteredAllowedUrlPatterns = [
35 ...ALLOWED_URL_PATTERNS,
36 // eslint-disable-next-line no-undef
37 ...allowedUrlPatterns,
38 ];
39
40 export default function EmbedCodeEdit({ attributes, setAttributes }) {
41 const { iframeCode, iframeWidth, iframeHeight } = attributes;
42 const [tempIframeCode, setTempIframeCode] = useState(iframeCode);
43 const prevIframeWidth = useRef(attributes.iframeWidth);
44
45 useEffect(() => {
46 // align がユーザーによって設定されていたら変更しない
47 if (attributes.align !== undefined) {
48 return;
49 }
50
51 const isFullWidth = String(attributes.iframeWidth) === '100%';
52 const wasFullWidth = String(prevIframeWidth.current) === '100%';
53
54 // 100% から 100% 以外に変わった瞬間のみ align: "center" を適用
55 if (wasFullWidth && !isFullWidth) {
56 setAttributes({ align: 'center' });
57 }
58
59 // iframeWidth の変更を記録
60 prevIframeWidth.current = attributes.iframeWidth;
61 }, [attributes.iframeWidth]);
62
63 useEffect(() => {
64 if (iframeWidth && isIframe) {
65 updateIframeAttributes(iframeWidth, iframeHeight);
66 }
67 }, [iframeWidth, iframeHeight]);
68
69 // iframeを解析する関数
70 const parseIframeCode = (code) => {
71 const parser = new window.DOMParser();
72 const doc = parser.parseFromString(code, 'text/html');
73 const iframe = doc.querySelector('iframe');
74 return iframe ? iframe : false;
75 };
76 const [isIframe, setIsIframe] = useState(!!parseIframeCode(iframeCode));
77
78 // 外部からの属性変更(RTC・Undo/Redo等)をローカルstateに反映
79 useEffect(() => {
80 setTempIframeCode(iframeCode);
81 setIsIframe(!!parseIframeCode(iframeCode));
82 }, [iframeCode]);
83
84 const blockProps = useBlockProps({
85 className: 'vk-visual-embed',
86 });
87
88 // iframeのsrc属性を検証する関数(許可パターン判定は utils に切り出し済み)
89 const isAllowedSrc = (src) =>
90 isAllowedSrcByPatterns(src, filteredAllowedUrlPatterns);
91
92 // iframeタグ以外を削除する関数
93 const sanitizeIframeCode = (code) => {
94 if (!code) {
95 return '';
96 }
97
98 // DOMParserが利用できない環境の場合は�
99 �力をそのまま返す
100 if (typeof window.DOMParser === 'undefined') {
101 return code;
102 }
103
104 const iframe = parseIframeCode(code);
105
106 if (!iframe) {
107 return '';
108 }
109
110 // src属性を検証
111 const src = iframe.getAttribute('src');
112 if (!isAllowedSrc(src)) {
113 return __('Only allowed URLs can be embedded.', 'vk-blocks');
114 }
115
116 return iframe.outerHTML;
117 };
118
119 // iframeの属性を解析して�
120 と高さを取得
121 const extractIframeAttributes = (code) => {
122 if (typeof window.DOMParser === 'undefined') {
123 return false;
124 }
125
126 const iframe = parseIframeCode(code);
127
128 if (iframe) {
129 const newWidth = iframe.getAttribute('width') || iframeWidth;
130 const newHeight = iframe.getAttribute('height') || iframeHeight;
131
132 // 抽出した値を設定パネルに反映
133 setAttributes({
134 iframeWidth: newWidth,
135 iframeHeight: newHeight,
136 });
137
138 return true; // iframeが見つかった場合はtrueを返す
139 }
140
141 return false; // iframeが見つからない場合はfalseを返す
142 };
143
144 // iframeの属性を解析・更新する関数
145 const updateIframeAttributes = (newWidth, newHeight) => {
146 if (!iframeCode || typeof window.DOMParser === 'undefined') {
147 return;
148 }
149
150 const iframe = parseIframeCode(iframeCode);
151
152 if (iframe) {
153 if (newWidth) {
154 iframe.setAttribute('width', newWidth);
155 }
156 if (newHeight) {
157 iframe.setAttribute('height', newHeight);
158 }
159
160 // 更新後のiframeコードを設定
161 setAttributes({
162 iframeCode: iframe.outerHTML,
163 iframeWidth: newWidth || iframeWidth,
164 iframeHeight: newHeight || iframeHeight,
165 });
166 setTempIframeCode(iframe.outerHTML);
167 }
168 };
169
170 return (
171 <div {...blockProps}>
172 <BlockControls />
173 <InspectorControls>
174 <PanelBody title={__('Embed Code Settings', 'vk-blocks')}>
175 <TextareaControl
176 label={__('Embed Code', 'vk-blocks')}
177 value={tempIframeCode}
178 onChange={(newCode) => {
179 setTempIframeCode(newCode);
180 }}
181 onBlur={() => {
182 if (!tempIframeCode) {
183 setAttributes({ iframeCode: '' });
184 setIsIframe(false);
185 return;
186 }
187 const sanitizedCode =
188 sanitizeIframeCode(tempIframeCode);
189 setAttributes({ iframeCode: sanitizedCode });
190 if (sanitizedCode) {
191 extractIframeAttributes(sanitizedCode);
192 }
193
194 setIsIframe(!!parseIframeCode(sanitizedCode));
195 setTempIframeCode(sanitizedCode);
196 }}
197 help={__(
198 'Please paste the iframe embed code directly. Only iframe tags with allowed URLs (Google Maps, Google Calendar, Google Forms, YouTube、OpenStreetMap, Vimeo) are permitted.',
199 'vk-blocks'
200 )}
201 />
202 {!iframeCode && (
203 <Notice
204 status="error"
205 isDismissible={false}
206 className="vk-visual-embed_notice"
207 >
208 {__(
209 'Please enter an iframe embed code.',
210 'vk-blocks'
211 )}
212 </Notice>
213 )}
214 {iframeCode && !sanitizeIframeCode(iframeCode) && (
215 <Notice
216 status="error"
217 isDismissible={false}
218 className="vk-visual-embed_notice"
219 >
220 {__(
221 'The provided URL is not allowed. Please use an approved embed source.',
222 'vk-blocks'
223 )}
224 </Notice>
225 )}
226 <TextControl
227 label={__('Iframe Width', 'vk-blocks')}
228 value={iframeWidth}
229 onChange={(newWidth) => {
230 setAttributes({ iframeWidth: newWidth });
231 }}
232 onBlur={() => {
233 if (!iframeWidth) {
234 extractIframeAttributes(iframeCode);
235 return;
236 }
237 if (/^\d+(px|%)?$/.test(iframeWidth)) {
238 updateIframeAttributes(
239 iframeWidth,
240 iframeHeight
241 );
242 } else {
243 setAttributes({ iframeWidth: '' });
244 updateIframeAttributes('', iframeHeight);
245 }
246 }}
247 disabled={!isIframe}
248 />
249 <TextControl
250 label={__('Iframe Height', 'vk-blocks')}
251 value={iframeHeight}
252 onChange={(newHeight) => {
253 setAttributes({ iframeHeight: newHeight });
254 }}
255 onBlur={() => {
256 if (!iframeHeight) {
257 extractIframeAttributes(iframeCode);
258 return;
259 }
260 if (/^\d+(px|%)?$/.test(iframeHeight)) {
261 updateIframeAttributes(
262 iframeWidth,
263 iframeHeight
264 );
265 } else {
266 setAttributes({ iframeHeight: '' });
267 updateIframeAttributes(iframeWidth, '');
268 }
269 }}
270 disabled={!isIframe}
271 />
272 {!isIframe && (
273 <Notice status="warning" isDismissible={false}>
274 {__(
275 'Note: These settings are only applicable to iframe tags. Other embed codes will not respond to these adjustments.',
276 'vk-blocks'
277 )}
278 </Notice>
279 )}
280 </PanelBody>
281 </InspectorControls>
282 <div style={{ position: 'relative' }}>
283 {iframeCode && (
284 <div
285 className="vk-visual-embed-preview"
286 dangerouslySetInnerHTML={{ __html: iframeCode }}
287 style={{
288 pointerEvents: 'none',
289 }}
290 />
291 )}
292 </div>
293 </div>
294 );
295 }
296