This commit is contained in:
2024-05-20 15:37:46 +03:00
commit 00b7dbd0b7
10404 changed files with 3285853 additions and 0 deletions

View File

@ -0,0 +1,204 @@
<?php
/**
* Admin init
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* Add menu item to wp-admin
*/
function bodhi_svgs_admin_menu() {
$bodhi_svgs_options_page = add_options_page(
__('SVG Support Settings and Usage', 'svg-support'),
__('SVG Support', 'svg-support'),
'manage_options',
'svg-support',
'bodhi_svg_support_settings_page'
);
// load the help menu on the SVG Support settings page
add_action( 'load-' . $bodhi_svgs_options_page, 'bodhi_svgs_help_tab' );
require( BODHI_SVGS_PLUGIN_PATH . 'admin/svgs-settings-page-help.php' );
}
add_action( 'admin_menu', 'bodhi_svgs_admin_menu' );
/**
* Create settings page
*/
function bodhi_svg_support_settings_page() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( __('You can\'t play with this.', 'svg-support') );
}
// Swapped the global with this line to work with WordPress Bedrock on LEMP stack | https://wordpress.org/support/topic/settings-not-saving-24/
// global $bodhi_svgs_options;
$bodhi_svgs_options = get_option( 'bodhi_svgs_settings' );
require( BODHI_SVGS_PLUGIN_PATH . 'admin/svgs-settings-page.php' );
}
/**
* Sanitize class before saving
*/
function bodhi_sanitize_fields( $value ) {
global $bodhi_svgs_options;
$bodhi_plugin_version_stored = get_option( 'bodhi_svgs_plugin_version' );
if( !isset($value['sanitize_svg']) ) {
$value['sanitize_svg'] = "none";
}
if( !isset($value['sanitize_on_upload_roles']) ) {
$value['sanitize_on_upload_roles'] = array("none");
}
if( !isset($value['restrict']) ) {
$value['restrict'] = array("none");
}
$value['css_target'] = esc_attr( sanitize_text_field( $value['css_target'] ) );
if( $value['sanitize_svg_front_end'] !== 'on' ) {
$value['sanitize_svg_front_end'] = false;
}
return $value;
}
/**
* Register settings in the database
*/
function bodhi_svgs_register_settings() {
$args = array(
'sanitize_callback' => 'bodhi_sanitize_fields'
);
register_setting( 'bodhi_svgs_settings_group', 'bodhi_svgs_settings', $args );
}
add_action( 'admin_init', 'bodhi_svgs_register_settings' );
/**
* Advanced Mode Check
*
* Creates a usable function for conditionals around the plugin
*/
function bodhi_svgs_advanced_mode() {
global $bodhi_svgs_options;
if ( ! empty( $bodhi_svgs_options['advanced_mode'] ) ) {
return true;
} else {
return false;
}
}
add_action( 'admin_init', 'bodhi_svgs_advanced_mode' );
/**
* Screen check function
* Checks if current page is SVG Support settings page
*/
function bodhi_svgs_specific_pages_settings() {
// check current page
$screen = get_current_screen();
// check if we're on SVG Support settings page
if ( is_object( $screen ) && $screen->id == 'settings_page_svg-support' ) {
return true;
} else {
return false;
}
}
/**
* Screen check function
* Checks if the current page is the Media Library page
*/
function bodhi_svgs_specific_pages_media_library() {
// check current page
$screen = get_current_screen();
// check if we're on Media Library page
if ( is_object( $screen ) && $screen->id == 'upload' ) {
return true;
} else {
return false;
}
}
/**
* Screen check function
* Check if the current page is a post edit page
*/
function bodhi_svgs_is_edit_page( $new_edit = null ) {
global $pagenow;
if ( ! is_admin() ) return false;
if ( $new_edit == 'edit' ) {
return in_array( $pagenow, array( 'post.php', ) );
} elseif ( $new_edit == "new" ) { //check for new post page
return in_array( $pagenow, array( 'post-new.php' ) );
} else { //check for either new or edit
return in_array( $pagenow, array( 'post.php', 'post-new.php' ) );
}
}
/**
* Add rating text to footer on settings page
*/
function bodhi_svgs_admin_footer_text( $default ) {
if ( bodhi_svgs_specific_pages_settings() || bodhi_svgs_specific_pages_media_library() ) {
printf( __( 'If you like <strong>SVG Support</strong> please leave a %s&#9733;&#9733;&#9733;&#9733;&#9733;%s rating. A huge thanks in advance!', 'svg-support' ), '<a href="https://wordpress.org/support/view/plugin-reviews/svg-support?filter=5#postform" target="_blank" class="svgs-rating-link">', '</a>' );
} else {
return $default;
}
}
add_filter( 'admin_footer_text', 'bodhi_svgs_admin_footer_text' );

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,46 @@
<?php
/**
* Plugin action and row meta links
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* Add plugin_action_links
*/
function bodhi_svgs_plugin_action_links( $links ) {
return array_merge(
array(
'<a href="' . admin_url( 'options-general.php?page=svg-support' ) . '" title="' . __( 'SVG Support Settings', 'svg-support' ) . '">' . __( 'Settings', 'svg-support') . '</a>'
), $links
);
return $links;
}
add_filter( 'plugin_action_links_' . $plugin_file, 'bodhi_svgs_plugin_action_links' );
/**
* Add plugin_row_meta links
*/
function bodhi_svgs_plugin_meta_links( $links, $file ) {
$plugin_file = 'svg-support/svg-support.php';
if ( $file == $plugin_file ) {
return array_merge(
$links,
array(
'<a target="_blank" href="https://wordpress.org/support/plugin/svg-support">' . __( 'Get Support', 'svg-support') . '</a>',
'<a target="_blank" href="https://wordpress.org/support/plugin/svg-support/reviews/">' . __( 'Leave a Review', 'svg-support' ) . '</a>',
'<a target="_blank" href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Z9R7JERS82EQQ&source=url">' . __( 'Donate to author', 'svg-support') . '</a>'
)
);
}
return $links;
}
add_filter( 'plugin_row_meta', 'bodhi_svgs_plugin_meta_links', 10, 2 );

View File

@ -0,0 +1,160 @@
<?php
/**
* Add SVG Support help tab to the top of the settings page
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
function bodhi_svgs_help_tab () {
$screen = get_current_screen();
/**
* Overview Tab
*/
// overview tab content
$bodhi_svgs_help_tab_overview = '<h3>' . __( 'Overview', 'svg-support' ) . '</h3>';
$bodhi_svgs_help_tab_overview .= '<p>' . __( 'At it\'s core, SVG Support allows you to upload SVG files and use them as you would any regular image with the added benefit of being scalable and looking great on any screen size, no matter what size it\'s displayed. Additionally, SVG file sizes are (more often than not) much smaller than conventional image formats.', 'svg-support' ) . '</p><p>' . __( 'Even this most basic of usage is very powerful for modern websites, however, there\'s lots of cool stuff you can do with SVG files!', 'svg-support' ) . '</p><p>' . __( 'SVG Support features an "Advanced Mode" which toggles extra features, allowing you to take more control of how your SVG files are used. By rendering SVG files inline, it opens up a huge number of possibilities including animations, embedded links within the SVG, odd shaped link areas, custom CSS targeting elements within the SVG and whole lot more!', 'svg-support' ) . '</p><p>' . __( 'So let\'s get into some more details! Simply click the tabs to the left to get more of an understanding of how powerful SVG Support is.', 'svg-support' ) . '</p>';
// register overview tab
$screen->add_help_tab( array(
'id' => 'bodhi_svgs_help_tab-overview',
'title' => __( 'Overview', 'svg-support' ),
'content' => $bodhi_svgs_help_tab_overview
));
/**
* The Settings Tab
*/
// the settings tab content
$bodhi_svgs_help_tab_the_settings = '<h3>' . __( 'The Settings', 'svg-support' ) . '</h3>';
$bodhi_svgs_help_tab_the_settings .= '<p><strong>' . __( 'Restrict To Administrators:', 'svg-support' ) . '</strong><br>' . __( 'SVG files are actually XML code, so allowing regular users to upload them can pose serious security risks. Please leave this checked unless you really know what you\'re doing.', 'svg-support' ) . '</p>';
$bodhi_svgs_help_tab_the_settings .= '<p><strong>' . __( 'Enable Advanced Mode:', 'svg-support' ) . '</strong><br>' . __( 'When using SVG files like regular images just isn\'t enough ;)', 'svg-support' ) . '<br>' . __( 'Enabling "Advanced Mode" displays options to give you more control over how you use SVG files on your site. It also includes extra JS on the front end, so leave this disabled unless you\'re actually using any of the advanced features.', 'svg-support' ) . '</p>';
$bodhi_svgs_help_tab_the_settings .= '<p><strong>' . __( 'Output JS in Footer:', 'svg-support' ) . '</strong><br>' . __( 'This setting allows you to choose whether the SVG Support JS file is enqueued in the header or the footer of the site. Usually you would enqueue in the footer unless you need it to be loaded sooner for some reason.', 'svg-support' ) . '</p>';
$bodhi_svgs_help_tab_the_settings .= '<p><strong>' . __( 'Use Expanded JS:', 'svg-support' ) . '</strong><br>' . __( 'This setting gives you the choice of JS file that is enqueued, the full expanded version or the minified version. You would usually enqueue the minified version, but if you want to bundle the JS file using a caching or minification plugin or similar, then you might want to enqueue the expanded, non-minified version.', 'svg-support' ) . '</p>';
$bodhi_svgs_help_tab_the_settings .= '<p><strong>' . __( 'CSS Class To Target:', 'svg-support' ) . '</strong><br>' . __( 'This allows you to set your own custom class that you will use in your SVG source IMG tags that you would like rendered inline. For example, it might be easier for you to remember to add the class "inline-svg" or something, in which case you would use your desired class name in this field to be used across your site.', 'svg-support' ) . '</p>';
$bodhi_svgs_help_tab_the_settings .= '<p><strong>' . __( 'Automatically Insert Class:', 'svg-support' ) . '</strong><br>' . __( 'When this is checked, you won\'t have to add the class to your SVG files during the embed process in the editor. When you pick your SVG, it will be placed in the editor with just the SVG Support class and others stripped. It does not change existing code, it\'s only a helper to allow you to quickly embed your SVG files and have them render inline without having to fiddle with the classes.', 'svg-support' ) . '</p>';
$bodhi_svgs_help_tab_the_settings .= '<p><strong>' . __( 'Force Inline SVG:', 'svg-support' ) . '</strong><br>' . __( 'This feature is to force all SVG files that are found in your site to be rendered inline. This can help if you aren\'t able to set a custom class on your IMG tags for some reason, usually when used in theme options or page builder elements.', 'svg-support' ) . '</p>';
// register the settings tab
$screen->add_help_tab( array(
'id' => 'bodhi_svgs_help_tab-the_settings',
'title' => __( 'The Settings', 'svg-support' ),
'content' => $bodhi_svgs_help_tab_the_settings
));
/**
* Standard Usage Tab
*/
// standard usage tab content
$bodhi_svgs_help_tab_usage_standard = '<h3>' . __( 'Standard Usage', 'svg-support' ) . '</h3>';
$bodhi_svgs_help_tab_usage_standard .= '<p>' . __( 'You can simply upload SVG files to your media library like any other image.<br>Make sure to select "Restrict to Administrators" if you only want to allow admins to upload SVG files.', 'svg-support' ) . '</p>';
$bodhi_svgs_help_tab_usage_standard .= '<p>' . __( 'If you want to enable sanitization or minification of uploaded SVG files, please enable advanced settings and then enable sanitization and/or minification.', 'svg-support' ) . '</p>';
// register standard usage tab
$screen->add_help_tab( array(
'id' => 'bodhi_svgs_help_tab_usage-standard',
'title' => __( 'Standard Usage', 'svg-support' ),
'content' => $bodhi_svgs_help_tab_usage_standard
));
/**
* Inline SVG Tab
*/
// inline SVG tab content
$bodhi_svgs_help_tab_inlining_svg = '<h3>' . __( 'Render SVG Inline', 'svg-support' ) . '</h3>';
$bodhi_svgs_help_tab_inlining_svg .= '<p>' . __( 'You can embed your SVG image like any standard image with the addition of adding the class <code>style-svg</code> (or your custom class) to any IMG tags that you want this plugin to swap out with your actual SVG code.', 'svg-support' ) . '<br>' . __( 'For example:', 'svg-support' ) . '</p>';
$bodhi_svgs_help_tab_inlining_svg .= '<pre><code>&lt;img class="style-svg" alt="alt-text" src="image-source.svg" /&gt;</code></pre>' . __( 'or', 'svg-support' ) . '<pre><code>&lt;img class="your-custom-class" alt="alt-text" src="image-source.svg" /&gt;</code></pre>';
$bodhi_svgs_help_tab_inlining_svg .= '<p>' . __( 'The whole IMG tag element will now be dynamically replaced by the actual code of your SVG, making the inner content targetable.', 'svg-support' ) . '<br>' . __( 'This allows you to target elements within your SVG using CSS.', 'svg-support' ) . '</p>';
$bodhi_svgs_help_tab_inlining_svg .= '<p><em>' . __( 'Please Note:', 'svg-support' ) . '</em><br><em>- ' . __( 'You will likely need to set your own height and width in your CSS for SVG files to display correctly.', 'svg-support' ) . '</em><br><em>- ' . __( 'Your uploaded image needs to be an SVG file for this plugin to replace the img tag with the inline SVG code. It will not create SVG files for you.', 'svg-support' ) . '</em><br><em>- ' . __( 'You can set this target class on any element and the script will traverse all children of that target element looking for IMG tags with SVG in the src to replace.', 'svg-support' ) . '</em></p>';
// register inline SVG tab
$screen->add_help_tab( array(
'id' => 'bodhi_svgs_help_tab-inlining_svg',
'title' => __( 'Render SVG Inline', 'svg-support' ),
'content' => $bodhi_svgs_help_tab_inlining_svg
));
/**
* Featured Images Tab
*/
// featured images tab content
$bodhi_svgs_help_tab_featured_images = '<h3>' . __( 'Featured Images', 'svg-support' ) . '</h3>';
$bodhi_svgs_help_tab_featured_images .= '<p>' . __( 'You can use SVG files as featured images just like any other image format, with the addition of being able to render your featured SVG inline on a per-post basis.', 'svg-support' ) . '</p>';
$bodhi_svgs_help_tab_featured_images .= '<p>' . __( 'To render your featured SVG inline:', 'svg-support' ) . '</p>';
$bodhi_svgs_help_tab_featured_images .= '<ol><li>' . __( 'Make sure "Advanced Mode" is enabled.', 'svg-support' ) . '</li><li>' . __( 'Add your featured SVG like you would any regular featured image format.', 'svg-support' ) . '</li><li>' . __( 'Publish, Save Draft, or Update the post.', 'svg-support' ) . '</li><li>' . __( 'Once the screen reloads, click the new checkbox below the featured image to render your SVG inline.', 'svg-support' ) . '</li><li>' . __( 'Publish, Save Draft, or Update the post a final time to render the SVG inline.', 'svg-support' ) . '</li></ol>';
// register featured images tab
$screen->add_help_tab( array(
'id' => 'bodhi_svgs_help_tab-featured_images',
'title' => __( 'Featured Images', 'svg-support' ),
'content' => $bodhi_svgs_help_tab_featured_images
));
/**
* Animation Tab
*/
$bodhi_svgs_help_tab_animation = '<h3>' . __( 'Animation', 'svg-support' ) . '</h3>';
$bodhi_svgs_help_tab_animation .= '<p>' . __( 'So you want to animate your SVG?', 'svg-support' ) . '<br>' . __( 'There\'s a number of ways you can animate an SVG. You could use CSS or JS to target elements within your SVG or even embed the animations in the file itself. Whichever way you choose, there is always a little bit of preparation required before uploading your SVG to your media library.', 'svg-support' ) . '</p>';
$bodhi_svgs_help_tab_animation .= '<p><strong>' . __( 'First, let\'s talk about using CSS or JS to target elements within your SVG.', 'svg-support' ) . '</strong><br>' . __( 'Before you upload your SVG, you\'re going to need some classes to target inside your SVG. To do this, open your SVG file in the code editor of choice (I use Sublime Text). You will see each element within your SVG file written in XML code. Each little part of your SVG has it\'s own bit of code, so it\'s up to you which ones you want to target. It\'s in here that you\'ll place your new classes on each element you want to target.', 'svg-support' ) . '</p>';
$bodhi_svgs_help_tab_animation .= '<p>' . __( 'Then there\'s the option of animating the SVG file itself. There is a number of online tools to do this, or you can use the software of your choice. Once your SVG is animated and ready to go, you then upload it like any other image to your WordPress media library. When you embed it on a page/post, you will need to make sure to add the class to the IMG tag so SVG Support can render it inline. This will ensure your animations are displayed.', 'svg-support' ) . '</p>';
// register animation tab
$screen->add_help_tab( array(
'id' => 'bodhi_svgs_help_tab-animation',
'title' => __( 'Animation', 'svg-support' ),
'content' => $bodhi_svgs_help_tab_animation
));
/**
* DONATIONS Tab
*/
// donations tab content
$bodhi_svgs_help_tab_donations = '<h3>' . __( 'Donations', 'svg-support' ) . '</h3>';
$bodhi_svgs_help_tab_donations .= '<p>' . __( 'SVG Support (this plugin) has grown to be used by over 800,000 websites actively and is maintained solely by one person. I couldn\'t possibly tell you how many hours have gone into the development, maintenance and support of this plugin. If you find it useful and would like to donate to help keep it going, that would be amazing! I truly appreciate the support and how far this has come.', 'svg-support' ) . '</p>';
$bodhi_svgs_help_tab_donations .= '<p><strong>' . __( 'Donation Methods:', 'svg-support' ) . '</strong></p>';
$bodhi_svgs_help_tab_donations .= '<p><strong>' . __( 'PayPal: ', 'svg-support' ) . '</strong><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Z9R7JERS82EQQ" target="_blank">Click Here</a><br/><strong>' . __( 'BTC: 1qF8r2HkTLifND7WLGfWmvxfXc9ze55DZ', 'svg-support' ) . '</strong><br/><strong>' . __( 'LTC: LUnQPJrSk6cVFmMqBMv5FAqweJbnzRUz4o', 'svg-support' ) . '</strong><br/><strong>' . __( 'ETH: 0x599695Eb51aFe2e5a0DAD60aD9c89Bc8f10B54f4', 'svg-support' ) . '</strong></p>';
$bodhi_svgs_help_tab_donations .= '<p>' . __( 'Need to buy some crypto to donate?', 'svg-support' ) . '</br>' . __( 'My Coinbase referral link will get $10 USD worth of BTC for free when you spend $100.', 'svg-support' ) . '</br>' . __( '(You don\'t need to send me that much though, anything is appreciated!)', 'svg-support' ) . '<br/><a href="https://www.coinbase.com/join/59be646cb87715012bbdcc6b" target="_blank">https://www.coinbase.com/join/59be646cb87715012bbdcc6b</a></p>';
// register featured images tab
$screen->add_help_tab( array(
'id' => 'bodhi_svgs_help_tab-donations',
'title' => __( 'DONATIONS', 'svg-support' ),
'content' => $bodhi_svgs_help_tab_donations
));
/**
* Help Tab Sidebar
*/
// add help tab sidebar
$screen->set_help_sidebar(
'<p><strong>' . __( 'For more help, visit:' ) . '</strong></p>' .
'<p>' . __( '<a target="_blank" href="https://wordpress.org/support/plugin/svg-support">SVG Support Forum</a>' ) . '</p>'
);
}

View File

@ -0,0 +1,472 @@
<div class="wrap">
<div id="icon-upload" class="icon32"></div>
<h2><?php _e( 'SVG Support Settings and Usage', 'svg-support' ); ?><span class="svgs-version">Version <?php global $svgs_plugin_version; echo esc_attr($svgs_plugin_version); ?></span></h2>
<div id="poststuff">
<div class="meta-box-sortables ui-sortable">
<div class="postbox">
<h3><span><?php _e( 'Introduction', 'svg-support' ); ?></span></h3>
<div class="inside">
<p><?php _e( 'When using SVG images on your WordPress site, it can be hard to style elements within the SVG using CSS. <strong>Now you can, easily!</strong>', 'svg-support' ); ?></p>
<p><?php _e( 'When you enable advanced mode, this plugin not only provides SVG Support like the name says, it also allows you to easily embed your full SVG file\'s code using a simple IMG tag. By adding the class <code>style-svg</code> to your IMG elements, this plugin dynamically replaces any IMG elements containing the <code>style-svg</code> class with your complete SVG.', 'svg-support' ); ?></p>
<p><?php _e( 'The main purpose of this is to allow styling of SVG elements. Usually your styling options are restricted when using <code>embed</code>, <code>object</code> or <code>img</code> tags alone.', 'svg-support' ); ?></p>
<p><strong><?php _e( 'For help and more information, please check the help tab (top right of your screen).', 'svg-support' ); ?></strong></p>
</div> <!-- .inside -->
</div> <!-- .postbox -->
</div> <!-- .meta-box-sortables .ui-sortable -->
<div class="meta-box-sortables ui-sortable">
<div class="postbox">
<h3><span><?php _e( 'Send Some Love', 'svg-support' ); ?></span></h3>
<div class="inside">
<p><?php _e( 'SVG Support has grown to be installed on 800,000+ active websites. That\'s insane! It\'s developed and maintained by one person alone. If you find it useful, please consider donating to help keep it going. I truly appreciate any contribution.', 'svg-support' ); ?></p>
<p><strong>
<?php _e( 'BTC: 1qF8r2HkTLifND7WLGfWmvxfXc9ze55DZ', 'svg-support' ); ?><br/>
<?php _e( 'ETH: 0x599695Eb51aFe2e5a0DAD60aD9c89Bc8f10B54f4', 'svg-support' ); ?>
</strong></p>
<p><?php _e( 'You can also', 'svg-support' ); ?> <a target="_blank" href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Z9R7JERS82EQQ&source=url"><?php _e( 'Donate using PayPal', 'svg-support' ); ?></a></p>
</div> <!-- .inside -->
</div> <!-- .postbox -->
</div> <!-- .meta-box-sortables .ui-sortable -->
<div id="post-body" class="metabox-holder columns-2">
<!-- main content -->
<div id="post-body-content">
<div class="meta-box-sortables ui-sortable">
<div class="postbox">
<h3><span><?php _e( 'Settings', 'svg-support' ); ?></span></h3>
<div class="inside">
<form name="bodhi_svgs_settings_form" method="post" action="options.php">
<?php settings_fields('bodhi_svgs_settings_group'); ?>
<table class="form-table svg-settings">
<tr valign="top">
<!-- Swap with future feature: Multiselect Roles -->
<th scope="row">
<strong><?php _e( 'Restrict SVG Uploads to?', 'svg-support' ); ?></strong>
</th>
<td>
<div class="upload_allowed_roles">
<?php $allowed_roles_array = $bodhi_svgs_options['restrict']; ?>
<select style="display:none" name="bodhi_svgs_settings[restrict][]" multiple>
<?php
global $wp_roles;
$all_roles = $wp_roles->roles;
foreach ($all_roles as $role => $details) {
$user_role_slug = esc_attr($role);
$user_role_name = translate_user_role($details['name']);
$role_selected = "";
if( in_array($user_role_slug, $allowed_roles_array) ) {
$role_selected = "selected";
}
?>
<option value="<?php echo $user_role_slug; ?>" <?php echo $role_selected; ?> ><?php echo $user_role_name; ?></option>
<?php } ?>
</select>
</div>
</td>
</tr>
<tr valign="top">
<!-- Option to avoid CSS file loading on frontend -->
<th scope="row">
<strong><?php _e( 'Load frontend CSS?', 'svg-support' ); ?></strong>
</th>
<td>
<label for="bodhi_svgs_settings[frontend_css]">
<?php printf(
'<input id="bodhi_svgs_settings[frontend_css]" name="bodhi_svgs_settings[frontend_css]" type="checkbox" %2$s />', 'bodhi_svgs_settings_restrict', checked( isset( $bodhi_svgs_options['frontend_css'] ), true, false ) ); ?>
<?php _e( 'Yes', 'svg-support' ); ?><br /><small class="description"><?php _e('A very small piece of code that helps with displaying SVGs on the frontend in some cases.', 'svg-support' ); ?></small>
</label>
</td>
</tr>
<tr valign="top" id="sanitize_svg_option">
<!-- Allow sanitization of svg -->
<th scope="row">
<strong><?php _e( 'Sanitize SVG while uploading', 'svg-support' ); ?></strong>
</th>
<td>
<label style="margin-bottom: 10px;display:block" for="bodhi_svgs_settings[sanitize_svg]">
<?php printf(
'<input id="bodhi_svgs_settings[sanitize_svg]" name="bodhi_svgs_settings[sanitize_svg]" type="checkbox" %2$s />', 'bodhi_svgs_settings_sanitize_svg', checked( $bodhi_svgs_options['sanitize_svg'], 'on', false ) ); ?>
<?php _e( 'Yes', 'svg-support' ); ?><br />
</label>
<label id="sanitize_svg_option_sction">
<div class="sanitize_on_upload_roles">
<strong style="margin-bottom: 5px;display: block;"><?php _e( 'Do not sanitize for these roles', 'svg-support' ); ?></strong>
<?php $sanitize_roles_array = $bodhi_svgs_options['sanitize_on_upload_roles']; ?>
<select style="display:none" name="bodhi_svgs_settings[sanitize_on_upload_roles][]" multiple>
<?php
global $wp_roles;
$all_roles = $wp_roles->roles;
foreach ($all_roles as $role => $details) {
$user_role_slug = esc_attr($role);
$user_role_name = translate_user_role($details['name']);
$role_selected = "";
if( in_array($user_role_slug, $sanitize_roles_array) ) {
$role_selected = "selected";
}
?>
<option value="<?php echo $user_role_slug; ?>" <?php echo $role_selected; ?> ><?php echo $user_role_name; ?></option>
<?php } ?>
</select>
</div>
<small class="description"><?php _e('Enhance security of SVG uploads by sanitizing all svg images before being uploaded. This is helpful when non-admins are allowed to upload SVG images.<br><em>All external references are automatically removed during sanitization to prevent XSS and Injection attacks.</em>', 'svg-support' ); ?></small>
</label>
</td>
</tr>
<tr valign="top">
<!-- Allow minification of svg -->
<th scope="row">
<label for="bodhi_svgs_settings[minify_svg]"><strong><?php _e( 'Minify SVG', 'svg-support' ); ?></strong>
</th>
<td>
<label for="bodhi_svgs_settings[minify_svg]">
<?php printf(
'<input id="bodhi_svgs_settings[minify_svg]" name="bodhi_svgs_settings[minify_svg]" type="checkbox" %2$s />', 'bodhi_svgs_settings_minify_svg', checked( isset( $bodhi_svgs_options['minify_svg'] ), true, false ) ); ?>
<?php _e( 'Yes', 'svg-support' ); ?><br /><small class="description"><?php _e('Enabling this option will auto-minify all svg uploads. Sanitization must be turned on for minification to work.', 'svg-support' ); ?></small>
</label>
</td>
</tr>
<tr valign="top">
<!-- Delete all plugin's data upon deletion -->
<th scope="row">
<label for="bodhi_svgs_settings[del_plugin_data]"><strong><?php _e( 'Delete Plugin\'s Data', 'svg-support' ); ?></strong>
</th>
<td>
<label for="bodhi_svgs_settings[del_plugin_data]">
<?php printf(
'<input id="bodhi_svgs_settings[del_plugin_data]" name="bodhi_svgs_settings[del_plugin_data]" type="checkbox" %2$s />', 'bodhi_svgs_settings_del_plugin_data', checked( isset( $bodhi_svgs_options['del_plugin_data'] ), true, false ) ); ?>
<?php _e( 'Yes', 'svg-support' ); ?><br /><small class="description"><?php _e('Delete all plugin\'s data during uninstallation process.', 'svg-support' ); ?></small>
</label>
</td>
</tr>
<tr valign="top" class="svgs-simple">
<!-- Simple/Advanced mode selector -->
<th scope="row">
<strong><?php _e( 'Enable Advanced Mode?', 'svg-support' ); ?></strong>
</th>
<td>
<label for="bodhi_svgs_settings[advanced_mode]">
<?php printf(
'<input id="bodhi_svgs_settings[advanced_mode]" name="bodhi_svgs_settings[advanced_mode]" type="checkbox" %2$s />', 'bodhi_svgs_settings_advanced_mode', checked( isset( $bodhi_svgs_options['advanced_mode'] ), true, false ) ); ?>
<?php _e( 'Yes', 'svg-support' ); ?><br /><small class="description"><?php _e(' You don\'t need to enable this to simply use SVG files as images. Enabling this will trigger advanced options and SVG functionality such as inline rendering.', 'svg-support' ); ?></small>
</label>
</td>
</tr>
<tr valign="top" class="svgs-advanced">
<!-- Advanced Header -->
<th scope="row">
<h3 class="inner-title"><?php _e( 'Advanced', 'svg-support' ); ?></h3>
</th>
<td>
<hr>
</td>
</tr>
<tr valign="top" class="svgs-advanced">
<!-- Allow sanitization of svg on Front-end -->
<th scope="row">
<strong><?php _e( 'Sanitize SVG on Front-end', 'svg-support' ); ?></strong>
</th>
<td>
<label for="bodhi_svgs_settings[sanitize_svg_front_end]">
<?php printf(
'<input id="bodhi_svgs_settings[sanitize_svg_front_end]" name="bodhi_svgs_settings[sanitize_svg_front_end]" type="checkbox" %2$s />', 'bodhi_svgs_settings_sanitize_svg_front_end', checked( $bodhi_svgs_options['sanitize_svg_front_end'], 'on', false ) ); ?>
<?php _e( 'Yes', 'svg-support' ); ?><br /><small class="description"><?php _e('Enhance security by sanitizing svg images on Front-end. This will help to prevent XSS and Injection attacks.', 'svg-support' ); ?></small>
</label>
</td>
</tr>
<tr valign="top" class="svgs-advanced">
<!-- Allow choice of js in footer true or false -->
<th scope="row">
<strong><?php _e( 'Output JS in Footer?', 'svg-support' ); ?></strong>
</th>
<td>
<label for="bodhi_svgs_settings[js_foot_choice]">
<?php printf(
'<input id="bodhi_svgs_settings[js_foot_choice]" name="bodhi_svgs_settings[js_foot_choice]" type="checkbox" %2$s />', 'bodhi_svgs_settings_js_foot_choice', checked( isset( $bodhi_svgs_options['js_foot_choice'] ), true, false ) ); ?>
<?php _e( 'Yes', 'svg-support' ); ?><br /><small class="description"><?php _e(' Normally, scripts are placed in <code>head</code> of the HTML document. If "Yes" is selected, the script is placed before the closing <code>body</code> tag. This requires the theme to have the <code>wp_footer()</code> template tag in the appropriate place.', 'svg-support' ); ?></small>
</label>
</td>
</tr>
<tr valign="top" class="svgs-advanced">
<!-- Select whether to use vanilla Js or jQuery -->
<th scope="row">
<strong><?php _e( 'Use Vanilla JS?', 'svg-support' ); ?></strong>
</th>
<td>
<label for="bodhi_svgs_settings[use_vanilla_js]">
<?php printf(
'<input id="bodhi_svgs_settings[use_vanilla_js]" name="bodhi_svgs_settings[use_vanilla_js]" type="checkbox" %2$s />', 'bodhi_svgs_settings_use_vanilla_js', checked( isset( $bodhi_svgs_options['use_vanilla_js'] ), true, false ) ); ?>
<?php _e( 'Yes', 'svg-support' ); ?><br /><small class="description"><?php _e(' Checking this will use vanilla JS file instead of the jQuery.', 'svg-support' ); ?></small>
</label>
</td>
</tr>
<tr valign="top" class="svgs-advanced">
<!-- Select whether to use minified or expanded JS file -->
<th scope="row">
<strong><?php _e( 'Use Expanded JS?', 'svg-support' ); ?></strong>
</th>
<td>
<label for="bodhi_svgs_settings[use_expanded_js]">
<?php printf(
'<input id="bodhi_svgs_settings[use_expanded_js]" name="bodhi_svgs_settings[use_expanded_js]" type="checkbox" %2$s />', 'bodhi_svgs_settings_use_expanded_js', checked( isset( $bodhi_svgs_options['use_expanded_js'] ), true, false ) ); ?>
<?php _e( 'Yes', 'svg-support' ); ?><br /><small class="description"><?php _e(' Checking this will use the expanded JS file instead of the minified JS file. Useful if you want to minify this externally using a caching plugin or similar.', 'svg-support' ); ?></small>
</label>
</td>
</tr>
<tr valign="top" class="svgs-advanced">
<!-- Custom CSS target field so users can set their own class to target -->
<th scope="row">
<strong><?php _e( 'CSS Class to target', 'svg-support' ); ?></strong>
</th>
<td>
<label for="bodhi_svgs_settings[css_target]">
<input id="bodhi_svgs_settings[css_target]" class="all-options code" name="bodhi_svgs_settings[css_target]" type="text" value="<?php if( isset( $bodhi_svgs_options['css_target'] ) ) echo esc_attr($bodhi_svgs_options['css_target']); ?>"><br />
<small class="description"><?php _e( 'The default target class is <code>style-svg</code>. You can change it to your own class such as <code>my-class</code> by typing it here. Leave blank to use the default class.<br><em>Plugin can now go any level down to find your SVG! It will keep looking as long as the element with the target class has children. If it finds any IMG tags with .svg in the src URL, it will replace the IMG tag with your SVG code.</em>', 'svg-support' ); ?></small>
</label>
</td>
</tr>
<tr valign="top" class="svgs-advanced">
<!-- Automatically insert class to target in images on front end page via jQuery -->
<th scope="row">
<strong><?php _e( 'Force Inline SVG?', 'svg-support' ); ?></strong></label>
</th>
<td>
<label for="bodhi_svgs_settings[force_inline_svg]">
<?php printf(
'<input id="bodhi_svgs_settings[force_inline_svg]" name="bodhi_svgs_settings[force_inline_svg]" type="checkbox" %2$s />', 'bodhi_svgs_settings_force_inline_svg', checked( isset( $bodhi_svgs_options['force_inline_svg'] ), true, false ) ); ?>
<?php _e( 'Yes', 'svg-support' ); ?><br /><small class="description"><?php _e(' <strong>Use with caution!</strong> Checking this will automatically add the SVG class to ALL image tags containing SVG file sources in the rendered HTML via javascript and will therefore render all of your SVG files inline.<br /><em>Use case scenario: When using a visual builder such as in the Divi Theme or The Divi Builder, the class is not automatically added with the "Automatically insert class?" option selected or the builder module doesn\'t give you the option to manually add a CSS class directly to your image.</em>', 'svg-support' ); ?></small>
</label>
</td>
</tr>
<tr valign="top" class="svgs-advanced">
<!-- Classic Editor Options Header -->
<th scope="row">
<h3 class="inner-title"><?php _e( 'Settings for Classic Editor', 'svg-support' ); ?></h3>
</th>
<td>
<hr>
</td>
</tr>
<tr valign="top" class="svgs-advanced">
<!-- Automatically insert class to target in images when inserting into posts/pages from admin edit screen -->
<th scope="row">
<strong><?php _e( 'Automatically insert class?', 'svg-support' ); ?></strong></label>
</th>
<td>
<label for="bodhi_svgs_settings[auto_insert_class]">
<?php printf(
'<input id="bodhi_svgs_settings[auto_insert_class]" name="bodhi_svgs_settings[auto_insert_class]" type="checkbox" %2$s />', 'bodhi_svgs_settings_auto_insert_class', checked( isset( $bodhi_svgs_options['auto_insert_class'] ), true, false ) ); ?>
<?php _e( 'Yes', 'svg-support' ); ?><br /><small class="description"><?php _e(' Checking this will make sure that either the default class or the custom one you set in <b>"CSS Class to target"</b> option will be inserted into the style attributes of <code>img</code> tags when you insert SVG images into a post. Additionally, it will remove all of the default WordPress classes. It will leave normal image types as default and only affect SVG files.', 'svg-support' ); ?></small>
</label>
</td>
</tr>
</table>
<p>
<input class="button-primary" type="submit" name="bodhi_svgs_settings_submit" value="<?php _e( 'Save Changes', 'svg-support' ); ?>" />
</p>
</form>
</div> <!-- .inside -->
</div> <!-- .postbox -->
<div class="postbox">
<?php
if ( empty( $bodhi_svgs_options['advanced_mode'] ) ) {
echo '<h3><span>';
_e( 'Usage', 'svg-support' );
echo '</span></h3>';
} else {
echo '<h3><span>';
_e( 'Advanced Usage', 'svg-support' );
echo '</span></h3>';
}
?>
<div class="inside">
<p><?php _e( 'You can simply upload SVG files to your media library like any other image. Make sure to select "Restrict to Administrators" if you only want to allow admins to upload SVG files.', 'svg-support' ); ?></p>
<div class="svgs-advanced">
<p>
<?php _e( 'Now, embed your SVG image like a standard image with the addition of adding the class <code>style-svg</code> (or your custom class from above) to any IMG tags that you want this plugin to swap out with your actual SVG code.', 'svg-support' ); ?><br />
<?php _e( 'You can even use the class on an outer container and it will traverse all child elements to find all of the IMG tags with SVG files in the src and replace them.', 'svg-support' ); ?>
</p>
<p>
<?php _e( 'For example:', 'svg-support' ); ?>
<pre><code>&lt;img class="style-svg" alt="alt-text" src="image-source.svg" /&gt;</code></pre>
<?php _e( 'or', 'svg-support' ); ?>
<pre><code>&lt;img class="your-custom-class" alt="alt-text" src="image-source.svg" /&gt;</code></pre>
</p>
<p>
<?php _e( 'The whole IMG tag element will now be dynamically replaced by the actual code of your SVG, making the inner content targetable.', 'svg-support' ); ?><br />
<?php _e( 'This allows you to target elements within your SVG using CSS.', 'svg-support' ); ?>
</p>
<p><em><?php _e( 'Please Note:', 'svg-support' ); ?></em>
<br><em><?php _e( '- You will need to set your own height and width in your CSS for SVG files to display correctly.', 'svg-support' ); ?></em>
<br><em><?php _e( '- Your uploaded image needs to be an SVG file for this plugin to replace the img tag with the inline SVG code. It will not create SVG files for you.', 'svg-support' ); ?></em>
<br><em><?php _e( '- You can set this target class on any element and the script will traverse all children of that target element looking for IMG tags with SVG in the src to replace.', 'svg-support' ); ?></em></p>
</div>
</div> <!-- .inside -->
</div> <!-- .postbox -->
<div class="postbox">
<h3><span><?php _e( 'Compress and Optimize Images with ShortPixel', 'svg-support' ); ?></span></h3>
<div class="inside">
<?php echo '<a target="_blank" class="shortpixel-logo" href="https://shortpixel.com/h/af/OLKMLXE207471"><img src="' . BODHI_SVGS_PLUGIN_URL . '/admin/img/shortpixel.png" /></a>'; ?>
<p><?php _e( 'Now that you\'ve set up SVG Support on your site, it\'s time to look at optimizing your existing images (jpg & png).', 'svg-support' ); ?></p>
<p><?php _e( 'ShortPixel improves website performance by reducing the size of your images. The results are no different in quality from the original, plus your originals are stored in a backup folder for you.', 'svg-support' ); ?></p>
<p><?php _e( 'If you upgrade to a paid plan, I\'ll receive a small commission... And that\'s really nice!', 'svg-support' ); ?></p>
<p><a class="shortpixel-button button-primary" href="https://shortpixel.com/h/af/OLKMLXE207471"><?php _e( 'Try ShortPixel WordPress Plugin for FREE', 'svg-support' ); ?></a></p>
</div> <!-- .inside -->
</div> <!-- .postbox -->
<div class="postbox">
<h3><span><?php _e( 'Animate and Optimize your SVG files using these open source projects', 'svg-support' ); ?></span></h3>
<div class="inside">
<p><a href="https://maxwellito.github.io/vivus-instant/" target="_blank">Vivus Instant for SVG animation</a> <?php _e( 'Upload your SVG files and use the tools provided to animate strokes.', 'svg-support' ); ?></p>
<p><a href="https://jakearchibald.github.io/svgomg/" target="_blank">SVGOMG for SVG optimisation</a> <?php _e( 'An online tool to optimize your SVG files.', 'svg-support' ); ?></p>
</div> <!-- .inside -->
</div> <!-- .postbox -->
</div> <!-- .meta-box-sortables .ui-sortable -->
</div> <!-- post-body-content -->
<!-- sidebar -->
<div id="postbox-container-1" class="postbox-container">
<div class="meta-box-sortables">
<div class="postbox">
<h3><span><?php _e( 'Ratings & Reviews', 'svg-support' ); ?></span></h3>
<div class="inside">
<p><?php _e( 'If you like <strong>SVG Support</strong> please consider leaving a', 'svg-support' ); ?> <a href="https://wordpress.org/support/view/plugin-reviews/svg-support?filter=5#postform" target="_blank" class="svgs-rating-link">&#9733;&#9733;&#9733;&#9733;&#9733;</a> <?php _e( 'rating.', 'svg-support' ); ?><br><?php _e( 'A huge thanks in advance!', 'svg-support' ); ?></p>
<p><a href="https://wordpress.org/support/view/plugin-reviews/svg-support?filter=5#postform" target="_blank" class="button-primary">Leave a rating</a></p>
</div> <!-- .inside -->
</div> <!-- .postbox -->
<div class="postbox">
<h3><span><?php _e( 'Having Issues?', 'svg-support' ); ?></span></h3>
<div class="inside">
<p><?php _e( 'I\'m always happy to help out!', 'svg-support' ); ?>
<br><?php _e( 'Support is handled exlusively through WordPress.org by my one man team - me.', 'svg-support' ); ?></p>
<p><a href="https://wordpress.org/support/plugin/svg-support/" target="_blank" class="button-primary">Get Support</a></p>
</div> <!-- .inside -->
</div> <!-- .postbox -->
<div class="postbox">
<h3><span><?php _e( 'SVG Support Features', 'svg-support' ); ?></span></h3>
<div class="inside">
<ul>
<li><strong><?php _e( 'Basic Use', 'svg-support' ); ?></strong></li>
<li><?php _e( 'SVG Support for your media library', 'svg-support' ); ?></li>
<li><?php _e( 'Restrict to Administrators only', 'svg-support' ); ?></li>
<hr>
<li><strong><?php _e( 'Advanced Mode', 'svg-support' ); ?></strong></li>
<li><?php _e( 'Sanitize SVG files on upload', 'svg-support' ); ?></li>
<li><?php _e( 'Style SVG elements using CSS', 'svg-support' ); ?></li>
<li><?php _e( 'Animate SVG using CSS or JS', 'svg-support' ); ?></li>
<li><?php _e( 'Include multiple URL\'s inside single SVG', 'svg-support' ); ?></li>
<li><?php _e( 'Use odd shapes as links', 'svg-support' ); ?></li>
<li><?php _e( 'Inline SVG featured image support', 'svg-support' ); ?></li>
<li><?php _e( 'Force all SVG files to be rendered inline', 'svg-support' ); ?></li>
</ul>
</div> <!-- .inside -->
</div> <!-- .postbox -->
<div class="postbox">
<h3><span><?php _e( 'About The Plugin', 'svg-support' ); ?></span></h3>
<div class="inside">
<p><?php _e( 'Learn more about SVG Support on:', 'svg-support' ); ?><br/><a target="_blank" href="http://wordpress.org/plugins/svg-support/"><?php _e( 'The WordPress Plugin Repository', 'svg-support' ); ?></a></p>
<p><?php _e( 'Need help?', 'svg-support' ); ?><br/><a target="_blank" href="http://wordpress.org/support/plugin/svg-support"><?php _e( 'Visit The Support Forum', 'svg-support' ); ?></a></p>
<p><?php _e( 'Follow', 'svg-support' ); ?> <a target="_blank" href="https://twitter.com/svgsupport"><?php _e( '@SVGSupport', 'svg-support' ); ?></a> <?php _e( 'on Twitter', 'svg-support' ); ?></p>
<p>&copy; <?php _e( 'Benbodhi', 'svg-support' ); ?> | <a target="_blank" href="https://benbodhi.com/">Benbodhi.com</a></p>
<p><?php _e( 'Thanks for your support, please consider donating.', 'svg-support' ); ?><br/><a target="_blank" href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Z9R7JERS82EQQ&source=url"><?php _e( 'Donate using PayPal', 'svg-support' ); ?></a></p>
</div> <!-- .inside -->
</div> <!-- .postbox -->
</div> <!-- .meta-box-sortables -->
</div> <!-- #postbox-container-1 .postbox-container -->
</div> <!-- #post-body .metabox-holder .columns-2 -->
<br class="clear">
</div> <!-- #poststuff -->
</div> <!-- .wrap -->

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
body #set-post-thumbnail,body #postimagediv .inside img[src$=".svg"]{width:100%}body .block-editor__container .components-panel .editor-post-featured-image__container img[src$='.svg']{position:relative}body .block-editor__container .edit-post-sidebar .components-panel .components-checkbox-control{margin-top:10px}

