Filigrane), live preview and AJAX tools.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
function wmguru_admin_menu() {
add_media_page(
__( 'Filigrane', 'filigrane' ),
__( 'Filigrane', 'filigrane' ),
'manage_options',
'wmguru',
'wmguru_render_settings_page'
);
}
add_action( 'admin_menu', 'wmguru_admin_menu' );
function wmguru_register_settings() {
register_setting(
'wmguru',
WMGURU_OPTION,
array(
'type' => 'array',
'sanitize_callback' => 'wmguru_sanitize_settings',
'default' => wmguru_default_settings(),
)
);
}
add_action( 'admin_init', 'wmguru_register_settings' );
/**
* @param array $links Plugin action links.
* @return array
*/
function wmguru_plugin_action_links( $links ) {
$url = admin_url( 'upload.php?page=wmguru' );
array_unshift( $links, '' . esc_html__( 'Settings', 'filigrane' ) . ' ' );
return $links;
}
add_filter( 'plugin_action_links_' . plugin_basename( WMGURU_PLUGIN_FILE ), 'wmguru_plugin_action_links' );
/**
* @param string $hook Admin page hook suffix.
*/
function wmguru_admin_assets( $hook ) {
if ( 'media_page_wmguru' !== $hook ) {
return;
}
wp_enqueue_media();
wp_enqueue_style( 'wmguru-admin', WMGURU_PLUGIN_URL . 'assets/css/admin.css', array(), WMGURU_VERSION );
wp_enqueue_script( 'wmguru-admin', WMGURU_PLUGIN_URL . 'assets/js/admin.js', array( 'media-views' ), WMGURU_VERSION, true );
wp_localize_script(
'wmguru-admin',
'wmguruAdmin',
array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'wmguru_admin' ),
'i18n' => array(
'chooseImage' => __( 'Choose an image', 'filigrane' ),
'useImage' => __( 'Use this image', 'filigrane' ),
'rendering' => __( 'Rendering preview…', 'filigrane' ),
'previewFailed' => __( 'Preview failed.', 'filigrane' ),
'working' => __( 'Working…', 'filigrane' ),
'requestFailed' => __( 'The request failed.', 'filigrane' ),
'sampleImage' => __( 'Built-in sample image', 'filigrane' ),
),
)
);
}
add_action( 'admin_enqueue_scripts', 'wmguru_admin_assets' );
/**
* 3x3 placement picker.
*
* @param string $name Input name.
* @param string $value Selected position.
*/
function wmguru_position_picker( $name, $value ) {
echo '
';
foreach ( wmguru_positions() as $key => $label ) {
printf(
'%1$s ',
esc_attr( $label ),
esc_attr( $name ),
esc_attr( $key ),
checked( $value, $key, false )
);
}
echo '
';
}
/**
* Tags and attributes the settings rows are built from.
*
* @return array wp_kses() allowed-HTML list.
*/
function wmguru_field_allowed_html() {
$common = array(
'class' => true,
'id' => true,
'title' => true,
'role' => true,
'aria-live' => true,
'aria-label' => true,
'hidden' => true,
);
return array(
'div' => $common,
'span' => $common,
'p' => $common,
'ul' => $common,
'li' => $common,
'code' => $common,
'strong' => $common,
'br' => array(),
'label' => $common + array( 'for' => true ),
'img' => $common + array(
'src' => true,
'alt' => true,
),
'input' => $common + array(
'type' => true,
'name' => true,
'value' => true,
'checked' => true,
'disabled' => true,
'min' => true,
'max' => true,
'step' => true,
'maxlength' => true,
),
'select' => $common + array( 'name' => true ),
'option' => array(
'value' => true,
'selected' => true,
),
'button' => $common + array(
'type' => true,
'data-wmguru-action' => true,
'data-reload' => true,
),
);
}
/**
* One table row.
*
* @param string $label Label.
* @param string $html Field HTML (already escaped).
* @param string $desc Optional description.
*/
function wmguru_row( $label, $html, $desc = '' ) {
echo '' . esc_html( $label ) . ' ' . wp_kses( $html, wmguru_field_allowed_html() );
if ( '' !== $desc ) {
echo '' . esc_html( $desc ) . '
';
}
echo ' ';
}
/**
* @param string $name Input name.
* @param mixed $value Value.
* @param array $attrs min / max / step.
* @return string
*/
function wmguru_number_input( $name, $value, $attrs ) {
return sprintf(
' ',
esc_attr( $name ),
esc_attr( $value ),
esc_attr( $attrs['min'] ),
esc_attr( $attrs['max'] ),
esc_attr( isset( $attrs['step'] ) ? $attrs['step'] : 1 )
);
}
/**
* Fields shared by the text and image layer cards.
*
* @param string $base Input name prefix, e.g. wmguru_settings[layers][0].
* @param array $layer Layer.
* @param bool $is_text True for a text layer.
*/
function wmguru_render_common_layer_fields( $base, $layer, $is_text ) {
wmguru_row(
$is_text ? __( 'Text size', 'filigrane' ) : __( 'Logo width', 'filigrane' ),
wmguru_number_input(
$base . '[size]',
$layer['size'],
array(
'min' => 0.5,
'max' => 100,
'step' => 0.1,
)
) . ' % ' . esc_html__( 'of the image width', 'filigrane' ) . ' ' .
esc_html__( 'min', 'filigrane' ) . ' ' . wmguru_number_input(
$base . '[min_px]',
$layer['min_px'],
array(
'min' => 1,
'max' => 4000,
)
) . ' px ' . esc_html__( 'max', 'filigrane' ) . ' ' . wmguru_number_input(
$base . '[max_px]',
$layer['max_px'],
array(
'min' => 1,
'max' => 8000,
)
) . ' px',
__( 'The watermark scales with each image, within the min/max limits.', 'filigrane' )
);
ob_start();
wmguru_position_picker( $base . '[position]', $layer['position'] );
wmguru_row( __( 'Position', 'filigrane' ), ob_get_clean() );
wmguru_row(
__( 'Margin', 'filigrane' ),
wmguru_number_input(
$base . '[margin]',
$layer['margin'],
array(
'min' => 0,
'max' => 30,
'step' => 0.1,
)
) . ' % ' . esc_html__( 'of the shorter side', 'filigrane' )
);
wmguru_row(
__( 'Opacity', 'filigrane' ),
wmguru_number_input(
$base . '[opacity]',
$layer['opacity'],
array(
'min' => 1,
'max' => 100,
)
) . ' %'
);
wmguru_row(
__( 'Rotation', 'filigrane' ),
wmguru_number_input(
$base . '[rotation]',
$layer['rotation'],
array(
'min' => -180,
'max' => 180,
)
) . '° ' . esc_html__( 'clockwise', 'filigrane' )
);
}
/**
* @param int $index Layer slot.
* @param array $layer Layer.
*/
function wmguru_render_layer_card( $index, $layer ) {
$base = WMGURU_OPTION . '[layers][' . (int) $index . ']';
$is_text = 'text' === $layer['type'];
echo '' . ( $is_text ? esc_html__( 'Text watermark', 'filigrane' ) : esc_html__( 'Image watermark (logo)', 'filigrane' ) ) . ' ';
echo '
';
echo '
';
}
function wmguru_render_settings_page() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$settings = wmguru_get_settings();
$slots = array(
'text' => wmguru_default_layer( 'text' ),
'image' => wmguru_default_layer( 'image' ),
);
$seen = array();
foreach ( $settings['layers'] as $layer ) {
if ( isset( $slots[ $layer['type'] ] ) && ! isset( $seen[ $layer['type'] ] ) ) {
$slots[ $layer['type'] ] = array_merge( $slots[ $layer['type'] ], $layer );
$seen[ $layer['type'] ] = true;
}
}
$support = wmguru_image_support();
$status = get_option( 'wmguru_delivery_status', array() );
$stats = wmguru_cache_stats();
$log = get_option( 'wmguru_error_log', array() );
echo '' . esc_html__( 'Filigrane', 'filigrane' ) . ' ';
settings_errors();
if ( empty( $settings['enabled'] ) ) {
echo '
' . esc_html__( 'Watermarks are switched off. Set up your watermark below, check the preview, then enable it under “General”. Your original files are never modified either way.', 'filigrane' ) . '
';
}
echo '
';
// Preview column.
echo '
' . esc_html__( 'Live preview', 'filigrane' ) . ' ';
echo '
';
echo '
' . esc_html__( 'Built-in sample image', 'filigrane' ) . ' ';
echo '' . esc_html__( 'Use one of my images', 'filigrane' ) . ' ';
echo '' . esc_html__( 'Back to sample', 'filigrane' ) . '
';
echo '
' . esc_html__( 'The preview is rendered by the same engine that creates the real copies, using your unsaved settings.', 'filigrane' ) . '
';
echo '
';
if ( ! defined( 'WMGURU_PRO_VERSION' ) ) {
echo '' . esc_html__( 'Need more?', 'filigrane' ) . ' ' . esc_html__( 'Multiple watermark profiles, unlimited layers and rules that pick a profile by post type, category or file type are available in Filigrane Pro, a separate optional add-on.', 'filigrane' ) . '
';
echo '
' . esc_html__( 'Learn about Pro', 'filigrane' ) . '
';
}
echo '';
do_action( 'wmguru_settings_page_after' );
echo '
';
}
/**
* Source file for previews: one of the user's images, or the bundled sample.
*
* @param int $attachment_id Attachment id, 0 for the sample.
* @return array array( path, mime )
*/
function wmguru_preview_source( $attachment_id ) {
if ( $attachment_id && wp_attachment_is_image( $attachment_id ) ) {
$mime = get_post_mime_type( $attachment_id );
$file = get_attached_file( $attachment_id );
$size = image_get_intermediate_size( $attachment_id, 'large' );
$uploads = wmguru_uploads();
if ( $size && ! empty( $size['path'] ) && $uploads && is_readable( $uploads['basedir'] . '/' . $size['path'] ) ) {
$file = $uploads['basedir'] . '/' . $size['path'];
}
if ( $file && is_readable( $file ) && wmguru_get_file_handler( $mime ) ) {
return array( $file, $mime );
}
}
return array( WMGURU_PLUGIN_DIR . 'assets/images/sample.jpg', 'image/jpeg' );
}
/**
* Render a profile onto a preview image and return it as a data URI.
*
* @param array $profile Profile.
* @param int $attachment_id Image to preview on, 0 for the sample.
* @param array $overrides 'quality' / 'driver'.
* @return array|WP_Error array( 'src' => data URI, 'notice' => string )
*/
function wmguru_render_preview( $profile, $attachment_id = 0, $overrides = array() ) {
list( $src, $mime ) = wmguru_preview_source( (int) $attachment_id );
$notice = '';
$tmp = wp_tempnam( 'wmguru-preview' );
if ( wmguru_effective_layers( $profile ) ) {
$result = wmguru_is_animated( $src, $mime ) ? new WP_Error( 'wmguru_animated', __( 'Animated images are shown and served unchanged.', 'filigrane' ) ) : wmguru_render_image( $src, $tmp, $profile, $mime, $overrides );
if ( is_wp_error( $result ) ) {
wp_delete_file( $tmp );
return $result;
}
$file = $tmp;
} else {
$notice = __( 'Nothing to draw yet: enable a layer and give it content.', 'filigrane' );
$file = $src;
}
$data = file_get_contents( $file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
wp_delete_file( $tmp );
if ( false === $data ) {
return new WP_Error( 'wmguru_preview_read', __( 'Could not read the rendered preview.', 'filigrane' ) );
}
return array(
'src' => 'data:' . $mime . ';base64,' . base64_encode( $data ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
'notice' => $notice,
);
}
/**
* Shared guard for the AJAX endpoints.
*/
function wmguru_ajax_guard() {
check_ajax_referer( 'wmguru_admin', 'nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => __( 'You are not allowed to do this.', 'filigrane' ) ), 403 );
}
}
function wmguru_ajax_preview() {
wmguru_ajax_guard();
$raw = isset( $_POST['wmguru_settings'] ) ? wp_unslash( $_POST['wmguru_settings'] ) : array(); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- nonce checked in wmguru_ajax_guard(); sanitized by wmguru_sanitize_settings().
$settings = wmguru_sanitize_settings( $raw );
$profile = array(
'id' => 'default',
'label' => '',
'layers' => $settings['layers'],
);
$attachment_id = isset( $_POST['preview_id'] ) ? absint( $_POST['preview_id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Missing
$result = wmguru_render_preview(
$profile,
$attachment_id,
array(
'quality' => $settings['quality'],
'driver' => $settings['driver'],
)
);
if ( is_wp_error( $result ) ) {
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
}
wp_send_json_success( $result );
}
add_action( 'wp_ajax_wmguru_preview', 'wmguru_ajax_preview' );
function wmguru_ajax_test_delivery() {
wmguru_ajax_guard();
$status = wmguru_run_delivery_test();
if ( true === $status['pretty'] ) {
wp_send_json_success( array( 'message' => __( 'Direct file URLs work.', 'filigrane' ) ) );
}
if ( false === $status['pretty'] ) {
wp_send_json_success( array( 'message' => __( 'Direct URLs do not work here; the fallback will be used.', 'filigrane' ) ) );
}
wp_send_json_error( array( 'message' => $status['error'] ? $status['error'] : __( 'The test could not run.', 'filigrane' ) ) );
}
add_action( 'wp_ajax_wmguru_test_delivery', 'wmguru_ajax_test_delivery' );
function wmguru_ajax_purge() {
wmguru_ajax_guard();
wmguru_purge_all();
wp_send_json_success( array( 'message' => __( 'Cache cleared.', 'filigrane' ) ) );
}
add_action( 'wp_ajax_wmguru_purge', 'wmguru_ajax_purge' );