Upload files to "/"
This commit is contained in:
parent
71a4031b20
commit
f349647df4
3 changed files with 235 additions and 0 deletions
8
ansico-embed-url.css
Normal file
8
ansico-embed-url.css
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
.wp-block-ansico-embed-url { margin-bottom: 24px; }
|
||||||
|
.ansico-embed-input-container { padding: 20px; border: 2px dashed #ccc; background: #f9f9f9; border-radius: 8px; }
|
||||||
|
.ansico-embed-link { text-decoration: none !important; color: inherit !important; display: block; }
|
||||||
|
.ansico-embed-preview { border: 1px solid #dadde1; border-radius: 8px; overflow: hidden; display: flex; flex-direction: column; background-color: #ffffff; }
|
||||||
|
.ansico-embed-image { width: 100%; height: auto; max-height: 300px; object-fit: cover; display: block; border-bottom: 1px solid #dadde1; margin: 0 !important; }
|
||||||
|
.ansico-embed-content { padding: 12px 16px; background-color: #f2f3f5; }
|
||||||
|
.ansico-embed-domain { margin: 0 0 4px 0 !important; font-size: 12px; color: #65676b; text-transform: uppercase; letter-spacing: 0.5px; }
|
||||||
|
.ansico-embed-title { margin: 0 !important; font-size: 16px; font-weight: 600; color: #050505; line-height: 1.3; }
|
||||||
116
ansico-embed-url.js
Normal file
116
ansico-embed-url.js
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
( function( blocks, element, editor, components, apiFetch ) {
|
||||||
|
var el = element.createElement;
|
||||||
|
var registerBlockType = blocks.registerBlockType;
|
||||||
|
var useBlockProps = editor.useBlockProps;
|
||||||
|
var BlockControls = editor.BlockControls;
|
||||||
|
var TextControl = components.TextControl;
|
||||||
|
var Button = components.Button;
|
||||||
|
var Spinner = components.Spinner;
|
||||||
|
var ToolbarGroup = components.ToolbarGroup;
|
||||||
|
var ToolbarButton = components.ToolbarButton;
|
||||||
|
var useState = element.useState;
|
||||||
|
|
||||||
|
registerBlockType( 'ansico/embed-url', {
|
||||||
|
title: 'Ansico Embed URL',
|
||||||
|
icon: 'admin-links',
|
||||||
|
category: 'embed',
|
||||||
|
attributes: {
|
||||||
|
url: { type: 'string' },
|
||||||
|
title: { type: 'string' },
|
||||||
|
imageUrl: { type: 'string' },
|
||||||
|
domain: { type: 'string' },
|
||||||
|
isEditing: { type: 'boolean', default: true }
|
||||||
|
},
|
||||||
|
|
||||||
|
edit: function( props ) {
|
||||||
|
var attributes = props.attributes;
|
||||||
|
var setAttributes = props.setAttributes;
|
||||||
|
var isFetching = useState( false );
|
||||||
|
var setIsFetching = isFetching[1];
|
||||||
|
var blockProps = useBlockProps();
|
||||||
|
|
||||||
|
function fetchUrlData() {
|
||||||
|
if ( ! attributes.url ) return;
|
||||||
|
setIsFetching( true );
|
||||||
|
apiFetch( {
|
||||||
|
path: '/ansico/v1/fetch-url',
|
||||||
|
method: 'POST',
|
||||||
|
data: { url: attributes.url }
|
||||||
|
} ).then( function( response ) {
|
||||||
|
setAttributes( {
|
||||||
|
title: response.title,
|
||||||
|
imageUrl: response.image,
|
||||||
|
domain: response.domain,
|
||||||
|
isEditing: false
|
||||||
|
} );
|
||||||
|
setIsFetching( false );
|
||||||
|
} ).catch( function( error ) {
|
||||||
|
alert( 'Error fetching data.' );
|
||||||
|
setIsFetching( false );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( attributes.isEditing ) {
|
||||||
|
return el( 'div', blockProps,
|
||||||
|
el( 'div', { className: 'ansico-embed-input-container' },
|
||||||
|
el( TextControl, {
|
||||||
|
label: 'Enter article URL',
|
||||||
|
value: attributes.url,
|
||||||
|
onChange: function( val ) { setAttributes( { url: val } ); },
|
||||||
|
placeholder: 'https://...'
|
||||||
|
} ),
|
||||||
|
el( Button, {
|
||||||
|
isPrimary: true,
|
||||||
|
onClick: fetchUrlData,
|
||||||
|
disabled: isFetching[0] || ! attributes.url
|
||||||
|
}, isFetching[0] ? el( Spinner ) : 'Generate Embed' )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return el( 'div', blockProps,
|
||||||
|
el( BlockControls, {},
|
||||||
|
el( ToolbarGroup, {},
|
||||||
|
el( ToolbarButton, {
|
||||||
|
icon: 'edit',
|
||||||
|
label: 'Edit URL',
|
||||||
|
onClick: function() { setAttributes( { isEditing: true } ); }
|
||||||
|
} )
|
||||||
|
)
|
||||||
|
),
|
||||||
|
el( 'div', { className: 'ansico-embed-preview' },
|
||||||
|
attributes.imageUrl && el( 'img', { src: attributes.imageUrl, className: 'ansico-embed-image' } ),
|
||||||
|
el( 'div', { className: 'ansico-embed-content' },
|
||||||
|
el( 'p', { className: 'ansico-embed-domain' }, attributes.domain ),
|
||||||
|
el( 'h3', { className: 'ansico-embed-title' }, attributes.title )
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
save: function( props ) {
|
||||||
|
var attributes = props.attributes;
|
||||||
|
var blockProps = useBlockProps.save();
|
||||||
|
|
||||||
|
if ( ! attributes.url || attributes.isEditing ) return null;
|
||||||
|
|
||||||
|
return el( 'div', blockProps,
|
||||||
|
el( 'a', { href: attributes.url, className: 'ansico-embed-link', target: '_blank', rel: 'noopener noreferrer' },
|
||||||
|
el( 'div', { className: 'ansico-embed-preview' },
|
||||||
|
attributes.imageUrl && el( 'img', { src: attributes.imageUrl, className: 'ansico-embed-image' } ),
|
||||||
|
el( 'div', { className: 'ansico-embed-content' },
|
||||||
|
el( 'p', { className: 'ansico-embed-domain' }, attributes.domain ),
|
||||||
|
el( 'h3', { className: 'ansico-embed-title' }, attributes.title )
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
} )(
|
||||||
|
window.wp.blocks,
|
||||||
|
window.wp.element,
|
||||||
|
window.wp.blockEditor,
|
||||||
|
window.wp.components,
|
||||||
|
window.wp.apiFetch
|
||||||
|
);
|
||||||
111
ansico-embed-url.php
Normal file
111
ansico-embed-url.php
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Plugin Name: Ansico Embed URL
|
||||||
|
* Description: A Gutenberg block to embed a URL with a social-media style preview card. Automatically fetches metadata and saves the image locally.
|
||||||
|
* Version: 1.0.0
|
||||||
|
* Author: Ansico
|
||||||
|
* Text Domain: ansico-embed-url
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the block and its assets
|
||||||
|
add_action( 'init', 'ansico_embed_url_register_block' );
|
||||||
|
function ansico_embed_url_register_block() {
|
||||||
|
wp_register_script(
|
||||||
|
'ansico-embed-url-script',
|
||||||
|
plugins_url( 'ansico-embed-url.js', __FILE__ ),
|
||||||
|
array( 'wp-blocks', 'wp-element', 'wp-editor', 'wp-components', 'wp-api-fetch' ),
|
||||||
|
'1.0.0'
|
||||||
|
);
|
||||||
|
|
||||||
|
wp_register_style(
|
||||||
|
'ansico-embed-url-style',
|
||||||
|
plugins_url( 'ansico-embed-url.css', __FILE__ ),
|
||||||
|
array(),
|
||||||
|
'1.0.0'
|
||||||
|
);
|
||||||
|
|
||||||
|
register_block_type( 'ansico/embed-url', array(
|
||||||
|
'editor_script' => 'ansico-embed-url-script',
|
||||||
|
'editor_style' => 'ansico-embed-url-style',
|
||||||
|
'style' => 'ansico-embed-url-style',
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register REST API Route
|
||||||
|
add_action( 'rest_api_init', function () {
|
||||||
|
register_rest_route( 'ansico/v1', '/fetch-url', array(
|
||||||
|
'methods' => 'POST',
|
||||||
|
'callback' => 'ansico_embed_url_fetch_metadata',
|
||||||
|
'permission_callback' => function () {
|
||||||
|
return current_user_can( 'edit_posts' );
|
||||||
|
}
|
||||||
|
) );
|
||||||
|
} );
|
||||||
|
|
||||||
|
function ansico_embed_url_fetch_metadata( $request ) {
|
||||||
|
$url = esc_url_raw( $request->get_param( 'url' ) );
|
||||||
|
|
||||||
|
if ( empty( $url ) ) {
|
||||||
|
return new WP_Error( 'missing_url', 'No URL provided', array( 'status' => 400 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = wp_remote_get( $url );
|
||||||
|
if ( is_wp_error( $response ) ) {
|
||||||
|
return new WP_Error( 'fetch_failed', 'Could not fetch URL', array( 'status' => 500 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
$html = wp_remote_retrieve_body( $response );
|
||||||
|
|
||||||
|
libxml_use_internal_errors(true);
|
||||||
|
$doc = new DOMDocument();
|
||||||
|
$doc->loadHTML( mb_convert_encoding( $html, 'HTML-ENTITIES', 'UTF-8' ) );
|
||||||
|
libxml_clear_errors();
|
||||||
|
|
||||||
|
$title = '';
|
||||||
|
$image_url = '';
|
||||||
|
$domain = parse_url( $url, PHP_URL_HOST );
|
||||||
|
$domain = str_replace( 'www.', '', $domain );
|
||||||
|
|
||||||
|
$tags = $doc->getElementsByTagName( 'meta' );
|
||||||
|
foreach ( $tags as $tag ) {
|
||||||
|
if ( $tag->getAttribute( 'property' ) === 'og:title' ) {
|
||||||
|
$title = $tag->getAttribute( 'content' );
|
||||||
|
}
|
||||||
|
if ( $tag->getAttribute( 'property' ) === 'og:image' ) {
|
||||||
|
$image_url = $tag->getAttribute( 'content' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( empty( $title ) ) {
|
||||||
|
$nodes = $doc->getElementsByTagName( 'title' );
|
||||||
|
if ( $nodes->length > 0 ) {
|
||||||
|
$title = $nodes->item(0)->nodeValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$local_image_url = '';
|
||||||
|
if ( ! empty( $image_url ) ) {
|
||||||
|
require_once( ABSPATH . 'wp-admin/includes/media.php' );
|
||||||
|
require_once( ABSPATH . 'wp-admin/includes/file.php' );
|
||||||
|
require_once( ABSPATH . 'wp-admin/includes/image.php' );
|
||||||
|
|
||||||
|
$sideloaded = media_sideload_image( $image_url, 0, null, 'src' );
|
||||||
|
|
||||||
|
if ( ! is_wp_error( $sideloaded ) ) {
|
||||||
|
$local_image_url = $sideloaded;
|
||||||
|
} else {
|
||||||
|
$local_image_url = $image_url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rest_ensure_response( array(
|
||||||
|
'title' => $title,
|
||||||
|
'image' => $local_image_url,
|
||||||
|
'domain' => $domain,
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
?>
|
||||||
Loading…
Reference in a new issue