View File

@ -0,0 +1 @@
.svgs-advanced{display:none}

View File

@ -0,0 +1 @@
.svgs-version{font-size:10px;margin-left:7px}.postbox .inside a{text-decoration:none}h3{padding:0 12px}.help-tab-content h3{padding:0}h3.inner-title{padding:0;font-size:1.2em}.metabox-holder .postbox>h3,.metabox-holder .stuffbox>h3,.metabox-holder h2.hndle,.metabox-holder h3.hndle{font-size:1.3em;font-weight:600}.shortpixel-logo img{position:absolute;right:10px;bottom:0;width:140px}table.media .column-title .media-icon img{width:60px}

View File

@ -0,0 +1 @@
.attachment svg,.widget_media_image svg{max-width:100%;height:auto}

View File

@ -0,0 +1,328 @@
<?php
/**
* Display SVG in attachment modal
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
function bodhi_svgs_response_for_svg( $response, $attachment, $meta ) {
if ( $response['mime'] == 'image/svg+xml' && empty( $response['sizes'] ) ) {
$svg_path = get_attached_file( $attachment->ID );
if ( ! file_exists( $svg_path ) ) {
// If SVG is external, use the URL instead of the path
$svg_path = $response['url'];
}
$dimensions = bodhi_svgs_get_dimensions( $svg_path );
$response['sizes'] = array(
'full' => array(
'url' => $response['url'],
'width' => $dimensions->width,
'height' => $dimensions->height,
'orientation' => $dimensions->width > $dimensions->height ? 'landscape' : 'portrait'
)
);
}
return $response;
}
add_filter( 'wp_prepare_attachment_for_js', 'bodhi_svgs_response_for_svg', 10, 3 );
function bodhi_svgs_get_dimensions( $svg ) {
$svg = simplexml_load_file( $svg );
if ( $svg === FALSE ) {
$width = '0';
$height = '0';
} else {
$attributes = $svg->attributes();
$width = (string) $attributes->width;
$height = (string) $attributes->height;
}
return (object) array( 'width' => $width, 'height' => $height );
}
/**
* Generate attachment metadata (Thanks @surml)
* Fixes Illegal String Offset Warning for Height & Width
*/
function bodhi_svgs_generate_svg_attachment_metadata( $metadata, $attachment_id ) {
$mime = get_post_mime_type( $attachment_id );
if ( $mime == 'image/svg+xml' ) {
$svg_path = get_attached_file( $attachment_id );
$upload_dir = wp_upload_dir();
// get the path relative to /uploads/ - found no better way:
$relative_path = str_replace($upload_dir['basedir'], '', $svg_path);
$filename = basename( $svg_path );
$dimensions = bodhi_svgs_get_dimensions( $svg_path );
$metadata = array(
'width' => intval($dimensions->width),
'height' => intval($dimensions->height),
'file' => $filename
);
$height = intval($dimensions->height);
$width = intval($dimensions->width);
// Might come in handy to create the sizes array too - But it's not needed for this workaround! Always links to original svg-file => Hey, it's a vector graphic! ;)
$sizes = array();
foreach ( get_intermediate_image_sizes() as $s ) {
$sizes[$s] = array( 'width' => '', 'height' => '', 'crop' => false );
// for svg with width and height set, we need to adjust the thumbnails accordingly
if ( $width !== 0 && $height !== 0 ) {
// follow width of request size e.g. 150, 300 etc..
if ( isset( $_wp_additional_image_sizes[$s]['width'] ) ) {
$width_current_size = intval( $_wp_additional_image_sizes[$s]['width'] );
} else {
$width_current_size = get_option( "{$s}_size_w" );
}
// we have dimensions available. Use them
if ( $width > $height ) {
$ratio = round($width / $height,2);
$new_height = round($width_current_size / $ratio);
} else {
$ratio = round($height / $width,2);
$new_height = round($width_current_size * $ratio);
}
$sizes[$s]['width'] = $width_current_size;
$sizes[$s]['height'] = $new_height;
// svgs can't be cropped by WP
$sizes[$s]['crop'] = false;
} else {
// no change is needed
if ( isset( $_wp_additional_image_sizes[$s]['width'] ) ) {
$sizes[$s]['width'] = intval( $_wp_additional_image_sizes[$s]['width'] ); // For theme-added sizes
} else {
$sizes[$s]['width'] = get_option( "{$s}_size_w" ); // For default sizes set in options
}
if ( isset( $_wp_additional_image_sizes[$s]['height'] ) ) {
$sizes[$s]['height'] = intval( $_wp_additional_image_sizes[$s]['height'] ); // For theme-added sizes
} else {
$sizes[$s]['height'] = get_option( "{$s}_size_h" ); // For default sizes set in options
}
if ( isset( $_wp_additional_image_sizes[$s]['crop'] ) ) {
$sizes[$s]['crop'] = intval( $_wp_additional_image_sizes[$s]['crop'] ); // For theme-added sizes
} else {
$sizes[$s]['crop'] = get_option( "{$s}_crop" ); // For default sizes set in options
}
}
$sizes[$s]['file'] = $filename;
$sizes[$s]['mime-type'] = 'image/svg+xml';
}
$metadata['sizes'] = $sizes;
}
return $metadata;
}
add_filter( 'wp_generate_attachment_metadata', 'bodhi_svgs_generate_svg_attachment_metadata', 10, 3 );
/*
* SVG Sanitization
* Only triggers when its enabled by admin
*/
function bodhi_svgs_sanitize( $file ){
global $sanitizer;
$sanitizer->setAllowedTags( new bodhi_svg_tags() );
$sanitizer->setAllowedAttrs( new bodhi_svg_attributes() );
$dirty = file_get_contents( $file );
// try to decode if gzipped is enabled
if ( $is_zipped = bodhi_svgs_is_gzipped( $dirty ) ) {
$dirty = gzdecode( $dirty );
// return on failure, since we can't read file
if ( $dirty === false ) {
return false;
}
}
// remove remote references since they are dangerous and lead to injection
$sanitizer->removeRemoteReferences(true);
// enable minify in library if its enabled in admin panel
bodhi_svgs_minify();
$clean = $sanitizer->sanitize( $dirty );
if ( $clean === false ) {
return false;
}
// if we were gzipped, we need to re-zip
if ( $is_zipped ) {
$clean = gzencode( $clean );
}
file_put_contents( $file, $clean );
return true;
}
function bodhi_svgs_minify() {
global $bodhi_svgs_options;
global $sanitizer;
if ( !empty($bodhi_svgs_options['minify_svg']) && $bodhi_svgs_options['minify_svg'] === 'on' ) {
$sanitizer->minify(true);
}
}
function bodhi_svgs_is_gzipped( $contents ) {
if ( function_exists( 'mb_strpos' ) ) {
return 0 === mb_strpos( $contents, "\x1f" . "\x8b" . "\x08" );
} else {
return 0 === strpos( $contents, "\x1f" . "\x8b" . "\x08" );
}
}
function bodhi_svgs_sanitize_svg( $file ) {
global $bodhi_svgs_options;
if ( !empty($bodhi_svgs_options['sanitize_svg']) && $bodhi_svgs_options['sanitize_svg'] === 'on' && $bodhi_svgs_options['sanitize_on_upload_roles'][0] != "none" ) {
if ( $file['type'] === 'image/svg+xml' ) {
$sanitize_on_upload_roles_array = array();
$should_sanitize_svg = array();
$sanitize_on_upload_roles_array = (array) $bodhi_svgs_options['sanitize_on_upload_roles'];
$user = wp_get_current_user();
$current_user_roles = ( array ) $user->roles;
$should_sanitize_svg = array_intersect($sanitize_on_upload_roles_array, $current_user_roles);
if( empty($should_sanitize_svg) ) {
// Do nothing Here
}
elseif ( ! bodhi_svgs_sanitize( $file['tmp_name'] ) ) {
$file['error'] = __( "Sorry, this file couldn't be sanitized for security reasons and wasn't uploaded",
'safe-svg' );
}
}
}
return $file;
}
// Sanitize svg if user has enabled option
add_filter( 'wp_handle_upload_prefilter', 'bodhi_svgs_sanitize_svg' );
// Fix image widget PHP warnings
function bodhi_svgs_get_attachment_metadata( $data ) {
$res = $data;
if ( !isset( $data['width'] ) || !isset( $data['height'] ) ) {
$res = false;
}
return $res;
}
// add_filter( 'wp_get_attachment_metadata' , 'bodhi_svgs_get_attachment_metadata' );
// Commented this out 20200307 because it was stripping metadata from other attachments as well. Need to make this target only SVG attachments.
// remove srcset for svg images
function bodhi_svgs_disable_srcset( $sources ) {
$first_element = reset($sources);
if ( isset($first_element) && !empty($first_element['url']) ) {
$ext = pathinfo(reset($sources)['url'], PATHINFO_EXTENSION);
if ( $ext == 'svg' ) {
// return empty array
$sources = array();
return $sources;
} else {
return $sources;
}
} else {
return $sources;
}
}
add_filter( 'wp_calculate_image_srcset', 'bodhi_svgs_disable_srcset' );
// fix for division by zero error for SVGs
// proposed by starsis
// https://github.com/WordPress/gutenberg/issues/36603
function bodhi_svgs_dimension_fallback( $image, $attachment_id, $size, $icon ) {
// only manipulate for svgs
if ( get_post_mime_type($attachment_id) == 'image/svg+xml' ) {
if ( isset($image[1]) && $image[1] === 0 ) {
$image[1] = 1;
}
if ( isset($image[2]) && $image[2] === 0 ) {
$image[2] = 1;
}
}
return $image;
}
add_filter( 'wp_get_attachment_image_src', 'bodhi_svgs_dimension_fallback', 10, 4 );

View File

@ -0,0 +1,58 @@
<?php
/**
* Attribute Control
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* If in Advanced Mode
*/
if ( bodhi_svgs_advanced_mode() ) {
/**
* Strip HTML of all attributes and add custom class if the file is .svg
*/
function bodhi_svgs_auto_insert_class( $html, $alt='' ) {
global $bodhi_svgs_options;
if ( ! empty( $bodhi_svgs_options['css_target'] ) ) {
// if custom class is set, use it
$class = $bodhi_svgs_options['css_target'];
} else {
// if no custom class set, use default
$class = 'style-svg';
}
// check if the src file has .svg extension
if ( strpos( $html, '.svg' ) !== FALSE ) {
// strip html for svg files
$html = preg_replace( '/(width|height|title|alt|class)=".*"\s/', 'class="' . esc_attr($class) . '"', $html );
} else {
// leave html intact for non-svg
$html = $html;
}
return $html;
}
/**
* Fire auto insert class
*/
if ( ! empty( $bodhi_svgs_options['auto_insert_class'] ) ) {
add_filter( 'image_send_to_editor', 'bodhi_svgs_auto_insert_class', 10 );
// add_filter( 'post_thumbnail_html', 'bodhi_svgs_auto_insert_class', 10 );
}
}

View File

@ -0,0 +1,224 @@
<?php
/**
* Enqueue scripts and styles
* This file is to enqueue the scripts and styles both admin and front end
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* Enqueue the admin CSS using screen check functions
*/
function bodhi_svgs_admin_css() {
// check if user is on SVG Support settings page or media library page
if ( bodhi_svgs_specific_pages_settings() || bodhi_svgs_specific_pages_media_library() ) {
// enqueue the admin CSS
wp_enqueue_style( 'bodhi-svgs-admin', BODHI_SVGS_PLUGIN_URL . 'css/svgs-admin.css' );
}
// check if user is on SVG Support settings page and not in "Advanced Mode"
if ( bodhi_svgs_specific_pages_settings() && ! bodhi_svgs_advanced_mode() ) {
// enqueue the simple mode admin CSS
wp_enqueue_style( 'bodhi-svgs-admin-simple-mode', BODHI_SVGS_PLUGIN_URL . 'css/svgs-admin-simple-mode.css' );
}
// check if user is on an edit post page
if ( bodhi_svgs_is_edit_page() ) {
// enqueue the edit post CSS
wp_enqueue_style( 'bodhi-svgs-admin-edit-post', BODHI_SVGS_PLUGIN_URL . 'css/svgs-admin-edit-post.css' );
}
}
add_action( 'admin_enqueue_scripts', 'bodhi_svgs_admin_css' );
function bodhi_svgs_admin_multiselect() {
wp_enqueue_style( 'CSS-for-multiselect', BODHI_SVGS_PLUGIN_URL . 'css/jquery.dropdown-min.css' );
wp_enqueue_script('js-for-multiselect', BODHI_SVGS_PLUGIN_URL . 'js/min/jquery.dropdown-min.js', array( 'jquery' ));
wp_enqueue_script('cstm-js-for-multiselect', BODHI_SVGS_PLUGIN_URL . 'js/min/cstm.js.multiselect-min.js', array( 'jquery' ));
wp_add_inline_script( 'js-for-multiselect', 'jQuery(document).ready(function(){jQuery(".upload_allowed_roles").dropdown({multipleMode: "label",input: \'<input type="text" maxLength="20" placeholder="Search">\',searchNoData: \'<li style="color:#ddd">No Results</li>\'});});', "after" );
wp_add_inline_script( 'js-for-multiselect', 'jQuery(document).ready(function(){jQuery(".sanitize_on_upload_roles").dropdown({multipleMode: "label",input: \'<input type="text" maxLength="20" placeholder="Search">\',searchNoData: \'<li style="color:#ddd">No Results</li>\'});});', "after" );
}
add_action( 'admin_enqueue_scripts', 'bodhi_svgs_admin_multiselect' );
/*
* Enqueue Block editor JS
*/
function bodhi_svgs_block_editor() {
if ( bodhi_svgs_advanced_mode() ) {
wp_enqueue_script('bodhi-svgs-gutenberg-filters', BODHI_SVGS_PLUGIN_URL . '/js/gutenberg-filters.js', ['wp-edit-post']);
}
}
add_action( 'enqueue_block_editor_assets', 'bodhi_svgs_block_editor' );
/**
* Enqueue front end CSS
*/
function bodhi_svgs_frontend_css() {
// get the settings
global $bodhi_svgs_options;
if ( ! empty( $bodhi_svgs_options['frontend_css'] ) ) {
// enqueue attachment CSS
wp_enqueue_style( 'bodhi-svgs-attachment', BODHI_SVGS_PLUGIN_URL . 'css/svgs-attachment.css' );
}
}
add_action( 'wp_enqueue_scripts', 'bodhi_svgs_frontend_css' );
/**
* Enqueue front end JS
*/
function bodhi_svgs_frontend_js() {
// get the settings
global $bodhi_svgs_options;
if ( !empty( $bodhi_svgs_options['sanitize_svg_front_end'] ) && $bodhi_svgs_options['sanitize_svg_front_end'] == 'on' && bodhi_svgs_advanced_mode() == true ) {
// check where the JS should be placed, header or footer
if ( ! empty( $bodhi_svgs_options['js_foot_choice'] ) ) {
$bodhi_svgs_js_footer = true;
} else {
$bodhi_svgs_js_footer = false;
}
// enqueue dompurify library js
wp_enqueue_script( 'bodhi-dompurify-library', BODHI_SVGS_PLUGIN_URL . 'vendor/DOMPurify/DOMPurify.min.js', array(), '1.0.1', $bodhi_svgs_js_footer );
}
}
add_action( 'wp_enqueue_scripts', 'bodhi_svgs_frontend_js', 9 );
/**
* Enqueue and localize JS for IMG tag replacement
*/
function bodhi_svgs_inline() {
if ( bodhi_svgs_advanced_mode() ) {
// get the settings
global $bodhi_svgs_options;
// check if force inline svg is active
if ( ! empty( $bodhi_svgs_options['force_inline_svg'] ) ) {
// set variable as true to pass to js
$force_inline_svg_active = 'true';
// set the class for use in JS
if ( ! empty( $bodhi_svgs_options['css_target'] ) ) {
// use custom class if set
$css_target_array = array(
'Bodhi' => 'img.'. esc_attr($bodhi_svgs_options['css_target']),
'ForceInlineSVG' => esc_attr($bodhi_svgs_options['css_target'])
);
} else {
// set default class
$css_target_array = array(
'Bodhi' => 'img.style-svg',
'ForceInlineSVG' => 'style-svg'
);
}
} else {
// set variable as false to pass to JS
$force_inline_svg_active = 'false';
// if custom target is set, use that, otherwise use default
if ( ! empty( $bodhi_svgs_options['css_target'] ) ) {
$css_target = 'img.'. esc_attr($bodhi_svgs_options['css_target']);
} else {
$css_target = 'img.style-svg';
}
// set the array to target for passing to JS
$css_target_array = $css_target;
}
// use expanded or minified JS
if ( ! empty( $bodhi_svgs_options['use_expanded_js'] ) ) {
// set variables to blank so we use the full JS version
$bodhi_svgs_js_folder = '';
$bodhi_svgs_js_file = '';
} else {
// set variables to the minified version in the min folder
$bodhi_svgs_js_folder = 'min/'; // min folder
$bodhi_svgs_js_file = '-min'; // min file
}
// check where the JS should be placed, header or footer
if ( ! empty( $bodhi_svgs_options['js_foot_choice'] ) ) {
$bodhi_svgs_js_footer = true;
} else {
$bodhi_svgs_js_footer = false;
}
// use vanilla js if user has enabled option in settings
if ( ! empty( $bodhi_svgs_options['use_vanilla_js'] ) ) {
$bodhi_svgs_js_vanilla = '-vanilla';
}else{
$bodhi_svgs_js_vanilla = '';
}
// create path for the correct js file
$bodhi_svgs_js_path = 'js/' . $bodhi_svgs_js_folder .'svgs-inline' . $bodhi_svgs_js_vanilla . $bodhi_svgs_js_file . '.js' ;
wp_register_script( 'bodhi_svg_inline', BODHI_SVGS_PLUGIN_URL . $bodhi_svgs_js_path, array( 'jquery' ), '1.0.1', $bodhi_svgs_js_footer );
wp_enqueue_script( 'bodhi_svg_inline' );
wp_add_inline_script(
'bodhi_svg_inline',
sprintf(
'cssTarget=%s;ForceInlineSVGActive=%s;frontSanitizationEnabled=%s;',
json_encode($css_target_array),
json_encode($force_inline_svg_active),
json_encode($bodhi_svgs_options['sanitize_svg_front_end'])
)
);
}
}
add_action( 'wp_enqueue_scripts', 'bodhi_svgs_inline' );

View File

@ -0,0 +1,122 @@
<?php
/**
* Featured image meta checkbox to inline SVG
*
* Allow users to select whether featured images should contain the SVG Support class
* Check if the featured image is SVG first, then display meta box for SVG only.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* Add checkbox to the featured image metabox
*/
function bodhi_svgs_featured_image_meta( $content ) {
global $post;
// check if featured image is set and has extension of .svg or .svgz
// need to make this check on the moment that the thumbnail shows up in the meta box.
if ( strpos( get_the_post_thumbnail(), '.svg' ) ) {
$text = __( 'Render this SVG inline (advanced)', 'svg-support' );
$id = 'inline_featured_image';
$value = esc_attr( get_post_meta( $post->ID, $id, true ) );
$label = '<label for="' . $id . '" class="selectit"><input name="' . $id . '" type="checkbox" id="' . $id . '" value="' . $value . ' "'. checked( $value, 1, false) .'> ' . $text .'</label>';
return $content .= $label;
} else {
return $content;
}
}
if ( bodhi_svgs_advanced_mode() ) {
add_filter( 'admin_post_thumbnail_html', 'bodhi_svgs_featured_image_meta' );
}
/**
* Save featured image meta data when saved
*/
function bodhi_svgs_save_featured_image_meta( $post_id, $post, $update ) {
// if gutenberg is active, disable the classic editor checkbox
if( isset($_REQUEST['hidden_post_status']) ){
$value = 0;
if ( isset( $_REQUEST['inline_featured_image'] ) ) {
$value = 1;
}
// Check if post type supports 'thumbnail' (Featured Image)
if ( post_type_supports( get_post_type( $post_id ), 'thumbnail' ) ) {
// set meta value to either 1 or 0
update_post_meta( $post_id, 'inline_featured_image', $value );
}
}
}
add_action( 'save_post', 'bodhi_svgs_save_featured_image_meta', 10, 3 );
/*
* Save featured image meta for Gutenberg Editor
*/
function bodhi_svgs_register_meta() {
register_meta( 'post', 'inline_featured_image', array(
'show_in_rest' => true,
'single' => true,
'type' => 'boolean',
'auth_callback' => '__return_true'
) );
}
add_action( 'init', 'bodhi_svgs_register_meta' );
/**
* Add class to the featured image output on front end
*/
function bodhi_svgs_add_class_to_thumbnail( $thumb ) {
$inline_featured_image = get_post_meta( get_the_ID(), 'inline_featured_image' );
if ( is_array( $inline_featured_image ) && in_array( 1, $inline_featured_image ) ) {
global $bodhi_svgs_options;
if ( ! empty( $bodhi_svgs_options['css_target'] ) ) {
$target_class = $bodhi_svgs_options['css_target'];
} else {
$target_class = 'style-svg';
}
if ( is_singular() ) {
$thumb = str_replace( 'attachment-', $target_class . ' attachment-', $thumb );
}
}
return $thumb;
}
if ( bodhi_svgs_advanced_mode() ) {
add_filter( 'post_thumbnail_html', 'bodhi_svgs_add_class_to_thumbnail' );
}

View File

@ -0,0 +1,14 @@
<?php
/**
* INTERNATIONALIZATION / LOCALIZATION
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
add_action( 'init', 'bodhi_svgs_localization' );
function bodhi_svgs_localization() {
load_plugin_textdomain( 'svg-support', false, basename( dirname( __FILE__ ) ) . '/languages' );
}

View File

@ -0,0 +1,93 @@
<?php
/**
* Add SVG mime types to WordPress
*
* Allows you to upload SVG files to the media library like any other image.
* Additionally provides a fix for WP 4.7.1 - 4.7.2 upload issues and for Avada theme.
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* Add Mime Types
*/
function bodhi_svgs_upload_mimes( $mimes = array() ) {
global $bodhi_svgs_options;
$allowed_roles_array = array();
$is_role_allowed = array();
if( !isset($bodhi_svgs_options['restrict']) ) {
return $mimes;
}
$allowed_roles_array = (array) $bodhi_svgs_options['restrict'];
$user = wp_get_current_user();
$current_user_roles = ( array ) $user->roles;
$is_role_allowed = array_intersect($allowed_roles_array, $current_user_roles);
if( empty($is_role_allowed) ) {
return $mimes;
}
else {
// allow SVG file upload
$mimes['svg'] = 'image/svg+xml';
$mimes['svgz'] = 'image/svg+xml';
return $mimes;
}
}
add_filter( 'upload_mimes', 'bodhi_svgs_upload_mimes', 99 );
/**
* Check Mime Types
*/
function bodhi_svgs_upload_check( $checked, $file, $filename, $mimes ) {
if ( ! $checked['type'] ) {
$check_filetype = wp_check_filetype( $filename, $mimes );
$ext = $check_filetype['ext'];
$type = $check_filetype['type'];
$proper_filename = $filename;
if ( $type && 0 === strpos( $type, 'image/' ) && $ext !== 'svg' ) {
$ext = $type = false;
}
$checked = compact( 'ext','type','proper_filename' );
}
return $checked;
}
add_filter( 'wp_check_filetype_and_ext', 'bodhi_svgs_upload_check', 10, 4 );
/**
* Mime Check fix for WP 4.7.1 / 4.7.2
*
* Fixes uploads for these 2 version of WordPress.
* Issue was fixed in 4.7.3 core.
*/
function bodhi_svgs_allow_svg_upload( $data, $file, $filename, $mimes ) {
global $wp_version;
if ( $wp_version !== '4.7.1' || $wp_version !== '4.7.2' ) {
return $data;
}
$filetype = wp_check_filetype( $filename, $mimes );
return [
'ext' => $filetype['ext'],
'type' => $filetype['type'],
'proper_filename' => $data['proper_filename']
];
}
add_filter( 'wp_check_filetype_and_ext', 'bodhi_svgs_allow_svg_upload', 10, 4 );

View File

@ -0,0 +1,51 @@
<?php
/**
* ADD ABILITY TO VIEW THUMBNAILS IN WP 4.0+
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
add_action( 'admin_init', 'bodhi_svgs_display_thumbs' );
function bodhi_svgs_display_thumbs() {
if ( bodhi_svgs_specific_pages_media_library() ) {
function bodhi_svgs_thumbs_filter( $content ) {
return apply_filters( 'final_output', $content );
}
ob_start( 'bodhi_svgs_thumbs_filter' );
add_filter( 'final_output', 'bodhi_svgs_final_output' );
function bodhi_svgs_final_output( $content ) {
$content = str_replace(
'<# } else if ( \'image\' === data.type && data.sizes && data.sizes.full ) { #>',
'<# } else if ( \'svg+xml\' === data.subtype ) { #>
<img class="details-image" src="{{ data.url }}" draggable="false" />
<# } else if ( \'image\' === data.type && data.sizes && data.sizes.full ) { #>',
$content
);
$content = str_replace(
'<# } else if ( \'image\' === data.type && data.sizes ) { #>',
'<# } else if ( \'svg+xml\' === data.subtype ) { #>
<div class="centered">
<img src="{{ data.url }}" class="thumbnail" draggable="false" />
</div>
<# } else if ( \'image\' === data.type && data.sizes ) { #>',
$content
);
return $content;
}
}
}

View File

@ -0,0 +1,20 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class bodhi_svg_attributes extends \enshrined\svgSanitize\data\AllowedAttributes {
/**
* Returns an array of attributes
*
* @return array
*/
public static function getAttributes() {
/**
* var array Attributes that are allowed.
*/
return apply_filters( 'svg_allowed_attributes', parent::getAttributes() );
}
}

View File

@ -0,0 +1,20 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class bodhi_svg_tags extends \enshrined\svgSanitize\data\AllowedTags {
/**
* Returns an array of tags
*
* @return array
*/
public static function getTags() {
/**
* var array Tags that are allowed.
*/
return apply_filters( 'svg_allowed_tags', parent::getTags() );
}
}

View File

@ -0,0 +1,21 @@
jQuery( document ).ready(function($) {
sanitize_svg_option_check();
$('#sanitize_svg_option input').change(function(){
sanitize_svg_option_check();
});
function sanitize_svg_option_check() {
var sanitize_svg_option = $('#sanitize_svg_option input').is(':checked');
if(sanitize_svg_option) {
$('#sanitize_svg_option_sction').slideDown("slow");
}
else {
$('#sanitize_svg_option_sction').slideUp("slow");
}
}
});

View File

@ -0,0 +1,75 @@
"use strict"
const el = wp.element.createElement;
const withState = wp.compose.withState;
const withSelect = wp.data.withSelect;
const withDispatch = wp.data.withDispatch;
wp.hooks.addFilter(
'editor.PostFeaturedImage',
'bodhi-svgs-featured-image/render-inline-image-checkbox',
wrapPostFeaturedImage
);
function wrapPostFeaturedImage( OriginalComponent ) {
return function( props ) {
return (
el(
wp.element.Fragment,
{},
'',
el(
OriginalComponent,
props
),
el(
composedCheckBox
)
)
);
}
}
class CheckBoxCustom extends React.Component {
render() {
const {
meta,
updateInlineFeaturedSvg,
} = this.props;
return (
el(
wp.components.CheckboxControl,
{
label: "Render this SVG inline (Advanced)",
checked: meta.inline_featured_image,
onChange:
( value ) => {
this.setState( { isChecked: value } );
updateInlineFeaturedSvg( value, meta );
}
}
)
)
}
}
const composedCheckBox = wp.compose.compose( [
withState( ( value ) => { isChecked: value } ),
withSelect( ( select ) => {
const currentMeta = select( 'core/editor' ).getCurrentPostAttribute( 'meta' );
const editedMeta = select( 'core/editor' ).getEditedPostAttribute( 'meta' );
return {
meta: { ...currentMeta, ...editedMeta },
};
} ),
withDispatch( ( dispatch ) => ( {
updateInlineFeaturedSvg( value, meta ) {
meta = {
...meta,
inline_featured_image: value,
};
dispatch( 'core/editor' ).editPost( { meta } );
},
} ) ),
] )( CheckBoxCustom );

View File

@ -0,0 +1,643 @@
;
(function ($) {
'use strict';
function noop() { }
function throttle(func, wait, options) {
var context, args, result;
var timeout = null;
// 上次执行时间点
var previous = 0;
if (!options) options = {};
// 延迟执行函数
var later = function () {
// 若设定了开始边界不执行选项上次执行时间始终为0
previous = options.leading === false ? 0 : new Date().getTime();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function () {
var now = new Date().getTime();
// 首次执行时,如果设定了开始边界不执行选项,将上次执行时间设定为当前时间。
if (!previous && options.leading === false) previous = now;
// 延迟执行时间间隔
var remaining = wait - (now - previous);
context = this;
args = arguments;
// 延迟时间间隔remaining小于等于0表示上次执行至此所间隔时间已经超过一个时间窗口
// remaining大于时间窗口wait表示客户端系统时间被调整过
if (remaining <= 0 || remaining > wait) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
//如果延迟执行不存在,且没有设定结尾边界不执行选项
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
}
var isSafari = function () {
var ua = navigator.userAgent.toLowerCase();
if (ua.indexOf('safari') !== -1) {
return ua.indexOf('chrome') > -1 ? false : true;
}
}();
var settings = {
readonly: false,
minCount: 0,
minCountErrorMessage: '',
limitCount: Infinity,
limitCountErrorMessage: '',
input: '<input type="text" maxLength="20" placeholder="Search...">',
data: [],
searchable: true,
searchNoData: '<li style="color:#ddd">No Results.</li>',
init: noop,
choice: noop,
extendProps: []
};
var KEY_CODE = {
up: 38,
down: 40,
enter: 13
};
var EVENT_SPACE = {
click: 'click.iui-dropdown',
focus: 'focus.iui-dropdown',
keydown: 'keydown.iui-dropdown',
keyup: 'keyup.iui-dropdown'
};
var ALERT_TIMEOUT_PERIOD = 1000;
// 创建模板
function createTemplate() {
var isLabelMode = this.isLabelMode;
var searchable = this.config.searchable;
var templateSearch = searchable ? '<span class="dropdown-search">' + this.config.input + '</span>' : '';
return isLabelMode ? '<div class="dropdown-display-label"><div class="dropdown-chose-list">' + templateSearch + '</div></div><div class="dropdown-main">{{ul}}</div>' : '<a href="javascript:;" class="dropdown-display" tabindex="0"><span class="dropdown-chose-list"></span><a href="javascript:;" class="dropdown-clear-all" tabindex="0">\xD7</a></a><div class="dropdown-main">' + templateSearch + '{{ul}}</div>';
}
// 小于minCount提示的元素
function minItemsAlert() {
var _dropdown = this;
var _config = _dropdown.config;
var $el = _dropdown.$el;
var $alert = $el.find('.dropdown-minItem-alert');
var alertMessage = _config.minCountErrorMessage;
clearTimeout(_dropdown.itemCountAlertTimer);
if ($alert.length === 0) {
if (!alertMessage) {
alertMessage = '\u6700\u4f4e\u9009\u62e9' + _config.minCount + '\u4E2A';
}
$alert = $('<div class="dropdown-minItem-alert">' + alertMessage + '</div>');
}
$el.append($alert);
_dropdown.itemCountAlertTimer = setTimeout(function () {
$el.find('.dropdown-minItem-alert').remove();
}, ALERT_TIMEOUT_PERIOD);
}
// 超出限制提示
function maxItemAlert() {
var _dropdown = this;
var _config = _dropdown.config;
var $el = _dropdown.$el;
var $alert = $el.find('.dropdown-maxItem-alert');
var alertMessage = _config.limitCountErrorMessage;
clearTimeout(_dropdown.itemLimitAlertTimer);
if ($alert.length === 0) {
if (!alertMessage) {
alertMessage = '\u6700\u591A\u53EF\u9009\u62E9' + _config.limitCount + '\u4E2A';
}
$alert = $('<div class="dropdown-maxItem-alert">' + alertMessage + '</div>');
}
$el.append($alert);
_dropdown.itemLimitAlertTimer = setTimeout(function () {
$el.find('.dropdown-maxItem-alert').remove();
}, ALERT_TIMEOUT_PERIOD);
}
// select-option 转 ul-li
function selectToDiv(str) {
var result = str || '';
// 移除select标签
result = result.replace(/<select[^>]*>/gi, '').replace('</select>', '');
// 移除 optgroup 结束标签
result = result.replace(/<\/optgroup>/gi, '');
result = result.replace(/<optgroup[^>]*>/gi, function (matcher) {
var groupName = /label="(.[^"]*)"(\s|>)/.exec(matcher);
var groupId = /data\-group\-id="(.[^"]*)"(\s|>)/.exec(matcher);
return '<li class="dropdown-group" data-group-id="' + (groupId ? groupId[1] : '') + '">' + (groupName ? groupName[1] : '') + '</li>';
});
result = result.replace(/<option(.*?)<\/option>/gi, function (matcher) {
// var value = /value="?([\w\u4E00-\u9FA5\uF900-\uFA2D]+)"?/.exec(matcher);
var value = $(matcher).val();
var name = />(.*)<\//.exec(matcher);
// 强制要求html中使用selected/disabled而不是selected="selected","disabled="disabled"
var isSelected = matcher.indexOf('selected') > -1 ? true : false;
var isDisabled = matcher.indexOf('disabled') > -1 ? true : false;
var extendAttr = ''
var extendProps = matcher.replace(/data-(\w+)="?(.[^"]+)"?/g, function ($1) {
extendAttr += $1 + ' '
});
return '<li ' + (isDisabled ? ' disabled' : ' tabindex="0"') + ' data-value="' + (value || '') + '" class="dropdown-option ' + (isSelected ? 'dropdown-chose' : '') + '" ' + extendAttr + '>' + (name ? name[1] : '') + '</li>';
});
return result;
}
// object-data 转 select-option
function objectToSelect(data) {
var dropdown = this;
var map = {};
var result = '';
var name = [];
var selectAmount = 0;
var extendProps = dropdown.config.extendProps;
if (!data || !data.length) {
return false;
}
$.each(data, function (index, val) {
// disable 权重高于 selected
var hasGroup = val.groupId;
var isDisabled = val.disabled ? ' disabled' : '';
var isSelected = val.selected && !isDisabled ? ' selected' : '';
var extendAttr = ''
$.each(extendProps, function (index, value) {
if (val[value]) {
extendAttr += 'data-' + value + '="' + val[value] + '" '
}
})
var temp = '<option' + isDisabled + isSelected + ' value="' + val.id + '" ' + extendAttr + '>' + val.name + '</option>';
if (isSelected) {
name.push('<span class="dropdown-selected">' + val.name + '<i class="del" data-id="' + val.id + '"></i></span>');
selectAmount++;
}
// 判断是否有分组
if (hasGroup) {
if (map[val.groupId]) {
map[val.groupId] += temp;
} else {
// &janking& just a separator
map[val.groupId] = val.groupName + '&janking&' + temp;
}
} else {
map[index] = temp;
}
});
$.each(map, function (index, val) {
var option = val.split('&janking&');
// 判断是否有分组
if (option.length === 2) {
var groupName = option[0];
var items = option[1];
result += '<optgroup label="' + groupName + '" data-group-id="' + index + '">' + items + '</optgroup>';
} else {
result += val;
}
});
return [result, name, selectAmount];
}
// select-option 转 object-data
//
function selectToObject(el) {
var $select = el;
var result = [];
function readOption(key, el) {
var $option = $(el);
this.id = $option.prop('value');
this.name = $option.text();
this.disabled = $option.prop('disabled');
this.selected = $option.prop('selected');
}
$.each($select.children(), function (key, el) {
var tmp = {};
var tmpGroup = {};
var $el = $(el);
if (el.nodeName === 'OPTGROUP') {
tmpGroup.groupId = $el.data('groupId');
tmpGroup.groupName = $el.attr('label');
$.each($el.children(), $.proxy(readOption, tmp));
$.extend(tmp, tmpGroup);
} else {
$.each($el, $.proxy(readOption, tmp));
}
result.push(tmp);
});
return result;
}
var action = {
show: function (event) {
event.stopPropagation();
var _dropdown = this;
$(document).trigger('click.dropdown');
_dropdown.$el.addClass('active');
},
search: throttle(function (event) {
var _dropdown = this;
var _config = _dropdown.config;
var $el = _dropdown.$el;
var $input = $(event.target);
var intputValue = $input.val();
var data = _dropdown.config.data;
var result = [];
if (event.keyCode > 36 && event.keyCode < 41) {
return;
}
$.each(data, function (key, value) {
if ((value.groupName && value.groupName.toLowerCase().indexOf(intputValue.toLowerCase()) > -1) || value.name.toLowerCase().indexOf(intputValue.toLowerCase()) > -1 || '' + value.id === '' + intputValue) {
result.push(value);
}
});
$el.find('ul').html(selectToDiv(objectToSelect.call(_dropdown, result)[0]) || _config.searchNoData);
}, 300),
control: function (event) {
var keyCode = event.keyCode;
var KC = KEY_CODE;
var index = 0;
var direct;
var itemIndex;
var $items;
if (keyCode === KC.down || keyCode === KC.up) {
// 方向
direct = keyCode === KC.up ? -1 : 1;
$items = this.$el.find('[tabindex]');
itemIndex = $items.index($(document.activeElement));
// 初始
if (itemIndex === -1) {
index = direct + 1 ? -1 : 0;
} else {
index = itemIndex;
}
// 确认位序
index = index + direct;
// 最后位循环
if (index === $items.length) {
index = 0;
}
$items.eq(index).focus();
event.preventDefault();
}
},
multiChoose: function (event, status) {
var _dropdown = this;
var _config = _dropdown.config;
var $select = _dropdown.$select;
var $target = $(event.target);
var value = $target.attr('data-value');
var hasSelected = $target.hasClass('dropdown-chose');
var selectedName = [];
var selectedProp;
if ($target.hasClass('dropdown-display')) {
return false;
}
if (hasSelected) {
$target.removeClass('dropdown-chose');
_dropdown.selectAmount--;
} else {
if (_dropdown.selectAmount < _config.limitCount) {
$target.addClass('dropdown-chose');
_dropdown.selectAmount++;
} else {
maxItemAlert.call(_dropdown);
return false;
}
}
_dropdown.name = [];
$.each(_config.data, function (key, item) {
if ('' + item.id === '' + value) {
selectedProp = item;
item.selected = hasSelected ? false : true;
}
if (item.selected) {
selectedName.push(item.name);
_dropdown.name.push('<span class="dropdown-selected">' + item.name + '<i class="del" data-id="' + item.id + '"></i></span>');
}
});
$select.find('option[value="' + value + '"]').prop('selected', hasSelected ? false : true);
if (hasSelected && _dropdown.selectAmount < _config.minCount) {
minItemsAlert.call(_dropdown);
}
_dropdown.$choseList.find('.dropdown-selected').remove();
_dropdown.$choseList.prepend(_dropdown.name.join(''));
_dropdown.$el.find('.dropdown-display').attr('title', selectedName.join(','));
_config.choice.call(_dropdown, event, selectedProp);
},
singleChoose: function (event) {
var _dropdown = this;
var _config = _dropdown.config;
var $el = _dropdown.$el;
var $select = _dropdown.$select;
var $target = $(event.target);
var value = $target.attr('data-value');
var hasSelected = $target.hasClass('dropdown-chose');
if ($target.hasClass('dropdown-chose') || $target.hasClass('dropdown-display')) {
return false;
}
_dropdown.name = [];
$el.removeClass('active').find('li').not($target).removeClass('dropdown-chose');
$target.toggleClass('dropdown-chose');
$.each(_config.data, function (key, item) {
// id 有可能是数字也有可能是字符串,强制全等有弊端 2017-03-20 22:19:21
item.selected = false;
if ('' + item.id === '' + value) {
item.selected = hasSelected ? 0 : 1;
if (item.selected) {
_dropdown.name.push('<span class="dropdown-selected">' + item.name + '<i class="del" data-id="' + item.id + '"></i></span>');
}
}
});
$select.find('option[value="' + value + '"]').prop('selected', true);
_dropdown.name.push('<span class="placeholder">' + _dropdown.placeholder + '</span>');
_dropdown.$choseList.html(_dropdown.name.join(''));
_config.choice.call(_dropdown, event);
},
del: function (event) {
var _dropdown = this;
var _config = _dropdown.config;
var $target = $(event.target);
var id = $target.data('id');
// 2017-03-23 15:58:50 测试
// 10000条数据测试删除耗时 ~3ms
$.each(_dropdown.name, function (key, value) {
if (value.indexOf('data-id="' + id + '"') !== -1) {
_dropdown.name.splice(key, 1);
return false;
}
});
$.each(_dropdown.config.data, function (key, item) {
if ('' + item.id == '' + id) {
item.selected = false;
return false;
}
});
_dropdown.selectAmount--;
_dropdown.$el.find('[data-value="' + id + '"]').removeClass('dropdown-chose');
_dropdown.$el.find('[value="' + id + '"]').prop('selected', false).removeAttr('selected');
$target.closest('.dropdown-selected').remove();
_config.choice.call(_dropdown, event);
return false;
},
clearAll: function (event) {
var _dropdown = this;
var _config = _dropdown.config;
event && event.preventDefault();
console.log(this)
this.$choseList.find('.del').each(function (index, el) {
$(el).trigger('click');
});
if (_config.minCount > 0) {
minItemsAlert.call(_dropdown);
}
this.$el.find('.dropdown-display').removeAttr('title');
return false;
}
};
function Dropdown(options, el) {
this.$el = $(el);
this.$select = this.$el.find('select');
this.placeholder = this.$select.attr('placeholder');
this.config = options;
this.name = [];
this.isSingleSelect = !this.$select.prop('multiple');
this.selectAmount = 0;
this.itemLimitAlertTimer = null;
this.isLabelMode = this.config.multipleMode === 'label';
this.init();
}
Dropdown.prototype = {
init: function () {
var _this = this;
var _config = _this.config;
var $el = _this.$el;
_this.$select.hide();
// 判断dropdown是否单选是否token模式
$el.addClass(_this.isSingleSelect ? 'dropdown-single' : _this.isLabelMode ? 'dropdown-multiple-label' : 'dropdown-multiple');
if (_config.data.length === 0) {
_config.data = selectToObject(_this.$select);
}
var processResult = objectToSelect.call(_this, _config.data);
_this.name = processResult[1];
_this.selectAmount = processResult[2];
_this.$select.html(processResult[0]);
_this.renderSelect();
// disabled权重高于readonly
_this.changeStatus(_config.disabled ? 'disabled' : _config.readonly ? 'readonly' : false);
_this.config.init();
},
// 渲染 select 为 dropdown
renderSelect: function (isUpdate, isCover) {
var _this = this;
var $el = _this.$el;
var $select = _this.$select;
var elemLi = selectToDiv($select.prop('outerHTML'));
var template;
if (isUpdate) {
$el.find('ul')[isCover ? 'html' : 'append'](elemLi);
} else {
template = createTemplate.call(_this).replace('{{ul}}', '<ul>' + elemLi + '</ul>');
$el.append(template).find('ul').removeAttr('style class');
}
if (isCover) {
_this.name = [];
_this.$el.find('.dropdown-selected').remove();
_this.$select.val('');
}
_this.$choseList = $el.find('.dropdown-chose-list');
if (!_this.isLabelMode) {
_this.$choseList.html($('<span class="placeholder"></span>').text(_this.placeholder));
}
_this.$choseList.prepend(_this.name ? _this.name.join('') : []);
},
bindEvent: function () {
var _this = this;
var $el = _this.$el;
var openHandle = isSafari ? EVENT_SPACE.click : EVENT_SPACE.focus;
$el.on(EVENT_SPACE.click, function (event) {
event.stopPropagation();
});
$el.on(EVENT_SPACE.click, '.del', $.proxy(action.del, _this));
// show
if (_this.isLabelMode) {
$el.on(EVENT_SPACE.click, '.dropdown-display-label', function () {
$el.find('input').focus();
});
if (_this.config.searchable) {
$el.on(EVENT_SPACE.focus, 'input', $.proxy(action.show, _this));
} else {
$el.on(EVENT_SPACE.click, $.proxy(action.show, _this));
}
$el.on(EVENT_SPACE.keydown, 'input', function (event) {
if (event.keyCode === 8 && this.value === '' && _this.name.length) {
$el.find('.del').eq(-1).trigger('click');
}
});
} else {
$el.on(openHandle, '.dropdown-display', $.proxy(action.show, _this));
$el.on(openHandle, '.dropdown-clear-all', $.proxy(action.clearAll, _this));
}
// 搜索
$el.on(EVENT_SPACE.keyup, 'input', $.proxy(action.search, _this));
// 按下enter键设置token
$el.on(EVENT_SPACE.keyup, function (event) {
var keyCode = event.keyCode;
var KC = KEY_CODE;
if (keyCode === KC.enter) {
$.proxy(_this.isSingleSelect ? action.singleChoose : action.multiChoose, _this, event)();
}
});
// 按下上下键切换token
$el.on(EVENT_SPACE.keydown, $.proxy(action.control, _this));
$el.on(EVENT_SPACE.click, 'li[tabindex]', $.proxy(_this.isSingleSelect ? action.singleChoose : action.multiChoose, _this));
},
unbindEvent: function () {
var _this = this;
var $el = _this.$el;
var openHandle = isSafari ? EVENT_SPACE.click : EVENT_SPACE.focus;
$el.off(EVENT_SPACE.click);
$el.off(EVENT_SPACE.click, '.del');
// show
if (_this.isLabelMode) {
$el.off(EVENT_SPACE.click, '.dropdown-display-label');
$el.off(EVENT_SPACE.focus, 'input');
$el.off(EVENT_SPACE.keydown, 'input');
} else {
$el.off(openHandle, '.dropdown-display');
$el.off(openHandle, '.dropdown-clear-all');
}
// 搜索
$el.off(EVENT_SPACE.keyup, 'input');
// 按下enter键设置token
$el.off(EVENT_SPACE.keyup);
// 按下上下键切换token
$el.off(EVENT_SPACE.keydown);
$el.off(EVENT_SPACE.click, '[tabindex]');
},
changeStatus: function (status) {
var _this = this;
if (status === 'readonly') {
_this.unbindEvent();
} else if (status === 'disabled') {
_this.$select.prop('disabled', true);
_this.unbindEvent();
} else {
_this.$select.prop('disabled', false);
_this.bindEvent();
}
},
update: function (data, isCover) {
var _this = this;
var _config = _this.config;
var $el = _this.$el;
var _isCover = isCover || false;
if (Object.prototype.toString.call(data) !== '[object Array]') {
return;
}
_config.data = _isCover ? data.slice(0) : _config.data.concat(data);
var processResult = objectToSelect.call(_this, _config.data);
_this.name = processResult[1];
_this.selectAmount = processResult[2];
_this.$select.html(processResult[0]);
_this.renderSelect(true, _isCover);
},
destroy: function () {
this.unbindEvent();
this.$el.children().not('select').remove();
this.$el.removeClass('dropdown-single dropdown-multiple-label dropdown-multiple');
this.$select.show();
},
choose: function (values, status) {
var valArr = Object.prototype.toString.call(values) === '[object Array]' ? values : [values];
var _this = this;
var _status = status !== void 0 ? !!status : true
$.each(valArr, function (index, value) {
var $target = _this.$el.find('[data-value="' + value + '"]');
var targetStatus = $target.hasClass('dropdown-chose');
if (targetStatus !== _status) {
$target.trigger(EVENT_SPACE.click, status || true)
}
});
},
reset: function () {
action.clearAll.call(this)
}
};
$(document).on('click.dropdown', function () {
$('.dropdown-single,.dropdown-multiple,.dropdown-multiple-label').removeClass('active');
});
$.fn.dropdown = function (options) {
this.each(function (index, el) {
$(el).data('dropdown', new Dropdown($.extend(true, {}, settings, options), el));
});
return this;
}
})(jQuery);

View File

@ -0,0 +1 @@
jQuery(document).ready((function($){function i(){var i;$("#sanitize_svg_option input").is(":checked")?$("#sanitize_svg_option_sction").slideDown("slow"):$("#sanitize_svg_option_sction").slideUp("slow")}i(),$("#sanitize_svg_option input").change((function(){i()}))}));

View File

@ -0,0 +1 @@
"use strict";function wrapPostFeaturedImage(e){return function(t){return el(wp.element.Fragment,{},"",el(e,t),el(composedCheckBox))}}const el=wp.element.createElement,withState=wp.compose.withState,withSelect=wp.data.withSelect,withDispatch=wp.data.withDispatch;wp.hooks.addFilter("editor.PostFeaturedImage","bodhi-svgs-featured-image/render-inline-image-checkbox",wrapPostFeaturedImage);class CheckBoxCustom extends React.Component{render(){const{meta:e,updateInlineFeaturedSvg:t}=this.props;return el(wp.components.CheckboxControl,{label:"Render this SVG inline (Advanced)",checked:e.inline_featured_image,onChange:o=>{this.setState({isChecked:o}),t(o,e)}})}}const composedCheckBox=wp.compose.compose([withState((e=>{})),withSelect((e=>{const t=undefined,o=undefined;return{meta:{...e("core/editor").getCurrentPostAttribute("meta"),...e("core/editor").getEditedPostAttribute("meta")}}})),withDispatch((e=>({updateInlineFeaturedSvg(t,o){o={...o,inline_featured_image:t},e("core/editor").editPost({meta:o})}})))])(CheckBoxCustom);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
jQuery(document).ready((function($){function t(t){var e=t.attr("id"),i=t.attr("class"),s=t.attr("src");s.endsWith("svg")&&$.get(s,(function(s){var n=$(s).find("svg"),a=n.attr("id");void 0===e?void 0===a?(e="svg-replaced-"+r,n=n.attr("id",e)):e=a:n=n.attr("id",e),void 0!==i&&(n=n.attr("class",i+" replaced-svg svg-replaced-"+r)),n=n.removeAttr("xmlns:a"),"on"==frontSanitizationEnabled&&""!=n[0].outerHTML&&(n=DOMPurify.sanitize(n[0].outerHTML)),t.replaceWith(n),$(document).trigger("svg.loaded",[e]),r++}),"xml")}let r=0;(bodhisvgsInlineSupport=function(){if("true"===ForceInlineSVGActive&&jQuery("img").each((function(){void 0!==jQuery(this).attr("src")&&!1!==jQuery(this).attr("src")&&jQuery(this).attr("src").match(/\.(svg)/)&&(jQuery(this).hasClass(cssTarget.ForceInlineSVG)||jQuery(this).addClass(cssTarget.ForceInlineSVG))})),String.prototype.endsWith||(String.prototype.endsWith=function(t,r){var e=this.toString();("number"!=typeof r||!isFinite(r)||Math.floor(r)!==r||r>e.length)&&(r=e.length),r-=t.length;var i=e.lastIndexOf(t,r);return-1!==i&&i===r}),String.prototype.endsWith=function(t){var r=this.length-t.length;return r>=0&&this.lastIndexOf(t)===r},"true"===ForceInlineSVGActive)var r="img."!==cssTarget.Bodhi?cssTarget.Bodhi:".style-svg";else var r="img."!==cssTarget?cssTarget:".style-svg";r=r.replace("img",""),$(r).each((function(r){void 0!==$(this).attr("src")&&!1!==$(this).attr("src")?t($(this)):$(this).find("img").each((function(r){void 0!==$(this).attr("src")&&!1!==$(this).attr("src")&&t($(this))}))}))})()}));

View File

@ -0,0 +1 @@
document.addEventListener("DOMContentLoaded",(function(e){function t(e){if("IMG"===e.nodeName){var t=e.id,n=e.classList,i=e.src;if(i.endsWith("svg")){var r=new XMLHttpRequest;r.onreadystatechange=function(){if(4==r.readyState&&200==r.status){let o;data=r.responseText;const d=undefined;var i,a=(i=(new DOMParser).parseFromString(data,"text/html").getElementsByTagName("svg")[0]).id;if(void 0===t?void 0===a?(t="svg-replaced-"+s,i.setAttribute("id",t)):t=a:i.setAttribute("id",t),void 0!==n&&i.setAttribute("class",n+" replaced-svg svg-replaced-"+s),i.removeAttribute("xmlns:a"),"on"==frontSanitizationEnabled&&""!=i.outerHTML){var i=DOMPurify.sanitize(i.outerHTML);e.outerHTML=i}else e.replaceWith(i);s++}},r.open("GET",i,!1),r.send(null)}}}function n(e){if(e.childNodes.length>0)for(var s=0;s<e.childNodes.length;s++){var i;if("IMG"==e.childNodes[s].nodeName)t(e.childNodes[s]);else n(e.childNodes[s])}}let s=0;(bodhisvgsInlineSupport=function(){if("true"===ForceInlineSVGActive)for(var e=document.getElementsByTagName("img"),s=0;s<e.length;s++)void 0!==e[s].src&&e[s].src.match(/\.(svg)/)&&(e[s].classList.contains(cssTarget.ForceInlineSVG)||e[s].classList.add(cssTarget.ForceInlineSVG));if(String.prototype.endsWith||(String.prototype.endsWith=function(e,t){var n=this.toString();("number"!=typeof t||!isFinite(t)||Math.floor(t)!==t||t>n.length)&&(t=n.length),t-=e.length;var s=n.lastIndexOf(e,t);return-1!==s&&s===t}),String.prototype.endsWith=function(e){var t=this.length-e.length;return t>=0&&this.lastIndexOf(e)===t},"true"===ForceInlineSVGActive)var i="img."!==cssTarget.Bodhi?cssTarget.ForceInlineSVG:"style-svg";else var i="img."!==cssTarget?cssTarget:"style-svg";i=i.replace("img.","");for(var e=document.getElementsByClassName(i),s=0;s<e.length;s++){var r;if(void 0===e[s].src)n(e[s]);else t(e[s])}})()}));

View File

@ -0,0 +1,184 @@
// wait for document to be ready
document.addEventListener("DOMContentLoaded", function(event) {
let bodhisvgsReplacements = 0;
function bodhisvgsReplace(img) {
// must be an image
if( img.nodeName !== 'IMG' ){
return;
}
var imgID = img.id;
var imgClass = img.classList;
var imgURL = img.src;
// must be svg
if( !imgURL.endsWith('svg') ){
return;
}
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4 && xmlHttp.status == 200){
data = xmlHttp.responseText;
let parser = new DOMParser();
const doc = parser.parseFromString(data, 'text/html');
// get svg now
var svg = doc.getElementsByTagName('svg')[0];
var svgID = svg.id;
// Add replaced image's ID to the new SVG if necessary
if( typeof imgID === 'undefined' ){
if( typeof svgID === 'undefined' ) {
imgID = 'svg-replaced-'+bodhisvgsReplacements;
svg.setAttribute('id', imgID);
} else {
imgID = svgID;
}
} else {
svg.setAttribute('id', imgID);
}
// Add replaced image's classes to the new SVG
if(typeof imgClass !== 'undefined') {
svg.setAttribute('class', imgClass+' replaced-svg svg-replaced-'+bodhisvgsReplacements);
}
// Remove any invalid XML tags as per http://validator.w3.org
svg.removeAttribute('xmlns:a');
if(frontSanitizationEnabled == 'on' && svg["outerHTML"] != "") { // Is sanitization enabled?
var svg = DOMPurify.sanitize(svg.outerHTML); // Sanitize SVG code via DOMPurify library
img.outerHTML = svg; // Replacing img tag with new SVG sanitized content
}
else {
// Replace image with new SVG
img.replaceWith(svg);
}
bodhisvgsReplacements++;
}
}
xmlHttp.open("GET", imgURL, false);
xmlHttp.send(null);
}
function bodhisvgsIterator(node) {
if( node.childNodes.length > 0 ){
for (var i = 0; i < node.childNodes.length; i++) {
if( node.childNodes[i].nodeName == 'IMG' ){
// its an image... replace it too
var img = node.childNodes[i];
bodhisvgsReplace(img);
}else{
// go to another level
bodhisvgsIterator(node.childNodes[i]);
}
}
}
}
// Wrap in IIFE so that it can be called again later as bodhisvgsInlineSupport();
(bodhisvgsInlineSupport = function() {
// If force inline SVG option is active then add class
if ( ForceInlineSVGActive === 'true' ) {
var allImages = document.getElementsByTagName('img'); // find all images on page
// loop on images
for(var i = 0; i < allImages.length ; i++) {
if( typeof allImages[i].src !== 'undefined' ){
// check if it has svg
if( allImages[i].src.match(/\.(svg)/) ){
// add our class - if not already added
if( !allImages[i].classList.contains(cssTarget.ForceInlineSVG) ){
// add class now
allImages[i].classList.add(cssTarget.ForceInlineSVG);
}
}
}
}
}
// Polyfill to support all ye old browsers
// delete when not needed in the future
if (!String.prototype.endsWith) {
String.prototype.endsWith = function(searchString, position) {
var subjectString = this.toString();
if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
position = subjectString.length;
}
position -= searchString.length;
var lastIndex = subjectString.lastIndexOf(searchString, position);
return lastIndex !== -1 && lastIndex === position;
};
} // end polyfill
// Another snippet to support IE11
String.prototype.endsWith = function(pattern) {
var d = this.length - pattern.length;
return d >= 0 && this.lastIndexOf(pattern) === d;
};
// End snippet to support IE11
// Check to see if user set alternate class
if ( ForceInlineSVGActive === 'true' ) {
var target = ( cssTarget.Bodhi !== 'img.' ? cssTarget.ForceInlineSVG : 'style-svg' );
} else {
var target = ( cssTarget !== 'img.' ? cssTarget : 'style-svg' );
}
// remove .img from class
target = target.replace("img.","");
var allImages = document.getElementsByClassName(target); // find all images with force svg class
for(var i = 0; i < allImages.length ; i++) {
if( typeof allImages[i].src == 'undefined' ){ // not an image
bodhisvgsIterator(allImages[i]);
}else{
var img = allImages[i];
bodhisvgsReplace(img);
}
}
})(); // Execute immediately
});

View File

@ -0,0 +1,142 @@
jQuery(document).ready(function ($) {
let bodhisvgsReplacements = 0;
function bodhisvgsReplace(img) {
var imgID = img.attr('id');
var imgClass = img.attr('class');
var imgURL = img.attr('src');
// Set svg size to the original img size
// var imgWidth = $img.attr('width');
// var imgHeight = $img.attr('height');
if (!imgURL.endsWith('svg')) {
return;
}
$.get(imgURL, function(data) {
// Get the SVG tag, ignore the rest
var $svg = $(data).find('svg');
var svgID = $svg.attr('id');
// Add replaced image's ID to the new SVG if necessary
if(typeof imgID === 'undefined') {
if(typeof svgID === 'undefined') {
imgID = 'svg-replaced-'+bodhisvgsReplacements;
$svg = $svg.attr('id', imgID);
} else {
imgID = svgID;
}
} else {
$svg = $svg.attr('id', imgID);
}
// Add replaced image's classes to the new SVG
if(typeof imgClass !== 'undefined') {
$svg = $svg.attr('class', imgClass+' replaced-svg svg-replaced-'+bodhisvgsReplacements);
}
// Remove any invalid XML tags as per http://validator.w3.org
$svg = $svg.removeAttr('xmlns:a');
if(frontSanitizationEnabled == 'on' && $svg[0]['outerHTML'] != "") { // Is sanitization enabled?
$svg = DOMPurify.sanitize($svg[0]['outerHTML']); // Sanitize SVG code via DOMPurify library
}
// Add size attributes
// $svg = $svg.attr('width', imgWidth);
// $svg = $svg.attr('height', imgHeight);
// Replace image with new SVG
img.replaceWith($svg);
$(document).trigger('svg.loaded', [imgID]);
bodhisvgsReplacements++;
}, 'xml');
}
// Wrap in IIFE so that it can be called again later as bodhisvgsInlineSupport();
(bodhisvgsInlineSupport = function() {
// If force inline SVG option is active then add class
if ( ForceInlineSVGActive === 'true' ) {
// Find all SVG inside img and add class if it hasn't got it
jQuery('img').each(function() {
// Check if the SRC attribute is present at all
if ( typeof jQuery(this).attr('src') !== typeof undefined && jQuery(this).attr('src') !== false) {
// Pick only those with the extension we want
if ( jQuery(this).attr('src').match(/\.(svg)/) ) {
// Add our class name
if ( !jQuery(this).hasClass(cssTarget.ForceInlineSVG) ) {
jQuery(this).addClass(cssTarget.ForceInlineSVG);
}
}
}
});
}
// Polyfill to support all ye old browsers
// delete when not needed in the future
if (!String.prototype.endsWith) {
String.prototype.endsWith = function(searchString, position) {
var subjectString = this.toString();
if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
position = subjectString.length;
}
position -= searchString.length;
var lastIndex = subjectString.lastIndexOf(searchString, position);
return lastIndex !== -1 && lastIndex === position;
};
} // end polyfill
// Another snippet to support IE11
String.prototype.endsWith = function(pattern) {
var d = this.length - pattern.length;
return d >= 0 && this.lastIndexOf(pattern) === d;
};
// End snippet to support IE11
// Check to see if user set alternate class
if ( ForceInlineSVGActive === 'true' ) {
var target = ( cssTarget.Bodhi !== 'img.' ? cssTarget.Bodhi : '.style-svg' );
} else {
var target = ( cssTarget !== 'img.' ? cssTarget : '.style-svg' );
}
target = target.replace("img","");
$(target).each(function(index){
// if image then send for replacement
if ( typeof $(this).attr('src') !== typeof undefined && $(this).attr('src') !== false) {
bodhisvgsReplace($(this));
}else{
// look for svg children and send for replacement
$(this).find("img").each(function(i){
if( typeof $(this).attr('src') !== typeof undefined && $(this).attr('src') !== false ){
bodhisvgsReplace($(this));
}
});
}
});
})(); // Execute immediately
});

View File

@ -0,0 +1,281 @@
msgid ""
msgstr ""
"Project-Id-Version: SVG Support 2.2.3.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-05-16 20:19:06+00:00\n"
"PO-Revision-Date: Mon May 16 2016 13:54:01 GMT-0700 (PDT)\n"
"Last-Translator: root <benbodhi@gmail.com>\n"
"Language-Team: Benbodhi <wp@benbodhi.com>\n"
"Language: Spanish (Spain)\n"
"Plural-Forms: nplurals=2; plural=n != 1\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Generator: Loco - https://localise.biz/\n"
"X-Poedit-KeywordsList: _:1;gettext:1;dgettext:2;ngettext:1,2;dngettext:2,3;"
"__:1;_e:1;_c:1;_n:1,2;_n_noop:1,2;_nc:1,2;__ngettext:1,2;__ngettext_noop:1,2;"
"_x:1,2c;_ex:1,2c;_nx:1,2,4c;_nx_noop:1,2,3c;_n_js:1,2;_nx_js:1,2,3c;"
"esc_attr__:1;esc_html__:1;esc_attr_e:1;esc_html_e:1;esc_attr_x:1,2c;"
"esc_html_x:1,2c;comments_number_link:2,3;t:1;st:1;trans:1;transChoice:1,2\n"
"X-Poedit-Basepath: ..\n"
"X-Textdomain-Support: yes\n"
"X-Loco-Target-Locale: es_ES\n"
"X-Poedit-SearchPath-0: ."
#. URI of the plugin
msgid "http://wordpress.org/plugins/svg-support"
msgstr ""
#: ../admin/plugin-action-meta-links.php:32
msgid "$25 Free Credit from GoWebben"
msgstr ""
#: ../admin/svgs-settings-page.php:57
msgid "Output JS in Footer?"
msgstr ""
#: ../admin/svgs-settings-page.php:62
msgid ""
" Normally, scripts are placed in <code>head</code> of the HTML document. If "
"this parameter is true, the script is placed before the closing "
"<code>body</code> tag. This requires the theme to have the "
"<code>wp_footer()</code> template tag in the appropriate place."
msgstr ""
#: ../admin/svgs-settings-page.php:68
msgid "Automatically insert class?"
msgstr ""
#: ../admin/svgs-settings-page.php:73
msgid ""
" Checking this will make sure that either the default class or the custom "
"one you set below is inserted into the style attributes of <code>img</code> "
"tags when you insert images. Additionally, it will remove all of the default "
"WordPress classes."
msgstr ""
#: ../admin/svgs-settings-page.php:112
msgid "Please Note:"
msgstr ""
#: ../admin/svgs-settings-page.php:113
msgid ""
"- You will need to set your own height and width in your CSS for SVG files "
"to display correctly."
msgstr ""
#: ../admin/svgs-settings-page.php:114
msgid ""
"- Your uploaded image needs to be an SVG file for this plugin to replace the "
"img tag with the inline SVG code. It will not create SVG files for you."
msgstr ""
#: ../admin/svgs-settings-page.php:131
msgid "Plugin Features"
msgstr ""
#: ../admin/svgs-settings-page.php:145
msgid "About The Plugin"
msgstr ""
#: ../admin/svgs-settings-page.php:157
msgid "GoWebben Hosting"
msgstr ""
#: ../admin/svgs-settings-page.php:159
msgid "Claim your FREE $25 credit from"
msgstr ""
#: ../admin/svgs-settings-page.php:159
msgid ""
"No catch, just free credit for using this plugin! It will be applied "
"automatically using the link provided, but in any case you can simply use "
"code: SVGSUPPORT during checkout."
msgstr ""
msgid "SVG Support"
msgstr "SVG Support"
msgid ""
"Allow SVG file uploads using the WordPress Media Library uploader plus "
"direct styling of SVG elements using CSS."
msgstr ""
"Permitir la subida de archivos SVG a través de la librería de medios además "
"de dar formato a los elementos SVG usando CSS."
msgid "Benbodhi"
msgstr "Benbodhi"
msgid "http://benbodhi.com"
msgstr "http://benbodhi.com"
#: ../admin/admin-init.php:15
msgid "SVG Support Options and Instructions"
msgstr "SVG Support Opciones e Instrucciones"
#: ../admin/admin-init.php:29
msgid "You can't play with this."
msgstr "No puedes jugar con esto."
#: ../admin/plugin-action-meta-links.php:30
msgid "Get Support"
msgstr "Obtener soporte"
#: ../admin/plugin-action-meta-links.php:31
msgid "Donate to author"
msgstr "Donar al autor"
#: ../admin/svgs-settings-page.php:4
msgid "SVG Support Settings and Usage"
msgstr "SVG Support Configuración y Uso"
#: ../admin/svgs-settings-page.php:12
msgid "Introduction"
msgstr "Introducción"
#: ../admin/svgs-settings-page.php:15
msgid ""
"When using SVG images on your WordPress site, it can be hard to style "
"elements within the SVG using CSS. <strong>Now you can, easily!</strong>"
msgstr ""
"Al usar imágenes SVG en un sitio de WordPress es complicado darle formato a "
"los elementos SVG usando CSS. <strong>¡Ahora esto es sencillo!</strong>"
#: ../admin/svgs-settings-page.php:16
msgid ""
"This plugin not only provides SVG Support like the name says, it also allows "
"you to easily embed your full SVG file's code using a simple IMG tag. By "
"adding the class <code>style-svg</code> to your IMG elements, this plugin "
"dynamically replaces any IMG elements containing the <code>style-svg</code> "
"class with your complete SVG."
msgstr ""
"Este plugin no solo da soporte SVG como su nombre indica, sino que además "
"permite insertar el código completo SVG utilizando una simple etiqueta IMG. "
"Añadiendo la clase <code>style-svg</code> a los elementos IMG este plugin "
"los reemplaza de forma dinámica con los SVG completos."
#: ../admin/svgs-settings-page.php:17
msgid ""
"The main purpose of this is to allow styling of SVG elements. Usually your "
"styling options are restricted when using <code>embed</code>, "
"<code>object</code> or <code>img</code> tags alone."
msgstr ""
"El objetivo principal es permitir darle formato a los elementos SVG. "
"Normalmente las opciones de formato están restringidas al usar solo las "
"etiquetas <code>embed</code>, <code>object</code> o <code>img</code>."
#: ../admin/svgs-settings-page.php:34
msgid "Settings"
msgstr "Configuración"
#: ../admin/svgs-settings-page.php:46
msgid "Restrict to Administrators"
msgstr "Restringir a los administradores"
#: ../admin/svgs-settings-page.php:51 ../admin/svgs-settings-page.php:62 ..
#: admin/svgs-settings-page.php:73
msgid "Yes"
msgstr "Si"
#: ../admin/svgs-settings-page.php:51
msgid " Restricts SVG upload priveledges to Administrators."
msgstr " Restringe la subida de archivos SVG a los administradores."
#: ../admin/svgs-settings-page.php:78
msgid "CSS Class to target"
msgstr "Clase CSS objetivo"
#: ../admin/svgs-settings-page.php:81
msgid ""
"The default target class is <code>style-svg</code>. You can change it to "
"your own class such as <code>my-class</code> by typing it here.<br />Leave "
"blank to use the default class."
msgstr ""
"La clase por defecto es <code>style-svg</code>. Puedes cambiarla por una "
"clase propia como <code>my-class</code> tecleándola aquí.<br />Déjalo en "
"blanco para usar la clase por defecto."
#: ../admin/svgs-settings-page.php:87
msgid "Save Changes"
msgstr "Guardar cambios"
#: ../admin/svgs-settings-page.php:98
msgid "Usage"
msgstr "Uso"
#: ../admin/svgs-settings-page.php:101
msgid "You can simply upload SVG images to your media library like any other file."
msgstr ""
"Puedes subir fácilmente imágenes SVG a tu librería de medios como otros "
"formatos de archivo."
#: ../admin/svgs-settings-page.php:102
msgid ""
"Now, embed your SVG image like a standard image with the addition of adding "
"the class <code>style-svg</code> (or your custom class from above) to any "
"IMG tags that you want this plugin to swap out with your actual SVG code."
msgstr ""
"Ahora, incluye tu imagen SVG como una imágen estándar añadiendo la clase "
"<code>style-svg</code> (o tu clase propia definida arriba) a cualquier "
"etiqueta IMG que quieres que se cambie por el código SVG."
#: ../admin/svgs-settings-page.php:103
msgid "For example:"
msgstr "por ejemplo:"
#: ../admin/svgs-settings-page.php:106
msgid "or"
msgstr "o"
#: ../admin/svgs-settings-page.php:109
msgid ""
"The whole IMG tag element will now be dynamically replaced by the actual "
"code of your SVG, making the inner content targetable."
msgstr ""
"Toda la etiqueta IMG ahora se reemplaza de forma dinámica con el código del "
"SVG haciendo su contenido accesible."
#: ../admin/svgs-settings-page.php:110
msgid "This allows you to target elements within your SVG using CSS."
msgstr "Esto permite acceder a los elementos SVG usando CSS."
#: ../admin/svgs-settings-page.php:134
msgid "SVG Support for your media library"
msgstr "Soporte SVG para la librería de medios"
#: ../admin/svgs-settings-page.php:135
msgid "Style SVG elements directly using CSS"
msgstr "Dar formato a los elementos SVG usando CSS"
#: ../admin/svgs-settings-page.php:136
msgid "Restrict to Administrators only"
msgstr "Restringido para administradores"
#: ../admin/svgs-settings-page.php:137
msgid "Extremely Simple To Use"
msgstr "Muy fácil de usar"
#: ../admin/svgs-settings-page.php:147
msgid "You can read about this plugin in detail on"
msgstr "Puedes leer todo sobre el plugin en"
#: ../admin/svgs-settings-page.php:147
msgid "The WordPress Plugin Repository"
msgstr "El repositorio de plugins de WordPress"
#: ../admin/svgs-settings-page.php:148
msgid "Need help?"
msgstr "¿Necesitas ayuda?"
#: ../admin/svgs-settings-page.php:148
msgid "Visit Support"
msgstr "Visitar soporte"
#: ../admin/svgs-settings-page.php:149
msgid "Buy Benbodhi a Beer &rarr;"
msgstr "Comprar una cerveza para Benbodhi &rarr;"
#: ../admin/svgs-settings-page.php:150
msgid "from"
msgstr "de"

View File

@ -0,0 +1,284 @@
msgid ""
msgstr ""
"Project-Id-Version: SVG Support 2.2.3.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-05-16 20:19:06+00:00\n"
"PO-Revision-Date: Mon May 16 2016 13:54:09 GMT-0700 (PDT)\n"
"Last-Translator: root <benbodhi@gmail.com>\n"
"Language-Team: Benbodhi <wp@benbodhi.com>\n"
"Language: Serbian\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10 >= 2 && "
"n%10<=4 &&(n%100<10||n%100 >= 20)? 1 : 2)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-SourceCharset: utf-8\n"
"X-Generator: Loco - https://localise.biz/\n"
"X-Poedit-KeywordsList: _:1;gettext:1;dgettext:2;ngettext:1,2;dngettext:2,3;"
"__:1;_e:1;_c:1;_n:1,2;_n_noop:1,2;_nc:1,2;__ngettext:1,2;__ngettext_noop:1,2;"
"_x:1,2c;_ex:1,2c;_nx:1,2,4c;_nx_noop:1,2,3c;_n_js:1,2;_nx_js:1,2,3c;"
"esc_attr__:1;esc_html__:1;esc_attr_e:1;esc_html_e:1;esc_attr_x:1,2c;"
"esc_html_x:1,2c;comments_number_link:2,3;t:1;st:1;trans:1;transChoice:1,2\n"
"X-Poedit-Basepath: ../\n"
"X-Textdomain-Support: yes\n"
"X-Poedit-SearchPath-0: .\n"
"X-Loco-Target-Locale: sr_RS"
#. URI of the plugin
msgid "http://wordpress.org/plugins/svg-support"
msgstr ""
#: ../admin/plugin-action-meta-links.php:32
msgid "$25 Free Credit from GoWebben"
msgstr ""
#: ../admin/svgs-settings-page.php:57
msgid "Output JS in Footer?"
msgstr ""
#: ../admin/svgs-settings-page.php:62
msgid ""
" Normally, scripts are placed in <code>head</code> of the HTML document. If "
"this parameter is true, the script is placed before the closing "
"<code>body</code> tag. This requires the theme to have the "
"<code>wp_footer()</code> template tag in the appropriate place."
msgstr ""
#: ../admin/svgs-settings-page.php:68
msgid "Automatically insert class?"
msgstr ""
#: ../admin/svgs-settings-page.php:73
msgid ""
" Checking this will make sure that either the default class or the custom "
"one you set below is inserted into the style attributes of <code>img</code> "
"tags when you insert images. Additionally, it will remove all of the default "
"WordPress classes."
msgstr ""
#: ../admin/svgs-settings-page.php:112
msgid "Please Note:"
msgstr ""
#: ../admin/svgs-settings-page.php:113
msgid ""
"- You will need to set your own height and width in your CSS for SVG files "
"to display correctly."
msgstr ""
#: ../admin/svgs-settings-page.php:114
msgid ""
"- Your uploaded image needs to be an SVG file for this plugin to replace the "
"img tag with the inline SVG code. It will not create SVG files for you."
msgstr ""
#: ../admin/svgs-settings-page.php:131
msgid "Plugin Features"
msgstr ""
#: ../admin/svgs-settings-page.php:145
msgid "About The Plugin"
msgstr ""
#: ../admin/svgs-settings-page.php:157
msgid "GoWebben Hosting"
msgstr ""
#: ../admin/svgs-settings-page.php:159
msgid "Claim your FREE $25 credit from"
msgstr ""
#: ../admin/svgs-settings-page.php:159
msgid ""
"No catch, just free credit for using this plugin! It will be applied "
"automatically using the link provided, but in any case you can simply use "
"code: SVGSUPPORT during checkout."
msgstr ""
msgid "SVG Support"
msgstr "SVG Podrška"
msgid ""
"Allow SVG file uploads using the WordPress Media Library uploader plus "
"direct styling of SVG elements using CSS."
msgstr ""
"Aktivirajte učitavanje SVG datoteka uz pomoć WordPress Media Library "
"uploader-a i direktno stilizovanje SVG elemenata uz pomoć CSS-a."
msgid "Benbodhi"
msgstr "Benbodhi"
msgid "http://benbodhi.com"
msgstr "http://benbodhi.com"
#: ../admin/admin-init.php:15
msgid "SVG Support Options and Instructions"
msgstr "SVG opcije podrške i uputstva"
#: ../admin/admin-init.php:29
msgid "You can't play with this."
msgstr "Ne možete se igrati ovim."
#: ../admin/plugin-action-meta-links.php:30
msgid "Get Support"
msgstr "Obezbedite podršku"
#: ../admin/plugin-action-meta-links.php:31
msgid "Donate to author"
msgstr "Donirajte autoru"
#: ../admin/svgs-settings-page.php:4
msgid "SVG Support Settings and Usage"
msgstr "SVG postavke podrške i upotreba"
#: ../admin/svgs-settings-page.php:12
msgid "Introduction"
msgstr "Uvod"
#: ../admin/svgs-settings-page.php:15
msgid ""
"When using SVG images on your WordPress site, it can be hard to style "
"elements within the SVG using CSS. <strong>Now you can, easily!</strong>"
msgstr ""
"Kad koristite SVG slike na svom WordPress sajtu, može vam biti teško da "
"stilizujete elemente u SVG-u uz pomoć CSS-a. <strong>Sad to možete uraditi s "
"lakoćom!</strong>"
#: ../admin/svgs-settings-page.php:16
msgid ""
"This plugin not only provides SVG Support like the name says, it also allows "
"you to easily embed your full SVG file's code using a simple IMG tag. By "
"adding the class <code>style-svg</code> to your IMG elements, this plugin "
"dynamically replaces any IMG elements containing the <code>style-svg</code> "
"class with your complete SVG."
msgstr ""
"Ovaj plugin ne obezbeđuje samo SVG podršku, kao što mu ime kaže, već vam "
"omogućava i da s lakoćom ugradite svoj puni kod SVG datoteke uz pomoć "
"jednostavne IMG oznake. Dodavanjem klase <code>style-svg</code> svojim IMG "
"elementima, ovaj plugin dinamički zamenjuje bilo koje IMG elemente koji "
"sadrže <code>style-svg</code klasu vašim kompletnim SVG-om."
#: ../admin/svgs-settings-page.php:17
msgid ""
"The main purpose of this is to allow styling of SVG elements. Usually your "
"styling options are restricted when using <code>embed</code>, "
"<code>object</code> or <code>img</code> tags alone."
msgstr ""
"Osnovna svrha ovoga je da omogući stilizovanje SVG elemenata. Obično su "
"opcije stilizovanja ograničene ako koristite samo oznake: <code>embed</code>,"
" <code>object</code> or <code>img</code>."
#: ../admin/svgs-settings-page.php:34
msgid "Settings"
msgstr "Postavke"
#: ../admin/svgs-settings-page.php:46
msgid "Restrict to Administrators"
msgstr "Ograniči administratorima"
#: ../admin/svgs-settings-page.php:51 ../admin/svgs-settings-page.php:62 ..
#: admin/svgs-settings-page.php:73
msgid "Yes"
msgstr "Da"
#: ../admin/svgs-settings-page.php:51
msgid " Restricts SVG upload priveledges to Administrators."
msgstr "Ograničava privilegije SVG učitavanja administratorima."
#: ../admin/svgs-settings-page.php:78
msgid "CSS Class to target"
msgstr "CSS meta za ciljanje"
#: ../admin/svgs-settings-page.php:81
msgid ""
"The default target class is <code>style-svg</code>. You can change it to "
"your own class such as <code>my-class</code> by typing it here.<br />Leave "
"blank to use the default class."
msgstr ""
"Podrazumevana ciljna klasa je <code>style-svg</code>. Možete je promeniti u "
"svoju klasu, kao što je <code>my-class</code> tako što ćete je ukucati ovde."
"<br /> Ostavite prazno polje da biste koristili podrazumevanu klasu."
#: ../admin/svgs-settings-page.php:87
msgid "Save Changes"
msgstr "Sačuvaj izmene"
#: ../admin/svgs-settings-page.php:98
msgid "Usage"
msgstr "Upotreba"
#: ../admin/svgs-settings-page.php:101
msgid "You can simply upload SVG images to your media library like any other file."
msgstr ""
"Jednostavno, možete učitati SVG slike u svoju biblioteku medija kao i bilo "
"koju drugu datoteku."
#: ../admin/svgs-settings-page.php:102
msgid ""
"Now, embed your SVG image like a standard image with the addition of adding "
"the class <code>style-svg</code> (or your custom class from above) to any "
"IMG tags that you want this plugin to swap out with your actual SVG code."
msgstr ""
"Sada ugradite svoju SVG sliku kao standardnu sliku uz dodatak klase "
"<code>style-svg</code> (ili svoje prilagođene navedene klase) u bilo koju od "
"IMG oznaka koju želite da ovaj plugin zameni vašim pravim SVG kodom."
#: ../admin/svgs-settings-page.php:103
msgid "For example:"
msgstr "Na primer:"
#: ../admin/svgs-settings-page.php:106
msgid "or"
msgstr "ili"
#: ../admin/svgs-settings-page.php:109
msgid ""
"The whole IMG tag element will now be dynamically replaced by the actual "
"code of your SVG, making the inner content targetable."
msgstr ""
"Sada će ceo IMG element oznake biti dinamički zamenjen pravim kodom ili "
"vašim SVG-om, što znači da će unutrašnji sadržaj biti raspoloživ za ciljanje."
#: ../admin/svgs-settings-page.php:110
msgid "This allows you to target elements within your SVG using CSS."
msgstr "Omogućava vam da ciljate elemente u svom SVG-u koristeći CSS."
#: ../admin/svgs-settings-page.php:134
msgid "SVG Support for your media library"
msgstr "SVG podrška za vaše biblioteke medija"
#: ../admin/svgs-settings-page.php:135
msgid "Style SVG elements directly using CSS"
msgstr "Stilizuj SVG elemente direktno koristeći CSS"
#: ../admin/svgs-settings-page.php:136
msgid "Restrict to Administrators only"
msgstr "Ograniči samo za administratore"
#: ../admin/svgs-settings-page.php:137
msgid "Extremely Simple To Use"
msgstr "Izuzetno jednostavan za upotrebu"
#: ../admin/svgs-settings-page.php:147
msgid "You can read about this plugin in detail on"
msgstr "O ovom plugin-u detaljnije možete pročitati na:"
#: ../admin/svgs-settings-page.php:147
msgid "The WordPress Plugin Repository"
msgstr "WordPress Plugin Repository"
#: ../admin/svgs-settings-page.php:148
msgid "Need help?"
msgstr "Potrebna vam je pomoć?"
#: ../admin/svgs-settings-page.php:148
msgid "Visit Support"
msgstr "Posetite podršku"
#: ../admin/svgs-settings-page.php:149
msgid "Buy Benbodhi a Beer &rarr;"
msgstr "Kupite Benbodhi-ju pivo &rarr;"
#: ../admin/svgs-settings-page.php:150
msgid "from"
msgstr "od"

View File

@ -0,0 +1,266 @@
# Loco Gettext template
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: SVG Support 2.2.3.1\n"
"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/svg-support\n"
"POT-Creation-Date: 2016-05-16 20:19:06+00:00\n"
"POT-Revision-Date: Mon May 16 2016 13:53:21 GMT-0700 (PDT)\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Benbodhi <wp@benbodhi.com>\n"
"Language-Team: Benbodhi <wp@benbodhi.com>\n"
"Language: \n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-Basepath: .\n"
"X-Poedit-SearchPath-0: ..\n"
"X-Poedit-KeywordsList: _:1;gettext:1;dgettext:2;ngettext:1,2;dngettext:2,3;"
"__:1;_e:1;_c:1;_n:1,2;_n_noop:1,2;_nc:1,2;__ngettext:1,2;__ngettext_noop:1,2;"
"_x:1,2c;_ex:1,2c;_nx:1,2,4c;_nx_noop:1,2,3c;_n_js:1,2;_nx_js:1,2,3c;"
"esc_attr__:1;esc_html__:1;esc_attr_e:1;esc_html_e:1;esc_attr_x:1,2c;"
"esc_html_x:1,2c;comments_number_link:2,3;t:1;st:1;trans:1;transChoice:1,2\n"
"X-Generator: Loco - https://localise.biz/"
#. #-#-#-#-# plugin.pot (SVG Support 2.2.3.1) #-#-#-#-#
#. Plugin Name of the plugin/theme
msgid "SVG Support"
msgstr ""
#. URI of the plugin
msgid "http://wordpress.org/plugins/svg-support"
msgstr ""
#. Description of the plugin/theme
msgid ""
"Allow SVG file uploads using the WordPress Media Library uploader plus "
"direct styling of SVG elements using CSS."
msgstr ""
#. Author of the plugin/theme
msgid "Benbodhi"
msgstr ""
#. Author URI of the plugin/theme
msgid "http://benbodhi.com"
msgstr ""
#: ../admin/admin-init.php:15
msgid "SVG Support Options and Instructions"
msgstr ""
#: ../admin/admin-init.php:29
msgid "You can't play with this."
msgstr ""
#: ../admin/plugin-action-meta-links.php:30
msgid "Get Support"
msgstr ""
#: ../admin/plugin-action-meta-links.php:31
msgid "Donate to author"
msgstr ""
#: ../admin/plugin-action-meta-links.php:32
msgid "$25 Free Credit from GoWebben"
msgstr ""
#: ../admin/svgs-settings-page.php:4
msgid "SVG Support Settings and Usage"
msgstr ""
#: ../admin/svgs-settings-page.php:12
msgid "Introduction"
msgstr ""
#: ../admin/svgs-settings-page.php:15
msgid ""
"When using SVG images on your WordPress site, it can be hard to style "
"elements within the SVG using CSS. <strong>Now you can, easily!</strong>"
msgstr ""
#: ../admin/svgs-settings-page.php:16
msgid ""
"This plugin not only provides SVG Support like the name says, it also allows "
"you to easily embed your full SVG file's code using a simple IMG tag. By "
"adding the class <code>style-svg</code> to your IMG elements, this plugin "
"dynamically replaces any IMG elements containing the <code>style-svg</code> "
"class with your complete SVG."
msgstr ""
#: ../admin/svgs-settings-page.php:17
msgid ""
"The main purpose of this is to allow styling of SVG elements. Usually your "
"styling options are restricted when using <code>embed</code>, "
"<code>object</code> or <code>img</code> tags alone."
msgstr ""
#: ../admin/svgs-settings-page.php:34
msgid "Settings"
msgstr ""
#: ../admin/svgs-settings-page.php:46
msgid "Restrict to Administrators"
msgstr ""
#: ../admin/svgs-settings-page.php:51 ../admin/svgs-settings-page.php:62 ..
#: /admin/svgs-settings-page.php:73
msgid "Yes"
msgstr ""
#: ../admin/svgs-settings-page.php:51
msgid " Restricts SVG upload priveledges to Administrators."
msgstr ""
#: ../admin/svgs-settings-page.php:57
msgid "Output JS in Footer?"
msgstr ""
#: ../admin/svgs-settings-page.php:62
msgid ""
" Normally, scripts are placed in <code>head</code> of the HTML document. If "
"this parameter is true, the script is placed before the closing "
"<code>body</code> tag. This requires the theme to have the "
"<code>wp_footer()</code> template tag in the appropriate place."
msgstr ""
#: ../admin/svgs-settings-page.php:68
msgid "Automatically insert class?"
msgstr ""
#: ../admin/svgs-settings-page.php:73
msgid ""
" Checking this will make sure that either the default class or the custom "
"one you set below is inserted into the style attributes of <code>img</code> "
"tags when you insert images. Additionally, it will remove all of the default "
"WordPress classes."
msgstr ""
#: ../admin/svgs-settings-page.php:78
msgid "CSS Class to target"
msgstr ""
#: ../admin/svgs-settings-page.php:81
msgid ""
"The default target class is <code>style-svg</code>. You can change it to "
"your own class such as <code>my-class</code> by typing it here.<br />Leave "
"blank to use the default class."
msgstr ""
#: ../admin/svgs-settings-page.php:87
msgid "Save Changes"
msgstr ""
#: ../admin/svgs-settings-page.php:98
msgid "Usage"
msgstr ""
#: ../admin/svgs-settings-page.php:101
msgid "You can simply upload SVG images to your media library like any other file."
msgstr ""
#: ../admin/svgs-settings-page.php:102
msgid ""
"Now, embed your SVG image like a standard image with the addition of adding "
"the class <code>style-svg</code> (or your custom class from above) to any "
"IMG tags that you want this plugin to swap out with your actual SVG code."
msgstr ""
#: ../admin/svgs-settings-page.php:103
msgid "For example:"
msgstr ""
#: ../admin/svgs-settings-page.php:106
msgid "or"
msgstr ""
#: ../admin/svgs-settings-page.php:109
msgid ""
"The whole IMG tag element will now be dynamically replaced by the actual "
"code of your SVG, making the inner content targetable."
msgstr ""
#: ../admin/svgs-settings-page.php:110
msgid "This allows you to target elements within your SVG using CSS."
msgstr ""
#: ../admin/svgs-settings-page.php:112
msgid "Please Note:"
msgstr ""
#: ../admin/svgs-settings-page.php:113
msgid ""
"- You will need to set your own height and width in your CSS for SVG files "
"to display correctly."
msgstr ""
#: ../admin/svgs-settings-page.php:114
msgid ""
"- Your uploaded image needs to be an SVG file for this plugin to replace the "
"img tag with the inline SVG code. It will not create SVG files for you."
msgstr ""
#: ../admin/svgs-settings-page.php:131
msgid "Plugin Features"
msgstr ""
#: ../admin/svgs-settings-page.php:134
msgid "SVG Support for your media library"
msgstr ""
#: ../admin/svgs-settings-page.php:135
msgid "Style SVG elements directly using CSS"
msgstr ""
#: ../admin/svgs-settings-page.php:136
msgid "Restrict to Administrators only"
msgstr ""
#: ../admin/svgs-settings-page.php:137
msgid "Extremely Simple To Use"
msgstr ""
#: ../admin/svgs-settings-page.php:145
msgid "About The Plugin"
msgstr ""
#: ../admin/svgs-settings-page.php:147
msgid "You can read about this plugin in detail on"
msgstr ""
#: ../admin/svgs-settings-page.php:147
msgid "The WordPress Plugin Repository"
msgstr ""
#: ../admin/svgs-settings-page.php:148
msgid "Need help?"
msgstr ""
#: ../admin/svgs-settings-page.php:148
msgid "Visit Support"
msgstr ""
#: ../admin/svgs-settings-page.php:149
msgid "Buy Benbodhi a Beer &rarr;"
msgstr ""
#: ../admin/svgs-settings-page.php:150
msgid "from"
msgstr ""
#: ../admin/svgs-settings-page.php:157
msgid "GoWebben Hosting"
msgstr ""
#: ../admin/svgs-settings-page.php:159
msgid "Claim your FREE $25 credit from"
msgstr ""
#: ../admin/svgs-settings-page.php:159
msgid ""
"No catch, just free credit for using this plugin! It will be applied "
"automatically using the link provided, but in any case you can simply use "
"code: SVGSUPPORT during checkout."
msgstr ""

View File

@ -0,0 +1,547 @@
=== SVG Support ===
Contributors: Benbodhi
Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=Z9R7JERS82EQQ
Tags: svg, vector, safesvg, safe svg, sanitization, sanitisation, sanitizer, sanitiser, sanitized, sanitised, sanitize, sanitise, css, style, mime, mime type, embed, img, render, inline, animation, animation, animate, js
Requires at least: 4.8
Tested up to: 6.4.1
Requires PHP: 7.2
Stable tag: 2.5.5
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Safely upload SVG files to the Media Library with optional sanitization and minification. Render your SVG files inline for direct styling/animation of an SVG's internal elements.
== Description ==
**Safely upload SVG files to your media library and use them like any other image. Now with optional sanitization!**
But SVG Support has more features!!! Read on to learn more.
When using SVG images on your WordPress site, it can be hard to style elements within the SVG using CSS. **Now you can, easily!**
Scalable Vector Graphics (SVG) are becoming common place in modern web design, allowing you to embed images with small file sizes that are scalable to any visual size without loss of quality.
This plugin not only provides SVG Support like the name says, it also allows you to easily embed your full SVG file's code using a simple IMG tag.
By adding the class `"style-svg"` to your IMG elements (or outer containers), this plugin dynamically replaces any IMG elements containing an SVG in the src that are found inside the target class with your complete SVG code, rendering it inline.
The main purpose of this is to allow styling and animation of your SVG elements. Usually your styling options are restricted when using `embed`, `object` or `img` tags alone.
= Features =
* SVG Support for your media library
* Sanitize SVG uploads
* Minify SVG files
* Inline your SVG code
* Style SVG elements directly using CSS
* Animate SVG elements using CSS and JS
* Super easy settings page with instructions
* Restrict SVG upload ability to Administrators only
* Set custom target class (can be used on outer elements as of 2.4)
* **Extremely Simple To Use - Simplifies complex usage of SVG files**
== Usage ==
Firstly, install and activate SVG Support (this plugin).
Once activated, you can simply upload SVG images to your media library like any other file.
As an administrator, you can go to the admin settings page "Settings > SVG Support" and restrict SVG file uploads to administrators only.
If you only need to upload SVG files to use as images, you don't need to enable "Advanced Mode”. Leaving it disabled ensures the frontend script is not enqueued and the unnecessary settings stay hidden.
**For advanced use:** Enable the "Advanced Mode" under "Settings > SVG Support".
Now you can enable santization and/or minification of your SVG uploads, define a custom CSS class to target and fiddle with some other settings.
With advanced mode enabled, you can embed your SVG images just like you would a standard image with the addition of adding (in text view) the class `"style-svg"` (or the custom class you defined) to your IMG tags that you want this plugin to swap out with your actual SVG code.
For example:
`<img class="style-svg" alt="alt-text" src="image-source.svg" />`
or
`<img class="your-custom-class" alt="alt-text" src="image-source.svg" />`
The whole IMG tag element will now be dynamically replaced by the actual code of your SVG, making the inner content targetable.<br />
This allows you to target elements within your SVG using CSS and JS.
You can remove all other attributes from the IMG tag as it will disappear anyway.
Additionally, you can now add the target class to outer elements if you're unable to add it directly to the IMG itself. This plugin will traverse the children looking for your SVG to replace with the inline code.
Theres a setting to automatically add your class to the IMG tag for you when you're inserting SVGs in to a post or page, which also removes unnecessary tags.
Since 2.3.11, you can force all SVG files sitewide to be rendered inline with a single checkbox (use with caution). Additionally, you can now choose whether to use the minified or expanded version of the JS file.
*Featured Images:* If a post/page is saved with your SVG as a featured image, a checkbox will display in the featured image meta box to allow you to render it inline (only if advanced mode is active).
Please Note: If your SVG isnt showing, its likely that it is being displayed with 0 height and width. In this case, you will need to set your own height and width in your CSS for SVG files to display correctly.
*If you're having any issues, please use the support tab and I will try my best to get back to you quickly*
== Spin up a test site ==
With a single click, you can spin up a completely free test site to test SVG Support using TasteWP! No sign up, no cards, nothing! How cool is that? Give it a go:
[Click Here to spin up a test site in seconds](https://tastewp.com/new?pre-installed-plugin-slug=svg-support&redirect=options-general.php%3Fpage%3Dsvg-support&ni=true)
== Security ==
As with allowing uploads of any files, there is potential risks involved. So I have added some features to help!
- Optional sanitization to help strip any malicious code form your SVG files upon upload.
- Restrict uploads to admin only.
Only allow users to upload SVG files if you trust them. You have the option to restrict SVG usage to Administrators only from the settings page plus the ability to enable SVG sanitization to attemnpt to clean up any SVG files uploaded.
By default, anyone with Media Library access or upload_files capability will be able to upload SVG files (that is Administrators, Authors and Editors). Please note that SVG files are actually XML which would allow someone to inject malicious code if you're not careful with who has upload privileges.
== Feedback ==
I'm open to your [suggestions and feedback](mailto:wp@benbodhi.com) - Thanks for using SVG Support!
Follow [@SVGSupport](https://twitter.com/svgsupport) on Twitter
Follow [@benbodhi](https://twitter.com/benbodhi) on Twitter
*Note:* I hope you like this plugin! Please take a moment to [rate it](https://wordpress.org/support/view/plugin-reviews/svg-support?filter=5#postform).
== Translations ==
You can [contribute your translation here](https://translate.wordpress.org/projects/wp-plugins/svg-support).
New to Translating WordPress?
Read through the [Translator Handbook](https://make.wordpress.org/polyglots/handbook/tools/glotpress-translate-wordpress-org/) to get started.
== Frequently Asked Questions ==
= SVG not rendering inline since 2.3 update =
SVG Support 2.3 includes a new settings section called "Advanced Mode". Users that were inlining SVG files need to make sure this setting is checked. Go to your dashboard > Settings > SVG Support and check "Advanced Mode". All of your original settings should still be there.
= How do I disable the Javascript on the front end if I am not using inline SVG? =
If you go to `Settings > SVG Support` in your admin dashboard, you can choose to enable "Advanced Mode" or not. If you leave it disabled, the advanced functionality and extraneous script is removed.
= I'm trying to use SVG in the customizer but it's not working. =
To allow SVG to work in the customizer, you will need to modify/add some code in your child theme's function file. [Here is a great tutorial](https://thebrandid.com/using-svg-logos-wordpress-customizer/) on how to do that. The important part is:
`
'flex-width' => true
'flex-height' => true
`
= How do I add animation to my SVG? =
You will need to edit your SVG file in a code editor so you can add CSS classes to each element you need to target within the SVG. Make sure that your IMG tag is being swapped out for your inline SVG and then you can use CSS or JS to apply animations to elements within your SVG file.
= Why is SVG Support not working in multisite? =
If you installed multisite prior to WordPress 3.5, then you will need to remove your ms-files. Here is a couple of resources to help you: [Dumping ms-files](http://halfelf.org/2012/dumping-ms-files/) [Removing ms-files after 3.5](https://www.yunomie.com/2298/removing-ms-files-php-after-upgrading-an-existing-multisite-installation-to-3-5/).
= Why is my SVG not working in Visual Composer? =
If you are using SVG Support with Visual Composer or any other page builders, you will need to make sure that you can add your own class to the image. The easiest way to do this is by using a simple text or code block in the builder to put your image code in to. Additionally, there is now a setting to force all SVG files to be rendered inline.
= How do I get this to work with the Media Library Assistant plugin? =
You need to add the mime type for svg and svgz to: "MLA Settings > Media Library Assistant > Uploads (tab)" and then it works.
== Screenshots ==
1. Basic Settings
2. Advanced Settings
3. Featured Image checkbox to render SVG inline
4. SVG used in WP native Image Widget (since 4.9)
5. Inline SVG in the front end markup
6. Help tab - Overview
7. Help tab - The Settings
8. Help tab - Standard Usage
9. Help tab - Render SVG Inline (advanced usage)
91. Help tab - Featured Images
92. Help tab - Animation
== Changelog ==
= 2.5.5 =
* More error fixes and general clean up.
= 2.5.4 =
* Fixed errors.
= 2.5.3 =
* Fixed fatal php error.
= 2.5.2 =
* Added some defaults for better security by default.
= 2.5.1 =
* Added missing quotes in uninstall.php.
= 2.5 =
* Cleaned up spelling mistakes and general formatting.
* Addressed security concern.
* Added more sanitization options - frontend and admin both supported.
* Added support for SVG minification.
* Added ability to choose jQuery or vanilla JS.
* Added DB cleanup on uninstall.
* Fixed dimensions fallback.
= 2.4.2 =
* Fixed srcset warning for some premium themes.
* Fixed original IMG IDs not getting preserved on replacement.
* Removed some rogue text from featured image box.
= 2.4.1 =
* Fixed issue causing WP-CLI to break.
= 2.4 =
* NEW FEATURE: Added optional SVG sanitization.
* NEW FEATURE: Added optional SVG minification.
* Added inline SVG checkbox to Gutenberg featured image.
* Better Gutenberg support in general.
* Modified class targeting to allow inline rendering of nested SVGs (any level deep) when you can't set the IMG class directly.
* Modified JS to use vanilla JS instead of jQuery.
* Fixed accessibility issues on settings page.
* Fixed dimensions metadata issue.
* Fixed division by 0 issue when SVG had no width or height set.
* Fixed featured image spacing issue in both classic and block editor.
* Bumped required PHP version.
* Removed obsolete admin notice.
* Removed srcset for SVG files.
* Removed directory name from filepath metadata.
= 2.3.21 =
* Fixed featured image SVG overlapping container.
= 2.3.20 =
* Fixed admin setting not being escaped when output.
= 2.3.19 =
* Fixed PHP Warning from localize_script in functions/enqueue.php.
* Added a check for SRC attribute in js/svgs-inline.js.
= 2.3.18 =
* Updated author URL in main plugin file.
* Updated donate links.
* Cleaned up plugin action meta links and settings page.
* Rolled back a fix in functions/attachment.php due to it removing meta from other attachments.
= 2.3.17 =
* Added setting to choose whether to load frontend CSS or not.
= 2.3.16 =
* Fix for files that have the XML declaration.
* Fix for PHP warnings from image widget.
* Some small CSS changes to the frontend when displaying SVG media attachments.
= 2.3.15 =
* Had to roll back a recent PHP warnings fix due to it breaking some theme compatibility.
= 2.3.14 =
* Fixed: Fatal error in some cases when removing old option from the database.
= 2.3.13 =
* Fixed: PHP warnings and notices from the image widget when using SVG files and wp_debug was on.
* Modified: Better front end CSS for displaying SVG attachments, both as images and inline.
* Removed: DB entry for deprecated admin notice.
= 2.3.12 =
* New: Native "Help" tab on the SVG Support settings page.
* New: Wrapped the inline JS in a function so you can call it at will using `bodhisvgsInlineSupport();`.
* Modified: Admin CSS to target SVG src only.
* Modified: SVG Support settings page - cleaned it up a little.
* Removed: Version update admin notice.
= 2.3.11 =
* New: Feature to use expanded JS file rather than the minified/compressed version (useful for bundling and minifying using external caching plugins).
* New: Force Inline SVG option. This feature allows you to force all of your SVG files to be rendered inline regardless of classes applied. Addresses issues where you can't add your own class to an image for some reason. For example, some page builder image elements. Also addresses changing your target class in the settings and needing to change all of your already embedded media, allowing you to simply force render rather than update all of the classes.
* Modified the readme file and descriptions a bit.
* Refined some code in functions/featured-image.php line 69 to address a warning.
* Updated "Requires at least" tag to 4.8 (though it should still work in older versions, there was issues with core during the 4.7 phase and it's time for you to update anyway).
= 2.3.10 =
* Fixed missing links in settings page.
= 2.3.9 =
* Modified plugin action meta link for settings page.
* Changed some language throughout the plugin.
* Added recommendation for ShortPixel Image Optimization.
* Added conditional to check post type supports thumbnail before setting meta data.
= 2.3.8 =
* Added some CSS to make sure featured images show on WooCommerce products, Sensei Courses and Lessons.
* Fix: Auto insert class setting was stripping featured image HTML in some cases.
= 2.3.7 =
* Added WP version check to wrap mime fix function needed for WP v4.7.1 - v4.7.2.
* Moved mime fix into mime type file.
* Modified admin notice code to make it neater.
* Fix: attachment-modal.php issues with some servers and external SVG files (props to @abstractourist & @malthejorgensen for providing fixes, as I could not consistently reproduce the issue).
* Compatibility: Changed a line to provide wider compatibility, specifically for WordPress Bedrock on a LEMP stack.
* Compatibility: Added another snippet to the JS to support IE11 (apparently people still use IE).
* Added more FAQ's.
= 2.3.6 =
* New: Added polyfill to make svgs-inline.js work with older browsers.
* New: Section to leave reviews on settings page.
* Removed: Redundant one time upgrade activate code.
* Fix: Errors reported on activation and on the settings page - [Related Support Thread](https://wordpress.org/support/topic/error-on-plugin-settings-page/).
= 2.3.5 =
* Revision and modification of the thumbnail display code.
= 2.3.4 =
* Fix: Fatal error for some because a function wasn't prefixed.
= 2.3.3 =
* Fix: Missing arguments PHP warnings from new attribute control file.
* Update settings page text.
= 2.3.2 =
* Modified the attribute control code that auto inserts our class to only apply to SVG files.
= 2.3.1 =
* Fix: Fatal error in some cases due to admin notice.
= 2.3 =
* New Feature - Advanced Mode: allows you to turn off the advanced features and simply upload SVG files like normal images. This addition also enables users to turn off the script added on front end by leaving Advanced Mode unchecked.
* New Feature - Featured Image Support: If your featured image is SVG, once the post is saved you will see a checkbox to render the SVG inline (advanced mode only).
* Performance - Stop inlining JS from running if image source is not SVG.
* Added new stylesheet for settings page.
* Moved SCSS files to their own folder.
* Changed donate link so I can track it and properly thank you for your generous donations.
* Added a rating link to the settings and media pages.
* Cleaned up code formatting, added more comments.
* Added a plugin version check.
* Added notice so people are aware they may need to turn on the advanced mode.
= 2.2.5 =
* FIX: Display SVG thumbnails in attachment modals.
= 2.2.4 =
* FIX: Added function to temporarily fix an issue with uploading in WP 4.7.1
= 2.2.32 =
* Changed text domain to match plugin slug for localization.
= 2.2.31 =
* Attempt to fix ability to translate
= 2.2.3 =
* Modified code in svg-support/js/svg-inline.js and svg-support/js/min/svg-inline-min.js to allow JS control of the SVG elements and detect if they have been loaded (IMG tag swapped out). Thanks to [laurosello](https://wordpress.org/support/profile/laurosollero) for this suggestion and code contribution.
* Fixed SVG thumbnails not displaying correctly in list view of the media library.
* Cleaned up the code and comments a bit.
* Added translation for Spanish. Thanks to [Apasionados del Marketing](http://apasionados.es) for the translation.
= 2.2.2 =
* Changed another anonymous function in svg-support/functions/thumbnail-display.php that was causing errors for some.
= 2.2.1 =
* Changed anonymous function in svg-support/functions/thumbnail-display.php line 15 to prevent fatal error in older PHP versions.
= 2.2 =
* Added support to make SVG thumbnails visible in all media library screens.
* Added SVGZ to the mime types.
* Automatically removes the width and height attributes when inserting SVG files.
* Added ability to choose whether the target class is automatically inserted into img tags or not, stripping the default WordPress classes.
* Added ability to choose whether script is output in footer - true or false.
* Blocked direct access to PHP files.
* Added SCSS support using CodeKit - minified CSS + JS files.
* Updated spelling for incorrect function name.
* Changed comment formatting across all files for consistency.
* Added link to $25 Free credit at GoWebben on the settings page.
* Tested in WordPress 4.3.
* Updated Readme file.
= 2.1.7 =
* Tested in WordPress 4.0 and added plugin icons for the new interface.
= 2.1.6 =
* Added missing jQuery dependency in /functions/enqueue.php (pointed out by [walbach](http://wordpress.org/support/profile/waldbach)) - was loading SVG Support JS before jQuery.
= 2.1.5 =
* Added Serbian translation, submitted by Ogi Djuraskovic.
= 2.1.4 =
* Fixed plugin settings link (on plugins page)
* Added more links - Support & Donate
* Modified the settings page a little
* Cleaned up settings page with CSS
* Satisfied my OCD tendencies a little
= 2.1.3 =
* Added plugin_action_links file for custom menus on plugin page.
= 2.1.2 =
* Cleaned up trunk, tags and readme.txt to show correct changelog and update notice.
= 2.1.1 =
* Fixed JS file conditional - worked in local testing but not live.
= 2.1 =
* Updates to language files for localization.
= 2.0 =
* Added an admin settings page with instructions plus options for restricting to admin use only and setting a custom CSS target class.
* Whole plugin completely re-written and re-structured.
* Added option to restrict SVG uploads to administrators only.
* Added field for custom CSS target class.
* Added stylesheet to admin settings page.
= 1.0 =
* Initial Release.
== Upgrade Notice ==
= 2.5.5 =
Updating to 2.5+ Adds new features and addresses a number of earlier issues raised. Please take a backup before updating!
2.5.5 fixes more reported errors in the 2.5 series of updates.
= 2.5.4 =
Updating to 2.5+ Adds new features and addresses a number of earlier issues raised. Please take a backup before updating!
2.5.4 fixes errors in the 2.5 series of updates.
= 2.5.3 =
Updating to 2.5+ Adds new features and addresses a number of earlier issues raised. Please take a backup before updating!
2.5.3 fixes fatal error in 2.5.2.
= 2.5.2 =
Updating to 2.5+ Adds new features and addresses a number of earlier issues raised. Please take a backup before updating!
2.5.2 introduces some defaults for better security.
= 2.5.1 =
2.5 Adds new features and addresses a number of recent issues raised. Please take a backup before updating!
2.5.1 fixes the uninstall file.
= 2.5 =
Adds new features and addresses a number of recent issues raised. Please take a backup before updating!
= 2.4.2 =
2.4.2 fixes srcset issue firing PHP warnings for some themes and original image IDs missing on replacement to inline SVG.
= 2.4.1 =
2.4.1 fixes broken WP-CLI. Now featuring optional SVG sanitization and ability to target nested SVGs! This update contains a lot, please BACKUP YOUR DATABASE AND FILES BEFORE UPDATING!
= 2.4 =
Now featuring optional SVG sanitization and ability to target nested SVGs! This update contains a lot, please BACKUP YOUR DATABASE AND FILES BEFORE UPDATING!
= 2.3.21 =
Fixes featured image display on edit post screen.
= 2.3.20 =
Added more security.
= 2.3.19 =
Quick update to address PHP warnings from localize_script and to add a SRC check.
= 2.3.18 =
General clean up of plugin, testing on latest nightly build plus fixed issue with metadata being removed from non SVG attachments.
= 2.3.17 =
Added a setting to choose whether to load the frontend CSS file or not. It was previously enabled by default, so you may want to flick that on after the update. This allows you to leave it out so your site has one less file to load :)
= 2.3.16 =
This update addresses upload issues, PHP warnings and some frontend CSS changes with attachment display.
= 2.3.15 =
Had to roll back a recent PHP warnings fix due to it breaking some theme compatibility.
= 2.3.14 =
Fixes fatal error in some cases when removing old option from the database.
= 2.3.13 =
Update to address PHP warnings and notices on the image widget when wp_debug is enabled.
= 2.3.12 =
* Wrapped the inline JS so you can call the inline function at will using `bodhisvgsInlineSupport();`. Added a native help tab. Removed the admin update notice. Did some general clean up of code and settings page. As always, it's best to take a full backup of your site before running any updates.
= 2.3.11 =
* New Features and Fixes! Now you can force ALL of your SVG files (old and new) to be rendered inline in a single click with the new "Force Inline SVG" setting. You can also choose to use an expanded version of the inline JS if you want to minify it separately using a caching plugin or similar.
= 2.3.10 =
* Fixed missing links in settings page.
= 2.3.9 =
* Cleaned up some code and language, now stores less meta when not needed and added a plugin recommendation for Image Optimization.
= 2.3.8 =
* Adds better support for WooCommerce and Sensei. Fixes issue with featured images not showing up when auto insert class setting is on.
= 2.3.7 =
* Fixes issues with media library not loading for some, attachment-modal errors and adds some wider compatibility.
= 2.3.6 =
* Adds support for older browsers, fixes a couple of seemingly isolated errors reported, removes some redundant code.
= 2.3.5 =
* Modifications to thumbnail display code to prevent output buffer clash with another plugin.
= 2.3.4 =
* Fixes fatal error for some because a function wasn't prefixed.
= 2.3.3 =
* This update fixes some PHP warnings introduced in 2.3.2 and also has updated settings page text.
= 2.3.2 =
* Changes to the way the auto class insert works.
= 2.3.1 =
* Fixes fatal error in some cases due to admin notice in V2.3.
= 2.3 =
IMPORTANT, MAJOR CHANGES, BACKUP BEFORE UPDATING: Users that are inlining SVG will need to make sure "Advanced Mode" is active under "Settings > SVG Support". Your settings should all still be there. Make sure you run a backup before updating just in case!!!
= 2.2.5 =
* Fix to display SVG thumbnails in attachment modals. (NOTE: You can not edit SVG files like other images in WordPress)
= 2.2.4 =
* IMPORTANT: Fixes upload ability in WP 4.7.1
= 2.2.32 =
* Changed text domain to match plugin slug for localization.
= 2.2.31 =
* This release attempts to fix translation issues.
= 2.2.3 =
* Feature - Changed code to allow JS detection if SVG has loaded and ability to control SVG using JS.
* Fix - Thumbnail display in media library list view.
* Added Spanish translation and cleaned up code/comments a bit.
= 2.2.2 =
* Fix - Another change from anonymous function that was triggering errors for some.
= 2.2.1 =
* Minor change to remove anonymous function that triggered a fatal error in older PHP versions.
= 2.2 =
* Significant changes, added functionality, please BACKUP BEFORE UPDATING just in case.
= 2.1.7 =
* Tested in WordPress 4.0 and added plugin icons for the new interface.
= 2.1.6 =
* Important update! Added missing jQuery dependency in /functions/enqueue.php - was loading SVG Support JS before jQuery.
= 2.1.5 =
* Added Serbian translation, submitted by Ogi Djuraskovic.
= 2.1.4 =
* Some more re-arranging, added a few helpful links, updated language files, tended to my OCD a bit.
= 2.1.3 =
* Added a link on the plugins page to the plugin settings page for easy access after install.
= 2.1.2 =
* A little bit of house cleaning, updates to changelog and readme.txt for correct output with current version.
= 2.1.1 =
* Update to conditional in JS file.
= 2.1 =
* Updated language files for localization that were missed in version 2.0.
= 2.0 =
* SVG Support has been completely re-written and re-structured. It now includes an admin settings page with instructions, plus options for restricting to admin use only and setting a custom CSS target class.
= 1.0 =
* Initial Release.

View File

@ -0,0 +1,398 @@
@-webkit-keyframes iui-fadeIn {
0% {
opacity: 0
}
100% {
opacity: 1
}
}
@-moz-keyframes iui-fadeIn {
0% {
opacity: 0
}
100% {
opacity: 1
}
}
@-ms-keyframes iui-fadeIn {
0% {
opacity: 0
}
100% {
opacity: 1
}
}
@-o-keyframes iui-fadeIn {
0% {
opacity: 0
}
100% {
opacity: 1
}
}
@keyframes iui-fadeIn {
0% {
opacity: 0
}
100% {
opacity: 1
}
}
@-webkit-keyframes iui-fadeOut {
0% {
opacity: 1
}
100% {
opacity: 0
}
}
@-moz-keyframes iui-fadeOut {
0% {
opacity: 1
}
100% {
opacity: 0
}
}
@-ms-keyframes iui-fadeOut {
0% {
opacity: 1
}
100% {
opacity: 0
}
}
@-o-keyframes iui-fadeOut {
0% {
opacity: 1
}
100% {
opacity: 0
}
}
@keyframes iui-fadeOut {
0% {
opacity: 1
}
100% {
opacity: 0
}
}
.dropdown-multiple,
.dropdown-multiple-label,
.dropdown-single {
position: relative
}
.dropdown-multiple-label.active .dropdown-main,
.dropdown-multiple.active .dropdown-main,
.dropdown-single.active .dropdown-main {
display: block;
-webkit-animation: iui-fadeIn .2s ease-in forwards;
-moz-animation: iui-fadeIn .2s ease-in forwards;
-ms-animation: iui-fadeIn .2s ease-in forwards;
-o-animation: iui-fadeIn .2s ease-in forwards;
animation: iui-fadeIn .2s ease-in forwards
}
.dropdown-multiple-label.active .dropdown-display-label:after,
.dropdown-multiple-label.active .dropdown-display:after,
.dropdown-multiple.active .dropdown-display-label:after,
.dropdown-multiple.active .dropdown-display:after,
.dropdown-single.active .dropdown-display-label:after,
.dropdown-single.active .dropdown-display:after {
border-top: none;
border-bottom: 10px solid #999;
border-left: 5px solid transparent;
border-right: 5px solid transparent
}
.dropdown-multiple-label.active .dropdown-display,
.dropdown-multiple-label.active .dropdown-display-label,
.dropdown-multiple.active .dropdown-display,
.dropdown-multiple.active .dropdown-display-label,
.dropdown-single.active .dropdown-display,
.dropdown-single.active .dropdown-display-label {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0
}
.dropdown-display,
.dropdown-display-label {
position: relative;
display: block;
margin-bottom: 0;
font-size: 14px;
line-height: 1.42857143;
vertical-align: middle;
touch-action: manipulation;
cursor: pointer;
user-select: none;
background-image: none;
border: 1px solid #ccc;
border-radius: 4px;
color: #333;
background-color: #fff
}
.dropdown-display-label:after,
.dropdown-display:after {
content: '';
position: absolute;
border-top: 10px solid #999;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
top: 15px;
right: 8px
}
.dropdown-clear-all {
background-color: #fff;
border: none;
font-size: 22px;
z-index: 999;
color: #999;
position: absolute;
right: 2px;
top: 2px;
display: none;
width: 25px;
height: 30px;
text-align: center;
line-height: 30px;
}
.dropdown-clear-all:focus {
outline: 0
}
.dropdown-clear-all:hover {
color: #ccc;
text-decoration: none;
}
.dropdown-display {
white-space: nowrap;
padding: 6px 20px 6px 12px
}
.dropdown-multiple:hover .dropdown-clear-all,
.dropdown-single:hover .dropdown-clear-all {
display: block
}
.dropdown-display .dropdown-chose-list {
display: inline-block;
vertical-align: middle;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap
}
.dropdown-display .dropdown-chose-list span:before {
content: ','
}
.dropdown-display .dropdown-chose-list span:first-child:before {
content: ''
}
.dropdown-display .placeholder {
display: none
}
.dropdown-display .placeholder:first-child {
position: absolute;
height: 100%;
width: 100%;
top: 0;
left: 0;
color: #999;
display: block;
text-indent: 10px;
font-size: 13px;
line-height: 32px
}
.dropdown-display input {
border: 0;
outline: 0
}
.dropdown-display-label {
cursor: text;
padding: 6px 25px 5px 0
}
.dropdown-display-label .dropdown-search {
display: block;
width: 100%;
border: 1px solid #eee;
}
.dropdown-display-label .dropdown-search input {
width: 100%;
}
.dropdown-option.dropdown-chose {
font-weight: bold;
}
.dropdown-display-label input,
.dropdown-display-label input:focus {
border: none;
outline: 0
}
.dropdown-display-label .dropdown-chose-list {
width: 100%;
display: inline-block;
padding: 0 5px
}
.dropdown-display-label .dropdown-chose-list .placeholder {
display: none
}
.dropdown-display-label .dropdown-selected {
position: relative;
margin: 0 5px 5px 0;
padding: 0 20px 0 5px;
border: 1px solid #aaa;
max-width: 100%;
border-radius: 3px;
background-repeat: repeat-x;
color: #333;
cursor: default;
display: inline-block
}
.dropdown-display-label .dropdown-selected .del {
-webkit-appearance: none;
padding: 0;
cursor: pointer;
background: 0 0;
border: 0;
float: right;
line-height: 1;
color: #999;
position: absolute;
right: 3px;
top: 0
}
.dropdown-display-label .dropdown-selected .del:after {
content: '\D7';
font-size: 16px
}
.dropdown-main {
position: absolute;
top: 100%;
left: 0;
z-index: 1010;
width: 100%;
color: #444;
box-sizing: border-box;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 0 0 4px 4px;
box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
margin-top: -1px;
border-top: 0;
padding: 4px 7px;
display: none
}
.dropdown-main ul {
overflow-x: hidden;
overflow-y: auto;
max-height: 240px;
margin: 0;
padding: 0
}
.dropdown-main input {
margin-top: 0;
display: block;
box-sizing: border-box;
height: 30px;
border: 1px solid #ccc;
width: 100%;
text-indent: 5px;
border-radius: 3px
}
.dropdown-main .dropdown-search {
display: block;
padding: 5px 0
}
.dropdown-group {
font-weight: 700
}
.dropdown-group,
.dropdown-option {
margin: 0;
padding-left: 12px;
list-style: none;
line-height: 26px;
word-wrap: break-word
}
.dropdown-option {
cursor: pointer
}
.dropdown-option:focus,
.dropdown-option:hover {
background-color: #efefef;
outline: 0
}
.dropdown-option[disabled] {
color: #ddd;
background-color: #fff;
cursor: not-allowed;
text-decoration: line-through
}
.dropdown-option.dropdown-chose:after {
content: '';
float: right;
width: 10px;
height: 10px;
background: #4AB1E9;
border-radius: 100%;
margin: 8px 5px 0 0
}
.dropdown-maxItem-alert, .dropdown-minItem-alert {
position: absolute;
top: 0;
left: 0;
background-color: #e4e3e2;
width: 100%;
height: 39px;
line-height: 39px;
padding: 0 5px;
border-radius: 5px;
color: #999;
-webkit-animation: iui-fadeIn .2s ease-in forwards;
-moz-animation: iui-fadeIn .2s ease-in forwards;
-ms-animation: iui-fadeIn .2s ease-in forwards;
-o-animation: iui-fadeIn .2s ease-in forwards;
animation: iui-fadeIn .2s ease-in forwards
}

View File

@ -0,0 +1,21 @@
/**
* Featured Image Display
*
* Fixes the 0px featured image on edit screen
* WooCommerce Product, Sensei Course
*/
body #set-post-thumbnail,
body #postimagediv .inside img[src$=".svg"] {
width: 100%;
}
// Block display of featured image SVG
body .block-editor__container {
.components-panel .editor-post-featured-image__container img[src$='.svg'] {
position: relative;
}
.edit-post-sidebar .components-panel .components-checkbox-control {
margin-top: 10px;
}
}

View File

@ -0,0 +1,6 @@
/**
* Hide advanced features
*/
.svgs-advanced {
display: none;
}

View File

@ -0,0 +1,45 @@
/**
* Settings Page
*/
.svgs-version {
font-size: 10px;
margin-left: 7px;
}
.postbox .inside a {
text-decoration: none;
}
h3 {
padding: 0 12px;
}
.help-tab-content h3 {
padding: 0;
}
h3.inner-title {
padding: 0;
font-size: 1.2em;
}
.metabox-holder {
.postbox>h3,
.stuffbox>h3,
h2.hndle,
h3.hndle {
font-size: 1.3em;
font-weight: 600;
}
}
.shortpixel-logo img {
position: absolute;
right: 10px;
bottom: 0;
width: 140px;
}
/**
* Media Library
*/
table.media .column-title .media-icon img {
width: 60px;
// height: 60px;
}

View File

@ -0,0 +1,17 @@
// .attachment img[src$=".svg"],
// .widget_media_image img[src$=".svg"] {
// width: 100%;
// }
.attachment,
.widget_media_image {
// img[src$=".svg"] {
// width: 100%;
// }
svg {
max-width: 100%;
height: auto;
}
}

View File

@ -0,0 +1,118 @@
<?php
/*
Plugin Name: SVG Support
Plugin URI: http://wordpress.org/plugins/svg-support/
Description: Upload SVG files to the Media Library and render SVG files inline for direct styling/animation of an SVG's internal elements using CSS/JS.
Version: 2.5.5
Author: Benbodhi
Author URI: https://benbodhi.com
Text Domain: svg-support
Domain Path: /languages
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Copyright 2013 and beyond | Benbodhi (email : wp@benbodhi.com)
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* Global variables
*/
global $bodhi_svgs_options;
$bodhi_svgs_options = array(); // Defining global array
$svgs_plugin_version = '2.5.5'; // for use on admin pages
$plugin_file = plugin_basename(__FILE__); // plugin file for reference
define( 'BODHI_SVGS_PLUGIN_PATH', plugin_dir_path( __FILE__ ) ); // define the absolute plugin path for includes
define( 'BODHI_SVGS_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); // define the plugin url for use in enqueue
$bodhi_svgs_options = get_option('bodhi_svgs_settings'); // retrieve our plugin settings from the options table
/*
* SVG Sanitizer class
*/
use enshrined\svgSanitize\Sanitizer; // init svg sanitizer for usage
if ( ( !empty($bodhi_svgs_options['sanitize_svg']) && $bodhi_svgs_options['sanitize_svg'] === 'on' ) || ( !empty($bodhi_svgs_options['minify_svg']) && $bodhi_svgs_options['minify_svg'] === 'on' ) ) {
include( BODHI_SVGS_PLUGIN_PATH . 'vendor/autoload.php' ); // svg sanitizer
// interfaces to enable custom whitelisting of svg tags and attributes
include( BODHI_SVGS_PLUGIN_PATH . 'includes/svg-tags.php' );
include( BODHI_SVGS_PLUGIN_PATH . 'includes/svg-attributes.php' );
$sanitizer = new Sanitizer(); // initialize if enabled
}
/**
* Includes - keeping it modular
*/
include( BODHI_SVGS_PLUGIN_PATH . 'admin/admin-init.php' ); // initialize admin menu & settings page
include( BODHI_SVGS_PLUGIN_PATH . 'admin/plugin-action-meta-links.php' ); // add links to the plugin on the plugins page
include( BODHI_SVGS_PLUGIN_PATH . 'functions/mime-types.php' ); // setup mime types support for SVG (with fix for WP 4.7.1 - 4.7.2)
include( BODHI_SVGS_PLUGIN_PATH . 'functions/thumbnail-display.php' ); // make SVG thumbnails display correctly in media library
include( BODHI_SVGS_PLUGIN_PATH . 'functions/attachment.php' ); // make SVG thumbnails display correctly in attachment modals and generate attachment sizes
include( BODHI_SVGS_PLUGIN_PATH . 'functions/enqueue.php' ); // enqueue js & css for inline replacement & admin
include( BODHI_SVGS_PLUGIN_PATH . 'functions/localization.php' ); // setup localization & languages
include( BODHI_SVGS_PLUGIN_PATH . 'functions/attribute-control.php' ); // auto set SVG class & remove dimensions during insertion
include( BODHI_SVGS_PLUGIN_PATH . 'functions/featured-image.php' ); // allow inline SVG for featured images
/**
* Version based conditional / Check for stored plugin version
*
* Versions prior to 2.3 did not store the version number,
* If no version number is stored, store current plugin version number.
* If there is a version number stored, update it with the new version number.
*/
// get the stored plugin version
$svgs_plugin_version_stored = get_option( 'bodhi_svgs_plugin_version' );
// only run this if there is no stored version number (have never stored the number in previous versions)
if ( empty( $svgs_plugin_version_stored ) ) {
// add plugin version number to options table
update_option( 'bodhi_svgs_plugin_version', $svgs_plugin_version );
} else {
// update plugin version number in options table
update_option( 'bodhi_svgs_plugin_version', $svgs_plugin_version );
}
/**
* Defaults for better security in versions >= 2.5
*/
// Enable 'sanitize_svg_front_end' by default
if ( !isset($bodhi_svgs_options['sanitize_svg_front_end']) ) {
$bodhi_svgs_options['sanitize_svg_front_end'] = 'on';
update_option( 'bodhi_svgs_settings', $bodhi_svgs_options );
}
// Allow only admins to upload SVGs by default
if ( !isset($bodhi_svgs_options['restrict']) || $bodhi_svgs_options['restrict'] == "on" ) {
$bodhi_svgs_options['restrict'] = array('administrator');
update_option( 'bodhi_svgs_settings', $bodhi_svgs_options );
}
elseif (isset($bodhi_svgs_options['restrict']) && $bodhi_svgs_options['restrict'] == "none" ) {
$bodhi_svgs_options['restrict'] = array("none");
update_option( 'bodhi_svgs_settings', $bodhi_svgs_options );
}
// By default turn on "Sanitize SVG while uploading" option
if ( !isset($bodhi_svgs_options['sanitize_svg']) ) {
$bodhi_svgs_options['sanitize_svg'] = "on";
update_option( 'bodhi_svgs_settings', $bodhi_svgs_options );
}
// By default sanitize on upload for everyone except administrator and editor roles
if ( !isset($bodhi_svgs_options['sanitize_on_upload_roles']) ) {
$bodhi_svgs_options['sanitize_on_upload_roles'] = array('administrator', 'editor');
update_option( 'bodhi_svgs_settings', $bodhi_svgs_options );
}
elseif ( isset($bodhi_svgs_options['sanitize_on_upload_roles']) && $bodhi_svgs_options['sanitize_on_upload_roles'] == "none") {
$bodhi_svgs_options['sanitize_on_upload_roles'] = array("none");
update_option( 'bodhi_svgs_settings', $bodhi_svgs_options );
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@ -0,0 +1,10 @@
<?php if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
exit;
}
$bodhi_options_on_deletion = get_option( 'bodhi_svgs_settings' );
if ( isset($bodhi_options_on_deletion[ 'del_plugin_data' ]) && $bodhi_options_on_deletion[ 'del_plugin_data' ] === 'on' ) {
delete_option( 'bodhi_svgs_plugin_version' );
delete_option( 'bodhi_svgs_settings' );
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,7 @@
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit61c7f72a5d76a6a96ee3c8825ef2f87a::getLoader();

View File

@ -0,0 +1,445 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
// PSR-4
private $prefixLengthsPsr4 = array();
private $prefixDirsPsr4 = array();
private $fallbackDirsPsr4 = array();
// PSR-0
private $prefixesPsr0 = array();
private $fallbackDirsPsr0 = array();
private $useIncludePath = false;
private $classMap = array();
private $classMapAuthoritative = false;
private $missingClasses = array();
private $apcuPrefix;
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*/
function includeFile($file)
{
include $file;
}

View File

@ -0,0 +1,228 @@
<?php
namespace Composer;
use Composer\Semver\VersionParser;
class InstalledVersions
{
private static $installed = array (
'root' =>
array (
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'aliases' =>
array (
),
'reference' => NULL,
'name' => '__root__',
),
'versions' =>
array (
'__root__' =>
array (
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'aliases' =>
array (
),
'reference' => NULL,
),
'enshrined/svg-sanitize' =>
array (
'pretty_version' => '0.14.1',
'version' => '0.14.1.0',
'aliases' =>
array (
),
'reference' => '307b42066fb0b76b5119f5e1f0826e18fefabe95',
),
),
);
public static function getInstalledPackages()
{
return array_keys(self::$installed['versions']);
}
public static function isInstalled($packageName)
{
return isset(self::$installed['versions'][$packageName]);
}
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints($constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
public static function getVersionRanges($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
$ranges = array();
if (isset(self::$installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = self::$installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', self::$installed['versions'][$packageName])) {
$ranges = array_merge($ranges, self::$installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
public static function getVersion($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
if (!isset(self::$installed['versions'][$packageName]['version'])) {
return null;
}
return self::$installed['versions'][$packageName]['version'];
}
public static function getPrettyVersion($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
if (!isset(self::$installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return self::$installed['versions'][$packageName]['pretty_version'];
}
public static function getReference($packageName)
{
if (!isset(self::$installed['versions'][$packageName])) {
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
if (!isset(self::$installed['versions'][$packageName]['reference'])) {
return null;
}
return self::$installed['versions'][$packageName]['reference'];
}
public static function getRootPackage()
{
return self::$installed['root'];
}
public static function getRawData()
{
return self::$installed;
}
public static function reload($data)
{
self::$installed = $data;
}
}

View File

@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,10 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
);

View File

@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
);

View File

@ -0,0 +1,10 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'enshrined\\svgSanitize\\' => array($vendorDir . '/enshrined/svg-sanitize/src'),
);

View File

@ -0,0 +1,57 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit61c7f72a5d76a6a96ee3c8825ef2f87a
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit61c7f72a5d76a6a96ee3c8825ef2f87a', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit61c7f72a5d76a6a96ee3c8825ef2f87a', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit61c7f72a5d76a6a96ee3c8825ef2f87a::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
return $loader;
}
}

View File

@ -0,0 +1,36 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit61c7f72a5d76a6a96ee3c8825ef2f87a
{
public static $prefixLengthsPsr4 = array (
'e' =>
array (
'enshrined\\svgSanitize\\' => 22,
),
);
public static $prefixDirsPsr4 = array (
'enshrined\\svgSanitize\\' =>
array (
0 => __DIR__ . '/..' . '/enshrined/svg-sanitize/src',
),
);
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit61c7f72a5d76a6a96ee3c8825ef2f87a::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit61c7f72a5d76a6a96ee3c8825ef2f87a::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit61c7f72a5d76a6a96ee3c8825ef2f87a::$classMap;
}, null, ClassLoader::class);
}
}

View File

@ -0,0 +1,55 @@
{
"packages": [
{
"name": "enshrined/svg-sanitize",
"version": "0.14.1",
"version_normalized": "0.14.1.0",
"source": {
"type": "git",
"url": "https://github.com/darylldoyle/svg-sanitizer.git",
"reference": "307b42066fb0b76b5119f5e1f0826e18fefabe95"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/307b42066fb0b76b5119f5e1f0826e18fefabe95",
"reference": "307b42066fb0b76b5119f5e1f0826e18fefabe95",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"php": "^7.0 || ^8.0"
},
"require-dev": {
"codeclimate/php-test-reporter": "^0.1.2",
"phpunit/phpunit": "^6.5 || ^8.5"
},
"time": "2021-08-09T23:46:54+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"enshrined\\svgSanitize\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-2.0-or-later"
],
"authors": [
{
"name": "Daryll Doyle",
"email": "daryll@enshrined.co.uk"
}
],
"description": "An SVG sanitizer for PHP",
"support": {
"issues": "https://github.com/darylldoyle/svg-sanitizer/issues",
"source": "https://github.com/darylldoyle/svg-sanitizer/tree/0.14.1"
},
"install-path": "../enshrined/svg-sanitize"
}
],
"dev": true,
"dev-package-names": []
}

View File

@ -0,0 +1,33 @@
<?php return array (
'root' =>
array (
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'aliases' =>
array (
),
'reference' => NULL,
'name' => '__root__',
),
'versions' =>
array (
'__root__' =>
array (
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'aliases' =>
array (
),
'reference' => NULL,
),
'enshrined/svg-sanitize' =>
array (
'pretty_version' => '0.14.1',
'version' => '0.14.1.0',
'aliases' =>
array (
),
'reference' => '307b42066fb0b76b5119f5e1f0826e18fefabe95',
),
),
);

View File

@ -0,0 +1,26 @@
<?php
// platform_check.php @generated by Composer
$issues = array();
if (!(PHP_VERSION_ID >= 50600)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 5.6.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
);
}

View File

@ -0,0 +1,36 @@
name: Tests
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
php: ['7.0', '7.1', '7.2', '7.3', '7.4', '8.0']
fail-fast: false
steps:
- uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: composer:v2
- name: Validate composer.json and composer.lock
run: composer validate
# - name: Lint PHP
# uses: overtrue/phplint@7.4
# with:
# path: .
# options: --exclude=vendor
- name: Install dependencies
run: composer install --prefer-dist --no-progress
- name: Run test suite
run: composer run-script test

View File

@ -0,0 +1,5 @@
/vendor
/build
/.idea
/.phpunit.result.cache
composer.lock

View File

@ -0,0 +1,16 @@
language: php
php:
- 7.0
- 7.1
- 7.2
before_script:
- composer install --dev
addons:
code_climate:
repo_token: c051f6d29cce2d4ab0d6dfa460798b050cced025311f94ab3ba1ed50c7ceb31e
after_script:
- CODECLIMATE_REPO_TOKEN="c051f6d29cce2d4ab0d6dfa460798b050cced025311f94ab3ba1ed50c7ceb31e" vendor/bin/test-reporter --stdout > build/logs/codeclimate.json
- "curl --verbose -X POST -d @build/logs/codeclimate.json -H 'Content-Type: application/json' -H 'User-Agent: Code Climate (PHP Test Reporter v0.1.1)' https://codeclimate.com/test_reports"

View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
{description}
Copyright (C) {year} {fullname}
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
{signature of Ty Coon}, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@ -0,0 +1,103 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta charset="utf-8"/>
</head>
<body>
<h1 id="svg-sanitizer">svg-sanitizer</h1>
<p><a href="https://travis-ci.org/darylldoyle/svg-sanitizer"><img src="https://travis-ci.org/darylldoyle/svg-sanitizer.svg?branch=master" alt="Build Status" /></a> <a href="https://codeclimate.com/github/darylldoyle/svg-sanitizer/coverage"><img src="https://codeclimate.com/github/darylldoyle/svg-sanitizer/badges/coverage.svg" alt="Test Coverage" /></a></p>
<p>This is my attempt at building a decent SVG sanitizer in PHP. The work is laregely borrowed from <a href="https://github.com/cure53/DOMPurify">DOMPurify</a>.</p>
<h2 id="installation">Installation</h2>
<p>Either require <code>enshrined/svg-sanitize</code> through composer or download the repo and include the old way!</p>
<h2 id="usage">Usage</h2>
<p>Using this is fairly easy. Create a new instance of <code>enshrined\svgSanitize\Sanitizer</code> and then call the <code>sanitize</code> whilst passing in your dirty SVG/XML</p>
<p><strong>Basic Example</strong></p>
<pre><code class="php">use enshrined\svgSanitize\Sanitizer;
// Create a new sanitizer instance
$sanitizer = new Sanitizer();
// Load the dirty svg
$dirtySVG = file_get_contents('filthy.svg');
// Pass it to the sanitizer and get it back clean
$cleanSVG = $sanitizer-&gt;sanitize($dirtySVG);
// Now do what you want with your clean SVG/XML data
</code></pre>
<h2 id="output">Output</h2>
<p>This will either return a sanitized SVG/XML string or boolean <code>false</code> if XML parsing failed (usually due to a badly formatted file).</p>
<h2 id="options">Options</h2>
<p>You may pass your own whitelist of tags and attributes by using the <code>Sanitizer::setAllowedTags</code> and <code>Sanitizer::setAllowedAttrs</code> methods respectively.</p>
<p>These methods require that you implement the <code>enshrined\svgSanitize\data\TagInterface</code> or <code>enshrined\svgSanitize\data\AttributeInterface</code>.</p>
<h2 id="removeremotereferences">Remove remote references</h2>
<p>You have the option to remove attributes that reference remote files, this will stop HTTP leaks but will add an overhead to the sanitizer.</p>
<p>This defaults to false, set to true to remove references.</p>
<p><code>$sanitizer-&gt;removeRemoteReferences(true);</code></p>
<h2 id="viewingsanitizationissues">Viewing Sanitization Issues</h2>
<p>You may use the <code>getXmlIssues()</code> method to return an array of issues that occurred during sanitization.</p>
<p>This may be useful for logging or providing feedback to the user on why an SVG was refused.</p>
<p><code>$issues = $sanitizer-&gt;getXmlIssues();</code></p>
<h2 id="minification">Minification</h2>
<p>You can minify the XML output by calling <code>$sanitizer-&gt;minify(true);</code>.</p>
<h2 id="demo">Demo</h2>
<p>There is a demo available at: <a href="http://svg.enshrined.co.uk/">http://svg.enshrined.co.uk/</a></p>
<h2 id="wordpress">WordPress</h2>
<p>I&#8217;ve just released a WordPress plugin containing this code so you can sanitize your WordPress uploads. It&#8217;s available from the WordPress plugin directory: <a href="https://wordpress.org/plugins/safe-svg/">https://wordpress.org/plugins/safe-svg/</a></p>
<h2 id="drupal">Drupal</h2>
<p><a href="https://github.com/heyMP">Michael Potter</a> has kindly created a Drupal module for this library which is available at: <a href="https://www.drupal.org/project/svg_sanitizer">https://www.drupal.org/project/svg_sanitizer</a></p>
<h2 id="typo3">TYPO3</h2>
<p>An integration for TYPO3 CMS of this library is available as composer package <code>t3g/svg-sanitizer</code> at <a href="https://github.com/TYPO3GmbH/svg_sanitizer">https://github.com/TYPO3GmbH/svg_sanitizer</a></p>
<h2 id="tests">Tests</h2>
<p>You can run these by running <code>vendor/bin/phpunit</code> from the base directory of this package.</p>
<h2 id="standalonescanningoffilesviacli">Standalone scanning of files via CLI</h2>
<p>Thanks to the work by <a href="https://github.com/gudmdharalds">gudmdharalds</a> there&#8217;s now a standalone scanner that can be used via the CLI.</p>
<p>Any errors will be output in JSON format. See <a href="https://github.com/darylldoyle/svg-sanitizer/pull/25">the PR</a> for an example.</p>
<p>Use it as follows: <code>php svg-scanner.php ~/svgs/myfile.svg</code></p>
<h2 id="to-do">To-Do</h2>
<p>More extensive testing for the SVGs/XML would be lovely, I&#8217;ll try and add these soon. If you feel like doing it for me, please do and make a PR!</p>
</body>
</html>

View File

@ -0,0 +1,92 @@
# svg-sanitizer
[![Build Status](https://travis-ci.org/darylldoyle/svg-sanitizer.svg?branch=master)](https://travis-ci.org/darylldoyle/svg-sanitizer) [![Test Coverage](https://codeclimate.com/github/darylldoyle/svg-sanitizer/badges/coverage.svg)](https://codeclimate.com/github/darylldoyle/svg-sanitizer/coverage)
This is my attempt at building a decent SVG sanitizer in PHP. The work is laregely borrowed from [DOMPurify](https://github.com/cure53/DOMPurify).
## Installation
Either require `enshrined/svg-sanitize` through composer or download the repo and include the old way!
## Usage
Using this is fairly easy. Create a new instance of `enshrined\svgSanitize\Sanitizer` and then call the `sanitize` whilst passing in your dirty SVG/XML
**Basic Example**
```php
use enshrined\svgSanitize\Sanitizer;
// Create a new sanitizer instance
$sanitizer = new Sanitizer();
// Load the dirty svg
$dirtySVG = file_get_contents('filthy.svg');
// Pass it to the sanitizer and get it back clean
$cleanSVG = $sanitizer->sanitize($dirtySVG);
// Now do what you want with your clean SVG/XML data
```
## Output
This will either return a sanitized SVG/XML string or boolean `false` if XML parsing failed (usually due to a badly formatted file).
## Options
You may pass your own whitelist of tags and attributes by using the `Sanitizer::setAllowedTags` and `Sanitizer::setAllowedAttrs` methods respectively.
These methods require that you implement the `enshrined\svgSanitize\data\TagInterface` or `enshrined\svgSanitize\data\AttributeInterface`.
## Remove remote references
You have the option to remove attributes that reference remote files, this will stop HTTP leaks but will add an overhead to the sanitizer.
This defaults to false, set to true to remove references.
`$sanitizer->removeRemoteReferences(true);`
## Viewing Sanitization Issues
You may use the `getXmlIssues()` method to return an array of issues that occurred during sanitization.
This may be useful for logging or providing feedback to the user on why an SVG was refused.
`$issues = $sanitizer->getXmlIssues();`
## Minification
You can minify the XML output by calling `$sanitizer->minify(true);`.
## Demo
There is a demo available at: [http://svg.enshrined.co.uk/](http://svg.enshrined.co.uk/)
## WordPress
I've just released a WordPress plugin containing this code so you can sanitize your WordPress uploads. It's available from the WordPress plugin directory: [https://wordpress.org/plugins/safe-svg/](https://wordpress.org/plugins/safe-svg/)
## Drupal
[Michael Potter](https://github.com/heyMP) has kindly created a Drupal module for this library which is available at: [https://www.drupal.org/project/svg_sanitizer](https://www.drupal.org/project/svg_sanitizer)
## TYPO3
An integration for TYPO3 CMS of this library is available as composer package `t3g/svg-sanitizer` at [https://github.com/TYPO3GmbH/svg_sanitizer](https://github.com/TYPO3GmbH/svg_sanitizer)
## Tests
You can run these by running `vendor/bin/phpunit` from the base directory of this package.
## Standalone scanning of files via CLI
Thanks to the work by [gudmdharalds](https://github.com/gudmdharalds) there's now a standalone scanner that can be used via the CLI.
Any errors will be output in JSON format. See [the PR](https://github.com/darylldoyle/svg-sanitizer/pull/25) for an example.
Use it as follows: `php svg-scanner.php ~/svgs/myfile.svg`
## To-Do
More extensive testing for the SVGs/XML would be lovely, I'll try and add these soon. If you feel like doing it for me, please do and make a PR!

View File

@ -0,0 +1,33 @@
{
"name": "enshrined/svg-sanitize",
"description": "An SVG sanitizer for PHP",
"license": "GPL-2.0-or-later",
"authors": [
{
"name": "Daryll Doyle",
"email": "daryll@enshrined.co.uk"
}
],
"scripts": {
"test": "phpunit --no-coverage"
},
"autoload": {
"psr-4": {
"enshrined\\svgSanitize\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"enshrined\\svgSanitize\\Tests\\": "tests"
}
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"php": "^7.0 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^6.5 || ^8.5",
"codeclimate/php-test-reporter": "^0.1.2"
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php"
colors="true"
stopOnFailure="false">
<testsuites>
<testsuite name="The project's test suite">
<directory>./tests</directory>
</testsuite>
</testsuites>
<logging>
<log type="coverage-clover" target="./build/logs/clover.xml"/>
</logging>
<filter>
<whitelist addUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./src</directory>
</whitelist>
</filter>
</phpunit>

View File

@ -0,0 +1,169 @@
<?php
namespace enshrined\svgSanitize\ElementReference;
use enshrined\svgSanitize\data\XPath;
use enshrined\svgSanitize\Exceptions\NestingException;
use enshrined\svgSanitize\Helper;
class Resolver
{
/**
* @var XPath
*/
protected $xPath;
/**
* @var Subject[]
*/
protected $subjects = [];
/**
* @var array DOMElement[]
*/
protected $elementsToRemove = [];
/**
* @var int
*/
protected $useNestingLimit;
public function __construct(XPath $xPath, $useNestingLimit)
{
$this->xPath = $xPath;
$this->useNestingLimit = $useNestingLimit;
}
public function collect()
{
$this->collectIdentifiedElements();
$this->processReferences();
$this->determineInvalidSubjects();
}
/**
* Resolves one subject by element.
*
* @param \DOMElement $element
* @param bool $considerChildren Whether to search in Subject's children as well
* @return Subject|null
*/
public function findByElement(\DOMElement $element, $considerChildren = false)
{
foreach ($this->subjects as $subject) {
if (
$element === $subject->getElement()
|| $considerChildren && Helper::isElementContainedIn($element, $subject->getElement())
) {
return $subject;
}
}
return null;
}
/**
* Resolves subjects (plural!) by element id - in theory malformed
* DOM might have same ids assigned to different elements and leaving
* it to client/browser implementation which element to actually use.
*
* @param string $elementId
* @return Subject[]
*/
public function findByElementId($elementId)
{
return array_filter(
$this->subjects,
function (Subject $subject) use ($elementId) {
return $elementId === $subject->getElementId();
}
);
}
/**
* Collects elements having `id` attribute (those that can be referenced).
*/
protected function collectIdentifiedElements()
{
/** @var \DOMNodeList|\DOMElement[] $elements */
$elements = $this->xPath->query('//*[@id]');
foreach ($elements as $element) {
$this->subjects[$element->getAttribute('id')] = new Subject($element, $this->useNestingLimit);
}
}
/**
* Processes references from and to elements having `id` attribute concerning
* their occurrence in `<use ... xlink:href="#identifier">` statements.
*/
protected function processReferences()
{
$useNodeName = $this->xPath->createNodeName('use');
foreach ($this->subjects as $subject) {
$useElements = $this->xPath->query(
$useNodeName . '[@href or @xlink:href]',
$subject->getElement()
);
/** @var \DOMElement $useElement */
foreach ($useElements as $useElement) {
$useId = Helper::extractIdReferenceFromHref(
Helper::getElementHref($useElement)
);
if ($useId === null || !isset($this->subjects[$useId])) {
continue;
}
$subject->addUse($this->subjects[$useId]);
$this->subjects[$useId]->addUsedIn($subject);
}
}
}
/**
* Determines and tags infinite loops.
*/
protected function determineInvalidSubjects()
{
foreach ($this->subjects as $subject) {
if (in_array($subject->getElement(), $this->elementsToRemove)) {
continue;
}
$useId = Helper::extractIdReferenceFromHref(
Helper::getElementHref($subject->getElement())
);
try {
if ($useId === $subject->getElementId()) {
$this->markSubjectAsInvalid($subject);
} elseif ($subject->hasInfiniteLoop()) {
$this->markSubjectAsInvalid($subject);
}
} catch (NestingException $e) {
$this->elementsToRemove[] = $e->getElement();
$this->markSubjectAsInvalid($subject);
}
}
}
/**
* Get all the elements that caused a nesting exception.
*
* @return array
*/
public function getElementsToRemove() {
return $this->elementsToRemove;
}
/**
* The Subject is invalid for some reason, therefore we should
* remove it and all it's child usages.
*
* @param Subject $subject
*/
protected function markSubjectAsInvalid(Subject $subject) {
$this->elementsToRemove = array_merge(
$this->elementsToRemove,
$subject->clearInternalAndGetAffectedElements()
);
}
}

View File

@ -0,0 +1,153 @@
<?php
namespace enshrined\svgSanitize\ElementReference;
class Subject
{
/**
* @var \DOMElement
*/
protected $element;
/**
* @var Usage[]
*/
protected $useCollection = [];
/**
* @var Usage[]
*/
protected $usedInCollection = [];
/**
* @var int
*/
protected $useNestingLimit;
/**
* Subject constructor.
*
* @param \DOMElement $element
* @param int $useNestingLimit
*/
public function __construct(\DOMElement $element, $useNestingLimit)
{
$this->element = $element;
$this->useNestingLimit = $useNestingLimit;
}
/**
* @return \DOMElement
*/
public function getElement()
{
return $this->element;
}
/**
* @return string
*/
public function getElementId()
{
return $this->element->getAttribute('id');
}
/**
* @param array $subjects Previously processed subjects
* @param int $level The current level of nesting.
* @return bool
* @throws \enshrined\svgSanitize\Exceptions\NestingException
*/
public function hasInfiniteLoop(array $subjects = [], $level = 1)
{
if ($level > $this->useNestingLimit) {
throw new \enshrined\svgSanitize\Exceptions\NestingException('Nesting level too high, aborting', 1570713498, null, $this->getElement());
}
if (in_array($this, $subjects, true)) {
return true;
}
$subjects[] = $this;
foreach ($this->useCollection as $usage) {
if ($usage->getSubject()->hasInfiniteLoop($subjects, $level + 1)) {
return true;
}
}
return false;
}
/**
* @param Subject $subject
*/
public function addUse(Subject $subject)
{
if ($subject === $this) {
throw new \LogicException('Cannot add self usage', 1570713416);
}
$identifier = $subject->getElementId();
if (isset($this->useCollection[$identifier])) {
$this->useCollection[$identifier]->increment();
return;
}
$this->useCollection[$identifier] = new Usage($subject);
}
/**
* @param Subject $subject
*/
public function addUsedIn(Subject $subject)
{
if ($subject === $this) {
throw new \LogicException('Cannot add self as usage', 1570713417);
}
$identifier = $subject->getElementId();
if (isset($this->usedInCollection[$identifier])) {
$this->usedInCollection[$identifier]->increment();
return;
}
$this->usedInCollection[$identifier] = new Usage($subject);
}
/**
* @param bool $accumulated
* @return int
*/
public function countUse($accumulated = false)
{
$count = 0;
foreach ($this->useCollection as $use) {
$useCount = $use->getSubject()->countUse();
$count += $use->getCount() * ($accumulated ? 1 + $useCount : max(1, $useCount));
}
return $count;
}
/**
* @return int
*/
public function countUsedIn()
{
$count = 0;
foreach ($this->usedInCollection as $usedIn) {
$count += $usedIn->getCount() * max(1, $usedIn->getSubject()->countUsedIn());
}
return $count;
}
/**
* Clear the internal arrays (to free up memory as they can get big)
* and return all the child usages DOMElement's
*
* @return array
*/
public function clearInternalAndGetAffectedElements()
{
$elements = array_map(function(Usage $usage) {
return $usage->getSubject()->getElement();
}, $this->useCollection);
$this->usedInCollection = [];
$this->useCollection = [];
return $elements;
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace enshrined\svgSanitize\ElementReference;
class Usage
{
/**
* @var Subject
*/
protected $subject;
/**
* @var int
*/
protected $count;
/**
* @param Subject $subject
* @param int $count
*/
public function __construct(Subject $subject, $count = 1)
{
$this->subject = $subject;
$this->count = (int)$count;
}
/**
* @param int $by
*/
public function increment($by = 1)
{
$this->count += (int)$by;
}
/**
* @return Subject
*/
public function getSubject()
{
return $this->subject;
}
/**
* @return int
*/
public function getCount()
{
return $this->count;
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace enshrined\svgSanitize\Exceptions;
use Exception;
class NestingException extends \Exception
{
/**
* @var \DOMElement
*/
protected $element;
/**
* NestingException constructor.
*
* @param string $message
* @param int $code
* @param Exception|null $previous
* @param \DOMElement|null $element
*/
public function __construct($message = "", $code = 0, Exception $previous = null, \DOMElement $element = null)
{
$this->element = $element;
parent::__construct($message, $code, $previous);
}
/**
* Get the element that caused the exception.
*
* @return \DOMElement
*/
public function getElement()
{
return $this->element;
}
}

View File

@ -0,0 +1,53 @@
<?php
namespace enshrined\svgSanitize;
class Helper
{
/**
* @param \DOMElement $element
* @return string|null
*/
public static function getElementHref(\DOMElement $element)
{
if ($element->hasAttribute('href')) {
return $element->getAttribute('href');
}
if ($element->hasAttributeNS('http://www.w3.org/1999/xlink', 'href')) {
return $element->getAttributeNS('http://www.w3.org/1999/xlink', 'href');
}
return null;
}
/**
* @param string $href
* @return string|null
*/
public static function extractIdReferenceFromHref($href)
{
if (!is_string($href) || strpos($href, '#') !== 0) {
return null;
}
return substr($href, 1);
}
/**
* @param \DOMElement $needle
* @param \DOMElement $haystack
* @return bool
*/
public static function isElementContainedIn(\DOMElement $needle, \DOMElement $haystack)
{
if ($needle === $haystack) {
return true;
}
foreach ($haystack->childNodes as $childNode) {
if (!$childNode instanceof \DOMElement) {
continue;
}
if (self::isElementContainedIn($needle, $childNode)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,630 @@
<?php
namespace enshrined\svgSanitize;
use enshrined\svgSanitize\data\AllowedAttributes;
use enshrined\svgSanitize\data\AllowedTags;
use enshrined\svgSanitize\data\AttributeInterface;
use enshrined\svgSanitize\data\TagInterface;
use enshrined\svgSanitize\data\XPath;
use enshrined\svgSanitize\ElementReference\Resolver;
use enshrined\svgSanitize\ElementReference\Subject;
/**
* Class Sanitizer
*
* @package enshrined\svgSanitize
*/
class Sanitizer
{
/**
* @var \DOMDocument
*/
protected $xmlDocument;
/**
* @var array
*/
protected $allowedTags;
/**
* @var array
*/
protected $allowedAttrs;
/**
* @var
*/
protected $xmlLoaderValue;
/**
* @var bool
*/
protected $minifyXML = false;
/**
* @var bool
*/
protected $removeRemoteReferences = false;
/**
* @var int
*/
protected $useThreshold = 1000;
/**
* @var bool
*/
protected $removeXMLTag = false;
/**
* @var int
*/
protected $xmlOptions = LIBXML_NOEMPTYTAG;
/**
* @var array
*/
protected $xmlIssues = array();
/**
* @var Resolver
*/
protected $elementReferenceResolver;
/**
* @var int
*/
protected $useNestingLimit = 15;
/**
*
*/
function __construct()
{
// Load default tags/attributes
$this->allowedAttrs = array_map('strtolower', AllowedAttributes::getAttributes());
$this->allowedTags = array_map('strtolower', AllowedTags::getTags());
}
/**
* Set up the DOMDocument
*/
protected function resetInternal()
{
$this->xmlDocument = new \DOMDocument();
$this->xmlDocument->preserveWhiteSpace = false;
$this->xmlDocument->strictErrorChecking = false;
$this->xmlDocument->formatOutput = !$this->minifyXML;
}
/**
* Set XML options to use when saving XML
* See: DOMDocument::saveXML
*
* @param int $xmlOptions
*/
public function setXMLOptions($xmlOptions)
{
$this->xmlOptions = $xmlOptions;
}
/**
* Get XML options to use when saving XML
* See: DOMDocument::saveXML
*
* @return int
*/
public function getXMLOptions()
{
return $this->xmlOptions;
}
/**
* Get the array of allowed tags
*
* @return array
*/
public function getAllowedTags()
{
return $this->allowedTags;
}
/**
* Set custom allowed tags
*
* @param TagInterface $allowedTags
*/
public function setAllowedTags(TagInterface $allowedTags)
{
$this->allowedTags = array_map('strtolower', $allowedTags::getTags());
}
/**
* Get the array of allowed attributes
*
* @return array
*/
public function getAllowedAttrs()
{
return $this->allowedAttrs;
}
/**
* Set custom allowed attributes
*
* @param AttributeInterface $allowedAttrs
*/
public function setAllowedAttrs(AttributeInterface $allowedAttrs)
{
$this->allowedAttrs = array_map('strtolower', $allowedAttrs::getAttributes());
}
/**
* Should we remove references to remote files?
*
* @param bool $removeRemoteRefs
*/
public function removeRemoteReferences($removeRemoteRefs = false)
{
$this->removeRemoteReferences = $removeRemoteRefs;
}
/**
* Get XML issues.
*
* @return array
*/
public function getXmlIssues() {
return $this->xmlIssues;
}
/**
* Sanitize the passed string
*
* @param string $dirty
* @return string
*/
public function sanitize($dirty)
{
// Don't run on an empty string
if (empty($dirty)) {
return '';
}
// Strip php tags
$dirty = preg_replace('/<\?(=|php)(.+?)\?>/i', '', $dirty);
$this->resetInternal();
$this->setUpBefore();
$loaded = $this->xmlDocument->loadXML($dirty);
// If we couldn't parse the XML then we go no further. Reset and return false
if (!$loaded) {
$this->resetAfter();
return false;
}
// Pre-process all identified elements
$xPath = new XPath($this->xmlDocument);
$this->elementReferenceResolver = new Resolver($xPath, $this->useNestingLimit);
$this->elementReferenceResolver->collect();
$elementsToRemove = $this->elementReferenceResolver->getElementsToRemove();
// Grab all the elements
$allElements = $this->xmlDocument->getElementsByTagName("*");
// remove doctype after node elements have been analyzed
$this->removeDoctype();
// Start the cleaning proccess
$this->startClean($allElements, $elementsToRemove);
// Save cleaned XML to a variable
if ($this->removeXMLTag) {
$clean = $this->xmlDocument->saveXML($this->xmlDocument->documentElement, $this->xmlOptions);
} else {
$clean = $this->xmlDocument->saveXML($this->xmlDocument, $this->xmlOptions);
}
$this->resetAfter();
// Remove any extra whitespaces when minifying
if ($this->minifyXML) {
$clean = preg_replace('/\s+/', ' ', $clean);
}
// Return result
return $clean;
}
/**
* Set up libXML before we start
*/
protected function setUpBefore()
{
// This function has been deprecated in PHP 8.0 because in libxml 2.9.0, external entity loading is
// disabled by default, so this function is no longer needed to protect against XXE attacks.
if (\LIBXML_VERSION < 20900) {
// Turn off the entity loader
$this->xmlLoaderValue = libxml_disable_entity_loader(true);
}
// Suppress the errors because we don't really have to worry about formation before cleansing
libxml_use_internal_errors(true);
// Reset array of altered XML
$this->xmlIssues = array();
}
/**
* Reset the class after use
*/
protected function resetAfter()
{
// This function has been deprecated in PHP 8.0 because in libxml 2.9.0, external entity loading is
// disabled by default, so this function is no longer needed to protect against XXE attacks.
if (\LIBXML_VERSION < 20900) {
// Reset the entity loader
libxml_disable_entity_loader($this->xmlLoaderValue);
}
}
/**
* Remove the XML Doctype
* It may be caught later on output but that seems to be buggy, so we need to make sure it's gone
*/
protected function removeDoctype()
{
foreach ($this->xmlDocument->childNodes as $child) {
if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
$child->parentNode->removeChild($child);
}
}
}
/**
* Start the cleaning with tags, then we move onto attributes and hrefs later
*
* @param \DOMNodeList $elements
* @param array $elementsToRemove
*/
protected function startClean(\DOMNodeList $elements, array $elementsToRemove)
{
// loop through all elements
// we do this backwards so we don't skip anything if we delete a node
// see comments at: http://php.net/manual/en/class.domnamednodemap.php
for ($i = $elements->length - 1; $i >= 0; $i--) {
/** @var \DOMElement $currentElement */
$currentElement = $elements->item($i);
/**
* If the element has exceeded the nesting limit, we should remove it.
*
* As it's only <use> elements that cause us issues with nesting DOS attacks
* we should check what the element is before removing it. For now we'll only
* remove <use> elements.
*/
if (in_array($currentElement, $elementsToRemove) && 'use' === $currentElement->nodeName) {
$currentElement->parentNode->removeChild($currentElement);
$this->xmlIssues[] = array(
'message' => 'Invalid \'' . $currentElement->tagName . '\'',
'line' => $currentElement->getLineNo(),
);
continue;
}
// If the tag isn't in the whitelist, remove it and continue with next iteration
if (!in_array(strtolower($currentElement->tagName), $this->allowedTags)) {
$currentElement->parentNode->removeChild($currentElement);
$this->xmlIssues[] = array(
'message' => 'Suspicious tag \'' . $currentElement->tagName . '\'',
'line' => $currentElement->getLineNo(),
);
continue;
}
$this->cleanHrefs($currentElement);
$this->cleanXlinkHrefs($currentElement);
$this->cleanAttributesOnWhitelist($currentElement);
if (strtolower($currentElement->tagName) === 'use') {
if ($this->isUseTagDirty($currentElement)
|| $this->isUseTagExceedingThreshold($currentElement)
) {
$currentElement->parentNode->removeChild($currentElement);
$this->xmlIssues[] = array(
'message' => 'Suspicious \'' . $currentElement->tagName . '\'',
'line' => $currentElement->getLineNo(),
);
continue;
}
}
}
}
/**
* Only allow attributes that are on the whitelist
*
* @param \DOMElement $element
*/
protected function cleanAttributesOnWhitelist(\DOMElement $element)
{
for ($x = $element->attributes->length - 1; $x >= 0; $x--) {
// get attribute name
$attrName = $element->attributes->item($x)->name;
// Remove attribute if not in whitelist
if (!in_array(strtolower($attrName), $this->allowedAttrs) && !$this->isAriaAttribute(strtolower($attrName)) && !$this->isDataAttribute(strtolower($attrName))) {
$element->removeAttribute($attrName);
$this->xmlIssues[] = array(
'message' => 'Suspicious attribute \'' . $attrName . '\'',
'line' => $element->getLineNo(),
);
}
/**
* This is used for when a namespace isn't imported properly.
* Such as xlink:href when the xlink namespace isn't imported.
* We have to do this as the link is still ran in this case.
*/
if (false !== strpos($attrName, 'href')) {
$href = $element->getAttribute($attrName);
if (false === $this->isHrefSafeValue($href)) {
$element->removeAttribute($attrName);
$this->xmlIssues[] = array(
'message' => 'Suspicious attribute \'href\'',
'line' => $element->getLineNo(),
);
}
}
// Do we want to strip remote references?
if($this->removeRemoteReferences) {
// Remove attribute if it has a remote reference
if (isset($element->attributes->item($x)->value) && $this->hasRemoteReference($element->attributes->item($x)->value)) {
$element->removeAttribute($attrName);
$this->xmlIssues[] = array(
'message' => 'Suspicious attribute \'' . $attrName . '\'',
'line' => $element->getLineNo(),
);
}
}
}
}
/**
* Clean the xlink:hrefs of script and data embeds
*
* @param \DOMElement $element
*/
protected function cleanXlinkHrefs(\DOMElement $element)
{
$xlinks = $element->getAttributeNS('http://www.w3.org/1999/xlink', 'href');
if (false === $this->isHrefSafeValue($xlinks)) {
$element->removeAttributeNS( 'http://www.w3.org/1999/xlink', 'href' );
$this->xmlIssues[] = array(
'message' => 'Suspicious attribute \'href\'',
'line' => $element->getLineNo(),
);
}
}
/**
* Clean the hrefs of script and data embeds
*
* @param \DOMElement $element
*/
protected function cleanHrefs(\DOMElement $element)
{
$href = $element->getAttribute('href');
if (false === $this->isHrefSafeValue($href)) {
$element->removeAttribute('href');
$this->xmlIssues[] = array(
'message' => 'Suspicious attribute \'href\'',
'line' => $element->getLineNo(),
);
}
}
/**
* Only allow whitelisted starts to be within the href.
*
* This will stop scripts etc from being passed through, with or without attempting to hide bypasses.
* This stops the need for us to use a complicated script regex.
*
* @param $value
* @return bool
*/
protected function isHrefSafeValue($value) {
// Allow empty values
if (empty($value)) {
return true;
}
// Allow fragment identifiers.
if ('#' === substr($value, 0, 1)) {
return true;
}
// Allow relative URIs.
if ('/' === substr($value, 0, 1)) {
return true;
}
// Allow HTTPS domains.
if ('https://' === substr($value, 0, 8)) {
return true;
}
// Allow HTTP domains.
if ('http://' === substr($value, 0, 7)) {
return true;
}
// Allow known data URIs.
if (in_array(substr($value, 0, 14), array(
'data:image/png', // PNG
'data:image/gif', // GIF
'data:image/jpg', // JPG
'data:image/jpe', // JPEG
'data:image/pjp', // PJPEG
))) {
return true;
}
// Allow known short data URIs.
if (in_array(substr($value, 0, 12), array(
'data:img/png', // PNG
'data:img/gif', // GIF
'data:img/jpg', // JPG
'data:img/jpe', // JPEG
'data:img/pjp', // PJPEG
))) {
return true;
}
return false;
}
/**
* Removes non-printable ASCII characters from string & trims it
*
* @param string $value
* @return bool
*/
protected function removeNonPrintableCharacters($value)
{
return trim(preg_replace('/[^ -~]/xu','',$value));
}
/**
* Does this attribute value have a remote reference?
*
* @param $value
* @return bool
*/
protected function hasRemoteReference($value)
{
$value = $this->removeNonPrintableCharacters($value);
$wrapped_in_url = preg_match('~^url\(\s*[\'"]\s*(.*)\s*[\'"]\s*\)$~xi', $value, $match);
if (!$wrapped_in_url){
return false;
}
$value = trim($match[1], '\'"');
return preg_match('~^((https?|ftp|file):)?//~xi', $value);
}
/**
* Should we minify the output?
*
* @param bool $shouldMinify
*/
public function minify($shouldMinify = false)
{
$this->minifyXML = (bool) $shouldMinify;
}
/**
* Should we remove the XML tag in the header?
*
* @param bool $removeXMLTag
*/
public function removeXMLTag($removeXMLTag = false)
{
$this->removeXMLTag = (bool) $removeXMLTag;
}
/**
* Whether `<use ... xlink:href="#identifier">` elements shall be
* removed in case expansion would exceed this threshold.
*
* @param int $useThreshold
*/
public function useThreshold($useThreshold = 1000)
{
$this->useThreshold = (int)$useThreshold;
}
/**
* Check to see if an attribute is an aria attribute or not
*
* @param $attributeName
*
* @return bool
*/
protected function isAriaAttribute($attributeName)
{
return strpos($attributeName, 'aria-') === 0;
}
/**
* Check to see if an attribute is an data attribute or not
*
* @param $attributeName
*
* @return bool
*/
protected function isDataAttribute($attributeName)
{
return strpos($attributeName, 'data-') === 0;
}
/**
* Make sure our use tag is only referencing internal resources
*
* @param \DOMElement $element
* @return bool
*/
protected function isUseTagDirty(\DOMElement $element)
{
$href = Helper::getElementHref($element);
return $href && strpos($href, '#') !== 0;
}
/**
* Determines whether `<use ... xlink:href="#identifier">` is expanded
* recursively in order to create DoS scenarios. The amount of a actually
* used element needs to be below `$this->useThreshold`.
*
* @param \DOMElement $element
* @return bool
*/
protected function isUseTagExceedingThreshold(\DOMElement $element)
{
if ($this->useThreshold <= 0) {
return false;
}
$useId = Helper::extractIdReferenceFromHref(
Helper::getElementHref($element)
);
if ($useId === null) {
return false;
}
foreach ($this->elementReferenceResolver->findByElementId($useId) as $subject) {
if ($subject->countUse() >= $this->useThreshold) {
return true;
}
}
return false;
}
/**
* Set the nesting limit for <use> tags.
*
* @param $limit
*/
public function setUseNestingLimit($limit)
{
$this->useNestingLimit = (int) $limit;
}
}

View File

@ -0,0 +1,357 @@
<?php
namespace enshrined\svgSanitize\data;
/**
* Class AllowedAttributes
*
* @package enshrined\svgSanitize\data
*/
class AllowedAttributes implements AttributeInterface
{
/**
* Returns an array of attributes
*
* @return array
*/
public static function getAttributes()
{
return array(
// HTML
'about',
'accept',
'action',
'align',
'alt',
'autocomplete',
'background',
'bgcolor',
'border',
'cellpadding',
'cellspacing',
'checked',
'cite',
'class',
'clear',
'color',
'cols',
'colspan',
'coords',
'crossorigin',
'datetime',
'default',
'dir',
'disabled',
'download',
'enctype',
'encoding',
'face',
'for',
'headers',
'height',
'hidden',
'high',
'href',
'hreflang',
'id',
'integrity',
'ismap',
'label',
'lang',
'list',
'loop',
'low',
'max',
'maxlength',
'media',
'method',
'min',
'multiple',
'name',
'noshade',
'novalidate',
'nowrap',
'open',
'optimum',
'pattern',
'placeholder',
'poster',
'preload',
'pubdate',
'radiogroup',
'readonly',
'rel',
'required',
'rev',
'reversed',
'role',
'rows',
'rowspan',
'spellcheck',
'scope',
'selected',
'shape',
'size',
'sizes',
'span',
'srclang',
'start',
'src',
'srcset',
'step',
'style',
'summary',
'tabindex',
'title',
'type',
'usemap',
'valign',
'value',
'version',
'width',
'xmlns',
// SVG
'accent-height',
'accumulate',
'additivive',
'alignment-baseline',
'ascent',
'attributename',
'attributetype',
'azimuth',
'basefrequency',
'baseline-shift',
'begin',
'bias',
'by',
'class',
'clip',
'clip-path',
'clip-rule',
'color',
'color-interpolation',
'color-interpolation-filters',
'color-profile',
'color-rendering',
'cx',
'cy',
'd',
'dx',
'dy',
'diffuseconstant',
'direction',
'display',
'divisor',
'dur',
'edgemode',
'elevation',
'end',
'fill',
'fill-opacity',
'fill-rule',
'filter',
'flood-color',
'flood-opacity',
'font-family',
'font-size',
'font-size-adjust',
'font-stretch',
'font-style',
'font-variant',
'font-weight',
'fx',
'fy',
'g1',
'g2',
'glyph-name',
'glyphref',
'gradientunits',
'gradienttransform',
'height',
'href',
'id',
'image-rendering',
'in',
'in2',
'k',
'k1',
'k2',
'k3',
'k4',
'kerning',
'keypoints',
'keysplines',
'keytimes',
'lang',
'lengthadjust',
'letter-spacing',
'kernelmatrix',
'kernelunitlength',
'lighting-color',
'local',
'marker-end',
'marker-mid',
'marker-start',
'markerheight',
'markerunits',
'markerwidth',
'maskcontentunits',
'maskunits',
'max',
'mask',
'media',
'method',
'mode',
'min',
'name',
'numoctaves',
'offset',
'operator',
'opacity',
'order',
'orient',
'orientation',
'origin',
'overflow',
'paint-order',
'path',
'pathlength',
'patterncontentunits',
'patterntransform',
'patternunits',
'points',
'preservealpha',
'preserveaspectratio',
'r',
'rx',
'ry',
'radius',
'refx',
'refy',
'repeatcount',
'repeatdur',
'restart',
'result',
'rotate',
'scale',
'seed',
'shape-rendering',
'specularconstant',
'specularexponent',
'spreadmethod',
'stddeviation',
'stitchtiles',
'stop-color',
'stop-opacity',
'stroke-dasharray',
'stroke-dashoffset',
'stroke-linecap',
'stroke-linejoin',
'stroke-miterlimit',
'stroke-opacity',
'stroke',
'stroke-width',
'style',
'surfacescale',
'tabindex',
'targetx',
'targety',
'transform',
'text-anchor',
'text-decoration',
'text-rendering',
'textlength',
'type',
'u1',
'u2',
'unicode',
'values',
'viewbox',
'visibility',
'vert-adv-y',
'vert-origin-x',
'vert-origin-y',
'width',
'word-spacing',
'wrap',
'writing-mode',
'xchannelselector',
'ychannelselector',
'x',
'x1',
'x2',
'xmlns',
'y',
'y1',
'y2',
'z',
'zoomandpan',
// MathML
'accent',
'accentunder',
'align',
'bevelled',
'close',
'columnsalign',
'columnlines',
'columnspan',
'denomalign',
'depth',
'dir',
'display',
'displaystyle',
'fence',
'frame',
'height',
'href',
'id',
'largeop',
'length',
'linethickness',
'lspace',
'lquote',
'mathbackground',
'mathcolor',
'mathsize',
'mathvariant',
'maxsize',
'minsize',
'movablelimits',
'notation',
'numalign',
'open',
'rowalign',
'rowlines',
'rowspacing',
'rowspan',
'rspace',
'rquote',
'scriptlevel',
'scriptminsize',
'scriptsizemultiplier',
'selection',
'separator',
'separators',
'slope',
'stretchy',
'subscriptshift',
'supscriptshift',
'symmetric',
'voffset',
'width',
'xmlns',
// XML
'xlink:href',
'xml:id',
'xlink:title',
'xml:space',
'xmlns:xlink',
);
}
}

View File

@ -0,0 +1,245 @@
<?php
namespace enshrined\svgSanitize\data;
/**
* Class AllowedTags
*
* @package enshrined\svgSanitize\data
*/
class AllowedTags implements TagInterface
{
/**
* Returns an array of tags
*
* @return array
*/
public static function getTags()
{
return array (
// HTML
'a',
'abbr',
'acronym',
'address',
'area',
'article',
'aside',
'audio',
'b',
'bdi',
'bdo',
'big',
'blink',
'blockquote',
'body',
'br',
'button',
'canvas',
'caption',
'center',
'cite',
'code',
'col',
'colgroup',
'content',
'data',
'datalist',
'dd',
'decorator',
'del',
'details',
'dfn',
'dir',
'div',
'dl',
'dt',
'element',
'em',
'fieldset',
'figcaption',
'figure',
'font',
'footer',
'form',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'head',
'header',
'hgroup',
'hr',
'html',
'i',
'image',
'img',
'input',
'ins',
'kbd',
'label',
'legend',
'li',
'main',
'map',
'mark',
'marquee',
'menu',
'menuitem',
'meter',
'nav',
'nobr',
'ol',
'optgroup',
'option',
'output',
'p',
'pre',
'progress',
'q',
'rp',
'rt',
'ruby',
's',
'samp',
'section',
'select',
'shadow',
'small',
'source',
'spacer',
'span',
'strike',
'strong',
'style',
'sub',
'summary',
'sup',
'table',
'tbody',
'td',
'template',
'textarea',
'tfoot',
'th',
'thead',
'time',
'tr',
'track',
'tt',
'u',
'ul',
'var',
'video',
'wbr',
// SVG
'svg',
'altglyph',
'altglyphdef',
'altglyphitem',
'animatecolor',
'animatemotion',
'animatetransform',
'circle',
'clippath',
'defs',
'desc',
'ellipse',
'filter',
'font',
'g',
'glyph',
'glyphref',
'hkern',
'image',
'line',
'lineargradient',
'marker',
'mask',
'metadata',
'mpath',
'path',
'pattern',
'polygon',
'polyline',
'radialgradient',
'rect',
'stop',
'switch',
'symbol',
'text',
'textpath',
'title',
'tref',
'tspan',
'use',
'view',
'vkern',
// SVG Filters
'feBlend',
'feColorMatrix',
'feComponentTransfer',
'feComposite',
'feConvolveMatrix',
'feDiffuseLighting',
'feDisplacementMap',
'feDistantLight',
'feFlood',
'feFuncA',
'feFuncB',
'feFuncG',
'feFuncR',
'feGaussianBlur',
'feMerge',
'feMergeNode',
'feMorphology',
'feOffset',
'fePointLight',
'feSpecularLighting',
'feSpotLight',
'feTile',
'feTurbulence',
//MathML
'math',
'menclose',
'merror',
'mfenced',
'mfrac',
'mglyph',
'mi',
'mlabeledtr',
'mmuliscripts',
'mn',
'mo',
'mover',
'mpadded',
'mphantom',
'mroot',
'mrow',
'ms',
'mpspace',
'msqrt',
'mystyle',
'msub',
'msup',
'msubsup',
'mtable',
'mtd',
'mtext',
'mtr',
'munder',
'munderover',
//text
'#text'
);
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace enshrined\svgSanitize\data;
/**
* Class AttributeInterface
*
* @package enshrined\svgSanitize\data
*/
interface AttributeInterface
{
/**
* Returns an array of attributes
*
* @return array
*/
public static function getAttributes();
}

View File

@ -0,0 +1,19 @@
<?php
namespace enshrined\svgSanitize\data;
/**
* Interface TagInterface
*
* @package enshrined\svgSanitize\tags
*/
interface TagInterface
{
/**
* Returns an array of tags
*
* @return array
*/
public static function getTags();
}

View File

@ -0,0 +1,64 @@
<?php
namespace enshrined\svgSanitize\data;
class XPath extends \DOMXPath
{
const DEFAULT_NAMESPACE_PREFIX = 'svg';
/**
* @var string
*/
protected $defaultNamespaceURI;
public function __construct(\DOMDocument $doc)
{
parent::__construct($doc);
$this->handleDefaultNamespace();
}
/**
* @param string $nodeName
* @return string
*/
public function createNodeName($nodeName)
{
if (empty($this->defaultNamespaceURI)) {
return $nodeName;
}
return self::DEFAULT_NAMESPACE_PREFIX . ':' . $nodeName;
}
protected function handleDefaultNamespace()
{
$rootElements = $this->getRootElements();
if (count($rootElements) !== 1) {
throw new \LogicException(
sprintf('Got %d svg elements, expected exactly one', count($rootElements)),
1570870568
);
}
$this->defaultNamespaceURI = (string)$rootElements[0]->namespaceURI;
if ($this->defaultNamespaceURI !== '') {
$this->registerNamespace(self::DEFAULT_NAMESPACE_PREFIX, $this->defaultNamespaceURI);
}
}
/**
* @return \DOMElement[]
*/
protected function getRootElements()
{
$rootElements = [];
$elements = $this->document->getElementsByTagName('svg');
/** @var \DOMElement $element */
foreach ($elements as $element) {
if ($element->parentNode !== $this->document) {
continue;
}
$rootElements[] = $element;
}
return $rootElements;
}
}

View File

@ -0,0 +1,192 @@
#!/usr/bin/env php
<?php
/*
* Simple program that uses svg-sanitizer
* to find issues in files specified on the
* command line, and prints a JSON output with
* the issues found on exit.
*/
require_once( __DIR__ . '/data/AttributeInterface.php' );
require_once( __DIR__ . '/data/TagInterface.php' );
require_once( __DIR__ . '/data/AllowedAttributes.php' );
require_once( __DIR__ . '/data/AllowedTags.php' );
require_once( __DIR__ . '/data/XPath.php' );
require_once( __DIR__ . '/ElementReference/Resolver.php' );
require_once( __DIR__ . '/ElementReference/Subject.php' );
require_once( __DIR__ . '/ElementReference/Usage.php' );
require_once( __DIR__ . '/Exceptions/NestingException.php' );
require_once( __DIR__ . '/Helper.php' );
require_once( __DIR__ . '/Sanitizer.php' );
/*
* Print array as JSON and then
* exit program with a particular
* exit-code.
*/
function sysexit(
$results,
$status
) {
echo json_encode(
$results,
JSON_PRETTY_PRINT
);
exit( $status );
}
/*
* Main part begins
*/
global $argv;
/*
* Set up results array, to
* be printed on exit.
*/
$results = array(
'totals' => array(
'errors' => 0,
),
'files' => array(
),
);
/*
* Catch files to scan from $argv.
*/
$files_to_scan = $argv;
unset( $files_to_scan[0] );
$files_to_scan = array_values(
$files_to_scan
);
/*
* Catch no file specified.
*/
if ( empty( $files_to_scan ) ) {
$results['totals']['errors']++;
$results['messages'] = array(
array( 'No files to scan specified' ),
);
sysexit(
$results,
1
);
}
/*
* Initialize the SVG scanner.
*
* Make sure to allow custom attributes,
* and to remove remote references.
*/
$sanitizer = new enshrined\svgSanitize\Sanitizer();
$sanitizer->removeRemoteReferences( true );
/*
* Scan each file specified to be scanned.
*/
foreach( $files_to_scan as $file_name ) {
/*
* Read SVG file.
*/
$svg_file = @file_get_contents( $file_name );
/*
* If not found, report that and continue.
*/
if ( false === $svg_file ) {
$results['totals']['errors']++;
$results['files'][ $file_name ][] = array(
'errors' => 1,
'messages' => array(
array(
'message' => 'File specified could not be read (' . $file_name . ')',
'line' => null,
),
),
);
continue;
}
/*
* Sanitize file and get issues found.
*/
$sanitize_status = $sanitizer->sanitize( $svg_file );
$xml_issues = $sanitizer->getXmlIssues();
/*
* If we find no issues, simply note that.
*/
if ( empty( $xml_issues ) && ( false !== $sanitize_status ) ) {
$results['files'][ $file_name ] = array(
'errors' => 0,
'messages' => array()
);
}
/*
* Could not sanitize the file.
*/
else if (
( '' === $sanitize_status ) ||
( false === $sanitize_status )
) {
$results['totals']['errors']++;
$results['files'][ $file_name ] = array(
'errors' => 1,
'messages' => array(
array(
'message' => 'Unable to sanitize file \'' . $file_name . '\'' ,
'line' => null,
)
),
);
}
/*
* If we find issues, note it and update statistics.
*/
else {
$results['totals']['errors'] += count( $xml_issues );
$results['files'][ $file_name ] = array(
'errors' => count( $xml_issues ),
'messages' => $xml_issues,
);
}
unset( $svg_file );
unset( $xml_issues );
unset( $sanitize_status );
}
/*
* Exit with a status
* that reflects what issues
* we found.
*/
sysexit(
$results,
( $results['totals']['errors'] === 0 ? 0 : 1 )
);

View File

@ -0,0 +1,29 @@
<?php
namespace enshrined\svgSanitize\Tests;
use enshrined\svgSanitize\data\AllowedAttributes;
use PHPUnit\Framework\TestCase;
/**
* Class AllowedAttributesTest
*/
class AllowedAttributesTest extends TestCase
{
/**
* Test that the class implements the interface
*/
public function testItImplementsTheInterface()
{
$class = new AllowedAttributes();
self::assertInstanceOf('enshrined\svgSanitize\data\AttributeInterface', $class);
}
/**
* Test that an array is returned
*/
public function testThatItReturnsAnArray()
{
$result = AllowedAttributes::getAttributes();
self::assertSame('array', gettype($result));
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace enshrined\svgSanitize\Tests;
use enshrined\svgSanitize\data\AllowedTags;
use PHPUnit\Framework\TestCase;
/**
* Class AllowedTagsTest
*/
class AllowedTagsTest extends TestCase
{
/**
* Test that the class implements the interface
*/
public function testItImplementsTheInterface()
{
$class = new AllowedTags();
self::assertInstanceOf('enshrined\svgSanitize\data\TagInterface', $class);
}
/**
* Test that an array is returned
*/
public function testThatItReturnsAnArray()
{
$result = AllowedTags::getTags();
self::assertSame('array', gettype($result));
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace enshrined\svgSanitize\Tests\Fixtures;
use enshrined\svgSanitize\data\AttributeInterface;
class TestAllowedAttributes implements AttributeInterface
{
/**
* Returns an array of attributes
*
* @return array
*/
public static function getAttributes()
{
return array(
'testAttribute',
);
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace enshrined\svgSanitize\Tests\Fixtures;
use enshrined\svgSanitize\data\TagInterface;
class TestAllowedTags implements TagInterface
{
/**
* Returns an array of tags
*
* @return array
*/
public static function getTags()
{
return array(
'testTag',
);
}
}

View File

@ -0,0 +1,297 @@
<?php
namespace enshrined\svgSanitize\Tests;
use enshrined\svgSanitize\Sanitizer;
use enshrined\svgSanitize\Tests\Fixtures\TestAllowedAttributes;
use enshrined\svgSanitize\Tests\Fixtures\TestAllowedTags;
use PHPUnit\Framework\TestCase;
/**
* Class SanitizerTest
*/
class SanitizerTest extends TestCase
{
/**
* Make sure the initial tags are loaded
*/
public function testLoadDefaultTags()
{
$sanitizer = new Sanitizer();
$tags = $sanitizer->getAllowedTags();
self::assertSame('array', gettype($tags));
}
/**
* Make sure the initial attributes are loaded
*/
public function testLoadDefaultAttributes()
{
$sanitizer = new Sanitizer();
$attributes = $sanitizer->getAllowedAttrs();
self::assertSame('array', gettype($attributes));
}
/**
* Test the custom tag setters and getters
*/
public function testSetCustomTags()
{
$sanitizer = new Sanitizer();
$sanitizer->setAllowedTags(new TestAllowedTags());
$tags = $sanitizer->getAllowedTags();
self::assertSame('array', gettype($tags));
self::assertSame(array_map('strtolower', TestAllowedTags::getTags()), $tags);
}
/**
* Test the custom attribute setters and getters
*/
public function testSetCustomAttributes()
{
$sanitizer = new Sanitizer();
$sanitizer->setAllowedAttrs(new TestAllowedAttributes());
$attributes = $sanitizer->getAllowedAttrs();
self::assertSame('array', gettype($attributes));
self::assertSame( array_map('strtolower', TestAllowedAttributes::getAttributes()), $attributes);
}
/**
* Test that malicious elements and attributes are removed from standard XML
*/
public function testSanitizeXMLDoc()
{
$dataDirectory = __DIR__ . '/data';
$initialData = file_get_contents($dataDirectory . '/xmlTestOne.xml');
$expected = file_get_contents($dataDirectory . '/xmlCleanOne.xml');
$sanitizer = new Sanitizer();
$cleanData = $sanitizer->sanitize($initialData);
self::assertXmlStringEqualsXmlString($expected, $cleanData);
}
/**
* Test that malicious elements and attributes are removed from an SVG
*/
public function testSanitizeSVGDoc()
{
$dataDirectory = __DIR__ . '/data';
$initialData = file_get_contents($dataDirectory . '/svgTestOne.svg');
$expected = file_get_contents($dataDirectory . '/svgCleanOne.svg');
$sanitizer = new Sanitizer();
$cleanData = $sanitizer->sanitize($initialData);
self::assertXmlStringEqualsXmlString($expected, $cleanData);
}
/**
* Test that a badly formatted XML document returns false
*/
public function testBadXMLReturnsFalse()
{
$dataDirectory = __DIR__ . '/data';
$initialData = file_get_contents($dataDirectory . '/badXmlTestOne.svg');
$sanitizer = new Sanitizer();
$cleanData = $sanitizer->sanitize($initialData);
self::assertSame(false, $cleanData);
}
/**
* Make sure that hrefs get sanitized correctly
*/
public function testSanitizeHrefs()
{
$dataDirectory = __DIR__ . '/data';
$initialData = file_get_contents($dataDirectory . '/hrefTestOne.svg');
$expected = file_get_contents($dataDirectory . '/hrefCleanOne.svg');
$sanitizer = new Sanitizer();
$cleanData = $sanitizer->sanitize($initialData);
self::assertXmlStringEqualsXmlString($expected, $cleanData);
}
/**
* Make sure that hrefs get sanitized correctly when the xlink namespace is omitted.
*/
public function testSanitizeHrefsNoXlinkNamespace()
{
$dataDirectory = __DIR__ . '/data';
$initialData = file_get_contents($dataDirectory . '/hrefTestTwo.svg');
$expected = file_get_contents($dataDirectory . '/hrefCleanTwo.svg');
$sanitizer = new Sanitizer();
$cleanData = $sanitizer->sanitize($initialData);
self::assertXmlStringEqualsXmlString($expected, $cleanData);
}
/**
* Make sure that external references get sanitized correctly
*/
public function testSanitizeExternal()
{
$dataDirectory = __DIR__ . '/data';
$initialData = file_get_contents($dataDirectory . '/externalTest.svg');
$expected = file_get_contents($dataDirectory . '/externalClean.svg');
$sanitizer = new Sanitizer();
$sanitizer->removeRemoteReferences(true);
$cleanData = $sanitizer->sanitize($initialData);
self::assertXmlStringEqualsXmlString($expected, $cleanData);
}
/**
* Test that minification of an SVG works
*/
public function testSanitizeAndMinifiySVGDoc()
{
$dataDirectory = __DIR__ . '/data';
$initialData = file_get_contents($dataDirectory . '/svgTestOne.svg');
$expected = file_get_contents($dataDirectory . '/svgCleanOneMinified.svg');
$sanitizer = new Sanitizer();
$sanitizer->minify(true);
$cleanData = $sanitizer->sanitize($initialData);
self::assertXmlStringEqualsXmlString($expected, $cleanData);
}
/**
* Test that ARIA and Data Attributes are allowed
*/
public function testThatAriaAndDataAttributesAreAllowed()
{
$dataDirectory = __DIR__ . '/data';
$initialData = file_get_contents($dataDirectory . '/ariaDataTest.svg');
$expected = file_get_contents($dataDirectory . '/ariaDataClean.svg');
$sanitizer = new Sanitizer();
$sanitizer->minify(false);
$cleanData = $sanitizer->sanitize($initialData);
self::assertXmlStringEqualsXmlString($expected, $cleanData);
}
/**
* Test that ARIA and Data Attributes are allowed
*/
public function testThatExternalUseElementsAreStripped()
{
$dataDirectory = __DIR__ . '/data';
$initialData = file_get_contents($dataDirectory . '/useTest.svg');
$expected = file_get_contents($dataDirectory . '/useClean.svg');
$sanitizer = new Sanitizer();
$sanitizer->minify(false);
$cleanData = $sanitizer->sanitize($initialData);
self::assertXmlStringEqualsXmlString($expected, $cleanData);
}
/**
* Test setXMLOptions and minifying works as expected
*/
public function testMinifiedOptions()
{
$sanitizer = new Sanitizer();
$sanitizer->minify(true);
$sanitizer->removeXMLTag(true);
$sanitizer->setXMLOptions(0);
$input = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><title>chevron-double-down</title><path d="M4 11.73l.68-.73L12 17.82 19.32 11l.68.73-7.66 7.13a.5.5 0 0 1-.68 0z"/><path d="M4 5.73L4.68 5 12 11.82 19.32 5l.68.73-7.66 7.13a.5.5 0 0 1-.68 0z"/></svg>';
$output = $sanitizer->sanitize($input);
self::assertSame($input, $output);
}
/**
* @test
*/
public function useRecursionsAreDetected()
{
$dataDirectory = __DIR__ . '/data';
$initialData = file_get_contents($dataDirectory . '/xlinkLaughsTest.svg');
$expected = file_get_contents($dataDirectory . '/xlinkLaughsClean.svg');
$sanitizer = new Sanitizer();
$sanitizer->minify(false);
$cleanData = $sanitizer->sanitize($initialData);
self::assertXmlStringEqualsXmlString($expected, $cleanData);
}
/**
* @test
*/
public function infiniteUseLoopsAreDetected()
{
$dataDirectory = __DIR__ . '/data';
$initialData = file_get_contents($dataDirectory . '/xlinkLoopTest.svg');
$expected = file_get_contents($dataDirectory . '/xlinkLoopClean.svg');
$sanitizer = new Sanitizer();
$sanitizer->minify(false);
$cleanData = $sanitizer->sanitize($initialData);
self::assertXmlStringEqualsXmlString($expected, $cleanData);
}
/**
* @test
*/
public function doctypeAndEntityAreRemoved()
{
$dataDirectory = __DIR__ . '/data';
$initialData = file_get_contents($dataDirectory . '/entityTest.svg');
$expected = file_get_contents($dataDirectory . '/entityClean.svg');
$sanitizer = new Sanitizer();
$sanitizer->minify(false);
$sanitizer->removeRemoteReferences(true);
$cleanData = $sanitizer->sanitize($initialData);
self::assertSame($expected, $cleanData);
}
/**
* Make sure that DOS attacks using the <use> element are detected.
*/
public function testUseDOSattacksAreNullified()
{
$dataDirectory = __DIR__ . '/data';
$initialData = file_get_contents($dataDirectory . '/useDosTest.svg');
$expected = file_get_contents($dataDirectory . '/useDosClean.svg');
$sanitizer = new Sanitizer();
$sanitizer->minify(false);
$cleanData = $sanitizer->sanitize($initialData);
self::assertXmlStringEqualsXmlString($expected, $cleanData);
}
/**
* Make sure that DOS attacks using the <use> element are detected,
* especially when the SVG is extremely large.
*/
public function testLargeUseDOSattacksAreNullified()
{
$dataDirectory = __DIR__ . '/data';
$initialData = file_get_contents($dataDirectory . '/useDosTestTwo.svg');
$expected = file_get_contents($dataDirectory . '/useDosCleanTwo.svg');
$sanitizer = new Sanitizer();
$sanitizer->minify(false);
$cleanData = $sanitizer->sanitize($initialData);
self::assertXmlStringEqualsXmlString($expected, $cleanData);
}
}

View File

@ -0,0 +1,148 @@
<?php
namespace enshrined\svgSanitize\Tests;
use enshrined\svgSanitize\ElementReference\Subject;
use PHPUnit\Framework\TestCase;
/**
* Class SubjectTest
*/
class SubjectTest extends TestCase
{
/**
* @var int
*/
protected $nestingLimit = 15;
/**
* <first>
* <!-- 0 -->
* </first>
*
* @test
*/
public function oneLevelCountsUseIsCorrect()
{
$first = new \DOMElement('first');
$firstSubject = new Subject($first, $this->nestingLimit);
self::assertSame(0, $firstSubject->countUse(false));
self::assertSame(0, $firstSubject->countUse(true));
}
/**
* <first>
* <second /> <!-- 1 -->
* <second /> <!-- 2 -->
* </first>
*
* @test
*/
public function twoLevelsCountUseIsCorrect()
{
$first = new \DOMElement('first');
$second = new \DOMElement('second');
$firstSubject = new Subject($first, $this->nestingLimit);
$secondSubject = new Subject($second, $this->nestingLimit);
$firstSubject->addUse($secondSubject);
$firstSubject->addUse($secondSubject);
self::assertSame(2, $firstSubject->countUse(false));
self::assertSame(2, $firstSubject->countUse(true));
}
/**
* <first>
* <second> <!-- accumulated=false: 0; accumulated=true: 1 -->
* <third /> <!-- accumulated=false: 1; accumulated=true: 2 -->
* <third /> <!-- accumulated=false: 2; accumulated=true: 3 -->
* <third /> <!-- accumulated=false: 3; accumulated=true: 4 -->
* </second>
* <second> <!-- accumulated=false: 3; accumulated=true: 5 -->
* <third /> <!-- accumulated=false: 4; accumulated=true: 6 -->
* <third /> <!-- accumulated=false: 5; accumulated=true: 7 -->
* <third /> <!-- accumulated=false: 6; accumulated=true: 8 -->
* </second>
* </first>
*
* @test
*/
public function threeLevelsCountUseIsCorrect()
{
$first = new \DOMElement('first');
$second = new \DOMElement('second');
$third = new \DOMElement('third');
$firstSubject = new Subject($first, $this->nestingLimit);
$secondSubject = new Subject($second, $this->nestingLimit);
$thirdSubject = new Subject($third, $this->nestingLimit);
$firstSubject->addUse($secondSubject);
$firstSubject->addUse($secondSubject);
$secondSubject->addUse($thirdSubject);
$secondSubject->addUse($thirdSubject);
$secondSubject->addUse($thirdSubject);
self::assertSame(6, $firstSubject->countUse(false));
self::assertSame(8, $firstSubject->countUse(true));
}
/**
* <first>
* </first>
*
* @test
*/
public function oneLevelCountsUsedInIsCorrect()
{
$first = new \DOMElement('first');
$firstSubject = new Subject($first, $this->nestingLimit);
self::assertSame(0, $firstSubject->countUsedIn());
}
/**
* <first>
* <second /> <!-- 1 -->
* <second /> <!-- 2 -->
* </first>
*
* @test
*/
public function twoLevelsCountUsedInIsCorrect()
{
$first = new \DOMElement('first');
$second = new \DOMElement('second');
$firstSubject = new Subject($first, $this->nestingLimit);
$secondSubject = new Subject($second, $this->nestingLimit);
$secondSubject->addUsedIn($firstSubject);
$secondSubject->addUsedIn($firstSubject);
self::assertSame(2, $secondSubject->countUsedIn());
}
/**
* <first>
* <second>
* <third /> <!-- 1 -->
* <third /> <!-- 2 -->
* <third /> <!-- 3 -->
* </second>
* <second>
* <third /> <!-- 4 -->
* <third /> <!-- 5 -->
* <third /> <!-- 6 -->
* </second>
* </first>
*
* @test
*/
public function threeLevelsCountUsedInIsCorrect()
{
$first = new \DOMElement('first');
$second = new \DOMElement('second');
$third = new \DOMElement('third');
$firstSubject = new Subject($first, $this->nestingLimit);
$secondSubject = new Subject($second, $this->nestingLimit);
$thirdSubject = new Subject($third, $this->nestingLimit);
$thirdSubject->addUsedIn($secondSubject);
$thirdSubject->addUsedIn($secondSubject);
$thirdSubject->addUsedIn($secondSubject);
$secondSubject->addUsedIn($firstSubject);
$secondSubject->addUsedIn($firstSubject);
self::assertSame(6, $thirdSubject->countUsedIn());
}
}

View File

@ -0,0 +1,56 @@
<svg id="cat" viewBox="0 0 720 800" aria-labelledby="catTitle catDesc" role="img" version="1">
<title id="catTitle">Pixels, My Super-friendly Cat</title>
<desc id="catDesc">An illustrated gray cat with bright green blinking eyes.</desc>
<path id="tail" data-name="tail" class="cls-1" d="M545.9,695.9c8,28.2,23.2,42.3,27.2,46.9,21.4,24.1,41.5,40.2,81.1,42.9s65.4-14.2,60.8-26.8-23.1-9.1-51.3-8.3c-35.2.9-66.6-31.3-74.8-63.9s-7.9-63.8-36.8-85.5c-44.1-33-135.6-7.1-159.8-3.4s-48.4,52.5-9.6,45.1,91.4-23.1,123.2-12.7C537.8,640.4,537.9,667.7,545.9,695.9Z" transform="translate(-9.7 -9.3)"/>
<g id="body">
<path id="bg" class="cls-2" d="M447.9,502.1c2.1,151.7-108.3,167-216.5,167S9.7,663.8,9.7,510.9,85,242.9,231.3,241,445.8,350.4,447.9,502.1h0Z" transform="translate(-9.7 -9.3)"/>
<g id="leftleg">
<path id="leg" class="cls-1" d="M195.6,671.5c-34.2-7.7-40.6-95.6-53.3-191-12-90-90.1-177.2-55.1-177.2s145.7,12,151.4,87.7S261.5,686.5,195.6,671.5Z" transform="translate(-9.7 -9.3)"/>
<path id="foot" class="cls-3" d="M172.2,688.1c31.6,2.1,56.6-8.7,59.8-32.4s-22.1-49.5-27.3-24.3c25-16.4-39.1-29.4-27.6-3.9,14-24.9-49.6-19.2-31.9-.1-6.5-27.2-35.6,8.2-30.1,29.3C121.5,681.8,140.5,686,172.2,688.1Z" transform="translate(-9.7 -9.3)"/>
</g>
<g id="rightleg">
<path id="leg-2" data-name="leg" class="cls-1" d="M260.4,670.4c42.4-9.2,48.7-87.7,53.9-185.2,5.1-96,98.2-176.1,63.1-176.1s-164,15.7-164,111.8C213.4,420.9,199.1,683.7,260.4,670.4Z" transform="translate(-9.7 -9.3)"/>
<path id="foot-2" data-name="foot" class="cls-3" d="M279.4,689.8c-31.7,2-56.6-9-59.6-32.6s22.3-49.4,27.4-24.1c-24.9-16.5,39.2-29.2,27.6-3.8-13.9-25,49.7-18.9,31.9,0,6.6-27.1,35.6,8.4,30,29.4-6.7,25-25.7,29.1-57.3,31.1h0Z" transform="translate(-9.7 -9.3)"/>
</g>
<path id="tuft" aria-haspopup="false" class="cls-3" d="M80,331.2c3.5,9.5,1.2,28.9,4.3,32.7s31.5-30,43-20.6c10.7,8.7,1.7,55.9,12.9,64.5,10.1,7.7,32.1-50.6,52.5-38.7,24.9,14.6,34.1,49.9,49,49.9,18.3,0,7.5-49.5,24.1-53.3s46.1,52.6,60.2,45.6c4.8-2.4,3-50.4,12-57.6,8.7-6.9,30.5,22.4,33.5,18.9,3.7-4.1.1-23.1,8.6-36.1,3.4-5.2,18.9-2.6,28.8-.4a3.46,3.46,0,0,0,3.7-5.2c-19.6-30.8-100-147.4-184.2-147.4-93.3,0-150.9,86.8-178.1,141.6a3.43,3.43,0,0,0,3.6,4.9C63,328.4,78.4,326.6,80,331.2Z" transform="translate(-9.7 -9.3)"/>
</g>
<g id="head">
<path id="collar" class="cls-4" d="M367,231.1c5.7,36.1-4.7,71-97.8,85.6s-184-18.5-189.7-54.5,16.7-17.3,109.8-31.9,172-35.3,177.7.8" transform="translate(-9.7 -9.3)"/>
<g id="bg-2" data-name="bg">
<path class="cls-1" d="M362.5,229.5C339.7,279,273.1,299.4,225,300c-60.6.7-134.7-29.5-153.5-86.4C45.6,135.4,132.2,32.6,225,35.8c96.1,3.4,171.7,119.4,137.5,193.7" transform="translate(-9.7 -9.3)"/>
<path class="cls-5" d="M362.5,229.5C339.7,279,273.1,299.4,225,300c-60.6.7-134.7-29.5-153.5-86.4C45.6,135.4,132.2,32.6,225,35.8,321.1,39.2,396.7,155.2,362.5,229.5Z" transform="translate(-9.7 -9.3)"/>
</g>
<g id="leftear" aria-label="Left Ear">
<path id="outer" class="cls-1" d="M92.7,117c-2.6,4.7-14.7-16.1-16.5-45-3.3-27.7,3.7-63.4,5.4-62C80.7,8,117,10,143,20c27.5,8.9,44.7,25.7,39.5,27.1-30,23.4-59.9,46.6-89.8,69.9" transform="translate(-9.7 -9.3)"/>
<path id="inner" class="cls-6" d="M105.8,106.9C103.9,110.3,95.3,95.5,94,75c-2.3-19.6,2.6-44.9,3.8-44-0.6-1.4,25.1,0,43.6,7.1,19.5,6.3,31.7,18.2,28,19.2q-31.8,24.9-63.6,49.6" transform="translate(-9.7 -9.3)"/>
</g>
<path id="mask" class="cls-2" d="M338.4,142.5c-2.2,3.3,19.4,19.6,17.2,23.2s-24.3-7.8-25.8-5.2c-1.9,3.3,33.4,24.1,31,29.2-2.3,4.9-34-14.4-84.3-18.1a141.76,141.76,0,0,1-16.4-2.1,91.21,91.21,0,0,1-13.7-3.9c-19.8-6.9-27.7-10.6-32.7-12-19.3-5.7-26.8,11.3-68.1,22.4-18.8,5-37.9,9.7-54.4,0-2.1-1.3-13.6-8.3-16.7-21.1-0.9-3.6-2.8-15.2,10.5-34C146.3,34.3,216.5,34,217.3,34a131.52,131.52,0,0,1,58.4,14.3c-7.6,4.9-11.2,9.5-9,10.1,21.5,16.5,43.1,33,64.6,49.5,0.9,1.7,3.6-1.3,6.3-7.3,19.3,30.5,22.1,41.5,18.9,44.3-3.8,3.6-16.4-4.8-18.1-2.4" transform="translate(-9.7 -9.3)"/>
<g id="rightear">
<path id="outer-2" data-name="outer" class="cls-2" d="M344.9,119.9c2.6,4.7,14.7-16.1,16.5-45,3.3-27.7-3.7-63.4-5.4-62,0.9-2-35.4,0-61.4,10-27.5,8.9-44.7,25.7-39.5,27.1q44.85,35,89.8,69.9" transform="translate(-9.7 -9.3)"/>
<path id="inner-2" data-name="inner" class="cls-6" d="M343.5,76.2a77.83,77.83,0,0,1-5.6,24.6c-15.1-20.3-36-39.8-61-52.4a82,82,0,0,1,19.2-9.1c18.5-7.1,44.2-8.5,43.6-7.1,1.2-.9,6.1,24.4,3.8,44" transform="translate(-9.7 -9.3)"/>
</g>
<g id="nose">
<path class="cls-7" d="M205.1,201.8l-10.6-18.3a9,9,0,0,1,7.7-13.4h21.2a8.9,8.9,0,0,1,7.7,13.4l-10.6,18.3a8.91,8.91,0,0,1-15.4,0" transform="translate(-9.7 -9.3)"/>
<path class="cls-6" d="M194.2,175.1a9,9,0,0,0,.3,8.4l10.6,18.3a8.92,8.92,0,0,0,15.5,0l8.7-15c-5.8-6.2-19.3-10.1-35.1-11.7" transform="translate(-9.7 -9.3)"/>
</g>
<g id="mouth">
<path class="cls-8" d="M166.7,260.4c-24.4,0-44.1-25-44.1-55.9m88.2,0c0,30.9-19.7,55.9-44.1,55.9m89.9,0c24.4,0,44.1-25,44.1-55.9m-88.2,0c0,30.9,19.7,55.9,44.1,55.9" transform="translate(-9.7 -9.3)"/>
<path class="cls-9" d="M300.7,204.5a65.16,65.16,0,0,1-8,32" transform="translate(-9.7 -9.3)"/>
</g>
<path id="wiskers" class="cls-10" d="M188.7,198.4c0-12.9-72.7-23.3-162.6-23.3m162.6,36.2c0-7.1-65.8-12.9-147.1-12.9m196,1.3c1.4-12.8,74.8-15.6,164.1-6.2m-165.4,19c0.7-7.1,66.8-5.9,147.6,2.6" transform="translate(-9.7 -9.3)"/>
<g id="lefteye" class="eye">
<path id="iris" class="cls-4" d="M188.6,141.5s-18.3,12.3-35.8,7.9-30-15.2-27.7-24c1.5-6,9.6-9.6,20.2-9.8a59.5,59.5,0,0,1,15.7,1.9,35.75,35.75,0,0,1,12.5,6.2,60,60,0,0,1,15.1,17.8" transform="translate(-9.7 -9.3)"/>
<path class="cls-11" d="M125.1,123.6c1.5-6,9.6-9.6,20.1-9.8a59.5,59.5,0,0,1,15.7,1.9,35.75,35.75,0,0,1,12.5,6.2,59.47,59.47,0,0,1,15.2,17.8" transform="translate(-9.7 -9.3)"/>
<path id="pupil" class="cls-12" d="M172.9,124.3c-2.3,9.2-10.7,15-18.7,13s-12.5-11.1-10.2-20.4a22.39,22.39,0,0,1,1.1-3.1,59.5,59.5,0,0,1,15.7,1.9,35.75,35.75,0,0,1,12.5,6.2,8.6,8.6,0,0,1-.4,2.4" transform="translate(-9.7 -9.3)"/>
<path id="eyelash" class="cls-13" d="M124.9,121.5c-7.6,2.6-17.1-4.7-21.1-16.3m33.6,9.5c-7.5,2.9-17.3-4-21.7-15.5m36.7,14.6c-8.1-.1-14.5-10.2-14.3-22.6" transform="translate(-9.7 -9.3)"/>
<path id="reflection" class="cls-14" d="M156.8,122c0,3.6-2.6,6.4-5.8,6.4s-5.8-2.9-5.8-6.4,2.6-6.4,5.8-6.4,5.8,2.9,5.8,6.4" transform="translate(-9.7 -9.3)"/>
</g>
<g id="righteye" class="eye">
<path id="iris-2" data-name="iris" class="cls-4" d="M241.4,143.6s18.5,11.9,36,7.1,29.6-15.8,27.2-24.6c-1.7-6-9.8-9.4-20.3-9.4a59.21,59.21,0,0,0-15.6,2.2,37.44,37.44,0,0,0-12.4,6.4,60.14,60.14,0,0,0-14.9,18.3" transform="translate(-9.7 -9.3)"/>
<path id="lid" class="cls-11" d="M304.5,124.4c-1.7-6-9.8-9.4-20.3-9.4a59.21,59.21,0,0,0-15.6,2.2,37.44,37.44,0,0,0-12.4,6.4,61.21,61.21,0,0,0-14.9,18.1" transform="translate(-9.7 -9.3)"/>
<path id="pupil-2" data-name="pupil" class="cls-12" d="M256.7,126.1c2.5,9.2,11,14.8,18.9,12.6s12.3-11.4,9.8-20.6a16.59,16.59,0,0,0-1.2-3.1,59.21,59.21,0,0,0-15.6,2.2,37.44,37.44,0,0,0-12.4,6.4,9.23,9.23,0,0,0,.5,2.5" transform="translate(-9.7 -9.3)"/>
<path id="eyelash-2" data-name="eyelash" class="cls-13" d="M302.9,122.3c7.7,2.5,17-5,20.8-16.8M292,115.7c7.6,2.8,17.2-4.4,21.4-16M277,115.1c8.1-.3,14.3-10.5,13.9-22.8" transform="translate(-9.7 -9.3)"/>
<path id="reflection-2" data-name="reflection" class="cls-14" d="M271.1,127.1c0,3.6-2.6,6.5-5.8,6.5s-5.8-2.9-5.8-6.5,2.6-6.4,5.8-6.4,5.8,2.9,5.8,6.4" transform="translate(-9.7 -9.3)"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -0,0 +1,56 @@
<svg version="1" id="cat" viewBox="0 0 720 800" aria-labelledby="catTitle catDesc" role="img">
<title id="catTitle" arial-dontallow="nope">Pixels, My Super-friendly Cat</title>
<desc id="catDesc">An illustrated gray cat with bright green blinking eyes.</desc>
<path id="tail" data-name="tail" class="cls-1" d="M545.9,695.9c8,28.2,23.2,42.3,27.2,46.9,21.4,24.1,41.5,40.2,81.1,42.9s65.4-14.2,60.8-26.8-23.1-9.1-51.3-8.3c-35.2.9-66.6-31.3-74.8-63.9s-7.9-63.8-36.8-85.5c-44.1-33-135.6-7.1-159.8-3.4s-48.4,52.5-9.6,45.1,91.4-23.1,123.2-12.7C537.8,640.4,537.9,667.7,545.9,695.9Z" transform="translate(-9.7 -9.3)"/>
<g id="body">
<path id="bg" class="cls-2" d="M447.9,502.1c2.1,151.7-108.3,167-216.5,167S9.7,663.8,9.7,510.9,85,242.9,231.3,241,445.8,350.4,447.9,502.1h0Z" transform="translate(-9.7 -9.3)"/>
<g id="leftleg" datas-dontallow="nope">
<path id="leg" class="cls-1" d="M195.6,671.5c-34.2-7.7-40.6-95.6-53.3-191-12-90-90.1-177.2-55.1-177.2s145.7,12,151.4,87.7S261.5,686.5,195.6,671.5Z" transform="translate(-9.7 -9.3)"/>
<path id="foot" class="cls-3" d="M172.2,688.1c31.6,2.1,56.6-8.7,59.8-32.4s-22.1-49.5-27.3-24.3c25-16.4-39.1-29.4-27.6-3.9,14-24.9-49.6-19.2-31.9-.1-6.5-27.2-35.6,8.2-30.1,29.3C121.5,681.8,140.5,686,172.2,688.1Z" transform="translate(-9.7 -9.3)"/>
</g>
<g id="rightleg">
<path id="leg-2" data-name="leg" class="cls-1" d="M260.4,670.4c42.4-9.2,48.7-87.7,53.9-185.2,5.1-96,98.2-176.1,63.1-176.1s-164,15.7-164,111.8C213.4,420.9,199.1,683.7,260.4,670.4Z" transform="translate(-9.7 -9.3)"/>
<path id="foot-2" data-name="foot" class="cls-3" d="M279.4,689.8c-31.7,2-56.6-9-59.6-32.6s22.3-49.4,27.4-24.1c-24.9-16.5,39.2-29.2,27.6-3.8-13.9-25,49.7-18.9,31.9,0,6.6-27.1,35.6,8.4,30,29.4-6.7,25-25.7,29.1-57.3,31.1h0Z" transform="translate(-9.7 -9.3)"/>
</g>
<path id="tuft" aria-haspopup="false" class="cls-3" d="M80,331.2c3.5,9.5,1.2,28.9,4.3,32.7s31.5-30,43-20.6c10.7,8.7,1.7,55.9,12.9,64.5,10.1,7.7,32.1-50.6,52.5-38.7,24.9,14.6,34.1,49.9,49,49.9,18.3,0,7.5-49.5,24.1-53.3s46.1,52.6,60.2,45.6c4.8-2.4,3-50.4,12-57.6,8.7-6.9,30.5,22.4,33.5,18.9,3.7-4.1.1-23.1,8.6-36.1,3.4-5.2,18.9-2.6,28.8-.4a3.46,3.46,0,0,0,3.7-5.2c-19.6-30.8-100-147.4-184.2-147.4-93.3,0-150.9,86.8-178.1,141.6a3.43,3.43,0,0,0,3.6,4.9C63,328.4,78.4,326.6,80,331.2Z" transform="translate(-9.7 -9.3)"/>
</g>
<g id="head">
<path id="collar" class="cls-4" d="M367,231.1c5.7,36.1-4.7,71-97.8,85.6s-184-18.5-189.7-54.5,16.7-17.3,109.8-31.9,172-35.3,177.7.8" transform="translate(-9.7 -9.3)"/>
<g id="bg-2" data-name="bg">
<path class="cls-1" d="M362.5,229.5C339.7,279,273.1,299.4,225,300c-60.6.7-134.7-29.5-153.5-86.4C45.6,135.4,132.2,32.6,225,35.8c96.1,3.4,171.7,119.4,137.5,193.7" transform="translate(-9.7 -9.3)"/>
<path class="cls-5" d="M362.5,229.5C339.7,279,273.1,299.4,225,300c-60.6.7-134.7-29.5-153.5-86.4C45.6,135.4,132.2,32.6,225,35.8,321.1,39.2,396.7,155.2,362.5,229.5Z" transform="translate(-9.7 -9.3)"/>
</g>
<g id="leftear" aria-label="Left Ear">
<path id="outer" class="cls-1" d="M92.7,117c-2.6,4.7-14.7-16.1-16.5-45-3.3-27.7,3.7-63.4,5.4-62C80.7,8,117,10,143,20c27.5,8.9,44.7,25.7,39.5,27.1-30,23.4-59.9,46.6-89.8,69.9" transform="translate(-9.7 -9.3)"/>
<path id="inner" class="cls-6" d="M105.8,106.9C103.9,110.3,95.3,95.5,94,75c-2.3-19.6,2.6-44.9,3.8-44-0.6-1.4,25.1,0,43.6,7.1,19.5,6.3,31.7,18.2,28,19.2q-31.8,24.9-63.6,49.6" transform="translate(-9.7 -9.3)"/>
</g>
<path id="mask" class="cls-2" d="M338.4,142.5c-2.2,3.3,19.4,19.6,17.2,23.2s-24.3-7.8-25.8-5.2c-1.9,3.3,33.4,24.1,31,29.2-2.3,4.9-34-14.4-84.3-18.1a141.76,141.76,0,0,1-16.4-2.1,91.21,91.21,0,0,1-13.7-3.9c-19.8-6.9-27.7-10.6-32.7-12-19.3-5.7-26.8,11.3-68.1,22.4-18.8,5-37.9,9.7-54.4,0-2.1-1.3-13.6-8.3-16.7-21.1-0.9-3.6-2.8-15.2,10.5-34C146.3,34.3,216.5,34,217.3,34a131.52,131.52,0,0,1,58.4,14.3c-7.6,4.9-11.2,9.5-9,10.1,21.5,16.5,43.1,33,64.6,49.5,0.9,1.7,3.6-1.3,6.3-7.3,19.3,30.5,22.1,41.5,18.9,44.3-3.8,3.6-16.4-4.8-18.1-2.4" transform="translate(-9.7 -9.3)"/>
<g id="rightear">
<path id="outer-2" data-name="outer" class="cls-2" d="M344.9,119.9c2.6,4.7,14.7-16.1,16.5-45,3.3-27.7-3.7-63.4-5.4-62,0.9-2-35.4,0-61.4,10-27.5,8.9-44.7,25.7-39.5,27.1q44.85,35,89.8,69.9" transform="translate(-9.7 -9.3)"/>
<path id="inner-2" data-name="inner" class="cls-6" d="M343.5,76.2a77.83,77.83,0,0,1-5.6,24.6c-15.1-20.3-36-39.8-61-52.4a82,82,0,0,1,19.2-9.1c18.5-7.1,44.2-8.5,43.6-7.1,1.2-.9,6.1,24.4,3.8,44" transform="translate(-9.7 -9.3)"/>
</g>
<g id="nose">
<path class="cls-7" d="M205.1,201.8l-10.6-18.3a9,9,0,0,1,7.7-13.4h21.2a8.9,8.9,0,0,1,7.7,13.4l-10.6,18.3a8.91,8.91,0,0,1-15.4,0" transform="translate(-9.7 -9.3)"/>
<path class="cls-6" d="M194.2,175.1a9,9,0,0,0,.3,8.4l10.6,18.3a8.92,8.92,0,0,0,15.5,0l8.7-15c-5.8-6.2-19.3-10.1-35.1-11.7" transform="translate(-9.7 -9.3)"/>
</g>
<g id="mouth">
<path class="cls-8" d="M166.7,260.4c-24.4,0-44.1-25-44.1-55.9m88.2,0c0,30.9-19.7,55.9-44.1,55.9m89.9,0c24.4,0,44.1-25,44.1-55.9m-88.2,0c0,30.9,19.7,55.9,44.1,55.9" transform="translate(-9.7 -9.3)"/>
<path class="cls-9" d="M300.7,204.5a65.16,65.16,0,0,1-8,32" transform="translate(-9.7 -9.3)"/>
</g>
<path id="wiskers" class="cls-10" d="M188.7,198.4c0-12.9-72.7-23.3-162.6-23.3m162.6,36.2c0-7.1-65.8-12.9-147.1-12.9m196,1.3c1.4-12.8,74.8-15.6,164.1-6.2m-165.4,19c0.7-7.1,66.8-5.9,147.6,2.6" transform="translate(-9.7 -9.3)"/>
<g id="lefteye" class="eye">
<path id="iris" class="cls-4" d="M188.6,141.5s-18.3,12.3-35.8,7.9-30-15.2-27.7-24c1.5-6,9.6-9.6,20.2-9.8a59.5,59.5,0,0,1,15.7,1.9,35.75,35.75,0,0,1,12.5,6.2,60,60,0,0,1,15.1,17.8" transform="translate(-9.7 -9.3)"/>
<path class="cls-11" d="M125.1,123.6c1.5-6,9.6-9.6,20.1-9.8a59.5,59.5,0,0,1,15.7,1.9,35.75,35.75,0,0,1,12.5,6.2,59.47,59.47,0,0,1,15.2,17.8" transform="translate(-9.7 -9.3)"/>
<path id="pupil" class="cls-12" d="M172.9,124.3c-2.3,9.2-10.7,15-18.7,13s-12.5-11.1-10.2-20.4a22.39,22.39,0,0,1,1.1-3.1,59.5,59.5,0,0,1,15.7,1.9,35.75,35.75,0,0,1,12.5,6.2,8.6,8.6,0,0,1-.4,2.4" transform="translate(-9.7 -9.3)"/>
<path id="eyelash" class="cls-13" d="M124.9,121.5c-7.6,2.6-17.1-4.7-21.1-16.3m33.6,9.5c-7.5,2.9-17.3-4-21.7-15.5m36.7,14.6c-8.1-.1-14.5-10.2-14.3-22.6" transform="translate(-9.7 -9.3)"/>
<path id="reflection" class="cls-14" d="M156.8,122c0,3.6-2.6,6.4-5.8,6.4s-5.8-2.9-5.8-6.4,2.6-6.4,5.8-6.4,5.8,2.9,5.8,6.4" transform="translate(-9.7 -9.3)"/>
</g>
<g id="righteye" class="eye">
<path id="iris-2" data-name="iris" class="cls-4" d="M241.4,143.6s18.5,11.9,36,7.1,29.6-15.8,27.2-24.6c-1.7-6-9.8-9.4-20.3-9.4a59.21,59.21,0,0,0-15.6,2.2,37.44,37.44,0,0,0-12.4,6.4,60.14,60.14,0,0,0-14.9,18.3" transform="translate(-9.7 -9.3)"/>
<path id="lid" class="cls-11" d="M304.5,124.4c-1.7-6-9.8-9.4-20.3-9.4a59.21,59.21,0,0,0-15.6,2.2,37.44,37.44,0,0,0-12.4,6.4,61.21,61.21,0,0,0-14.9,18.1" transform="translate(-9.7 -9.3)"/>
<path id="pupil-2" data-name="pupil" class="cls-12" d="M256.7,126.1c2.5,9.2,11,14.8,18.9,12.6s12.3-11.4,9.8-20.6a16.59,16.59,0,0,0-1.2-3.1,59.21,59.21,0,0,0-15.6,2.2,37.44,37.44,0,0,0-12.4,6.4,9.23,9.23,0,0,0,.5,2.5" transform="translate(-9.7 -9.3)"/>
<path id="eyelash-2" data-name="eyelash" class="cls-13" d="M302.9,122.3c7.7,2.5,17-5,20.8-16.8M292,115.7c7.6,2.8,17.2-4.4,21.4-16M277,115.1c8.1-.3,14.3-10.5,13.9-22.8" transform="translate(-9.7 -9.3)"/>
<path id="reflection-2" data-name="reflection" class="cls-14" d="M271.1,127.1c0,3.6-2.6,6.5-5.8,6.5s-5.8-2.9-5.8-6.5,2.6-6.4,5.8-6.4,5.8,2.9,5.8,6.4" transform="translate(-9.7 -9.3)"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve">
<line onload="alert(2)" fill="none" stroke="#000000" stroke-miterlimit="10" x1="119" y1="84.5" x2="454" y2="84.5"/>
<line fill="none" stroke="#000000" stroke-miterlimit="10" x1="111.212" y1="102.852" x2="112.032" y2="476.623"/>
<line fill="none" stroke="#000000" stroke-miterlimit="10" x1="198.917" y1="510.229" x2="486.622" y2="501.213">
<line fill="none" stroke="#000000" stroke-miterlimit="10" x1="484.163" y1="442.196" x2="89.901" y2="60.229"/>
<line fill="none" stroke="#000000" stroke-miterlimit="10" x1="101.376" y1="478.262" x2="443.18" y2="75.803"/>
<line fill="none" stroke="#000000" stroke-miterlimit="10" x1="457.114" y1="126.623" x2="458.753" y2="363.508"/>
<this>shouldn't be here</this>
<script>alert(1);</script>
<line fill="none" stroke="#000000" stroke-miterlimit="10" x1="541.54" y1="299.573" x2="543.179" y2="536.458"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg">
<text x="0" y="20" font-size="20">&lab;</text>
</svg>

After

Width:  |  Height:  |  Size: 152 B

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE fortiguard [ <!ENTITY lab "cool, text as an image">]>
<svg xmlns="http://www.w3.org/2000/svg">
<text x="0" y="20" font-size="20">&lab;</text>
</svg>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" xml:space="preserve">
<rect x="0" y="0" width="1000" height="1000"></rect>
<rect x="0" y="0" width="1000" height="1000"></rect>
<rect x="0" y="0" width="1000" height="1000"></rect>
<rect x="0" y="0" width="1000" height="1000"></rect>
<rect x="0" y="0" width="1000" height="1000"></rect>
<rect fill="url('/benis.svg')" x="0" y="0" width="1000" height="1000"></rect>
<rect fill="url('#benis.svg')" x="0" y="0" width="1000" height="1000"></rect>
</svg>

After

Width:  |  Height:  |  Size: 587 B

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve">
<rect fill="url('http://example.com/benis.svg')" x="0" y="0" width="1000" height="1000"></rect>
<rect fill="url('https://example.com/benis.svg')" x="0" y="0" width="1000" height="1000"></rect>
<rect fill=" url( ' https://example.com/benis.svg ' ) " x="0" y="0" width="1000" height="1000"></rect>
<rect fill="url('ftp://192.168.2.1/benis.svg')" x="0" y="0" width="1000" height="1000"></rect>
<rect fill="url('//example.com/benis.svg')" x="0" y="0" width="1000" height="1000"></rect>
<rect fill="url('/benis.svg')" x="0" y="0" width="1000" height="1000"></rect>
<rect fill="url('#benis.svg')" x="0" y="0" width="1000" height="1000"></rect>
</svg>

After

Width:  |  Height:  |  Size: 906 B

View File

@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="600px" id="Layer_1" width="600px" x="0px" y="0px" xml:space="preserve">
<a>test 1</a>
<a>test 2</a>
<a href="#test3">test 3</a>
<a xlink:href="#test">test 4</a>
<a>test 5</a>
<a>test 6</a>
<a>test 7</a>
<a>test 8</a>
</svg>

After

Width:  |  Height:  |  Size: 348 B

View File

@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" height="600px" id="Layer_1" width="600px" x="0px" y="0px" xml:space="preserve">
<a>test 1</a>
<a>test 2</a>
<a href="#test3">test 3</a>
<a xlink:href="#test">test 4</a>
<a>test 5</a>
<a>test 6</a>
<a>test 7</a>
<a>test 8</a>
</svg>

After

Width:  |  Height:  |  Size: 305 B

View File

@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="600px" id="Layer_1" width="600px" x="0px" y="0px" xml:space="preserve">
<a href="javascript:alert(2)">test 1</a>
<a xlink:href="javascript:alert(2)">test 2</a>
<a href="#test3">test 3</a>
<a xlink:href="#test">test 4</a>
<a href="data:data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' onload='alert(88)'%3E%3C/svg%3E">test 5</a>
<a xlink:href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' onload='alert(88)'%3E%3C/svg%3E">test 6</a>
<a href="javascript&#9;:alert(document.domain)">test 7</a>
<a href="javascrip&#9;t:alert('0xd0ff9')">test 8</a>
</svg>

After

Width:  |  Height:  |  Size: 703 B

View File

@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" height="600px" id="Layer_1" width="600px" x="0px" y="0px" xml:space="preserve">
<a href="javascript:alert(2)">test 1</a>
<a xlink:href="javascript:alert(2)">test 2</a>
<a href="#test3">test 3</a>
<a xlink:href="#test">test 4</a>
<a href="data:data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' onload='alert(88)'%3E%3C/svg%3E">test 5</a>
<a xlink:href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' onload='alert(88)'%3E%3C/svg%3E">test 6</a>
<a href="javascript&#9;:alert(document.domain)">test 7</a>
<a href="javascrip&#9;t:alert('0xd0ff9')">test 8</a>
</svg>

After

Width:  |  Height:  |  Size: 660 B

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" version="1.1" x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" xml:space="preserve">
<line fill="none" stroke="#000000" stroke-miterlimit="10" x1="119" y1="84.5" x2="454" y2="84.5"/>
<line fill="none" stroke="#000000" stroke-miterlimit="10" x1="111.212" y1="102.852" x2="112.032" y2="476.623"/>
<line fill="none" stroke="#000000" stroke-miterlimit="10" x1="198.917" y1="510.229" x2="486.622" y2="501.213"/>
<line fill="none" stroke="#000000" stroke-miterlimit="10" x1="484.163" y1="442.196" x2="89.901" y2="60.229"/>
<line fill="none" stroke="#000000" stroke-miterlimit="10" x1="101.376" y1="478.262" x2="443.18" y2="75.803"/>
<line fill="none" stroke="#000000" stroke-miterlimit="10" x1="457.114" y1="126.623" x2="458.753" y2="363.508"/>
<line fill="none" stroke="#000000" stroke-miterlimit="10" x1="541.54" y1="299.573" x2="543.179" y2="536.458"/>
</svg>

After

Width:  |  Height:  |  Size: 1011 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" x="0px" y="0px" width="600px" height="600px" viewBox="0 0 600 600" version="1.1" xml:space="preserve"> <line fill="none" stroke="#000000" stroke-miterlimit="10" x1="119" y1="84.5" x2="454" y2="84.5"></line> <line fill="none" stroke="#000000" stroke-miterlimit="10" x1="111.212" y1="102.852" x2="112.032" y2="476.623"></line> <line fill="none" stroke="#000000" stroke-miterlimit="10" x1="198.917" y1="510.229" x2="486.622" y2="501.213"></line> <line fill="none" stroke="#000000" stroke-miterlimit="10" x1="484.163" y1="442.196" x2="89.901" y2="60.229"></line> <line fill="none" stroke="#000000" stroke-miterlimit="10" x1="101.376" y1="478.262" x2="443.18" y2="75.803"></line> <line fill="none" stroke="#000000" stroke-miterlimit="10" x1="457.114" y1="126.623" x2="458.753" y2="363.508"></line> <line fill="none" stroke="#000000" stroke-miterlimit="10" x1="541.54" y1="299.573" x2="543.179" y2="536.458"></line> </svg>

After

Width:  |  Height:  |  Size: 1012 B

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="600px" height="600px" viewBox="0 0 600 600" enable-background="new 0 0 600 600" xml:space="preserve">
<line onload="alert(2)" fill="none" stroke="#000000" stroke-miterlimit="10" x1="119" y1="84.5" x2="454" y2="84.5"/>
<line fill="none" stroke="#000000" stroke-miterlimit="10" x1="111.212" y1="102.852" x2="112.032" y2="476.623"/>
<line fill="none" stroke="#000000" stroke-miterlimit="10" x1="198.917" y1="510.229" x2="486.622" y2="501.213"/>
<line fill="none" stroke="#000000" stroke-miterlimit="10" x1="484.163" y1="442.196" x2="89.901" y2="60.229"/>
<line fill="none" stroke="#000000" stroke-miterlimit="10" x1="101.376" y1="478.262" x2="443.18" y2="75.803"/>
<line fill="none" stroke="#000000" stroke-miterlimit="10" x1="457.114" y1="126.623" x2="458.753" y2="363.508"/>
<this>shouldn't be here</this>
<script>alert(1);</script>
<line fill="none" stroke="#000000" stroke-miterlimit="10" x1="541.54" y1="299.573" x2="543.179" y2="536.458"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 68 65">
<use xlink:href="#a" x="28" fill="#1A374D"/>
<path id="a" d="M14 27v-20c0-3.7-3.3-7-7-7s-7 3.3-7 7v41c0 8.2 9.2 17 20 17s20-9.2 20-20c0-13.3-13.4-21.8-26-18zm6 25c-4 0-7-3-7-7s3-7 7-7 7 3 7 7-3 7-7 7z"/>
<use />
</svg>

After

Width:  |  Height:  |  Size: 334 B

View File

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 10 10">
<defs>
<g id="thing">
<g id="t0a"></g>
<g id="t0b"></g>
<g id="t1a"></g>
<g id="t1b"></g>
<g id="t2a"></g>
<g id="t2b"></g>
<g id="t3a"></g>
<g id="t3b"></g>
<g id="t4a"></g>
<g id="t4b"></g>
<g id="t5a"></g>
<g id="t5b"></g>
<g id="t6a"></g>
<g id="t6b"></g>
<g id="t7a"></g>
<g id="t7b"></g>
<g id="t8a"></g>
<g id="t8b"></g>
<g id="t9a"></g>
<g id="t9b"></g>
<g id="t10a"></g>
<g id="t10b"></g>
<g id="t11a"></g>
<g id="t11b"></g>
<g id="t12a"></g>
<g id="t12b"></g>
<g id="t13a"></g>
<g id="t13b"></g>
<g id="t14a"></g>
<g id="t14b"></g>
<g id="t15a"></g>
<g id="t15b"></g>
<g id="t16a"></g>
<g id="t16b"></g>
<g id="t17a"></g>
<g id="t17b"></g>
<g id="t18a"></g>
<g id="t18b"></g>
<g id="t19a"></g>
<g id="t19b"></g>
<g id="t20a"></g>
<g id="t20b"></g>
<g id="t21a"></g>
<g id="t21b"></g>
<g id="t22a"></g>
<g id="t22b"></g>
<g id="t23a"></g>
<g id="t23b"></g>
<g id="t24a"></g>
<g id="t24b"></g>
<g id="t25a"></g>
<g id="t25b"></g>
<g id="t26a"></g>
<g id="t26b"></g>
<g id="t27a"></g>
<g id="t27b"></g>
<g id="t28a"></g>
<g id="t28b"></g>
<g id="t29a"></g>
<g id="t29b"></g>
<g id="t30a"></g>
<g id="t30b"></g>
<g id="t31a"></g>
<g id="t31b"></g>
<g id="t32a"></g>
<g id="t32b"></g>
<g id="t33a"></g>
<g id="t33b"></g>
<g id="t34a"></g>
<g id="t34b"></g>
<g id="t35a"></g>
<g id="t35b"></g>
<g id="t36a"></g>
<g id="t36b"></g>
<g id="t37a"></g>
<g id="t37b"></g>
<g id="t38a"></g>
<g id="t38b"></g>
<g id="t39a"></g>
<g id="t39b"></g>
<g id="t40a"></g>
<g id="t40b"></g>
<g id="t41a"></g>
<g id="t41b"></g>
<g id="t42a"></g>
<g id="t42b"></g>
<g id="t43a"></g>
<g id="t43b"></g>
<g id="t44a"></g>
<g id="t44b"></g>
<g id="t45a"></g>
<g id="t45b"></g>
<g id="t46a"></g>
<g id="t46b"></g>
<g id="t47a"></g>
<g id="t47b"></g>
<g id="t48a"></g>
<g id="t48b"></g>
<g id="t49a"></g>
<g id="t49b"></g>
<g id="t50a">
<circle cx="0" cy="0" r="5"></circle>
</g>
<g id="t50b">
<circle cx="0" cy="0" r="5"></circle>
</g>
</g>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

Some files were not shown because too many files have changed in this diff Show More