| Current Path : /home/x/b/o/xbodynamge/namtation/wp-content/ |
| Current File : /home/x/b/o/xbodynamge/namtation/wp-content/classes.tar |
class-twentynineteen-walker-comment.php 0000666 00000010000 15111734242 0014356 0 ustar 00 <?php
/**
* Custom comment walker for this theme
*
* @package WordPress
* @subpackage Twenty_Nineteen
* @since 1.0.0
*/
/**
* This class outputs custom comment walker for HTML5 friendly WordPress comment and threaded replies.
*
* @since 1.0.0
*/
class TwentyNineteen_Walker_Comment extends Walker_Comment {
/**
* Outputs a comment in the HTML5 format.
*
* @see wp_list_comments()
*
* @param WP_Comment $comment Comment to display.
* @param int $depth Depth of the current comment.
* @param array $args An array of arguments.
*/
protected function html5_comment( $comment, $depth, $args ) {
$tag = ( 'div' === $args['style'] ) ? 'div' : 'li';
?>
<<?php echo $tag; ?> id="comment-<?php comment_ID(); ?>" <?php comment_class( $this->has_children ? 'parent' : '', $comment ); ?>>
<article id="div-comment-<?php comment_ID(); ?>" class="comment-body">
<footer class="comment-meta">
<div class="comment-author vcard">
<?php
$comment_author_url = get_comment_author_url( $comment );
$comment_author = get_comment_author( $comment );
$avatar = get_avatar( $comment, $args['avatar_size'] );
if ( 0 != $args['avatar_size'] ) {
if ( empty( $comment_author_url ) ) {
echo $avatar;
} else {
printf( '<a href="%s" rel="external nofollow" class="url">', $comment_author_url );
echo $avatar;
}
}
/*
* Using the `check` icon instead of `check_circle`, since we can't add a
* fill color to the inner check shape when in circle form.
*/
if ( twentynineteen_is_comment_by_post_author( $comment ) ) {
printf( '<span class="post-author-badge" aria-hidden="true">%s</span>', twentynineteen_get_icon_svg( 'check', 24 ) );
}
/*
* Using the `check` icon instead of `check_circle`, since we can't add a
* fill color to the inner check shape when in circle form.
*/
if ( twentynineteen_is_comment_by_post_author( $comment ) ) {
printf( '<span class="post-author-badge" aria-hidden="true">%s</span>', twentynineteen_get_icon_svg( 'check', 24 ) );
}
printf(
/* translators: %s: comment author link */
wp_kses(
__( '%s <span class="screen-reader-text says">says:</span>', 'twentynineteen' ),
array(
'span' => array(
'class' => array(),
),
)
),
'<b class="fn">' . $comment_author . '</b>'
);
if ( ! empty( $comment_author_url ) ) {
echo '</a>';
}
?>
</div><!-- .comment-author -->
<div class="comment-metadata">
<a href="<?php echo esc_url( get_comment_link( $comment, $args ) ); ?>">
<?php
/* translators: 1: comment date, 2: comment time */
$comment_timestamp = sprintf( __( '%1$s at %2$s', 'twentynineteen' ), get_comment_date( '', $comment ), get_comment_time() );
?>
<time datetime="<?php comment_time( 'c' ); ?>" title="<?php echo $comment_timestamp; ?>">
<?php echo $comment_timestamp; ?>
</time>
</a>
<?php
$edit_comment_icon = twentynineteen_get_icon_svg( 'edit', 16 );
edit_comment_link( __( 'Edit', 'twentynineteen' ), '<span class="edit-link-sep">—</span> <span class="edit-link">' . $edit_comment_icon, '</span>' );
?>
</div><!-- .comment-metadata -->
<?php if ( '0' == $comment->comment_approved ) : ?>
<p class="comment-awaiting-moderation"><?php _e( 'Your comment is awaiting moderation.', 'twentynineteen' ); ?></p>
<?php endif; ?>
</footer><!-- .comment-meta -->
<div class="comment-content">
<?php comment_text(); ?>
</div><!-- .comment-content -->
</article><!-- .comment-body -->
<?php
comment_reply_link(
array_merge(
$args,
array(
'add_below' => 'div-comment',
'depth' => $depth,
'max_depth' => $args['max_depth'],
'before' => '<div class="comment-reply">',
'after' => '</div>',
)
)
);
?>
<?php
}
}
class-twentynineteen-svg-icons.php 0000666 00000167743 15111734242 0013374 0 ustar 00 <?php
/**
* SVG Icons class
*
* @package WordPress
* @subpackage Twenty_Nineteen
* @since 1.0.0
*/
/**
* This class is in charge of displaying SVG icons across the site.
*
* Place each <svg> source on its own array key, without adding the
* both `width` and `height` attributes, since these are added dnamically,
* before rendering the SVG code.
*
* All icons are assumed to have equal width and height, hence the option
* to only specify a `$size` parameter in the svg methods.
*
* @since 1.0.0
*/
class TwentyNineteen_SVG_Icons {
/**
* Gets the SVG code for a given icon.
*/
public static function get_svg( $group, $icon, $size ) {
if ( 'ui' == $group ) {
$arr = self::$ui_icons;
} elseif ( 'social' == $group ) {
$arr = self::$social_icons;
} else {
$arr = array();
}
if ( array_key_exists( $icon, $arr ) ) {
$repl = sprintf( '<svg class="svg-icon" width="%d" height="%d" aria-hidden="true" role="img" focusable="false" ', $size, $size );
$svg = preg_replace( '/^<svg /', $repl, trim( $arr[ $icon ] ) ); // Add extra attributes to SVG code.
$svg = preg_replace( "/([\n\t]+)/", ' ', $svg ); // Remove newlines & tabs.
$svg = preg_replace( '/>\s*</', '><', $svg ); // Remove white space between SVG tags.
return $svg;
}
return null;
}
/**
* Detects the social network from a URL and returns the SVG code for its icon.
*/
public static function get_social_link_svg( $uri, $size ) {
static $regex_map; // Only compute regex map once, for performance.
if ( ! isset( $regex_map ) ) {
$regex_map = array();
$map = &self::$social_icons_map; // Use reference instead of copy, to save memory.
foreach ( array_keys( self::$social_icons ) as $icon ) {
$domains = array_key_exists( $icon, $map ) ? $map[ $icon ] : array( sprintf( '%s.com', $icon ) );
$domains = array_map( 'trim', $domains ); // Remove leading/trailing spaces, to prevent regex from failing to match.
$domains = array_map( 'preg_quote', $domains );
$regex_map[ $icon ] = sprintf( '/(%s)/i', implode( '|', $domains ) );
}
}
foreach ( $regex_map as $icon => $regex ) {
if ( preg_match( $regex, $uri ) ) {
return self::get_svg( 'social', $icon, $size );
}
}
return null;
}
/**
* User Interface icons – svg sources.
*
* @var array
*/
static $ui_icons = array(
'link' => /* material-design – link */ '
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none"></path>
<path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"></path>
</svg>',
'watch' => /* material-design – watch-later */ '
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<defs>
<path id="a" d="M0 0h24v24H0V0z"></path>
</defs>
<clipPath id="b">
<use xlink:href="#a" overflow="visible"></use>
</clipPath>
<path clip-path="url(#b)" d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2zm4.2 14.2L11 13V7h1.5v5.2l4.5 2.7-.8 1.3z"></path>
</svg>',
'archive' => /* material-design – folder */ '
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M10 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2h-8l-2-2z"></path>
<path d="M0 0h24v24H0z" fill="none"></path>
</svg>',
'tag' => /* material-design – local_offer */ '
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M21.41 11.58l-9-9C12.05 2.22 11.55 2 11 2H4c-1.1 0-2 .9-2 2v7c0 .55.22 1.05.59 1.42l9 9c.36.36.86.58 1.41.58.55 0 1.05-.22 1.41-.59l7-7c.37-.36.59-.86.59-1.41 0-.55-.23-1.06-.59-1.42zM5.5 7C4.67 7 4 6.33 4 5.5S4.67 4 5.5 4 7 4.67 7 5.5 6.33 7 5.5 7z"></path>
<path d="M0 0h24v24H0z" fill="none"></path>
</svg>',
'comment' => /* material-design – comment */ '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M21.99 4c0-1.1-.89-2-1.99-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4-.01-18z"></path>
<path d="M0 0h24v24H0z" fill="none"></path>
</svg>',
'person' => /* material-design – person */ '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"></path>
<path d="M0 0h24v24H0z" fill="none"></path>
</svg>',
'edit' => /* material-design – edit */ '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"></path>
<path d="M0 0h24v24H0z" fill="none"></path>
</svg>',
'chevron_left' => /* material-design – chevron_left */ '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"></path>
<path d="M0 0h24v24H0z" fill="none"></path>
</svg>',
'chevron_right' => /* material-design – chevron_right */ '
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path>
<path d="M0 0h24v24H0z" fill="none"></path>
</svg>',
'check' => /* material-design – check */ '
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none"></path>
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"></path>
</svg>',
'arrow_drop_down_circle' => /* material-design – arrow_drop_down_circle */ '
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 12l-4-4h8l-4 4z"></path>
</svg>',
'keyboard_arrow_down' => /* material-design – keyboard_arrow_down */ '
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"></path>
<path fill="none" d="M0 0h24v24H0V0z"></path>
</svg>',
'keyboard_arrow_right' => /* material-design – keyboard_arrow_right */ '
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M10 17l5-5-5-5v10z"></path>
<path fill="none" d="M0 24V0h24v24H0z"></path>
</svg>',
'keyboard_arrow_left' => /* material-design – keyboard_arrow_left */ '
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M14 7l-5 5 5 5V7z"></path>
<path fill="none" d="M24 0v24H0V0h24z"></path>
</svg>',
'arrow_drop_down_ellipsis' => /* custom – arrow_drop_down_ellipsis */ '
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path fill="currentColor" fill-rule="nonzero" d="M12 2c5.52 0 10 4.48 10 10s-4.48 10-10 10S2 17.52 2 12 6.48 2 12 2zM6 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm6 0a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm6 0a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/>
</g>
</svg>',
);
/**
* Social Icons – domain mappings.
*
* By default, each Icon ID is matched against a .com TLD. To override this behavior,
* specify all the domains it covers (including the .com TLD too, if applicable).
*
* @var array
*/
static $social_icons_map = array(
'amazon' => array(
'amazon.com',
'amazon.cn',
'amazon.in',
'amazon.fr',
'amazon.de',
'amazon.it',
'amazon.nl',
'amazon.es',
'amazon.co',
'amazon.ca',
),
'apple' => array(
'apple.com',
'itunes.com',
),
'behance' => array(
'behance.net',
),
'codepen' => array(
'codepen.io',
),
'facebook' => array(
'facebook.com',
'fb.me',
),
'feed' => array(
'feed',
),
'google-plus' => array(
'plus.google.com',
),
'lastfm' => array(
'last.fm',
),
'mail' => array(
'mailto:',
),
'slideshare' => array(
'slideshare.net',
),
'pocket' => array(
'getpocket.com',
),
'twitch' => array(
'twitch.tv',
),
'wordpress' => array(
'wordpress.com',
'wordpress.org',
),
);
/**
* Social Icons – svg sources.
*
* @var array
*/
static $social_icons = array(
'500px' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M6.94026,15.1412c.00437.01213.108.29862.168.44064a6.55008,6.55008,0,1,0,6.03191-9.09557,6.68654,6.68654,0,0,0-2.58357.51467A8.53914,8.53914,0,0,0,8.21268,8.61344L8.209,8.61725V3.22948l9.0504-.00008c.32934-.0036.32934-.46353.32934-.61466s0-.61091-.33035-.61467L7.47248,2a.43.43,0,0,0-.43131.42692v7.58355c0,.24466.30476.42131.58793.4819.553.11812.68074-.05864.81617-.2457l.018-.02481A10.52673,10.52673,0,0,1,9.32258,9.258a5.35268,5.35268,0,1,1,7.58985,7.54976,5.417,5.417,0,0,1-3.80867,1.56365,5.17483,5.17483,0,0,1-2.69822-.74478l.00342-4.61111a2.79372,2.79372,0,0,1,.71372-1.78792,2.61611,2.61611,0,0,1,1.98282-.89477,2.75683,2.75683,0,0,1,1.95525.79477,2.66867,2.66867,0,0,1,.79656,1.909,2.724,2.724,0,0,1-2.75849,2.748,4.94651,4.94651,0,0,1-.86254-.13719c-.31234-.093-.44519.34058-.48892.48349-.16811.54966.08453.65862.13687.67489a3.75751,3.75751,0,0,0,1.25234.18375,3.94634,3.94634,0,1,0-2.82444-6.742,3.67478,3.67478,0,0,0-1.13028,2.584l-.00041.02323c-.0035.11667-.00579,2.881-.00644,3.78811l-.00407-.00451a6.18521,6.18521,0,0,1-1.0851-1.86092c-.10544-.27856-.34358-.22925-.66857-.12917-.14192.04372-.57386.17677-.47833.489Zm4.65165-1.08338a.51346.51346,0,0,0,.19513.31818l.02276.022a.52945.52945,0,0,0,.3517.18416.24242.24242,0,0,0,.16577-.0611c.05473-.05082.67382-.67812.73287-.738l.69041.68819a.28978.28978,0,0,0,.21437.11032.53239.53239,0,0,0,.35708-.19486c.29792-.30419.14885-.46821.07676-.54751l-.69954-.69975.72952-.73469c.16-.17311.01874-.35708-.12218-.498-.20461-.20461-.402-.25742-.52855-.14083l-.7254.72665-.73354-.73375a.20128.20128,0,0,0-.14179-.05695.54135.54135,0,0,0-.34379.19648c-.22561.22555-.274.38149-.15656.5059l.73374.7315-.72942.73072A.26589.26589,0,0,0,11.59191,14.05782Zm1.59866-9.915A8.86081,8.86081,0,0,0,9.854,4.776a.26169.26169,0,0,0-.16938.22759.92978.92978,0,0,0,.08619.42094c.05682.14524.20779.531.50006.41955a8.40969,8.40969,0,0,1,2.91968-.55484,7.87875,7.87875,0,0,1,3.086.62286,8.61817,8.61817,0,0,1,2.30562,1.49315.2781.2781,0,0,0,.18318.07586c.15529,0,.30425-.15253.43167-.29551.21268-.23861.35873-.4369.1492-.63538a8.50425,8.50425,0,0,0-2.62312-1.694A9.0177,9.0177,0,0,0,13.19058,4.14283ZM19.50945,18.6236h0a.93171.93171,0,0,0-.36642-.25406.26589.26589,0,0,0-.27613.06613l-.06943.06929A7.90606,7.90606,0,0,1,7.60639,18.505a7.57284,7.57284,0,0,1-1.696-2.51537,8.58715,8.58715,0,0,1-.5147-1.77754l-.00871-.04864c-.04939-.25873-.28755-.27684-.62981-.22448-.14234.02178-.5755.088-.53426.39969l.001.00712a9.08807,9.08807,0,0,0,15.406,4.99094c.00193-.00192.04753-.04718.0725-.07436C19.79425,19.16234,19.87422,18.98728,19.50945,18.6236Z"></path>
</svg>',
'amazon' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M13.582,8.182C11.934,8.367,9.78,8.49,8.238,9.166c-1.781,0.769-3.03,2.337-3.03,4.644 c0,2.953,1.86,4.429,4.253,4.429c2.02,0,3.125-0.477,4.685-2.065c0.516,0.747,0.685,1.109,1.629,1.894 c0.212,0.114,0.483,0.103,0.672-0.066l0.006,0.006c0.567-0.505,1.599-1.401,2.18-1.888c0.231-0.188,0.19-0.496,0.009-0.754 c-0.52-0.718-1.072-1.303-1.072-2.634V8.305c0-1.876,0.133-3.599-1.249-4.891C15.23,2.369,13.422,2,12.04,2 C9.336,2,6.318,3.01,5.686,6.351C5.618,6.706,5.877,6.893,6.109,6.945l2.754,0.298C9.121,7.23,9.308,6.977,9.357,6.72 c0.236-1.151,1.2-1.706,2.284-1.706c0.584,0,1.249,0.215,1.595,0.738c0.398,0.584,0.346,1.384,0.346,2.061V8.182z M13.049,14.088 c-0.451,0.8-1.169,1.291-1.967,1.291c-1.09,0-1.728-0.83-1.728-2.061c0-2.42,2.171-2.86,4.227-2.86v0.615 C13.582,12.181,13.608,13.104,13.049,14.088z M20.683,19.339C18.329,21.076,14.917,22,11.979,22c-4.118,0-7.826-1.522-10.632-4.057 c-0.22-0.199-0.024-0.471,0.241-0.317c3.027,1.762,6.771,2.823,10.639,2.823c2.608,0,5.476-0.541,8.115-1.66 C20.739,18.62,21.072,19.051,20.683,19.339z M21.336,21.043c-0.194,0.163-0.379,0.076-0.293-0.139 c0.284-0.71,0.92-2.298,0.619-2.684c-0.301-0.386-1.99-0.183-2.749-0.092c-0.23,0.027-0.266-0.173-0.059-0.319 c1.348-0.946,3.555-0.673,3.811-0.356C22.925,17.773,22.599,19.986,21.336,21.043z"></path>
</svg>',
'apple' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M20.07,17.586a10.874,10.874,0,0,1-1.075,1.933,9.822,9.822,0,0,1-1.385,1.674,2.687,2.687,0,0,1-1.78.784,4.462,4.462,0,0,1-1.644-.393,4.718,4.718,0,0,0-1.77-.391,4.878,4.878,0,0,0-1.82.391A4.9,4.9,0,0,1,9.021,22a2.53,2.53,0,0,1-1.82-.8A10.314,10.314,0,0,1,5.752,19.46,11.987,11.987,0,0,1,4.22,16.417a11.143,11.143,0,0,1-.643-3.627,6.623,6.623,0,0,1,.87-3.465A5.1,5.1,0,0,1,6.268,7.483a4.9,4.9,0,0,1,2.463-.695,5.8,5.8,0,0,1,1.9.443,6.123,6.123,0,0,0,1.511.444,9.04,9.04,0,0,0,1.675-.523,5.537,5.537,0,0,1,2.277-.4,4.835,4.835,0,0,1,3.788,1.994,4.213,4.213,0,0,0-2.235,3.827,4.222,4.222,0,0,0,1.386,3.181,4.556,4.556,0,0,0,1.385.909q-.167.483-.353.927ZM16.211,2.4a4.267,4.267,0,0,1-1.094,2.8,3.726,3.726,0,0,1-3.1,1.528A3.114,3.114,0,0,1,12,6.347a4.384,4.384,0,0,1,1.16-2.828,4.467,4.467,0,0,1,1.414-1.061A4.215,4.215,0,0,1,16.19,2a3.633,3.633,0,0,1,.021.4Z"></path>
</svg>',
'bandcamp' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M15.27 17.289 3 17.289 8.73 6.711 21 6.711 15.27 17.289"></path>
</svg>',
'behance' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M7.799,5.698c0.589,0,1.12,0.051,1.606,0.156c0.482,0.102,0.894,0.273,1.241,0.507c0.344,0.235,0.612,0.546,0.804,0.938 c0.188,0.387,0.281,0.871,0.281,1.443c0,0.619-0.141,1.137-0.421,1.551c-0.284,0.413-0.7,0.751-1.255,1.014 c0.756,0.218,1.317,0.601,1.689,1.146c0.374,0.549,0.557,1.205,0.557,1.975c0,0.623-0.12,1.161-0.359,1.612 c-0.241,0.457-0.569,0.828-0.973,1.114c-0.408,0.288-0.876,0.5-1.399,0.637C9.052,17.931,8.514,18,7.963,18H2V5.698H7.799 M7.449,10.668c0.481,0,0.878-0.114,1.192-0.345c0.311-0.228,0.463-0.603,0.463-1.119c0-0.286-0.051-0.523-0.152-0.707 C8.848,8.315,8.711,8.171,8.536,8.07C8.362,7.966,8.166,7.894,7.94,7.854c-0.224-0.044-0.457-0.06-0.697-0.06H4.709v2.874H7.449z M7.6,15.905c0.267,0,0.521-0.024,0.759-0.077c0.243-0.053,0.457-0.137,0.637-0.261c0.182-0.12,0.332-0.283,0.441-0.491 C9.547,14.87,9.6,14.602,9.6,14.278c0-0.633-0.18-1.084-0.533-1.357c-0.356-0.27-0.83-0.404-1.413-0.404H4.709v3.388L7.6,15.905z M16.162,15.864c0.367,0.358,0.897,0.538,1.583,0.538c0.493,0,0.92-0.125,1.277-0.374c0.354-0.248,0.571-0.514,0.654-0.79h2.155 c-0.347,1.072-0.872,1.838-1.589,2.299C19.534,18,18.67,18.23,17.662,18.23c-0.701,0-1.332-0.113-1.899-0.337 c-0.567-0.227-1.041-0.544-1.439-0.958c-0.389-0.415-0.689-0.907-0.904-1.484c-0.213-0.574-0.32-1.21-0.32-1.899 c0-0.666,0.11-1.288,0.329-1.863c0.222-0.577,0.529-1.075,0.933-1.492c0.406-0.42,0.885-0.751,1.444-0.994 c0.558-0.241,1.175-0.363,1.857-0.363c0.754,0,1.414,0.145,1.98,0.44c0.563,0.291,1.026,0.686,1.389,1.181 c0.363,0.493,0.622,1.057,0.783,1.69c0.16,0.632,0.217,1.292,0.171,1.983h-6.428C15.557,14.84,15.795,15.506,16.162,15.864 M18.973,11.184c-0.291-0.321-0.783-0.496-1.384-0.496c-0.39,0-0.714,0.066-0.973,0.2c-0.254,0.132-0.461,0.297-0.621,0.491 c-0.157,0.197-0.265,0.405-0.328,0.628c-0.063,0.217-0.101,0.413-0.111,0.587h3.98C19.478,11.969,19.265,11.509,18.973,11.184z M15.057,7.738h4.985V6.524h-4.985L15.057,7.738z"></path>
</svg>',
'chain' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M19.647,16.706a1.134,1.134,0,0,0-.343-.833l-2.549-2.549a1.134,1.134,0,0,0-.833-.343,1.168,1.168,0,0,0-.883.392l.233.226q.2.189.264.264a2.922,2.922,0,0,1,.184.233.986.986,0,0,1,.159.312,1.242,1.242,0,0,1,.043.337,1.172,1.172,0,0,1-1.176,1.176,1.237,1.237,0,0,1-.337-.043,1,1,0,0,1-.312-.159,2.76,2.76,0,0,1-.233-.184q-.073-.068-.264-.264l-.226-.233a1.19,1.19,0,0,0-.4.895,1.134,1.134,0,0,0,.343.833L15.837,19.3a1.13,1.13,0,0,0,.833.331,1.18,1.18,0,0,0,.833-.318l1.8-1.789a1.12,1.12,0,0,0,.343-.821Zm-8.615-8.64a1.134,1.134,0,0,0-.343-.833L8.163,4.7a1.134,1.134,0,0,0-.833-.343,1.184,1.184,0,0,0-.833.331L4.7,6.473a1.12,1.12,0,0,0-.343.821,1.134,1.134,0,0,0,.343.833l2.549,2.549a1.13,1.13,0,0,0,.833.331,1.184,1.184,0,0,0,.883-.38L8.728,10.4q-.2-.189-.264-.264A2.922,2.922,0,0,1,8.28,9.9a.986.986,0,0,1-.159-.312,1.242,1.242,0,0,1-.043-.337A1.172,1.172,0,0,1,9.254,8.079a1.237,1.237,0,0,1,.337.043,1,1,0,0,1,.312.159,2.761,2.761,0,0,1,.233.184q.073.068.264.264l.226.233a1.19,1.19,0,0,0,.4-.895ZM22,16.706a3.343,3.343,0,0,1-1.042,2.488l-1.8,1.789a3.536,3.536,0,0,1-4.988-.025l-2.525-2.537a3.384,3.384,0,0,1-1.017-2.488,3.448,3.448,0,0,1,1.078-2.561l-1.078-1.078a3.434,3.434,0,0,1-2.549,1.078,3.4,3.4,0,0,1-2.5-1.029L3.029,9.794A3.4,3.4,0,0,1,2,7.294,3.343,3.343,0,0,1,3.042,4.806l1.8-1.789A3.384,3.384,0,0,1,7.331,2a3.357,3.357,0,0,1,2.5,1.042l2.525,2.537a3.384,3.384,0,0,1,1.017,2.488,3.448,3.448,0,0,1-1.078,2.561l1.078,1.078a3.551,3.551,0,0,1,5.049-.049l2.549,2.549A3.4,3.4,0,0,1,22,16.706Z"></path>
</svg>',
'codepen' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M22.016,8.84c-0.002-0.013-0.005-0.025-0.007-0.037c-0.005-0.025-0.008-0.048-0.015-0.072 c-0.003-0.015-0.01-0.028-0.013-0.042c-0.008-0.02-0.015-0.04-0.023-0.062c-0.007-0.015-0.013-0.028-0.02-0.042 c-0.008-0.02-0.018-0.037-0.03-0.057c-0.007-0.013-0.017-0.027-0.025-0.038c-0.012-0.018-0.023-0.035-0.035-0.052 c-0.01-0.013-0.02-0.025-0.03-0.037c-0.015-0.017-0.028-0.032-0.043-0.045c-0.01-0.012-0.022-0.023-0.035-0.035 c-0.015-0.015-0.032-0.028-0.048-0.04c-0.012-0.01-0.025-0.02-0.037-0.03c-0.005-0.003-0.01-0.008-0.015-0.012l-9.161-6.096 c-0.289-0.192-0.666-0.192-0.955,0L2.359,8.237C2.354,8.24,2.349,8.245,2.344,8.249L2.306,8.277 c-0.017,0.013-0.033,0.027-0.048,0.04C2.246,8.331,2.234,8.342,2.222,8.352c-0.015,0.015-0.028,0.03-0.042,0.047 c-0.012,0.013-0.022,0.023-0.03,0.037C2.139,8.453,2.125,8.471,2.115,8.488C2.107,8.501,2.099,8.514,2.09,8.526 C2.079,8.548,2.069,8.565,2.06,8.585C2.054,8.6,2.047,8.613,2.04,8.626C2.032,8.648,2.025,8.67,2.019,8.69 c-0.005,0.013-0.01,0.027-0.013,0.042C1.999,8.755,1.995,8.778,1.99,8.803C1.989,8.817,1.985,8.828,1.984,8.84 C1.978,8.879,1.975,8.915,1.975,8.954v6.093c0,0.037,0.003,0.075,0.008,0.112c0.002,0.012,0.005,0.025,0.007,0.038 c0.005,0.023,0.008,0.047,0.015,0.072c0.003,0.015,0.008,0.028,0.013,0.04c0.007,0.022,0.013,0.042,0.022,0.063 c0.007,0.015,0.013,0.028,0.02,0.04c0.008,0.02,0.018,0.038,0.03,0.058c0.007,0.013,0.015,0.027,0.025,0.038 c0.012,0.018,0.023,0.035,0.035,0.052c0.01,0.013,0.02,0.025,0.03,0.037c0.013,0.015,0.028,0.032,0.042,0.045 c0.012,0.012,0.023,0.023,0.035,0.035c0.015,0.013,0.032,0.028,0.048,0.04l0.038,0.03c0.005,0.003,0.01,0.007,0.013,0.01 l9.163,6.095C11.668,21.953,11.833,22,12,22c0.167,0,0.332-0.047,0.478-0.144l9.163-6.095l0.015-0.01 c0.013-0.01,0.027-0.02,0.037-0.03c0.018-0.013,0.035-0.028,0.048-0.04c0.013-0.012,0.025-0.023,0.035-0.035 c0.017-0.015,0.03-0.032,0.043-0.045c0.01-0.013,0.02-0.025,0.03-0.037c0.013-0.018,0.025-0.035,0.035-0.052 c0.008-0.013,0.018-0.027,0.025-0.038c0.012-0.02,0.022-0.038,0.03-0.058c0.007-0.013,0.013-0.027,0.02-0.04 c0.008-0.022,0.015-0.042,0.023-0.063c0.003-0.013,0.01-0.027,0.013-0.04c0.007-0.025,0.01-0.048,0.015-0.072 c0.002-0.013,0.005-0.027,0.007-0.037c0.003-0.042,0.007-0.079,0.007-0.117V8.954C22.025,8.915,22.022,8.879,22.016,8.84z M12.862,4.464l6.751,4.49l-3.016,2.013l-3.735-2.492V4.464z M11.138,4.464v4.009l-3.735,2.494L4.389,8.954L11.138,4.464z M3.699,10.562L5.853,12l-2.155,1.438V10.562z M11.138,19.536l-6.749-4.491l3.015-2.011l3.735,2.492V19.536z M12,14.035L8.953,12 L12,9.966L15.047,12L12,14.035z M12.862,19.536v-4.009l3.735-2.492l3.016,2.011L12.862,19.536z M20.303,13.438L18.147,12 l2.156-1.438L20.303,13.438z"></path>
</svg>',
'deviantart' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M 18.19 5.636 18.19 2 18.188 2 14.553 2 14.19 2.366 12.474 5.636 11.935 6 5.81 6 5.81 10.994 9.177 10.994 9.477 11.357 5.81 18.363 5.81 22 5.811 22 9.447 22 9.81 21.634 11.526 18.364 12.065 18 18.19 18 18.19 13.006 14.823 13.006 14.523 12.641 18.19 5.636z"></path>
</svg>',
'digg' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M4.5,5.4h2.2V16H1V8.5h3.5V5.4L4.5,5.4z M4.5,14.2v-4H3.2v4H4.5z M7.6,8.5V16h2.2V8.5C9.8,8.5,7.6,8.5,7.6,8.5z M7.6,5.4 v2.2h2.2V5.4C9.8,5.4,7.6,5.4,7.6,5.4z M10.7,8.5h5.7v10.1h-5.7v-1.8h3.5V16h-3.5C10.7,16,10.7,8.5,10.7,8.5z M14.2,14.2v-4h-1.3v4 H14.2z M17.3,8.5H23v10.1h-5.7v-1.8h3.5V16h-3.5C17.3,16,17.3,8.5,17.3,8.5z M20.8,14.2v-4h-1.3v4H20.8z"></path>
</svg>',
'dribbble' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M12,22C6.486,22,2,17.514,2,12S6.486,2,12,2c5.514,0,10,4.486,10,10S17.514,22,12,22z M20.434,13.369 c-0.292-0.092-2.644-0.794-5.32-0.365c1.117,3.07,1.572,5.57,1.659,6.09C18.689,17.798,20.053,15.745,20.434,13.369z M15.336,19.876c-0.127-0.749-0.623-3.361-1.822-6.477c-0.019,0.006-0.038,0.013-0.056,0.019c-4.818,1.679-6.547,5.02-6.701,5.334 c1.448,1.129,3.268,1.803,5.243,1.803C13.183,20.555,14.311,20.313,15.336,19.876z M5.654,17.724 c0.193-0.331,2.538-4.213,6.943-5.637c0.111-0.036,0.224-0.07,0.337-0.102c-0.214-0.485-0.448-0.971-0.692-1.45 c-4.266,1.277-8.405,1.223-8.778,1.216c-0.003,0.087-0.004,0.174-0.004,0.261C3.458,14.207,4.29,16.21,5.654,17.724z M3.639,10.264 c0.382,0.005,3.901,0.02,7.897-1.041c-1.415-2.516-2.942-4.631-3.167-4.94C5.979,5.41,4.193,7.613,3.639,10.264z M9.998,3.709 c0.236,0.316,1.787,2.429,3.187,5c3.037-1.138,4.323-2.867,4.477-3.085C16.154,4.286,14.17,3.471,12,3.471 C11.311,3.471,10.641,3.554,9.998,3.709z M18.612,6.612C18.432,6.855,17,8.69,13.842,9.979c0.199,0.407,0.389,0.821,0.567,1.237 c0.063,0.148,0.124,0.295,0.184,0.441c2.842-0.357,5.666,0.215,5.948,0.275C20.522,9.916,19.801,8.065,18.612,6.612z"></path>
</svg>',
'dropbox' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M12,6.134L6.069,9.797L2,6.54l5.883-3.843L12,6.134z M2,13.054l5.883,3.843L12,13.459L6.069,9.797L2,13.054z M12,13.459 l4.116,3.439L22,13.054l-4.069-3.257L12,13.459z M22,6.54l-5.884-3.843L12,6.134l5.931,3.663L22,6.54z M12.011,14.2l-4.129,3.426 l-1.767-1.153v1.291l5.896,3.539l5.897-3.539v-1.291l-1.769,1.153L12.011,14.2z"></path>
</svg>',
'etsy' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M9.16033,4.038c0-.27174.02717-.43478.48913-.43478h6.22283c1.087,0,1.68478.92391,2.11957,2.663l.35326,1.38587h1.05978C19.59511,3.712,19.75815,2,19.75815,2s-2.663.29891-4.23913.29891h-7.962L3.29076,2.163v1.1413L4.731,3.57609c1.00543.19022,1.25.40761,1.33152,1.33152,0,0,.08152,2.71739.08152,7.20109s-.08152,7.17391-.08152,7.17391c0,.81522-.32609,1.11413-1.33152,1.30435l-1.44022.27174V22l4.2663-.13587h7.11957c1.60326,0,5.32609.13587,5.32609.13587.08152-.97826.625-5.40761.70652-5.89674H19.7038L18.644,18.52174c-.84239,1.90217-2.06522,2.038-3.42391,2.038H11.1712c-1.3587,0-2.01087-.54348-2.01087-1.712V12.65217s3.0163,0,3.99457.08152c.76087.05435,1.22283.27174,1.46739,1.33152l.32609,1.413h1.16848l-.08152-3.55978.163-3.587H15.02989l-.38043,1.57609c-.24457,1.03261-.40761,1.22283-1.46739,1.33152-1.38587.13587-4.02174.1087-4.02174.1087Z"></path>
</svg>',
'facebook' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M20.007,3H3.993C3.445,3,3,3.445,3,3.993v16.013C3,20.555,3.445,21,3.993,21h8.621v-6.971h-2.346v-2.717h2.346V9.31 c0-2.325,1.42-3.591,3.494-3.591c0.993,0,1.847,0.074,2.096,0.107v2.43l-1.438,0.001c-1.128,0-1.346,0.536-1.346,1.323v1.734h2.69 l-0.35,2.717h-2.34V21h4.587C20.555,21,21,20.555,21,20.007V3.993C21,3.445,20.555,3,20.007,3z"></path>
</svg>',
'feed' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M2,8.667V12c5.515,0,10,4.485,10,10h3.333C15.333,14.637,9.363,8.667,2,8.667z M2,2v3.333 c9.19,0,16.667,7.477,16.667,16.667H22C22,10.955,13.045,2,2,2z M4.5,17C3.118,17,2,18.12,2,19.5S3.118,22,4.5,22S7,20.88,7,19.5 S5.882,17,4.5,17z"></path>
</svg>',
'flickr' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M6.5,7c-2.75,0-5,2.25-5,5s2.25,5,5,5s5-2.25,5-5S9.25,7,6.5,7z M17.5,7c-2.75,0-5,2.25-5,5s2.25,5,5,5s5-2.25,5-5 S20.25,7,17.5,7z"></path>
</svg>',
'foursquare' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M17.573,2c0,0-9.197,0-10.668,0S5,3.107,5,3.805s0,16.948,0,16.948c0,0.785,0.422,1.077,0.66,1.172 c0.238,0.097,0.892,0.177,1.285-0.275c0,0,5.035-5.843,5.122-5.93c0.132-0.132,0.132-0.132,0.262-0.132h3.26 c1.368,0,1.588-0.977,1.732-1.552c0.078-0.318,0.692-3.428,1.225-6.122l0.675-3.368C19.56,2.893,19.14,2,17.573,2z M16.495,7.22 c-0.053,0.252-0.372,0.518-0.665,0.518c-0.293,0-4.157,0-4.157,0c-0.467,0-0.802,0.318-0.802,0.787v0.508 c0,0.467,0.337,0.798,0.805,0.798c0,0,3.197,0,3.528,0s0.655,0.362,0.583,0.715c-0.072,0.353-0.407,2.102-0.448,2.295 c-0.04,0.193-0.262,0.523-0.655,0.523c-0.33,0-2.88,0-2.88,0c-0.523,0-0.683,0.068-1.033,0.503 c-0.35,0.437-3.505,4.223-3.505,4.223c-0.032,0.035-0.063,0.027-0.063-0.015V4.852c0-0.298,0.26-0.648,0.648-0.648 c0,0,8.228,0,8.562,0c0.315,0,0.61,0.297,0.528,0.683L16.495,7.22z"></path>
</svg>',
'goodreads' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M17.3,17.5c-0.2,0.8-0.5,1.4-1,1.9c-0.4,0.5-1,0.9-1.7,1.2C13.9,20.9,13.1,21,12,21c-0.6,0-1.3-0.1-1.9-0.2 c-0.6-0.1-1.1-0.4-1.6-0.7c-0.5-0.3-0.9-0.7-1.2-1.2c-0.3-0.5-0.5-1.1-0.5-1.7h1.5c0.1,0.5,0.2,0.9,0.5,1.2 c0.2,0.3,0.5,0.6,0.9,0.8c0.3,0.2,0.7,0.3,1.1,0.4c0.4,0.1,0.8,0.1,1.2,0.1c1.4,0,2.5-0.4,3.1-1.2c0.6-0.8,1-2,1-3.5v-1.7h0 c-0.4,0.8-0.9,1.4-1.6,1.9c-0.7,0.5-1.5,0.7-2.4,0.7c-1,0-1.9-0.2-2.6-0.5C8.7,15,8.1,14.5,7.7,14c-0.5-0.6-0.8-1.3-1-2.1 c-0.2-0.8-0.3-1.6-0.3-2.5c0-0.9,0.1-1.7,0.4-2.5c0.3-0.8,0.6-1.5,1.1-2c0.5-0.6,1.1-1,1.8-1.4C10.3,3.2,11.1,3,12,3 c0.5,0,0.9,0.1,1.3,0.2c0.4,0.1,0.8,0.3,1.1,0.5c0.3,0.2,0.6,0.5,0.9,0.8c0.3,0.3,0.5,0.6,0.6,1h0V3.4h1.5V15 C17.6,15.9,17.5,16.7,17.3,17.5z M13.8,14.1c0.5-0.3,0.9-0.7,1.3-1.1c0.3-0.5,0.6-1,0.8-1.6c0.2-0.6,0.3-1.2,0.3-1.9 c0-0.6-0.1-1.2-0.2-1.9c-0.1-0.6-0.4-1.2-0.7-1.7c-0.3-0.5-0.7-0.9-1.3-1.2c-0.5-0.3-1.1-0.5-1.9-0.5s-1.4,0.2-1.9,0.5 c-0.5,0.3-1,0.7-1.3,1.2C8.5,6.4,8.3,7,8.1,7.6C8,8.2,7.9,8.9,7.9,9.5c0,0.6,0.1,1.3,0.2,1.9C8.3,12,8.6,12.5,8.9,13 c0.3,0.5,0.8,0.8,1.3,1.1c0.5,0.3,1.1,0.4,1.9,0.4C12.7,14.5,13.3,14.4,13.8,14.1z"></path>
</svg>',
'google-plus' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M8,11h6.61c0.06,0.35,0.11,0.7,0.11,1.16c0,4-2.68,6.84-6.72,6.84c-3.87,0-7-3.13-7-7s3.13-7,7-7 c1.89,0,3.47,0.69,4.69,1.83l-1.9,1.83C10.27,8.16,9.36,7.58,8,7.58c-2.39,0-4.34,1.98-4.34,4.42S5.61,16.42,8,16.42 c2.77,0,3.81-1.99,3.97-3.02H8V11L8,11z M23,11h-2V9h-2v2h-2v2h2v2h2v-2h2"></path>
</svg>',
'google' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M12.02,10.18v3.72v0.01h5.51c-0.26,1.57-1.67,4.22-5.5,4.22c-3.31,0-6.01-2.75-6.01-6.12s2.7-6.12,6.01-6.12 c1.87,0,3.13,0.8,3.85,1.48l2.84-2.76C16.99,2.99,14.73,2,12.03,2c-5.52,0-10,4.48-10,10s4.48,10,10,10c5.77,0,9.6-4.06,9.6-9.77 c0-0.83-0.11-1.42-0.25-2.05H12.02z"></path>
</svg>',
'github' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M12,2C6.477,2,2,6.477,2,12c0,4.419,2.865,8.166,6.839,9.489c0.5,0.09,0.682-0.218,0.682-0.484 c0-0.236-0.009-0.866-0.014-1.699c-2.782,0.602-3.369-1.34-3.369-1.34c-0.455-1.157-1.11-1.465-1.11-1.465 c-0.909-0.62,0.069-0.608,0.069-0.608c1.004,0.071,1.532,1.03,1.532,1.03c0.891,1.529,2.341,1.089,2.91,0.833 c0.091-0.647,0.349-1.086,0.635-1.337c-2.22-0.251-4.555-1.111-4.555-4.943c0-1.091,0.39-1.984,1.03-2.682 C6.546,8.54,6.202,7.524,6.746,6.148c0,0,0.84-0.269,2.75,1.025C10.295,6.95,11.15,6.84,12,6.836 c0.85,0.004,1.705,0.114,2.504,0.336c1.909-1.294,2.748-1.025,2.748-1.025c0.546,1.376,0.202,2.394,0.1,2.646 c0.64,0.699,1.026,1.591,1.026,2.682c0,3.841-2.337,4.687-4.565,4.935c0.359,0.307,0.679,0.917,0.679,1.852 c0,1.335-0.012,2.415-0.012,2.741c0,0.269,0.18,0.579,0.688,0.481C19.138,20.161,22,16.416,22,12C22,6.477,17.523,2,12,2z"></path>
</svg>',
'instagram' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M12,4.622c2.403,0,2.688,0.009,3.637,0.052c0.877,0.04,1.354,0.187,1.671,0.31c0.42,0.163,0.72,0.358,1.035,0.673 c0.315,0.315,0.51,0.615,0.673,1.035c0.123,0.317,0.27,0.794,0.31,1.671c0.043,0.949,0.052,1.234,0.052,3.637 s-0.009,2.688-0.052,3.637c-0.04,0.877-0.187,1.354-0.31,1.671c-0.163,0.42-0.358,0.72-0.673,1.035 c-0.315,0.315-0.615,0.51-1.035,0.673c-0.317,0.123-0.794,0.27-1.671,0.31c-0.949,0.043-1.233,0.052-3.637,0.052 s-2.688-0.009-3.637-0.052c-0.877-0.04-1.354-0.187-1.671-0.31c-0.42-0.163-0.72-0.358-1.035-0.673 c-0.315-0.315-0.51-0.615-0.673-1.035c-0.123-0.317-0.27-0.794-0.31-1.671C4.631,14.688,4.622,14.403,4.622,12 s0.009-2.688,0.052-3.637c0.04-0.877,0.187-1.354,0.31-1.671c0.163-0.42,0.358-0.72,0.673-1.035 c0.315-0.315,0.615-0.51,1.035-0.673c0.317-0.123,0.794-0.27,1.671-0.31C9.312,4.631,9.597,4.622,12,4.622 M12,3 C9.556,3,9.249,3.01,8.289,3.054C7.331,3.098,6.677,3.25,6.105,3.472C5.513,3.702,5.011,4.01,4.511,4.511 c-0.5,0.5-0.808,1.002-1.038,1.594C3.25,6.677,3.098,7.331,3.054,8.289C3.01,9.249,3,9.556,3,12c0,2.444,0.01,2.751,0.054,3.711 c0.044,0.958,0.196,1.612,0.418,2.185c0.23,0.592,0.538,1.094,1.038,1.594c0.5,0.5,1.002,0.808,1.594,1.038 c0.572,0.222,1.227,0.375,2.185,0.418C9.249,20.99,9.556,21,12,21s2.751-0.01,3.711-0.054c0.958-0.044,1.612-0.196,2.185-0.418 c0.592-0.23,1.094-0.538,1.594-1.038c0.5-0.5,0.808-1.002,1.038-1.594c0.222-0.572,0.375-1.227,0.418-2.185 C20.99,14.751,21,14.444,21,12s-0.01-2.751-0.054-3.711c-0.044-0.958-0.196-1.612-0.418-2.185c-0.23-0.592-0.538-1.094-1.038-1.594 c-0.5-0.5-1.002-0.808-1.594-1.038c-0.572-0.222-1.227-0.375-2.185-0.418C14.751,3.01,14.444,3,12,3L12,3z M12,7.378 c-2.552,0-4.622,2.069-4.622,4.622S9.448,16.622,12,16.622s4.622-2.069,4.622-4.622S14.552,7.378,12,7.378z M12,15 c-1.657,0-3-1.343-3-3s1.343-3,3-3s3,1.343,3,3S13.657,15,12,15z M16.804,6.116c-0.596,0-1.08,0.484-1.08,1.08 s0.484,1.08,1.08,1.08c0.596,0,1.08-0.484,1.08-1.08S17.401,6.116,16.804,6.116z"></path>
</svg>',
'lastfm' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M10.5002,0 C4.7006,0 0,4.70109753 0,10.4998496 C0,16.2989526 4.7006,21 10.5002,21 C16.299,21 21,16.2989526 21,10.4998496 C21,4.70109753 16.299,0 10.5002,0 Z M14.69735,14.7204413 C13.3164,14.7151781 12.4346,14.0870017 11.83445,12.6859357 L11.6816001,12.3451305 L10.35405,9.31011397 C9.92709997,8.26875064 8.85260001,7.57120012 7.68010001,7.57120012 C6.06945001,7.57120012 4.75925001,8.88509738 4.75925001,10.5009524 C4.75925001,12.1164565 6.06945001,13.4303036 7.68010001,13.4303036 C8.77200001,13.4303036 9.76514999,12.827541 10.2719501,11.8567047 C10.2893,11.8235214 10.3239,11.8019673 10.36305,11.8038219 C10.4007,11.8053759 10.43535,11.8287847 10.4504,11.8631709 L10.98655,13.1045863 C11.0016,13.1389726 10.9956,13.17782 10.97225,13.2068931 C10.1605001,14.1995341 8.96020001,14.7683115 7.68010001,14.7683115 C5.33305,14.7683115 3.42340001,12.8535563 3.42340001,10.5009524 C3.42340001,8.14679459 5.33300001,6.23203946 7.68010001,6.23203946 C9.45720002,6.23203946 10.8909,7.19074535 11.6138,8.86359341 C11.6205501,8.88018505 12.3412,10.5707777 12.97445,12.0190621 C13.34865,12.8739575 13.64615,13.3959676 14.6288,13.4291508 C15.5663001,13.4612814 16.25375,12.9121534 16.25375,12.1484869 C16.25375,11.4691321 15.8320501,11.3003585 14.8803,10.98216 C13.2365,10.4397989 12.34495,9.88605929 12.34495,8.51817658 C12.34495,7.1809207 13.26665,6.31615054 14.692,6.31615054 C15.62875,6.31615054 16.3155,6.7286858 16.79215,7.5768142 C16.80495,7.60062396 16.8079001,7.62814302 16.8004001,7.65420843 C16.7929,7.68027384 16.7748,7.70212868 16.7507001,7.713808 L15.86145,8.16900031 C15.8178001,8.19200805 15.7643,8.17807308 15.73565,8.13847371 C15.43295,7.71345711 15.0956,7.52513451 14.6423,7.52513451 C14.05125,7.52513451 13.6220001,7.92899802 13.6220001,8.48649708 C13.6220001,9.17382194 14.1529001,9.34144259 15.0339,9.61923972 C15.14915,9.65578139 15.26955,9.69397731 15.39385,9.73432853 C16.7763,10.1865133 17.57675,10.7311301 17.57675,12.1836251 C17.57685,13.629654 16.3389,14.7204413 14.69735,14.7204413 Z"></path>
</svg>',
'linkedin' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M19.7,3H4.3C3.582,3,3,3.582,3,4.3v15.4C3,20.418,3.582,21,4.3,21h15.4c0.718,0,1.3-0.582,1.3-1.3V4.3 C21,3.582,20.418,3,19.7,3z M8.339,18.338H5.667v-8.59h2.672V18.338z M7.004,8.574c-0.857,0-1.549-0.694-1.549-1.548 c0-0.855,0.691-1.548,1.549-1.548c0.854,0,1.547,0.694,1.547,1.548C8.551,7.881,7.858,8.574,7.004,8.574z M18.339,18.338h-2.669 v-4.177c0-0.996-0.017-2.278-1.387-2.278c-1.389,0-1.601,1.086-1.601,2.206v4.249h-2.667v-8.59h2.559v1.174h0.037 c0.356-0.675,1.227-1.387,2.526-1.387c2.703,0,3.203,1.779,3.203,4.092V18.338z"></path>
</svg>',
'mail' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M20,4H4C2.895,4,2,4.895,2,6v12c0,1.105,0.895,2,2,2h16c1.105,0,2-0.895,2-2V6C22,4.895,21.105,4,20,4z M20,8.236l-8,4.882 L4,8.236V6h16V8.236z"></path>
</svg>',
'meetup' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M19.24775,14.722a3.57032,3.57032,0,0,1-2.94457,3.52073,3.61886,3.61886,0,0,1-.64652.05634c-.07314-.0008-.10187.02846-.12507.09547A2.38881,2.38881,0,0,1,13.49453,20.094a2.33092,2.33092,0,0,1-1.827-.50716.13635.13635,0,0,0-.19878-.00408,3.191,3.191,0,0,1-2.104.60248,3.26309,3.26309,0,0,1-3.00324-2.71993,2.19076,2.19076,0,0,1-.03512-.30865c-.00156-.08579-.03413-.1189-.11608-.13493a2.86421,2.86421,0,0,1-1.23189-.56111,2.945,2.945,0,0,1-1.166-2.05749,2.97484,2.97484,0,0,1,.87524-2.50774.112.112,0,0,0,.02091-.16107,2.7213,2.7213,0,0,1-.36648-1.48A2.81256,2.81256,0,0,1,6.57673,7.58838a.35764.35764,0,0,0,.28869-.22819,4.2208,4.2208,0,0,1,6.02892-1.90111.25161.25161,0,0,0,.22023.0243,3.65608,3.65608,0,0,1,3.76031.90678A3.57244,3.57244,0,0,1,17.95918,8.626a2.97339,2.97339,0,0,1,.01829.57356.10637.10637,0,0,0,.0853.12792,1.97669,1.97669,0,0,1,1.27939,1.33733,2.00266,2.00266,0,0,1-.57112,2.12652c-.05284.05166-.04168.08328-.01173.13489A3.51189,3.51189,0,0,1,19.24775,14.722Zm-6.35959-.27836a1.6984,1.6984,0,0,0,1.14556,1.61113,3.82039,3.82039,0,0,0,1.036.17935,1.46888,1.46888,0,0,0,.73509-.12255.44082.44082,0,0,0,.26057-.44274.45312.45312,0,0,0-.29211-.43375.97191.97191,0,0,0-.20678-.063c-.21326-.03806-.42754-.0701-.63973-.11215a.54787.54787,0,0,1-.50172-.60926,2.75864,2.75864,0,0,1,.1773-.901c.1763-.535.414-1.045.64183-1.55913A12.686,12.686,0,0,0,15.85,10.47863a1.58461,1.58461,0,0,0,.04861-.87208,1.04531,1.04531,0,0,0-.85432-.83981,1.60658,1.60658,0,0,0-1.23654.16594.27593.27593,0,0,1-.36286-.03413c-.085-.0747-.16594-.15379-.24918-.23055a.98682.98682,0,0,0-1.33577-.04933,6.1468,6.1468,0,0,1-.4989.41615.47762.47762,0,0,1-.51535.03566c-.17448-.09307-.35512-.175-.53531-.25665a1.74949,1.74949,0,0,0-.56476-.2016,1.69943,1.69943,0,0,0-1.61654.91787,8.05815,8.05815,0,0,0-.32952.80126c-.45471,1.2557-.82507,2.53825-1.20838,3.81639a1.24151,1.24151,0,0,0,.51532,1.44389,1.42659,1.42659,0,0,0,1.22008.17166,1.09728,1.09728,0,0,0,.66994-.69764c.44145-1.04111.839-2.09989,1.25981-3.14926.11581-.28876.22792-.57874.35078-.86438a.44548.44548,0,0,1,.69189-.19539.50521.50521,0,0,1,.15044.43836,1.75625,1.75625,0,0,1-.14731.50453c-.27379.69219-.55265,1.38236-.82766,2.074a2.0836,2.0836,0,0,0-.14038.42876.50719.50719,0,0,0,.27082.57722.87236.87236,0,0,0,.66145.02739.99137.99137,0,0,0,.53406-.532q.61571-1.20914,1.228-2.42031.28423-.55863.57585-1.1133a.87189.87189,0,0,1,.29055-.35253.34987.34987,0,0,1,.37634-.01265.30291.30291,0,0,1,.12434.31459.56716.56716,0,0,1-.04655.1915c-.05318.12739-.10286.25669-.16183.38156-.34118.71775-.68754,1.43273-1.02568,2.152A2.00213,2.00213,0,0,0,12.88816,14.44366Zm4.78568,5.28972a.88573.88573,0,0,0-1.77139.00465.8857.8857,0,0,0,1.77139-.00465Zm-14.83838-7.296a.84329.84329,0,1,0,.00827-1.68655.8433.8433,0,0,0-.00827,1.68655Zm10.366-9.43673a.83506.83506,0,1,0-.0091,1.67.83505.83505,0,0,0,.0091-1.67Zm6.85014,5.22a.71651.71651,0,0,0-1.433.0093.71656.71656,0,0,0,1.433-.0093ZM5.37528,6.17908A.63823.63823,0,1,0,6.015,5.54483.62292.62292,0,0,0,5.37528,6.17908Zm6.68214,14.80843a.54949.54949,0,1,0-.55052.541A.54556.54556,0,0,0,12.05742,20.98752Zm8.53235-8.49689a.54777.54777,0,0,0-.54027.54023.53327.53327,0,0,0,.532.52293.51548.51548,0,0,0,.53272-.5237A.53187.53187,0,0,0,20.58977,12.49063ZM7.82846,2.4715a.44927.44927,0,1,0,.44484.44766A.43821.43821,0,0,0,7.82846,2.4715Zm13.775,7.60492a.41186.41186,0,0,0-.40065.39623.40178.40178,0,0,0,.40168.40168A.38994.38994,0,0,0,22,10.48172.39946.39946,0,0,0,21.60349,10.07642ZM5.79193,17.96207a.40469.40469,0,0,0-.397-.39646.399.399,0,0,0-.396.405.39234.39234,0,0,0,.39939.389A.39857.39857,0,0,0,5.79193,17.96207Z"></path>
</svg>',
'medium' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M20.962,7.257l-5.457,8.867l-3.923-6.375l3.126-5.08c0.112-0.182,0.319-0.286,0.527-0.286c0.05,0,0.1,0.008,0.149,0.02 c0.039,0.01,0.078,0.023,0.114,0.041l5.43,2.715l0.006,0.003c0.004,0.002,0.007,0.006,0.011,0.008 C20.971,7.191,20.98,7.227,20.962,7.257z M9.86,8.592v5.783l5.14,2.57L9.86,8.592z M15.772,17.331l4.231,2.115 C20.554,19.721,21,19.529,21,19.016V8.835L15.772,17.331z M8.968,7.178L3.665,4.527C3.569,4.479,3.478,4.456,3.395,4.456 C3.163,4.456,3,4.636,3,4.938v11.45c0,0.306,0.224,0.669,0.498,0.806l4.671,2.335c0.12,0.06,0.234,0.088,0.337,0.088 c0.29,0,0.494-0.225,0.494-0.602V7.231C9,7.208,8.988,7.188,8.968,7.178z"></path>
</svg>',
'pinterest' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M12.289,2C6.617,2,3.606,5.648,3.606,9.622c0,1.846,1.025,4.146,2.666,4.878c0.25,0.111,0.381,0.063,0.439-0.169 c0.044-0.175,0.267-1.029,0.365-1.428c0.032-0.128,0.017-0.237-0.091-0.362C6.445,11.911,6.01,10.75,6.01,9.668 c0-2.777,2.194-5.464,5.933-5.464c3.23,0,5.49,2.108,5.49,5.122c0,3.407-1.794,5.768-4.13,5.768c-1.291,0-2.257-1.021-1.948-2.277 c0.372-1.495,1.089-3.112,1.089-4.191c0-0.967-0.542-1.775-1.663-1.775c-1.319,0-2.379,1.309-2.379,3.059 c0,1.115,0.394,1.869,0.394,1.869s-1.302,5.279-1.54,6.261c-0.405,1.666,0.053,4.368,0.094,4.604 c0.021,0.126,0.167,0.169,0.25,0.063c0.129-0.165,1.699-2.419,2.142-4.051c0.158-0.59,0.817-2.995,0.817-2.995 c0.43,0.784,1.681,1.446,3.013,1.446c3.963,0,6.822-3.494,6.822-7.833C20.394,5.112,16.849,2,12.289,2"></path>
</svg>',
'pocket' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M21.927,4.194C21.667,3.48,20.982,3,20.222,3h-0.01h-1.721H3.839C3.092,3,2.411,3.47,2.145,4.17 C2.066,4.378,2.026,4.594,2.026,4.814v6.035l0.069,1.2c0.29,2.73,1.707,5.115,3.899,6.778c0.039,0.03,0.079,0.059,0.119,0.089 l0.025,0.018c1.175,0.859,2.491,1.441,3.91,1.727c0.655,0.132,1.325,0.2,1.991,0.2c0.615,0,1.232-0.057,1.839-0.17 c0.073-0.014,0.145-0.028,0.219-0.044c0.02-0.004,0.042-0.012,0.064-0.023c1.359-0.297,2.621-0.864,3.753-1.691l0.025-0.018 c0.04-0.029,0.08-0.058,0.119-0.089c2.192-1.664,3.609-4.049,3.898-6.778l0.069-1.2V4.814C22.026,4.605,22,4.398,21.927,4.194z M17.692,10.481l-4.704,4.512c-0.266,0.254-0.608,0.382-0.949,0.382c-0.342,0-0.684-0.128-0.949-0.382l-4.705-4.512 C5.838,9.957,5.82,9.089,6.344,8.542c0.524-0.547,1.392-0.565,1.939-0.04l3.756,3.601l3.755-3.601 c0.547-0.524,1.415-0.506,1.939,0.04C18.256,9.089,18.238,9.956,17.692,10.481z"></path>
</svg>',
'reddit' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M22,11.816c0-1.256-1.021-2.277-2.277-2.277c-0.593,0-1.122,0.24-1.526,0.614c-1.481-0.965-3.455-1.594-5.647-1.69 l1.171-3.702l3.18,0.748c0.008,1.028,0.846,1.862,1.876,1.862c1.035,0,1.877-0.842,1.877-1.878c0-1.035-0.842-1.877-1.877-1.877 c-0.769,0-1.431,0.466-1.72,1.13l-3.508-0.826c-0.203-0.047-0.399,0.067-0.46,0.261l-1.35,4.268 c-2.316,0.038-4.411,0.67-5.97,1.671C5.368,9.765,4.853,9.539,4.277,9.539C3.021,9.539,2,10.56,2,11.816 c0,0.814,0.433,1.523,1.078,1.925c-0.037,0.221-0.061,0.444-0.061,0.672c0,3.292,4.011,5.97,8.941,5.97s8.941-2.678,8.941-5.97 c0-0.214-0.02-0.424-0.053-0.632C21.533,13.39,22,12.661,22,11.816z M18.776,4.394c0.606,0,1.1,0.493,1.1,1.1s-0.493,1.1-1.1,1.1 s-1.1-0.494-1.1-1.1S18.169,4.394,18.776,4.394z M2.777,11.816c0-0.827,0.672-1.5,1.499-1.5c0.313,0,0.598,0.103,0.838,0.269 c-0.851,0.676-1.477,1.479-1.812,2.36C2.983,12.672,2.777,12.27,2.777,11.816z M11.959,19.606c-4.501,0-8.164-2.329-8.164-5.193 S7.457,9.22,11.959,9.22s8.164,2.329,8.164,5.193S16.46,19.606,11.959,19.606z M20.636,13.001c-0.326-0.89-0.948-1.701-1.797-2.384 c0.248-0.186,0.55-0.301,0.883-0.301c0.827,0,1.5,0.673,1.5,1.5C21.223,12.299,20.992,12.727,20.636,13.001z M8.996,14.704 c-0.76,0-1.397-0.616-1.397-1.376c0-0.76,0.637-1.397,1.397-1.397c0.76,0,1.376,0.637,1.376,1.397 C10.372,14.088,9.756,14.704,8.996,14.704z M16.401,13.328c0,0.76-0.616,1.376-1.376,1.376c-0.76,0-1.399-0.616-1.399-1.376 c0-0.76,0.639-1.397,1.399-1.397C15.785,11.931,16.401,12.568,16.401,13.328z M15.229,16.708c0.152,0.152,0.152,0.398,0,0.55 c-0.674,0.674-1.727,1.002-3.219,1.002c-0.004,0-0.007-0.002-0.011-0.002c-0.004,0-0.007,0.002-0.011,0.002 c-1.492,0-2.544-0.328-3.218-1.002c-0.152-0.152-0.152-0.398,0-0.55c0.152-0.152,0.399-0.151,0.55,0 c0.521,0.521,1.394,0.775,2.669,0.775c0.004,0,0.007,0.002,0.011,0.002c0.004,0,0.007-0.002,0.011-0.002 c1.275,0,2.148-0.253,2.669-0.775C14.831,16.556,15.078,16.556,15.229,16.708z"></path>
</svg>',
'skype' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M10.113,2.699c0.033-0.006,0.067-0.013,0.1-0.02c0.033,0.017,0.066,0.033,0.098,0.051L10.113,2.699z M2.72,10.223 c-0.006,0.034-0.011,0.069-0.017,0.103c0.018,0.032,0.033,0.064,0.051,0.095L2.72,10.223z M21.275,13.771 c0.007-0.035,0.011-0.071,0.018-0.106c-0.018-0.031-0.033-0.064-0.052-0.095L21.275,13.771z M13.563,21.199 c0.032,0.019,0.065,0.035,0.096,0.053c0.036-0.006,0.071-0.011,0.105-0.017L13.563,21.199z M22,16.386 c0,1.494-0.581,2.898-1.637,3.953c-1.056,1.057-2.459,1.637-3.953,1.637c-0.967,0-1.914-0.251-2.75-0.725 c0.036-0.006,0.071-0.011,0.105-0.017l-0.202-0.035c0.032,0.019,0.065,0.035,0.096,0.053c-0.543,0.096-1.099,0.147-1.654,0.147 c-1.275,0-2.512-0.25-3.676-0.743c-1.125-0.474-2.135-1.156-3.002-2.023c-0.867-0.867-1.548-1.877-2.023-3.002 c-0.493-1.164-0.743-2.401-0.743-3.676c0-0.546,0.049-1.093,0.142-1.628c0.018,0.032,0.033,0.064,0.051,0.095L2.72,10.223 c-0.006,0.034-0.011,0.069-0.017,0.103C2.244,9.5,2,8.566,2,7.615c0-1.493,0.582-2.898,1.637-3.953 c1.056-1.056,2.46-1.638,3.953-1.638c0.915,0,1.818,0.228,2.622,0.655c-0.033,0.007-0.067,0.013-0.1,0.02l0.199,0.031 c-0.032-0.018-0.066-0.034-0.098-0.051c0.002,0,0.003-0.001,0.004-0.001c0.586-0.112,1.187-0.169,1.788-0.169 c1.275,0,2.512,0.249,3.676,0.742c1.124,0.476,2.135,1.156,3.002,2.024c0.868,0.867,1.548,1.877,2.024,3.002 c0.493,1.164,0.743,2.401,0.743,3.676c0,0.575-0.054,1.15-0.157,1.712c-0.018-0.031-0.033-0.064-0.052-0.095l0.034,0.201 c0.007-0.035,0.011-0.071,0.018-0.106C21.754,14.494,22,15.432,22,16.386z M16.817,14.138c0-1.331-0.613-2.743-3.033-3.282 l-2.209-0.49c-0.84-0.192-1.807-0.444-1.807-1.237c0-0.794,0.679-1.348,1.903-1.348c2.468,0,2.243,1.696,3.468,1.696 c0.645,0,1.209-0.379,1.209-1.031c0-1.521-2.435-2.663-4.5-2.663c-2.242,0-4.63,0.952-4.63,3.488c0,1.221,0.436,2.521,2.839,3.123 l2.984,0.745c0.903,0.223,1.129,0.731,1.129,1.189c0,0.762-0.758,1.507-2.129,1.507c-2.679,0-2.307-2.062-3.743-2.062 c-0.645,0-1.113,0.444-1.113,1.078c0,1.236,1.501,2.886,4.856,2.886C15.236,17.737,16.817,16.199,16.817,14.138z"></path>
</svg>',
'slideshare' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M11.738,10.232a2.142,2.142,0,0,1-.721,1.619,2.556,2.556,0,0,1-3.464,0,2.183,2.183,0,0,1,0-3.243,2.572,2.572,0,0,1,3.464,0A2.136,2.136,0,0,1,11.738,10.232Zm5.7,0a2.15,2.15,0,0,1-.715,1.619,2.563,2.563,0,0,1-3.469,0,2.183,2.183,0,0,1,0-3.243,2.58,2.58,0,0,1,3.469,0A2.144,2.144,0,0,1,17.439,10.232Zm2.555,2.045V4.7a2.128,2.128,0,0,0-.363-1.4,1.614,1.614,0,0,0-1.261-.415H5.742a1.656,1.656,0,0,0-1.278.386A2.246,2.246,0,0,0,4.129,4.7v7.643a8.212,8.212,0,0,0,1,.454q.516.193.92.318a6.847,6.847,0,0,0,.92.21q.516.085.806.125a6.615,6.615,0,0,0,.795.045l.665.006q.16,0,.642-.023t.506-.023a1.438,1.438,0,0,1,1.079.307,1.134,1.134,0,0,0,.114.1,7.215,7.215,0,0,0,.693.579q.079-1.033,1.34-.988.057,0,.415.017l.488.023q.13.006.517.011t.6-.011l.619-.051a5.419,5.419,0,0,0,.693-.1l.7-.153a5.353,5.353,0,0,0,.761-.221q.345-.131.766-.307a8.727,8.727,0,0,0,.818-.392Zm1.851-.057a10.4,10.4,0,0,1-4.225,2.862,6.5,6.5,0,0,1-.261,5.281,3.524,3.524,0,0,1-2.078,1.681,2.452,2.452,0,0,1-2.067-.17,1.915,1.915,0,0,1-.931-1.863l-.011-3.7V16.3l-.279-.068q-.188-.045-.267-.057l-.011,3.839a1.9,1.9,0,0,1-.943,1.863,2.481,2.481,0,0,1-2.078.17,3.519,3.519,0,0,1-2.067-1.7,6.546,6.546,0,0,1-.25-5.258A10.4,10.4,0,0,1,2.152,12.22a.56.56,0,0,1-.045-.715q.238-.3.681.011l.125.079a.767.767,0,0,1,.125.091V3.8a1.987,1.987,0,0,1,.534-1.4,1.7,1.7,0,0,1,1.295-.579H19.141a1.7,1.7,0,0,1,1.295.579,1.985,1.985,0,0,1,.534,1.4v7.882l.238-.17q.443-.307.681-.011a.56.56,0,0,1-.045.715Z"></path>
</svg>',
'snapchat' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M12.065,2a5.526,5.526,0,0,1,3.132.892A5.854,5.854,0,0,1,17.326,5.4a5.821,5.821,0,0,1,.351,2.33q0,.612-.117,2.487a.809.809,0,0,0,.365.091,1.93,1.93,0,0,0,.664-.176,1.93,1.93,0,0,1,.664-.176,1.3,1.3,0,0,1,.729.234.7.7,0,0,1,.351.6.839.839,0,0,1-.41.7,2.732,2.732,0,0,1-.9.41,3.192,3.192,0,0,0-.9.378.728.728,0,0,0-.41.618,1.575,1.575,0,0,0,.156.56,6.9,6.9,0,0,0,1.334,1.953,5.6,5.6,0,0,0,1.881,1.315,5.875,5.875,0,0,0,1.042.3.42.42,0,0,1,.365.456q0,.911-2.852,1.341a1.379,1.379,0,0,0-.143.507,1.8,1.8,0,0,1-.182.605.451.451,0,0,1-.429.241,5.878,5.878,0,0,1-.807-.085,5.917,5.917,0,0,0-.833-.085,4.217,4.217,0,0,0-.807.065,2.42,2.42,0,0,0-.82.293,6.682,6.682,0,0,0-.755.5q-.351.267-.755.527a3.886,3.886,0,0,1-.989.436A4.471,4.471,0,0,1,11.831,22a4.307,4.307,0,0,1-1.256-.176,3.784,3.784,0,0,1-.976-.436q-.4-.26-.749-.527a6.682,6.682,0,0,0-.755-.5,2.422,2.422,0,0,0-.807-.293,4.432,4.432,0,0,0-.82-.065,5.089,5.089,0,0,0-.853.1,5,5,0,0,1-.762.1.474.474,0,0,1-.456-.241,1.819,1.819,0,0,1-.182-.618,1.411,1.411,0,0,0-.143-.521q-2.852-.429-2.852-1.341a.42.42,0,0,1,.365-.456,5.793,5.793,0,0,0,1.042-.3,5.524,5.524,0,0,0,1.881-1.315,6.789,6.789,0,0,0,1.334-1.953A1.575,1.575,0,0,0,6,12.9a.728.728,0,0,0-.41-.618,3.323,3.323,0,0,0-.9-.384,2.912,2.912,0,0,1-.9-.41.814.814,0,0,1-.41-.684.71.71,0,0,1,.338-.593,1.208,1.208,0,0,1,.716-.241,1.976,1.976,0,0,1,.625.169,2.008,2.008,0,0,0,.69.169.919.919,0,0,0,.416-.091q-.117-1.849-.117-2.474A5.861,5.861,0,0,1,6.385,5.4,5.516,5.516,0,0,1,8.625,2.819,7.075,7.075,0,0,1,12.062,2Z"></path>
</svg>',
'soundcloud' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M8.9,16.1L9,14L8.9,9.5c0-0.1,0-0.1-0.1-0.1c0,0-0.1-0.1-0.1-0.1c-0.1,0-0.1,0-0.1,0.1c0,0-0.1,0.1-0.1,0.1L8.3,14l0.1,2.1 c0,0.1,0,0.1,0.1,0.1c0,0,0.1,0.1,0.1,0.1C8.8,16.3,8.9,16.3,8.9,16.1z M11.4,15.9l0.1-1.8L11.4,9c0-0.1,0-0.2-0.1-0.2 c0,0-0.1,0-0.1,0s-0.1,0-0.1,0c-0.1,0-0.1,0.1-0.1,0.2l0,0.1l-0.1,5c0,0,0,0.7,0.1,2v0c0,0.1,0,0.1,0.1,0.1c0.1,0.1,0.1,0.1,0.2,0.1 c0.1,0,0.1,0,0.2-0.1c0.1,0,0.1-0.1,0.1-0.2L11.4,15.9z M2.4,12.9L2.5,14l-0.2,1.1c0,0.1,0,0.1-0.1,0.1c0,0-0.1,0-0.1-0.1L2.1,14 l0.1-1.1C2.2,12.9,2.3,12.9,2.4,12.9C2.3,12.9,2.4,12.9,2.4,12.9z M3.1,12.2L3.3,14l-0.2,1.8c0,0.1,0,0.1-0.1,0.1 c-0.1,0-0.1,0-0.1-0.1L2.8,14L3,12.2C3,12.2,3,12.2,3.1,12.2C3.1,12.2,3.1,12.2,3.1,12.2z M3.9,11.9L4.1,14l-0.2,2.1 c0,0.1,0,0.1-0.1,0.1c-0.1,0-0.1,0-0.1-0.1L3.5,14l0.2-2.1c0-0.1,0-0.1,0.1-0.1C3.9,11.8,3.9,11.8,3.9,11.9z M4.7,11.9L4.9,14 l-0.2,2.1c0,0.1-0.1,0.1-0.1,0.1c-0.1,0-0.1,0-0.1-0.1L4.3,14l0.2-2.2c0-0.1,0-0.1,0.1-0.1C4.7,11.7,4.7,11.8,4.7,11.9z M5.6,12 l0.2,2l-0.2,2.1c0,0.1-0.1,0.1-0.1,0.1c0,0-0.1,0-0.1,0c0,0,0-0.1,0-0.1L5.1,14l0.2-2c0,0,0-0.1,0-0.1s0.1,0,0.1,0 C5.5,11.9,5.5,11.9,5.6,12L5.6,12z M6.4,10.7L6.6,14l-0.2,2.1c0,0,0,0.1,0,0.1c0,0-0.1,0-0.1,0c-0.1,0-0.1-0.1-0.2-0.2L5.9,14 l0.2-3.3c0-0.1,0.1-0.2,0.2-0.2c0,0,0.1,0,0.1,0C6.4,10.7,6.4,10.7,6.4,10.7z M7.2,10l0.2,4.1l-0.2,2.1c0,0,0,0.1,0,0.1 c0,0-0.1,0-0.1,0c-0.1,0-0.2-0.1-0.2-0.2l-0.1-2.1L6.8,10c0-0.1,0.1-0.2,0.2-0.2c0,0,0.1,0,0.1,0S7.2,9.9,7.2,10z M8,9.6L8.2,14 L8,16.1c0,0.1-0.1,0.2-0.2,0.2c-0.1,0-0.2-0.1-0.2-0.2L7.5,14l0.1-4.4c0-0.1,0-0.1,0.1-0.1c0,0,0.1-0.1,0.1-0.1c0.1,0,0.1,0,0.1,0.1 C8,9.6,8,9.6,8,9.6z M11.4,16.1L11.4,16.1L11.4,16.1z M9.7,9.6L9.8,14l-0.1,2.1c0,0.1,0,0.1-0.1,0.2s-0.1,0.1-0.2,0.1 c-0.1,0-0.1,0-0.1-0.1s-0.1-0.1-0.1-0.2L9.2,14l0.1-4.4c0-0.1,0-0.1,0.1-0.2s0.1-0.1,0.2-0.1c0.1,0,0.1,0,0.2,0.1S9.7,9.5,9.7,9.6 L9.7,9.6z M10.6,9.8l0.1,4.3l-0.1,2c0,0.1,0,0.1-0.1,0.2c0,0-0.1,0.1-0.2,0.1c-0.1,0-0.1,0-0.2-0.1c0,0-0.1-0.1-0.1-0.2L10,14 l0.1-4.3c0-0.1,0-0.1,0.1-0.2c0,0,0.1-0.1,0.2-0.1c0.1,0,0.1,0,0.2,0.1S10.6,9.7,10.6,9.8z M12.4,14l-0.1,2c0,0.1,0,0.1-0.1,0.2 c-0.1,0.1-0.1,0.1-0.2,0.1c-0.1,0-0.1,0-0.2-0.1c-0.1-0.1-0.1-0.1-0.1-0.2l-0.1-1l-0.1-1l0.1-5.5v0c0-0.1,0-0.2,0.1-0.2 c0.1,0,0.1-0.1,0.2-0.1c0,0,0.1,0,0.1,0c0.1,0,0.1,0.1,0.1,0.2L12.4,14z M22.1,13.9c0,0.7-0.2,1.3-0.7,1.7c-0.5,0.5-1.1,0.7-1.7,0.7 h-6.8c-0.1,0-0.1,0-0.2-0.1c-0.1-0.1-0.1-0.1-0.1-0.2V8.2c0-0.1,0.1-0.2,0.2-0.3c0.5-0.2,1-0.3,1.6-0.3c1.1,0,2.1,0.4,2.9,1.1 c0.8,0.8,1.3,1.7,1.4,2.8c0.3-0.1,0.6-0.2,1-0.2c0.7,0,1.3,0.2,1.7,0.7C21.8,12.6,22.1,13.2,22.1,13.9L22.1,13.9z"></path>
</svg>',
'spotify' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M12,2C6.477,2,2,6.477,2,12c0,5.523,4.477,10,10,10c5.523,0,10-4.477,10-10C22,6.477,17.523,2,12,2 M16.586,16.424 c-0.18,0.295-0.563,0.387-0.857,0.207c-2.348-1.435-5.304-1.76-8.785-0.964c-0.335,0.077-0.67-0.133-0.746-0.469 c-0.077-0.335,0.132-0.67,0.469-0.746c3.809-0.871,7.077-0.496,9.713,1.115C16.673,15.746,16.766,16.13,16.586,16.424 M17.81,13.7 c-0.226,0.367-0.706,0.482-1.072,0.257c-2.687-1.652-6.785-2.131-9.965-1.166C6.36,12.917,5.925,12.684,5.8,12.273 C5.675,11.86,5.908,11.425,6.32,11.3c3.632-1.102,8.147-0.568,11.234,1.328C17.92,12.854,18.035,13.335,17.81,13.7 M17.915,10.865 c-3.223-1.914-8.54-2.09-11.618-1.156C5.804,9.859,5.281,9.58,5.131,9.086C4.982,8.591,5.26,8.069,5.755,7.919 c3.532-1.072,9.404-0.865,13.115,1.338c0.445,0.264,0.59,0.838,0.327,1.282C18.933,10.983,18.359,11.129,17.915,10.865"></path>
</svg>',
'stumbleupon' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M12,4.294c-2.469,0-4.471,2.002-4.471,4.471v6.353c0,0.585-0.474,1.059-1.059,1.059c-0.585,0-1.059-0.474-1.059-1.059 v-2.824H2v2.941c0,2.469,2.002,4.471,4.471,4.471c2.469,0,4.471-2.002,4.471-4.471V8.765c0-0.585,0.474-1.059,1.059-1.059 s1.059,0.474,1.059,1.059v1.294l1.412,0.647l2-0.647V8.765C16.471,6.296,14.469,4.294,12,4.294z M13.059,12.353v2.882 c0,2.469,2.002,4.471,4.471,4.471S22,17.704,22,15.235v-2.824h-3.412v2.824c0,0.585-0.474,1.059-1.059,1.059 c-0.585,0-1.059-0.474-1.059-1.059v-2.882l-2,0.647L13.059,12.353z"></path>
</svg>',
'tumblr' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M16.749,17.396c-0.357,0.17-1.041,0.319-1.551,0.332c-1.539,0.041-1.837-1.081-1.85-1.896V9.847h3.861V6.937h-3.847V2.039 c0,0-2.77,0-2.817,0c-0.046,0-0.127,0.041-0.138,0.144c-0.165,1.499-0.867,4.13-3.783,5.181v2.484h1.945v6.282 c0,2.151,1.587,5.206,5.775,5.135c1.413-0.024,2.982-0.616,3.329-1.126L16.749,17.396z"></path>
</svg>',
'twitch' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M16.499,8.089h-1.636v4.91h1.636V8.089z M12,8.089h-1.637v4.91H12V8.089z M4.228,3.178L3,6.451v13.092h4.499V22h2.456 l2.454-2.456h3.681L21,14.636V3.178H4.228z M19.364,13.816l-2.864,2.865H12l-2.453,2.453V16.68H5.863V4.814h13.501V13.816z"></path>
</svg>',
'twitter' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M22.23,5.924c-0.736,0.326-1.527,0.547-2.357,0.646c0.847-0.508,1.498-1.312,1.804-2.27 c-0.793,0.47-1.671,0.812-2.606,0.996C18.324,4.498,17.257,4,16.077,4c-2.266,0-4.103,1.837-4.103,4.103 c0,0.322,0.036,0.635,0.106,0.935C8.67,8.867,5.647,7.234,3.623,4.751C3.27,5.357,3.067,6.062,3.067,6.814 c0,1.424,0.724,2.679,1.825,3.415c-0.673-0.021-1.305-0.206-1.859-0.513c0,0.017,0,0.034,0,0.052c0,1.988,1.414,3.647,3.292,4.023 c-0.344,0.094-0.707,0.144-1.081,0.144c-0.264,0-0.521-0.026-0.772-0.074c0.522,1.63,2.038,2.816,3.833,2.85 c-1.404,1.1-3.174,1.756-5.096,1.756c-0.331,0-0.658-0.019-0.979-0.057c1.816,1.164,3.973,1.843,6.29,1.843 c7.547,0,11.675-6.252,11.675-11.675c0-0.178-0.004-0.355-0.012-0.531C20.985,7.47,21.68,6.747,22.23,5.924z"></path>
</svg>',
'vimeo' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M22.396,7.164c-0.093,2.026-1.507,4.799-4.245,8.32C15.322,19.161,12.928,21,10.97,21c-1.214,0-2.24-1.119-3.079-3.359 c-0.56-2.053-1.119-4.106-1.68-6.159C5.588,9.243,4.921,8.122,4.206,8.122c-0.156,0-0.701,0.328-1.634,0.98L1.594,7.841 c1.027-0.902,2.04-1.805,3.037-2.708C6.001,3.95,7.03,3.327,7.715,3.264c1.619-0.156,2.616,0.951,2.99,3.321 c0.404,2.557,0.685,4.147,0.841,4.769c0.467,2.121,0.981,3.181,1.542,3.181c0.435,0,1.09-0.688,1.963-2.065 c0.871-1.376,1.338-2.422,1.401-3.142c0.125-1.187-0.343-1.782-1.401-1.782c-0.498,0-1.012,0.115-1.541,0.341 c1.023-3.35,2.977-4.977,5.862-4.884C21.511,3.066,22.52,4.453,22.396,7.164z"></path>
</svg>',
'vk' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M22,7.1c0.2,0.4-0.4,1.5-1.6,3.1c-0.2,0.2-0.4,0.5-0.7,0.9c-0.5,0.7-0.9,1.1-0.9,1.4c-0.1,0.3-0.1,0.6,0.1,0.8 c0.1,0.1,0.4,0.4,0.8,0.9h0l0,0c1,0.9,1.6,1.7,2,2.3c0,0,0,0.1,0.1,0.1c0,0.1,0,0.1,0.1,0.3c0,0.1,0,0.2,0,0.4 c0,0.1-0.1,0.2-0.3,0.3c-0.1,0.1-0.4,0.1-0.6,0.1l-2.7,0c-0.2,0-0.4,0-0.6-0.1c-0.2-0.1-0.4-0.1-0.5-0.2l-0.2-0.1 c-0.2-0.1-0.5-0.4-0.7-0.7s-0.5-0.6-0.7-0.8c-0.2-0.2-0.4-0.4-0.6-0.6C14.8,15,14.6,15,14.4,15c0,0,0,0-0.1,0c0,0-0.1,0.1-0.2,0.2 c-0.1,0.1-0.2,0.2-0.2,0.3c-0.1,0.1-0.1,0.3-0.2,0.5c-0.1,0.2-0.1,0.5-0.1,0.8c0,0.1,0,0.2,0,0.3c0,0.1-0.1,0.2-0.1,0.2l0,0.1 c-0.1,0.1-0.3,0.2-0.6,0.2h-1.2c-0.5,0-1,0-1.5-0.2c-0.5-0.1-1-0.3-1.4-0.6s-0.7-0.5-1.1-0.7s-0.6-0.4-0.7-0.6l-0.3-0.3 c-0.1-0.1-0.2-0.2-0.3-0.3s-0.4-0.5-0.7-0.9s-0.7-1-1.1-1.6c-0.4-0.6-0.8-1.3-1.3-2.2C2.9,9.4,2.5,8.5,2.1,7.5C2,7.4,2,7.3,2,7.2 c0-0.1,0-0.1,0-0.2l0-0.1c0.1-0.1,0.3-0.2,0.6-0.2l2.9,0c0.1,0,0.2,0,0.2,0.1S5.9,6.9,5.9,7L6,7c0.1,0.1,0.2,0.2,0.3,0.3 C6.4,7.7,6.5,8,6.7,8.4C6.9,8.8,7,9,7.1,9.2l0.2,0.3c0.2,0.4,0.4,0.8,0.6,1.1c0.2,0.3,0.4,0.5,0.5,0.7s0.3,0.3,0.4,0.4 c0.1,0.1,0.3,0.1,0.4,0.1c0.1,0,0.2,0,0.3-0.1c0,0,0,0,0.1-0.1c0,0,0.1-0.1,0.1-0.2c0.1-0.1,0.1-0.3,0.1-0.5c0-0.2,0.1-0.5,0.1-0.8 c0-0.4,0-0.8,0-1.3c0-0.3,0-0.5-0.1-0.8c0-0.2-0.1-0.4-0.1-0.5L9.6,7.6C9.4,7.3,9.1,7.2,8.7,7.1C8.6,7.1,8.6,7,8.7,6.9 C8.9,6.7,9,6.6,9.1,6.5c0.4-0.2,1.2-0.3,2.5-0.3c0.6,0,1,0.1,1.4,0.1c0.1,0,0.3,0.1,0.3,0.1c0.1,0.1,0.2,0.1,0.2,0.3 c0,0.1,0.1,0.2,0.1,0.3s0,0.3,0,0.5c0,0.2,0,0.4,0,0.6c0,0.2,0,0.4,0,0.7c0,0.3,0,0.6,0,0.9c0,0.1,0,0.2,0,0.4c0,0.2,0,0.4,0,0.5 c0,0.1,0,0.3,0,0.4s0.1,0.3,0.1,0.4c0.1,0.1,0.1,0.2,0.2,0.3c0.1,0,0.1,0,0.2,0c0.1,0,0.2,0,0.3-0.1c0.1-0.1,0.2-0.2,0.4-0.4 s0.3-0.4,0.5-0.7c0.2-0.3,0.5-0.7,0.7-1.1c0.4-0.7,0.8-1.5,1.1-2.3c0-0.1,0.1-0.1,0.1-0.2c0-0.1,0.1-0.1,0.1-0.1l0,0l0.1,0 c0,0,0,0,0.1,0s0.2,0,0.2,0l3,0c0.3,0,0.5,0,0.7,0S21.9,7,21.9,7L22,7.1z"></path>
</svg>',
'wordpress' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M12.158,12.786L9.46,20.625c0.806,0.237,1.657,0.366,2.54,0.366c1.047,0,2.051-0.181,2.986-0.51 c-0.024-0.038-0.046-0.079-0.065-0.124L12.158,12.786z M3.009,12c0,3.559,2.068,6.634,5.067,8.092L3.788,8.341 C3.289,9.459,3.009,10.696,3.009,12z M18.069,11.546c0-1.112-0.399-1.881-0.741-2.48c-0.456-0.741-0.883-1.368-0.883-2.109 c0-0.826,0.627-1.596,1.51-1.596c0.04,0,0.078,0.005,0.116,0.007C16.472,3.904,14.34,3.009,12,3.009 c-3.141,0-5.904,1.612-7.512,4.052c0.211,0.007,0.41,0.011,0.579,0.011c0.94,0,2.396-0.114,2.396-0.114 C7.947,6.93,8.004,7.642,7.52,7.699c0,0-0.487,0.057-1.029,0.085l3.274,9.739l1.968-5.901l-1.401-3.838 C9.848,7.756,9.389,7.699,9.389,7.699C8.904,7.67,8.961,6.93,9.446,6.958c0,0,1.484,0.114,2.368,0.114 c0.94,0,2.397-0.114,2.397-0.114c0.485-0.028,0.542,0.684,0.057,0.741c0,0-0.488,0.057-1.029,0.085l3.249,9.665l0.897-2.996 C17.841,13.284,18.069,12.316,18.069,11.546z M19.889,7.686c0.039,0.286,0.06,0.593,0.06,0.924c0,0.912-0.171,1.938-0.684,3.22 l-2.746,7.94c2.673-1.558,4.47-4.454,4.47-7.771C20.991,10.436,20.591,8.967,19.889,7.686z M12,22C6.486,22,2,17.514,2,12 C2,6.486,6.486,2,12,2c5.514,0,10,4.486,10,10C22,17.514,17.514,22,12,22z"></path>
</svg>',
'yelp' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M12.271,16.718v1.417q-.011,3.257-.067,3.4a.707.707,0,0,1-.569.446,4.637,4.637,0,0,1-2.024-.424A4.609,4.609,0,0,1,7.8,20.565a.844.844,0,0,1-.19-.4.692.692,0,0,1,.044-.29,3.181,3.181,0,0,1,.379-.524q.335-.412,2.019-2.409.011,0,.669-.781a.757.757,0,0,1,.44-.274.965.965,0,0,1,.552.039.945.945,0,0,1,.418.324.732.732,0,0,1,.139.468Zm-1.662-2.8a.783.783,0,0,1-.58.781l-1.339.435q-3.067.981-3.257.981a.711.711,0,0,1-.6-.4,2.636,2.636,0,0,1-.19-.836,9.134,9.134,0,0,1,.011-1.857,3.559,3.559,0,0,1,.335-1.389.659.659,0,0,1,.625-.357,22.629,22.629,0,0,1,2.253.859q.781.324,1.283.524l.937.379a.771.771,0,0,1,.4.34A.982.982,0,0,1,10.609,13.917Zm9.213,3.313a4.467,4.467,0,0,1-1.021,1.8,4.559,4.559,0,0,1-1.512,1.417.671.671,0,0,1-.7-.078q-.156-.112-2.052-3.2l-.524-.859a.761.761,0,0,1-.128-.513.957.957,0,0,1,.217-.513.774.774,0,0,1,.926-.29q.011.011,1.327.446,2.264.736,2.7.887a2.082,2.082,0,0,1,.524.229.673.673,0,0,1,.245.68Zm-7.5-7.049q.056,1.137-.6,1.361-.647.19-1.272-.792L6.237,4.08a.7.7,0,0,1,.212-.691,5.788,5.788,0,0,1,2.314-1,5.928,5.928,0,0,1,2.5-.352.681.681,0,0,1,.547.5q.034.2.245,3.407T12.327,10.181Zm7.384,1.2a.679.679,0,0,1-.29.658q-.167.112-3.67.959-.747.167-1.015.257l.011-.022a.769.769,0,0,1-.513-.044.914.914,0,0,1-.413-.357.786.786,0,0,1,0-.971q.011-.011.836-1.137,1.394-1.908,1.673-2.275a2.423,2.423,0,0,1,.379-.435A.7.7,0,0,1,17.435,8a4.482,4.482,0,0,1,1.372,1.489,4.81,4.81,0,0,1,.9,1.868v.034Z"></path>
</svg>',
'youtube' => '
<svg viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<path d="M21.8,8.001c0,0-0.195-1.378-0.795-1.985c-0.76-0.797-1.613-0.801-2.004-0.847c-2.799-0.202-6.997-0.202-6.997-0.202 h-0.009c0,0-4.198,0-6.997,0.202C4.608,5.216,3.756,5.22,2.995,6.016C2.395,6.623,2.2,8.001,2.2,8.001S2,9.62,2,11.238v1.517 c0,1.618,0.2,3.237,0.2,3.237s0.195,1.378,0.795,1.985c0.761,0.797,1.76,0.771,2.205,0.855c1.6,0.153,6.8,0.201,6.8,0.201 s4.203-0.006,7.001-0.209c0.391-0.047,1.243-0.051,2.004-0.847c0.6-0.607,0.795-1.985,0.795-1.985s0.2-1.618,0.2-3.237v-1.517 C22,9.62,21.8,8.001,21.8,8.001z M9.935,14.594l-0.001-5.62l5.404,2.82L9.935,14.594z"></path>
</svg>',
);
}
class-view.php 0000666 00000036373 15112004103 0007335 0 ustar 00 <?php
/**
* TablePress Base View with members and methods for all views
*
* @package TablePress
* @subpackage Views
* @author Tobias Bäthge
* @since 1.0.0
*/
// Prohibit direct script loading.
defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
/**
* TablePress Base View class
* @package TablePress
* @subpackage Views
* @author Tobias Bäthge
* @since 1.0.0
*/
abstract class TablePress_View {
/**
* Data for the view.
*
* @since 1.0.0
* @var array
*/
protected $data = array();
/**
* Number of screen columns for post boxes.
*
* @since 1.0.0
* @var int
*/
protected $screen_columns = 0;
/**
* User action for this screen.
*
* @since 1.0.0
* @var string
*/
protected $action = '';
/**
* Instance of the Admin Page Helper Class, with necessary functions.
*
* @since 1.0.0
* @var TablePress_Admin_Page
*/
protected $admin_page;
/**
* List of text boxes (similar to post boxes, but just with text and without extra functionality).
*
* @since 1.0.0
* @var array
*/
protected $textboxes = array();
/**
* List of messages that are to be displayed as boxes below the page title.
*
* @since 1.0.0
* @var array
*/
protected $header_messages = array();
/**
* Whether there are post boxes registered for this screen,
* is automatically set to true, when a meta box is added.
*
* @since 1.0.0
* @var bool
*/
protected $has_meta_boxes = false;
/**
* List of WP feature pointers for this view.
*
* @since 1.0.0
* @var array
*/
protected $wp_pointers = array();
/**
* Initialize the View class, by setting the correct screen columns and adding help texts.
*
* @since 1.0.0
*/
public function __construct() {
$screen = get_current_screen();
if ( 0 !== $this->screen_columns ) {
$screen->add_option( 'layout_columns', array( 'max' => $this->screen_columns ) );
}
// Enable two column layout.
add_filter( "get_user_option_screen_layout_{$screen->id}", array( $this, 'set_current_screen_layout_columns' ) );
$screen->add_help_tab( array(
'id' => 'tablepress-help', // This should be unique for the screen.
'title' => __( 'TablePress Help', 'tablepress' ),
'content' => '<p>' . $this->help_tab_content() . '</p>'
. '<p>' . sprintf( __( 'More information about TablePress can be found on the <a href="%1$s">plugin’s website</a> or on its page in the <a href="%2$s">WordPress Plugin Directory</a>.', 'tablepress' ), 'https://tablepress.org/', 'https://wordpress.org/plugins/tablepress/' ) . ' '
. sprintf( __( 'For technical information, please see the <a href="%s">documentation</a>.', 'tablepress' ), 'https://tablepress.org/documentation/' ) . ' '
. sprintf( __( '<a href="%1$s">Support</a> is provided through the <a href="%2$s">WordPress Support Forums</a>.', 'tablepress' ), 'https://tablepress.org/support/', 'https://wordpress.org/tags/tablepress' ) . ' '
. sprintf( __( 'Before asking for support, please carefully read the <a href="%s">Frequently Asked Questions</a>, where you will find answers to the most common questions, and search through the forums.', 'tablepress' ), 'https://tablepress.org/faq/' ) . '<br />'
. sprintf( __( 'If you like the plugin, <a href="%1$s"><strong>a donation</strong></a> is recommended.', 'tablepress' ), 'https://tablepress.org/donate/' ) . '</p>',
) );
// "sidebar" in the help tab.
$screen->set_help_sidebar( '<p><strong>' . __( 'For more information:', 'tablepress' ) . '</strong></p><p><a href="https://tablepress.org/" target="_blank">TablePress Website</a></p><p><a href="https://tablepress.org/faq/" target="_blank">TablePress FAQ</a></p><p><a href="https://tablepress.org/documentation/" target="_blank">TablePress Documentation</a></p><p><a href="https://tablepress.org/support/" target="_blank">TablePress Support</a></p>' );
}
/**
* Change the value of the user option "screen_layout_{$screen->id}" through a filter.
*
* @since 1.0.0
*
* @param int|bool Current value of the user option.
* @return int New value for the user option.
*/
public function set_current_screen_layout_columns( $result ) {
if ( false === $result ) {
// The user option does not yet exist.
$result = $this->screen_columns;
} elseif ( $result > $this->screen_columns ) {
// The value of the user option is bigger than what is possible on this screen (e.g. because the number of columns was reduced in an update).
$result = $this->screen_columns;
}
return $result;
}
/**
* Set up the view with data and do things that are necessary for all views.
*
* @since 1.0.0
*
* @param string $action Action for this view.
* @param array $data Data for this view.
*/
public function setup( $action, array $data ) {
$this->action = $action;
$this->data = $data;
// Set page title.
$GLOBALS['title'] = sprintf( __( '%1$s ‹ %2$s', 'tablepress' ), $this->data['view_actions'][ $this->action ]['page_title'], 'TablePress' );
// Admin page helpers, like script/style loading, could be moved to view.
$this->admin_page = TablePress::load_class( 'TablePress_Admin_Page', 'class-admin-page-helper.php', 'classes' );
$this->admin_page->enqueue_style( 'common' );
// RTL styles for the admin interface.
if ( is_rtl() ) {
$this->admin_page->enqueue_style( 'common-rtl', array( 'tablepress-common' ) );
}
$this->admin_page->enqueue_script( 'common', array( 'jquery-core', 'postbox' ), array(
'common' => array(
'ays_delete_single_table' => _n( 'Do you really want to delete this table?', 'Do you really want to delete these tables?', 1, 'tablepress' ),
'ays_delete_multiple_tables' => _n( 'Do you really want to delete this table?', 'Do you really want to delete these tables?', 2, 'tablepress' ),
)
) );
$this->admin_page->add_admin_footer_text();
// Initialize WP feature pointers for TablePress.
$this->_init_wp_pointers();
// Necessary fields for all views.
$this->add_text_box( 'default_nonce_fields', array( $this, 'default_nonce_fields' ), 'header', false );
$this->add_text_box( 'action_nonce_field', array( $this, 'action_nonce_field' ), 'header', false );
$this->add_text_box( 'action_field', array( $this, 'action_field' ), 'header', false );
}
/**
* Register a header message for the view.
*
* @since 1.0.0
*
* @param string $text Text for the header message.
* @param string $class Optional. Additional CSS class for the header message.
*/
protected function add_header_message( $text, $class = 'notice-success' ) {
if ( ! stripos( $class, 'not-dismissible' ) ) {
$class .= ' is-dismissible';
}
$this->header_messages[] = "<div class=\"notice {$class}\"><p>{$text}</p></div>\n";
}
/**
* Process header action messages, i.e. check if a message should be added to the page.
*
* @since 1.0.0
*
* @param array $action_messages Action messages for the screen.
*/
protected function process_action_messages( array $action_messages ) {
if ( $this->data['message'] && isset( $action_messages[ $this->data['message'] ] ) ) {
$class = ( 'error' === substr( $this->data['message'], 0, 5 ) ) ? 'notice-error' : 'notice-success';
$this->add_header_message( "<strong>{$action_messages[ $this->data['message'] ]}</strong>", $class );
}
}
/**
* Register a text box for the view.
*
* @since 1.0.0
*
* @param string $id Unique HTML ID for the text box container (only visible with $wrap = true).
* @param callback $callback Callback that prints the contents of the text box.
* @param string $context Optional. Context/position of the text box (normal, side, additional, header, submit).
* @param bool $ wrap Whether the content of the text box shall be wrapped in a <div> container.
*/
protected function add_text_box( $id, $callback, $context = 'normal', $wrap = false ) {
if ( ! isset( $this->textboxes[ $context ] ) ) {
$this->textboxes[ $context ] = array();
}
$long_id = "tablepress_{$this->action}-{$id}";
$this->textboxes[ $context ][ $id ] = array(
'id' => $long_id,
'callback' => $callback,
'context' => $context,
'wrap' => $wrap,
);
}
/**
* Register a post meta box for the view, that is drag/droppable with WordPress functionality.
*
* @since 1.0.0
*
* @param string $id Unique ID for the meta box.
* @param string $title Title for the meta box.
* @param callback $callback Callback that prints the contents of the post meta box.
* @param string $context Optional. Context/position of the post meta box (normal, side, additional).
* @param string $priority Optional. Order of the post meta box for the $context position (high, default, low).
* @param bool $callback_args Optional. Additional data for the callback function (e.g. useful when in different class).
*/
protected function add_meta_box( $id, $title, $callback, $context = 'normal', $priority = 'default', $callback_args = null ) {
$this->has_meta_boxes = true;
add_meta_box( "tablepress_{$this->action}-{$id}", $title, $callback, null, $context, $priority, $callback_args );
}
/**
* Render all text boxes for the given context.
*
* @since 1.0.0
*
* @param string $context Context (normal, side, additional, header, submit) for which registered text boxes shall be rendered.
*/
protected function do_text_boxes( $context ) {
if ( empty( $this->textboxes[ $context ] ) ) {
return;
}
foreach ( $this->textboxes[ $context ] as $box ) {
if ( $box['wrap'] ) {
echo "<div id=\"{$box['id']}\" class=\"textbox\">\n";
}
call_user_func( $box['callback'], $this->data, $box );
if ( $box['wrap'] ) {
echo "</div>\n";
}
}
}
/**
* Render all post meta boxes for the given context, if there are post meta boxes.
*
* @since 1.0.0
*
* @param string $context Context (normal, side, additional) for which registered post meta boxes shall be rendered.
*/
protected function do_meta_boxes( $context ) {
if ( ! $this->has_meta_boxes ) {
return;
}
do_meta_boxes( null, $context, $this->data );
}
/**
* Print hidden fields with nonces for post meta box AJAX handling, if there are post meta boxes on the screen.
*
* The check is possible as this function is executed after post meta boxes have to be registered.
*
* @since 1.0.0
*
* @param array $data Data for this screen.
* @param array $box Information about the text box.
*/
protected function default_nonce_fields( array $data, array $box ) {
if ( ! $this->has_meta_boxes ) {
return;
}
wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
echo "\n";
wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false );
echo "\n";
}
/**
* Print hidden field with a nonce for the screen's action, to be transmitted in HTTP requests.
*
* @since 1.0.0
*
* @param array $data Data for this screen.
* @param array $box Information about the text box.
*/
protected function action_nonce_field( array $data, array $box ) {
wp_nonce_field( TablePress::nonce( $this->action ) );
echo "\n";
}
/**
* Print hidden field with the screen action.
*
* @since 1.0.0
*
* @param array $data Data for this screen.
* @param array $box Information about the text box.
*/
protected function action_field( array $data, array $box ) {
echo "<input type=\"hidden\" name=\"action\" value=\"tablepress_{$this->action}\" />\n";
}
/**
* Render the current view.
*
* @since 1.0.0
*/
public function render() {
?>
<div id="tablepress-page" class="wrap">
<?php
$this->print_nav_tab_menu();
// Print all header messages.
foreach ( $this->header_messages as $message ) {
echo $message;
}
// "Import" screen has file upload.
$enctype = ( 'import' === $this->action ) ? ' enctype="multipart/form-data"' : '';
?>
<form action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" method="post"<?php echo $enctype; ?>>
<?php
$this->do_text_boxes( 'header' );
?>
<div id="poststuff">
<div id="post-body" class="metabox-holder columns-<?php echo ( isset( $GLOBALS['screen_layout_columns'] ) && ( 2 === $GLOBALS['screen_layout_columns'] ) ) ? '2' : '1'; ?>">
<div id="postbox-container-2" class="postbox-container">
<?php
$this->do_text_boxes( 'normal' );
$this->do_meta_boxes( 'normal' );
$this->do_text_boxes( 'additional' );
$this->do_meta_boxes( 'additional' );
// Print all submit buttons.
$this->do_text_boxes( 'submit' );
?>
</div>
<div id="postbox-container-1" class="postbox-container">
<?php
// Print all boxes in the sidebar.
$this->do_text_boxes( 'side' );
$this->do_meta_boxes( 'side' );
?>
</div>
</div>
<br class="clear" />
</div>
</form>
</div>
<?php
}
/**
* Render the navigation menu with links to the possible actions, highlighting the current one.
*
* @since 1.0.0
*/
protected function print_nav_tab_menu() {
?>
<div id="tablepress-nav" class="nav-tab-wrapper">
<h1 class="wp-heading-inline"><?php _e( 'TablePress', 'tablepress' ); ?></h1>
<?php
foreach ( $this->data['view_actions'] as $action => $entry ) {
if ( '' === $entry['nav_tab_title'] ) {
continue;
}
if ( ! current_user_can( $entry['required_cap'] ) ) {
continue;
}
$url = esc_url( TablePress::url( array( 'action' => $action ) ) );
$active = ( $action === $this->action ) ? ' nav-tab-active' : '';
$separator = ( 'options' === $action ) ? ' nav-tab-separator' : ''; // Make the "Plugin Options" entry a separator, for some spacing.
echo "<a class=\"nav-tab{$active}{$separator}\" href=\"{$url}\">{$entry['nav_tab_title']}</a>";
}
?>
</div><hr class="wp-header-end" />
<?php
}
/**
* Print a submit button (only done when function is used as a callback for a text box).
*
* @since 1.0.0
*
* @param array $data Data for this screen.
* @param array $box Information about the text box.
*/
protected function textbox_submit_button( array $data, array $box ) {
$caption = isset( $data['submit_button_caption'] ) ? $data['submit_button_caption'] : __( 'Save Changes', 'tablepress' );
?>
<p class="submit"><input type="submit" value="<?php echo esc_attr( $caption ); ?>" class="button button-primary button-large" name="submit" /></p>
<?php
}
/**
* Return the content for the help tab for this screen.
*
* Has to be implemented for every view that is visible in the WP Dashboard!
*
* @since 1.0.0
*/
protected function help_tab_content() {
// Has to be implemented for every view that is visible in the WP Dashboard!
return '';
}
/**
* Initialize the WP feature pointers for TablePress.
*
* @since 1.0.0
*/
protected function _init_wp_pointers() {
// Check if there are WP pointers for this view.
if ( empty( $this->wp_pointers ) ) {
return;
}
// Get dismissed pointers.
$dismissed = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) );
$got_pointers = false;
foreach ( array_diff( $this->wp_pointers, $dismissed ) as $pointer ) {
// Bind pointer print function.
add_action( "admin_footer-{$GLOBALS['hook_suffix']}", array( $this, 'wp_pointer_' . $pointer ) );
$got_pointers = true;
}
if ( ! $got_pointers ) {
return;
}
// Add pointers script and style to queue.
wp_enqueue_style( 'wp-pointer' );
wp_enqueue_script( 'wp-pointer' );
}
} // class TablePress_View
class-wp_option.php 0000666 00000007774 15112004103 0010404 0 ustar 00 <?php
/**
* TablePress WP Option Wrapper class for WordPress Options
*
* Wraps the WordPress Options API, so that (especially) arrays are stored as JSON, instead of being serialized by PHP.
*
* @package TablePress
* @subpackage Classes
* @author Tobias Bäthge
* @since 1.0.0
*/
// Prohibit direct script loading.
defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
/**
* TablePress WP Option Wrapper class
* @package TablePress
* @subpackage Classes
* @author Tobias Bäthge
* @since 1.0.0
*/
class TablePress_WP_Option {
/**
* Name/Key of the Option (in its location in the database).
*
* @since 1.0.0
* @var string
*/
protected $option_name;
/**
* Current value of the option.
*
* @since 1.0.0
* @var array
*/
protected $option_value;
/**
* Initialize with Option Name.
*
* @since 1.0.0
*
* @param array $params {
* @type string $option_name Name of the Option.
* @type array $default_value Default values for the Option.
* }
*/
public function __construct( array $params ) {
$this->option_name = $params['option_name'];
$option_value = $this->_get_option( $this->option_name, null );
if ( ! is_null( $option_value ) ) {
$this->option_value = (array) json_decode( $option_value, true );
} else {
$this->option_value = $params['default_value'];
}
}
/**
* Check if Option is set.
*
* @since 1.0.0
*
* @param string $name Name of the option to check.
* @return bool Whether the option is set.
*/
public function is_set( $name ) {
return isset( $this->option_value[ $name ] );
}
/**
* Get a single Option, or get all Options.
*
* @since 1.0.0
*
* @param string|bool $name Optional. Name of a single option to get, or false for all options.
* @param mixed $default_value Optional. Default value to return, if a single option $name does not exist.
* @return mixed|array Value of the retrieved option $name or $default_value if it does not exist, or all options.
*/
public function get( $name = false, $default_value = null ) {
if ( false === $name ) {
return $this->option_value;
}
// Single Option wanted.
if ( isset( $this->option_value[ $name ] ) ) {
return $this->option_value[ $name ];
} else {
return $default_value;
}
}
/**
* Update Option.
*
* @since 1.0.0
*
* @param array $new_options New options (name => value).
* @return bool True on success, false on failure.
*/
public function update( array $new_options ) {
$this->option_value = $new_options;
return $this->_update_option( $this->option_name, wp_json_encode( $this->option_value, TABLEPRESS_JSON_OPTIONS ) );
}
/**
* Delete Option.
*
* @since 1.0.0
*
* @return bool True on success, false on failure.
*/
public function delete() {
return $this->_delete_option( $this->option_name );
}
/**
* Internal functions mapping - This needs to be re-defined by child classes.
*/
/**
* Get the value of a WP Option with the WP Options API.
*
* @since 1.0.0
*
* @param string $option_name Name of the WP Option.
* @param mixed $default_value Default value of the WP Option.
* @return mixed Current value of the WP Option, or $default_value if it does not exist.
*/
protected function _get_option( $option_name, $default_value ) {
return get_option( $option_name, $default_value );
}
/**
* Update the value of a WP Option with the WP Options API.
*
* @since 1.0.0
*
* @param string $option_name Name of the WP Option.
* @param string $new_value New value of the WP Option.
* @return bool True on success, false on failure.
*/
protected function _update_option( $option_name, $new_value ) {
return update_option( $option_name, $new_value );
}
/**
* Delete a WP Option with the WP Options API.
*
* @since 1.0.0
*
* @param string $option_name Name of the WP Option.
* @return bool True on success, false on failure.
*/
protected function _delete_option( $option_name ) {
return delete_option( $option_name );
}
} // class TablePress_WP_Option
class-wp_user_option.php 0000666 00000005606 15112004103 0011432 0 ustar 00 <?php
/**
* TablePress WP User Option Wrapper class for WordPress Options
*
* Wraps the WordPress Options API, so that (especially) arrays are stored as JSON, instead of being serialized by PHP.
*
* @package TablePress
* @subpackage Classes
* @author Tobias Bäthge
* @since 1.0.0
*/
// Prohibit direct script loading.
defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
// Load parent class.
TablePress::load_file( 'class-wp_option.php', 'classes' );
/**
* TablePress WP User Option Wrapper class
* @package TablePress
* @subpackage Classes
* @author Tobias Bäthge
* @since 1.0.0
*/
class TablePress_WP_User_Option extends TablePress_WP_Option {
/**
* Get the value of a WP User Option with the WP User Options API.
*
* @since 1.0.0
*
* @param string $option_name Name of the WP User Option.
* @param mixed $default_value Default value of the WP User Option.
* @return mixed Current value of the WP User Option, or $default_value if it does not exist.
*/
protected function _get_option( $option_name, $default_value ) {
// Non-logged-in user can never have a saved option value.
if ( ! is_user_logged_in() ) {
return $default_value;
}
$option_value = get_user_option( $option_name );
// get_user_option() only knows false as the default value, so we have to wrap that.
if ( false === $option_value ) {
$option_value = $default_value;
}
return $option_value;
}
/**
* Update the value of a WP User Option with the WP User Options API.
*
* @since 1.0.0
*
* @param string $option_name Name of the WP User Option.
* @param string $new_value New value of the WP User Option (not slashed).
* @return bool True on success, false on failure.
*/
protected function _update_option( $option_name, $new_value ) {
// Non-logged-in user can never have a saved option value to be updated.
if ( ! is_user_logged_in() ) {
return false;
}
// WP expects a slashed value.
$new_value = wp_slash( $new_value );
return update_user_option( get_current_user_id(), $option_name, $new_value, false );
}
/**
* Delete a WP User Option with the WP User Options API.
*
* @since 1.0.0
*
* @param string $option_name Name of the WP User Option.
* @return bool True on success, false on failure.
*/
protected function _delete_option( $option_name ) {
// Non-logged-in user can never have a saved option value to be deleted.
if ( ! is_user_logged_in() ) {
return false;
}
return delete_user_option( get_current_user_id(), $option_name, false );
}
/**
* Delete a WP User Option with the WP User Options API, for all users of the site.
*
* @since 1.0.0
*/
public function delete_for_all_users() {
$users = get_users();
foreach ( $users as $user ) {
delete_user_option( $user->ID, $this->option_name, false );
// @TODO: Add other user options for different actions as well.
}
}
} // class TablePress_WP_User_Option
class-import.php 0000666 00000033766 15112004103 0007700 0 ustar 00 <?php
/**
* TablePress Table Import Class
*
* @package TablePress
* @subpackage Export/Import
* @author Tobias Bäthge
* @since 1.0.0
*/
// Prohibit direct script loading.
defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
/**
* TablePress Table Import Class
* @package TablePress
* @subpackage Export/Import
* @author Tobias Bäthge
* @since 1.0.0
*/
class TablePress_Import {
/**
* File/Data Formats that are available for import.
*
* @since 1.0.0
* @var array
*/
public $import_formats = array();
/**
* Whether ZIP archive support is available in the PHP installation on the server.
*
* @since 1.0.0
* @var bool
*/
public $zip_support_available = false;
/**
* Whether HTML import support is available in the PHP installation on the server.
*
* @since 1.0.0
* @var bool
*/
public $html_import_support_available = false;
/**
* Data to be imported.
*
* @since 1.0.0
* @var string
*/
protected $import_data;
/**
* Imported table.
*
* @since 1.0.0
* @var array
*/
protected $imported_table = false;
/**
* Initialize the Import class.
*
* @since 1.0.0
*/
public function __construct() {
/** This filter is documented in the WordPress function unzip_file() in wp-admin/includes/file.php */
if ( class_exists( 'ZipArchive', false ) && apply_filters( 'unzip_file_use_ziparchive', true ) ) {
$this->zip_support_available = true;
}
if ( class_exists( 'DOMDocument', false ) && function_exists( 'simplexml_import_dom' ) && function_exists( 'libxml_use_internal_errors' ) ) {
$this->html_import_support_available = true;
}
// Initiate here, because function call not possible outside a class method.
$this->import_formats = array();
$this->import_formats['csv'] = __( 'CSV - Character-Separated Values', 'tablepress' );
if ( $this->html_import_support_available ) {
$this->import_formats['html'] = __( 'HTML - Hypertext Markup Language', 'tablepress' );
}
$this->import_formats['json'] = __( 'JSON - JavaScript Object Notation', 'tablepress' );
$this->import_formats['xls'] = __( 'XLS - Microsoft Excel 97-2003 (experimental)', 'tablepress' );
$this->import_formats['xlsx'] = __( 'XLSX - Microsoft Excel 2007-2013 (experimental)', 'tablepress' );
}
/**
* Import a table.
*
* @since 1.0.0
*
* @param string $format Import format.
* @param string $data Data to import.
* @return bool|array False on error, table array on success.
*/
public function import_table( $format, $data ) {
$this->import_data = apply_filters( 'tablepress_import_table_data', $data, $format );
if ( ! in_array( $format, array( 'xlsx', 'xls' ) ) ) {
$this->fix_table_encoding();
}
switch ( $format ) {
case 'csv':
$this->import_csv();
break;
case 'html':
$this->import_html();
break;
case 'json':
$this->import_json();
break;
case 'xlsx':
$this->import_xlsx();
break;
case 'xls':
$this->import_xls();
break;
default:
return false;
}
return $this->imported_table;
}
/**
* Import CSV data.
*
* @since 1.0.0
*/
protected function import_csv() {
$csv_parser = TablePress::load_class( 'CSV_Parser', 'csv-parser.class.php', 'libraries' );
$csv_parser->load_data( $this->import_data );
$delimiter = $csv_parser->find_delimiter();
$data = $csv_parser->parse( $delimiter );
$this->imported_table = array( 'data' => $this->pad_array_to_max_cols( $data ) );
}
/**
* Import HTML data.
*
* @since 1.0.0
*/
protected function import_html() {
if ( ! $this->html_import_support_available ) {
return false;
}
if ( false === stripos( $this->import_data, '<table' ) || false === stripos( $this->import_data, '</table>' ) ) {
$this->imported_table = false;
return;
}
// Prepend XML declaration, for better encoding support.
$full_html = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' . $this->import_data;
if ( function_exists( 'libxml_disable_entity_loader' ) ) {
// Don't expand external entities, see https://websec.io/2012/08/27/Preventing-XXE-in-PHP.html.
libxml_disable_entity_loader( true );
}
// No warnings/errors raised, but stored internally.
libxml_use_internal_errors( true );
$dom = new DOMDocument( '1.0', 'UTF-8' );
// No strict checking for invalid HTML.
$dom->strictErrorChecking = false;
$dom->loadHTML( $full_html );
if ( false === $dom ) {
$this->imported_table = false;
return;
}
$dom_tables = $dom->getElementsByTagName( 'table' );
if ( 0 === count( $dom_tables ) ) {
$this->imported_table = false;
return;
}
libxml_clear_errors(); // Clear errors so that we only catch those inside the table in the next line.
$table = simplexml_import_dom( $dom_tables->item( 0 ) );
if ( false === $table ) {
$this->imported_table = false;
return;
}
$errors = libxml_get_errors();
libxml_clear_errors();
if ( ! empty( $errors ) ) {
$output = '<strong>' . __( 'The imported file contains errors:', 'tablepress' ) . '</strong><br /><br />';
foreach ( $errors as $error ) {
switch ( $error->level ) {
case LIBXML_ERR_WARNING:
$output .= "Warning {$error->code}: {$error->message} in line {$error->line}, column {$error->column}<br />";
break;
case LIBXML_ERR_ERROR:
$output .= "Error {$error->code}: {$error->message} in line {$error->line}, column {$error->column}<br />";
break;
case LIBXML_ERR_FATAL:
$output .= "Fatal Error {$error->code}: {$error->message} in line {$error->line}, column {$error->column}<br />";
break;
}
}
wp_die( $output, 'Import Error', array( 'response' => 200, 'back_link' => true ) );
}
$html_table = array(
'data' => array(),
'options' => array(),
);
if ( isset( $table->thead ) ) {
$html_table['data'] = array_merge( $html_table['data'], $this->_import_html_rows( $table->thead[0]->tr ) );
$html_table['options']['table_head'] = true;
}
if ( isset( $table->tbody ) ) {
$html_table['data'] = array_merge( $html_table['data'], $this->_import_html_rows( $table->tbody[0]->tr ) );
}
if ( isset( $table->tr ) ) {
$html_table['data'] = array_merge( $html_table['data'], $this->_import_html_rows( $table->tr ) );
}
if ( isset( $table->tfoot ) ) {
$html_table['data'] = array_merge( $html_table['data'], $this->_import_html_rows( $table->tfoot[0]->tr ) );
$html_table['options']['table_foot'] = true;
}
$html_table['data'] = $this->pad_array_to_max_cols( $html_table['data'] );
$this->imported_table = $html_table;
}
/**
* Helper for HTML import.
*
* @since 1.0.0
*
* @param SimpleXMLElement $element XMLElement.
* @return array SimpleXMLElement exported to an array.
*/
protected function _import_html_rows( $element ) {
$rows = array();
foreach ( $element as $row ) {
$new_row = array();
foreach ( $row as $cell ) {
// Get text between <td>...</td>, or <th>...</th>, possibly with attributes.
if ( 1 === preg_match( '#<t(?:d|h).*?>(.*)</t(?:d|h)>#is', $cell->asXML(), $matches ) ) {
/*
* Decode HTML entities again, as there might be some left especially in attributes of HTML tags in the cells,
* see https://secure.php.net/manual/en/simplexmlelement.asxml.php#107137.
*/
$matches[1] = html_entity_decode( $matches[1], ENT_NOQUOTES, 'UTF-8' );
$new_row[] = $matches[1];
// Look for colspan and add correct number of cells.
if ( 1 === preg_match( '#<t(?:d|h).*colspan="(\d+)".*?>#is', $cell->asXml(), $matches ) ) {
for ( $i = 1; $i < (int) $matches[1]; $i++ ) {
$new_row[] = '#colspan#';
}
}
} else {
$new_row[] = '';
}
}
$rows[] = $new_row;
}
return $rows;
}
/**
* Import JSON data.
*
* @since 1.0.0
*/
protected function import_json() {
$json_table = json_decode( $this->import_data, true );
// Check if JSON could be decoded.
if ( is_null( $json_table ) ) {
// If possible, try to find out what error prevented the JSON from being decoded.
$json_error = 'The error could not be determined.';
$json_error_msg = json_last_error_msg();
if ( false !== $json_error_msg ) {
$json_error = $json_error_msg;
}
$output = '<strong>' . __( 'The imported file contains errors:', 'tablepress' ) . "</strong><br /><br />JSON error: {$json_error}<br />";
wp_die( $output, 'Import Error', array( 'response' => 200, 'back_link' => true ) );
} else {
// Specifically cast to an array again.
$json_table = (array) $json_table;
}
if ( isset( $json_table['data'] ) ) {
// JSON data contained a full export.
$table = $json_table;
} else {
// JSON data contained only the data of a table, but no options.
$table = array( 'data' => array() );
foreach ( $json_table as $row ) {
$table['data'][] = array_values( (array) $row );
}
}
$table['data'] = $this->pad_array_to_max_cols( $table['data'] );
$this->imported_table = $table;
}
/**
* Import Microsoft Excel 97-2003 data.
*
* @since 1.1.0
*/
protected function import_xls() {
$excel_reader = TablePress::load_class( 'Spreadsheet_Excel_Reader', 'excel-reader.class.php', 'libraries', $this->import_data );
// Loop through Excel file and retrieve value and colspan/rowspan properties for each cell.
$sheet = 0; // 0 means first sheet of the Workbook
$table = array();
for ( $row = 1; $row <= $excel_reader->rowcount( $sheet ); $row++ ) {
$table_row = array();
for ( $col = 1; $col <= $excel_reader->colcount( $sheet ); $col++ ) {
$cell = array();
$cell['rowspan'] = $excel_reader->rowspan( $row, $col, $sheet );
$cell['colspan'] = $excel_reader->colspan( $row, $col, $sheet );
$cell['val'] = $excel_reader->val( $row, $col, $sheet );
$table_row[] = $cell;
}
$table[] = $table_row;
}
// Transform colspan/rowspan properties to TablePress equivalent (cell content).
foreach ( $table as $row_idx => $row ) {
foreach ( $row as $col_idx => $cell ) {
if ( 1 === $cell['rowspan'] && 1 === $cell['colspan'] ) {
continue;
}
if ( 1 < $cell['colspan'] ) {
for ( $i = 1; $i < $cell['colspan']; $i++ ) {
$table[ $row_idx ][ $col_idx + $i ]['val'] = '#colspan#';
}
}
if ( 1 < $cell['rowspan'] ) {
for ( $i = 1; $i < $cell['rowspan']; $i++ ) {
$table[ $row_idx + $i ][ $col_idx ]['val'] = '#rowspan#';
}
}
if ( 1 < $cell['rowspan'] && 1 < $cell['colspan'] ) {
for ( $i = 1; $i < $cell['rowspan']; $i++ ) {
for ( $j = 1; $j < $cell['colspan']; $j++ ) {
$table[ $row_idx + $i ][ $col_idx + $j ]['val'] = '#span#';
}
}
}
}
}
// Flatten value property to two-dimensional array.
$result_table = array();
foreach ( $table as $row_idx => $row ) {
$table_row = array();
foreach ( $row as $col_idx => $cell ) {
$table_row[] = (string) $cell['val'];
}
$result_table[] = $table_row;
}
$this->imported_table = array( 'data' => $this->pad_array_to_max_cols( $result_table ) );
}
/**
* Import Microsoft Excel 2007-2013 data.
*
* @since 1.1.0
*/
protected function import_xlsx() {
TablePress::load_file( 'simplexlsx.class.php', 'libraries' );
$xlsx_file = SimpleXLSX::parse( $this->import_data, true );
if ( $xlsx_file ) {
$this->imported_table = array( 'data' => $xlsx_file->rows() );
} else {
$output = '<strong>' . __( 'The imported file contains errors:', 'tablepress' ) . '</strong><br /><br />' . SimpleXLSX::parse_error() . '<br />';
wp_die( $output, 'Import Error', array( 'response' => 200, 'back_link' => true ) );
}
}
/**
* Make sure array is rectangular with $max_cols columns in every row.
*
* @since 1.0.0
*
* @param array $array Two-dimensional array to be padded.
* @return array Padded array.
*/
public function pad_array_to_max_cols( array $array ) {
$rows = count( $array );
$rows = ( $rows > 0 ) ? $rows : 1;
$max_columns = $this->count_max_columns( $array );
$max_columns = ( $max_columns > 0 ) ? $max_columns : 1;
// array_map wants arrays as additional parameters, so we create one with the max_columns to pad to and one with the value to use (empty string).
$max_columns_array = array_fill( 1, $rows, $max_columns );
$pad_values_array = array_fill( 1, $rows, '' );
return array_map( 'array_pad', $array, $max_columns_array, $pad_values_array );
}
/**
* Get the highest number of columns in the rows.
*
* @since 1.0.0
*
* @param array $array Two-dimensional array.
* @return int Highest number of columns in the rows of the array.
*/
protected function count_max_columns( array $array ) {
$max_columns = 0;
foreach ( $array as $row_idx => $row ) {
$num_columns = count( $row );
$max_columns = max( $num_columns, $max_columns );
}
return $max_columns;
}
/**
* Fixes the encoding to UTF-8 for the entire string that is to be imported.
*
* @since 1.0.0
*
* @link http://stevephillips.me/blog/dealing-php-and-character-encoding
*/
protected function fix_table_encoding() {
// Check and remove possible UTF-8 Byte-Order Mark (BOM).
$bom = pack( 'CCC', 0xef, 0xbb, 0xbf );
if ( 0 === strncmp( $this->import_data, $bom, 3 ) ) {
$this->import_data = substr( $this->import_data, 3 );
// If data has a BOM, it's UTF-8, so further checks unnecessary.
return;
}
// Require the iconv() function for the following checks.
if ( ! function_exists( 'iconv' ) ) {
return;
}
// Check for possible UTF-16 BOMs ("little endian" and "big endian") and try to convert the data to UTF-8.
if ( "\xFF\xFE" === substr( $this->import_data, 0, 2 ) || "\xFE\xFF" === substr( $this->import_data, 0, 2 ) ) {
$data = @iconv( 'UTF-16', 'UTF-8', $this->import_data );
if ( false !== $data ) {
$this->import_data = $data;
return;
}
}
// Detect the character encoding and convert to UTF-8, if it's different.
if ( function_exists( 'mb_detect_encoding' ) ) {
$current_encoding = mb_detect_encoding( $this->import_data, 'ASCII, UTF-8, ISO-8859-1' );
if ( 'UTF-8' !== $current_encoding ) {
$data = @iconv( $current_encoding, 'UTF-8', $this->import_data );
if ( false !== $data ) {
$this->import_data = $data;
return;
}
}
}
}
} // class TablePress_Import
class-admin-page-helper.php 0000666 00000010332 15112004103 0011625 0 ustar 00 <?php
/**
* Admin Page Helper Class for TablePress with functions needed in the admin area
*
* @package TablePress
* @subpackage Views
* @author Tobias Bäthge
* @since 1.0.0
*/
// Prohibit direct script loading.
defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
/**
* Admin Page class
* @package TablePress
* @subpackage Views
* @author Tobias Bäthge
* @since 1.0.0
*/
class TablePress_Admin_Page {
/**
* Enqueue a CSS file.
*
* @since 1.0.0
*
* @param string $name Name of the CSS file, without extension.
* @param array $dependencies Optional. List of names of CSS stylesheets that this stylesheet depends on, and which need to be included before this one.
*/
public function enqueue_style( $name, array $dependencies = array() ) {
$suffix = SCRIPT_DEBUG ? '' : '.min';
$css_file = "admin/css/{$name}{$suffix}.css";
$css_url = plugins_url( $css_file, TABLEPRESS__FILE__ );
wp_enqueue_style( "tablepress-{$name}", $css_url, $dependencies, TablePress::version );
}
/**
* Enqueue a JavaScript file, possibly with dependencies and extra information.
*
* @since 1.0.0
*
* @param string $name Name of the JS file, without extension.
* @param array $dependencies Optional. List of names of JS scripts that this script depends on, and which need to be included before this one.
* @param array $localize_script Optional. An array with strings that gets transformed into a JS object and is added to the page before the script is included.
* @param bool $force_minified Optional. Always load the minified version, regardless of SCRIPT_DEBUG constant value.
*/
public function enqueue_script( $name, array $dependencies = array(), array $localize_script = array(), $force_minified = false ) {
$suffix = ( ! $force_minified && SCRIPT_DEBUG ) ? '' : '.min';
$js_file = "admin/js/{$name}{$suffix}.js";
$js_url = plugins_url( $js_file, TABLEPRESS__FILE__ );
wp_enqueue_script( "tablepress-{$name}", $js_url, $dependencies, TablePress::version, true );
if ( ! empty( $localize_script ) ) {
foreach ( $localize_script as $var_name => $var_data ) {
wp_localize_script( "tablepress-{$name}", "tablepress_{$var_name}", $var_data );
}
}
}
/**
* Register a filter hook on the admin footer.
*
* @since 1.0.0
*/
public function add_admin_footer_text() {
// Show admin footer message (only on TablePress admin screens).
add_filter( 'admin_footer_text', array( $this, '_admin_footer_text' ) );
}
/**
* Add a TablePress "Thank You" message to the admin footer content.
*
* @since 1.0.0
*
* @param string $content Current admin footer content.
* @return string New admin footer content.
*/
public function _admin_footer_text( $content ) {
$content .= ' • ' . sprintf( __( 'Thank you for using <a href="%s">TablePress</a>.', 'tablepress' ), 'https://tablepress.org/' );
$content .= ' ' . sprintf( __( 'Support the plugin with your <a href="%s">donation</a>!', 'tablepress' ), 'https://tablepress.org/donate/' );
return $content;
}
/**
* Print the JavaScript code for a WP feature pointer.
*
* @since 1.0.0
*
* @param string $pointer_id The pointer ID.
* @param string $selector The HTML elements, on which the pointer should be attached.
* @param array $args Arguments to be passed to the pointer JS (see wp-pointer.js).
*/
public function print_wp_pointer_js( $pointer_id, $selector, array $args ) {
if ( empty( $pointer_id ) || empty( $selector ) || empty( $args ) || empty( $args['content'] ) ) {
return;
}
?>
<script type="text/javascript">
( function( $ ) {
var options = <?php echo wp_json_encode( $args, TABLEPRESS_JSON_OPTIONS ); ?>, setup;
if ( ! options ) {
return;
}
options = $.extend( options, {
close: function() {
$.post( ajaxurl, {
pointer: '<?php echo $pointer_id; ?>',
action: 'dismiss-wp-pointer'
} );
}
} );
setup = function() {
$( '<?php echo $selector; ?>' ).pointer( options ).pointer( 'open' );
};
if ( options.position && options.position.defer_loading ) {
$( window ).bind( 'load.wp-pointers', setup );
} else {
$( document ).ready( setup );
}
} )( jQuery );
</script>
<?php
}
} // class TablePress_Admin_Page
class-model.php 0000666 00000001114 15112004103 0007444 0 ustar 00 <?php
/**
* TablePress Base Model with members and methods for all models
*
* @package TablePress
* @subpackage Models
* @author Tobias Bäthge
* @since 1.0.0
*/
// Prohibit direct script loading.
defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
/**
* TablePress Base Model class
* @package TablePress
* @subpackage Models
* @author Tobias Bäthge
* @since 1.0.0
*/
abstract class TablePress_Model {
/**
* Initialize all models.
*
* @since 1.0.0
*/
public function __construct() {
// Intentionally left blank.
}
} // class TablePress_Model
class-evaluate.php 0000666 00000016400 15112004103 0010156 0 ustar 00 <?php
/**
* TablePress Formula Evaluation Class
*
* @package TablePress
* @subpackage Formulas
* @author Tobias Bäthge
* @since 1.5.0
*/
// Prohibit direct script loading.
defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
/**
* TablePress Formula Evaluation Class
*
* Before TablePress 1.5, this was part of the TablePress_Render class.
*
* @package TablePress
* @subpackage Formulas
* @author Tobias Bäthge
* @since 1.5.0
*/
class TablePress_Evaluate {
/**
* Instance of the EvalMath class.
*
* @since 1.0.0
* @var EvalMath
*/
protected $evalmath;
/**
* Table data in which formulas shall be evaluated.
*
* @since 1.5.0
* @var array
*/
protected $table_data;
/**
* Storage for cell ranges that have been replaced in formulas.
*
* @since 1.0.0
* @var array
*/
protected $known_ranges = array();
/**
* Initialize the Formula Evaluation class, include the EvalMath class.
*
* @since 1.0.0
*/
public function __construct() {
$this->evalmath = TablePress::load_class( 'EvalMath', 'evalmath.class.php', 'libraries' );
// Don't raise PHP warnings.
$this->evalmath->suppress_errors = true;
}
/**
* Evaluate formulas in the passed table.
*
* @since 1.0.0
*
* @param array $table_data Table data in which formulas shall be evaluated.
* @return array Table data with evaluated formulas.
*/
public function evaluate_table_data( array $table_data ) {
$this->table_data = $table_data;
$rows = count( $this->table_data );
$columns = count( $this->table_data[0] );
// Use two for-loops instead of foreach here to be sure to always work on the "live" table data and not some in-memory copy.
for ( $row_idx = 0; $row_idx < $rows; $row_idx++ ) {
for ( $col_idx = 0; $col_idx < $columns; $col_idx++ ) {
$this->table_data[ $row_idx ][ $col_idx ] = $this->_evaluate_cell( $this->table_data[ $row_idx ][ $col_idx ] );
}
}
return $this->table_data;
}
/**
* Parse and evaluate the content of a cell.
*
* @since 1.0.0
*
* @param string $content Content of a cell.
* @param array $parents Optional. List of cells that depend on this cell (to prevent circle references).
* @return string Result of the parsing/evaluation.
*/
protected function _evaluate_cell( $content, array $parents = array() ) {
if ( '' === $content || '=' === $content || '=' !== $content[0] ) {
return $content;
}
// Cut off the leading =.
$content = substr( $content, 1 );
// Support putting formulas in strings, like =Total: {A3+A4}.
$expressions = array();
if ( preg_match_all( '#{(.+?)}#', $content, $expressions, PREG_SET_ORDER ) ) {
$formula_in_string = true;
} else {
$formula_in_string = false;
// Fill array so that it has the same structure as if it came from preg_match_all().
$expressions[] = array( $content, $content );
}
foreach ( $expressions as $expression ) {
$orig_expression = $expression[0];
$expression = $expression[1];
$replaced_references = $replaced_ranges = array();
// Remove all whitespace characters.
$expression = str_replace( array( "\n", "\r", "\t", ' ' ), '', $expression );
// Expand cell ranges (like A3:A6) to a list of single cells (like A3,A4,A5,A6).
if ( preg_match_all( '#([A-Z]+)([0-9]+):([A-Z]+)([0-9]+)#', $expression, $referenced_cell_ranges, PREG_SET_ORDER ) ) {
foreach ( $referenced_cell_ranges as $cell_range ) {
if ( in_array( $cell_range[0], $replaced_ranges, true ) ) {
continue;
}
$replaced_ranges[] = $cell_range[0];
if ( isset( $this->known_ranges[ $cell_range[0] ] ) ) {
$expression = preg_replace( '#(?<![A-Z])' . preg_quote( $cell_range[0], '#' ) . '(?![0-9])#', $this->known_ranges[ $cell_range[0] ], $expression );
continue;
}
// No -1 necessary for this transformation, as we don't actually access the table.
$first_col = TablePress::letter_to_number( $cell_range[1] );
$first_row = $cell_range[2];
$last_col = TablePress::letter_to_number( $cell_range[3] );
$last_row = $cell_range[4];
$col_start = min( $first_col, $last_col );
$col_end = max( $first_col, $last_col ) + 1; // +1 for loop below
$row_start = min( $first_row, $last_row );
$row_end = max( $first_row, $last_row ) + 1; // +1 for loop below
$cell_list = array();
for ( $col = $col_start; $col < $col_end; $col++ ) {
for ( $row = $row_start; $row < $row_end; $row++ ) {
$column = TablePress::number_to_letter( $col );
$cell_list[] = "{$column}{$row}";
}
}
$cell_list = implode( ',', $cell_list );
$expression = preg_replace( '#(?<![A-Z])' . preg_quote( $cell_range[0], '#' ) . '(?![0-9])#', $cell_list, $expression );
$this->known_ranges[ $cell_range[0] ] = $cell_list;
}
}
// Parse and evaluate single cell references (like A3 or XY312), while prohibiting circle references.
if ( preg_match_all( '#([A-Z]+)([0-9]+)#', $expression, $referenced_cells, PREG_SET_ORDER ) ) {
foreach ( $referenced_cells as $cell_reference ) {
if ( in_array( $cell_reference[0], $parents, true ) ) {
return '!ERROR! Circle Reference';
}
if ( in_array( $cell_reference[0], $replaced_references, true ) ) {
continue;
}
$replaced_references[] = $cell_reference[0];
$ref_col = TablePress::letter_to_number( $cell_reference[1] ) - 1;
$ref_row = $cell_reference[2] - 1;
if ( ! isset( $this->table_data[ $ref_row ] ) || ! isset( $this->table_data[ $ref_row ][ $ref_col ] ) ) {
return "!ERROR! Cell {$cell_reference[0]} does not exist";
}
$ref_parents = $parents;
$ref_parents[] = $cell_reference[0];
$result = $this->table_data[ $ref_row ][ $ref_col ] = $this->_evaluate_cell( $this->table_data[ $ref_row ][ $ref_col ], $ref_parents );
// Bail if there was an error already.
if ( false !== strpos( $result, '!ERROR!' ) ) {
return $result;
}
// Remove all whitespace characters.
$result = str_replace( array( "\n", "\r", "\t", ' ' ), '', $result );
// Treat empty cells as 0.
if ( '' === $result ) {
$result = 0;
}
// Bail if the cell does not result in a number (meaning it was a number or expression before being evaluated).
if ( ! is_numeric( $result ) ) {
return "!ERROR! {$cell_reference[0]} does not contain a number or expression";
}
$expression = preg_replace( '#(?<![A-Z])' . $cell_reference[0] . '(?![0-9])#', $result, $expression );
}
}
$result = $this->_evaluate_math_expression( $expression );
// Support putting formulas in strings, like =Total: {A3+A4}.
if ( $formula_in_string ) {
$content = str_replace( $orig_expression, $result, $content );
} else {
$content = $result;
}
}
return $content;
}
/**
* Evaluate a math expression.
*
* @since 1.0.0
*
* @param string $expression without leading = sign.
* @return string Result of the evaluation.
*/
protected function _evaluate_math_expression( $expression ) {
// Straight up evaluation, without parsing of variable or function assignments (which is why we only need one instance of the object).
$result = $this->evalmath->evaluate( $expression );
if ( false === $result ) {
return '!ERROR! ' . $this->evalmath->last_error;
} else {
return (string) $result;
}
}
} // class TablePress_Evaluate
class-export.php 0000666 00000013504 15112004103 0007673 0 ustar 00 <?php
/**
* TablePress Table Export Class
*
* @package TablePress
* @subpackage Export/Import
* @author Tobias Bäthge
* @since 1.0.0
*/
// Prohibit direct script loading.
defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
/**
* TablePress Table Export Class
* @package TablePress
* @subpackage Export/Import
* @author Tobias Bäthge
* @since 1.0.0
*/
class TablePress_Export {
/**
* File/Data Formats that are available for the export.
*
* @since 1.0.0
* @var array
*/
public $export_formats = array();
/**
* Delimiters for the CSV export.
*
* @since 1.0.0
* @var array
*/
public $csv_delimiters = array();
/**
* Whether ZIP archive support is available in the PHP installation on the server.
*
* @since 1.0.0
* @var bool
*/
public $zip_support_available = false;
/**
* Initialize the Export class.
*
* @since 1.0.0
*/
public function __construct() {
// Initiate here, because function call not possible outside a class method.
$this->export_formats = array(
'csv' => __( 'CSV - Character-Separated Values', 'tablepress' ),
'html' => __( 'HTML - Hypertext Markup Language', 'tablepress' ),
'json' => __( 'JSON - JavaScript Object Notation', 'tablepress' ),
);
$this->csv_delimiters = array(
';' => __( '; (semicolon)', 'tablepress' ),
',' => __( ', (comma)', 'tablepress' ),
'tab' => __( '\t (tabulator)', 'tablepress' ),
);
/** This filter is documented in the WordPress function unzip_file() in wp-admin/includes/file.php */
if ( class_exists( 'ZipArchive', false ) && apply_filters( 'unzip_file_use_ziparchive', true ) ) {
$this->zip_support_available = true;
}
}
/**
* Export a table.
*
* @since 1.0.0
*
* @param array $table Table to be exported.
* @param string $export_format Format for the export ('csv', 'html', 'json').
* @param string $csv_delimiter Delimiter for CSV export.
* @return string Exported table (only data for CSV and HTML, full tables (including options) for JSON).
*/
public function export_table( array $table, $export_format, $csv_delimiter ) {
switch ( $export_format ) {
case 'csv':
$output = '';
if ( 'tab' === $csv_delimiter ) {
$csv_delimiter = "\t";
}
foreach ( $table['data'] as $row_idx => $row ) {
$csv_row = array();
foreach ( $row as $column_idx => $cell_content ) {
$csv_row[] = $this->csv_wrap_and_escape( $cell_content, $csv_delimiter );
}
$output .= implode( $csv_delimiter, $csv_row );
$output .= "\n";
}
break;
case 'html':
$num_rows = count( $table['data'] );
$last_row_idx = $num_rows - 1;
$thead = '';
$tfoot = '';
$tbody = array();
foreach ( $table['data'] as $row_idx => $row ) {
// First row, need to check for head (but only if at least two rows).
if ( 0 === $row_idx && $table['options']['table_head'] && $num_rows > 1 ) {
$thead = $this->html_render_row( $row, 'th' );
continue;
}
// Last row, need to check for footer (but only if at least two rows).
if ( $last_row_idx === $row_idx && $table['options']['table_foot'] && $num_rows > 1 ) {
$tfoot = $this->html_render_row( $row, 'th' );
continue;
}
// Neither first nor last row (with respective head/foot enabled), so render as body row.
$tbody[] = $this->html_render_row( $row, 'td' );
}
// <thead>, <tfoot>, and <tbody> tags.
if ( ! empty( $thead ) ) {
$thead = "\t<thead>\n{$thead}\t</thead>\n";
}
if ( ! empty( $tfoot ) ) {
$tfoot = "\t<tfoot>\n{$tfoot}\t</tfoot>\n";
}
$tbody = "\t<tbody>\n" . implode( '', $tbody ) . "\t</tbody>\n";
$output = "<table>\n" . $thead . $tfoot . $tbody . "</table>\n";
break;
case 'json':
$output = wp_json_encode( $table, TABLEPRESS_JSON_OPTIONS );
break;
default:
$output = '';
}
return $output;
}
/**
* Wrap and escape a cell for CSV export.
*
* @since 1.0.0
*
* @param string $string Content of a cell.
* @param string $delimiter CSV delimiter character.
* @return string Wrapped string for CSV export
*/
protected function csv_wrap_and_escape( $string, $delimiter ) {
// Escape CSV delimiter for RegExp (e.g. '|').
$delimiter = preg_quote( $delimiter, '#' );
if ( 1 === preg_match( '#' . $delimiter . '|"|\n|\r#i', $string ) || ' ' === substr( $string, 0, 1 ) || ' ' === substr( $string, -1 ) ) {
// Escape single " as double "".
$string = str_replace( '"', '""', $string );
// Wrap string in "".
$string = '"' . $string . '"';
}
return $string;
}
/**
* Generate the HTML of a row.
*
* @since 1.0.0
*
* @param array $row Cells of the row to be rendered.
* @param string $tag HTML tag to use for the cells (td or th).
* @return string HTML code for the row.
*/
protected function html_render_row( array $row, $tag ) {
$output = "\t\t<tr>\n";
array_walk( $row, array( $this, 'html_wrap_and_escape' ), $tag );
$output .= implode( '', $row );
$output .= "\t\t</tr>\n";
return $output;
}
/**
* Wrap and escape a cell for HTML export.
*
* @since 1.0.0
*
* @param string $cell_content Content of a cell.
* @param int|null $column_idx Column index, or null if omitted. Unused, but defined to be able to use function as callback in array_walk().
* @param string $html_tag HTML tag that shall be used for the cell.
*/
protected function html_wrap_and_escape( &$cell_content, $column_idx, $html_tag ) {
/*
* Replace any & with & that is not already an encoded entity (from function htmlentities2 in WP 2.8).
* A complete htmlentities2() or htmlspecialchars() would encode <HTML> tags, which we don't want.
*/
$cell_content = preg_replace( '/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,4};)/', '&', $cell_content );
$cell_content = "\t\t\t<{$html_tag}>{$cell_content}</{$html_tag}>\n";
}
} // class TablePress_Export
class-css.php 0000666 00000036346 15112004103 0007153 0 ustar 00 <?php
/**
* TablePress CSS Class
*
* @package TablePress
* @subpackage CSS
* @author Tobias Bäthge
* @since 1.1.0
*/
// Prohibit direct script loading.
defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
/**
* TablePress CSS Class
* @package TablePress
* @subpackage CSS
* @author Tobias Bäthge
* @since 1.1.0
*/
class TablePress_CSS {
/**
* Sanitize and tidy a string of CSS.
*
* @since 1.1.0
*
* @param string $css CSS code.
* @return string Sanitized and tidied CSS code.
*/
public function sanitize_css( $css ) {
$csstidy = TablePress::load_class( 'TablePress_CSSTidy', 'class.csstidy.php', 'libraries/csstidy' );
// Sanitization and not just tidying for users without enough privileges.
if ( ! current_user_can( 'unfiltered_html' ) ) {
$csstidy->optimise = new TablePress_CSSTidy_custom_sanitize( $csstidy );
// Let "arrows" survive, otherwise this might be recognized as the beginning of an HTML tag and removed with other stuff behind it.
$css = str_replace( '<=', '<=', $css );
// Remove all HTML tags.
$css = wp_kses( $css, 'strip' );
// KSES replaces single ">" with ">", but ">" is valid in CSS selectors.
$css = str_replace( '>', '>', $css );
// strip_tags again, because of the just added ">" (KSES for a second time would again bring the ">" problem).
$css = strip_tags( $css );
}
$csstidy->set_cfg( 'remove_bslash', false );
$csstidy->set_cfg( 'compress_colors', false );
$csstidy->set_cfg( 'compress_font-weight', false );
$csstidy->set_cfg( 'lowercase_s', false );
$csstidy->set_cfg( 'optimise_shorthands', false );
$csstidy->set_cfg( 'remove_last_;', false );
$csstidy->set_cfg( 'case_properties', false );
$csstidy->set_cfg( 'sort_properties', false );
$csstidy->set_cfg( 'sort_selectors', false );
$csstidy->set_cfg( 'discard_invalid_selectors', false );
$csstidy->set_cfg( 'discard_invalid_properties', true );
$csstidy->set_cfg( 'merge_selectors', false );
$csstidy->set_cfg( 'css_level', 'CSS3.0' );
$csstidy->set_cfg( 'preserve_css', true );
$csstidy->set_cfg( 'timestamp', false );
$csstidy->set_cfg( 'template', dirname( TABLEPRESS__FILE__ ) . '/libraries/csstidy/tablepress-standard.tpl' );
$csstidy->parse( $css );
return $csstidy->print->plain();
}
/**
* Minify a string of CSS code, that should have been sanitized/tidied before.
*
* @since 1.1.0
*
* @param string $css CSS code.
* @return string Minified CSS code.
*/
public function minify_css( $css ) {
$csstidy = TablePress::load_class( 'TablePress_CSSTidy', 'class.csstidy.php', 'libraries/csstidy' );
$csstidy->optimise = new TablePress_CSSTidy_custom_sanitize( $csstidy );
$csstidy->set_cfg( 'remove_bslash', false );
$csstidy->set_cfg( 'compress_colors', true );
$csstidy->set_cfg( 'compress_font-weight', true );
$csstidy->set_cfg( 'lowercase_s', false );
$csstidy->set_cfg( 'optimise_shorthands', 1 );
$csstidy->set_cfg( 'remove_last_;', true );
$csstidy->set_cfg( 'case_properties', false );
$csstidy->set_cfg( 'sort_properties', false );
$csstidy->set_cfg( 'sort_selectors', false );
$csstidy->set_cfg( 'discard_invalid_selectors', false );
$csstidy->set_cfg( 'discard_invalid_properties', true );
$csstidy->set_cfg( 'merge_selectors', false );
$csstidy->set_cfg( 'css_level', 'CSS3.0' );
$csstidy->set_cfg( 'preserve_css', false );
$csstidy->set_cfg( 'timestamp', false );
$csstidy->set_cfg( 'template', 'highest' );
$csstidy->parse( $css );
return $csstidy->print->plain();
}
/**
* Get the location (file path or URL) of the "Custom CSS" file, depending on whether it's a Multisite install or not.
*
* @since 1.0.0
*
* @param string $type "normal" version, "minified" version, or "combined" (with TablePress Default CSS) version.
* @param string $location "path" or "url", for file path or URL.
* @return string Full file path or full URL for the "Custom CSS" file.
*/
public function get_custom_css_location( $type, $location ) {
switch ( $type ) {
case 'combined':
$file = 'tablepress-combined.min.css';
break;
case 'minified':
$file = 'tablepress-custom.min.css';
break;
case 'normal':
default:
$file = 'tablepress-custom.css';
break;
}
if ( is_multisite() ) {
// Multisite installation: /wp-content/uploads/sites/<ID>/
$upload_location = wp_upload_dir();
} else {
// Singlesite installation: /wp-content/
$upload_location = array(
'basedir' => WP_CONTENT_DIR,
'baseurl' => content_url(),
);
}
switch ( $location ) {
case 'url':
$url = set_url_scheme( $upload_location['baseurl'] . '/' . $file );
/**
* Filter the URL from which the "Custom CSS" file is loaded.
*
* @since 1.0.0
*
* @param string $url URL of the "Custom CSS" file.
* @param string $file File name of the "Custom CSS" file.
* @param string $type Type of the "Custom CSS" file ("normal", "minified", or "combined").
*/
$url = apply_filters( 'tablepress_custom_css_url', $url, $file, $type );
return $url;
// break; // unreachable
case 'path':
$path = $upload_location['basedir'] . '/' . $file;
/**
* Filter the file path on the server from which the "Custom CSS" file is loaded.
*
* @since 1.0.0
*
* @param string $path File path of the "Custom CSS" file.
* @param string $file File name of the "Custom CSS" file.
* @param string $type Type of the "Custom CSS" file ("normal", "minified", or "combined").
*/
$path = apply_filters( 'tablepress_custom_css_file_name', $path, $file, $type );
return $path;
// break; // unreachable
}
}
/**
* Load the contents of the file with the "Custom CSS".
*
* @since 1.0.0
*
* @param string $type Optional. Whether to load "normal" version or "minified" version. Default "normal".
* @return string|bool Custom CSS on success, false on error.
*/
public function load_custom_css_from_file( $type = 'normal' ) {
$filename = $this->get_custom_css_location( $type, 'path' );
// Check if file name is valid (0 means yes).
if ( 0 !== validate_file( $filename ) ) {
return false;
}
if ( ! @is_file( $filename ) ) {
return false;
}
if ( ! @is_readable( $filename ) ) {
return false;
}
return file_get_contents( $filename );
}
/**
* Load the contents of the file with the TablePress Default CSS.
*
* @since 1.1.0
*
* @return string|bool TablePress Default CSS on success, false on error.
*/
public function load_default_css_from_file() {
$filename = TABLEPRESS_ABSPATH . 'css/default.min.css';
// Check if file name is valid (0 means yes).
if ( 0 !== validate_file( $filename ) ) {
return false;
}
if ( ! @is_file( $filename ) ) {
return false;
}
if ( ! @is_readable( $filename ) ) {
return false;
}
return file_get_contents( $filename );
}
/**
* Try to save "Custom CSS" to a file (requires "direct" method in WP_Filesystem, or stored FTP credentials).
*
* @since 1.1.0
*
* @param string $custom_css_normal Custom CSS code to be saved.
* @param string $custom_css_minified Minified CSS code to be saved.
* @return bool True on success, false on failure.
*/
public function save_custom_css_to_file( $custom_css_normal, $custom_css_minified ) {
/**
* Filter whether the "Custom CSS" code shall be saved to a file on the server.
*
* @since 1.1.0
*
* @param bool $save Whether to save the "Custom CSS" to a file. Default true.
*/
if ( ! apply_filters( 'tablepress_save_custom_css_to_file', true ) ) {
return false;
}
// Start capturing the output, to later prevent it.
ob_start();
$credentials = request_filesystem_credentials( '', '', false, false, null );
/*
* Do we have credentials already? (Otherwise the form will have been rendered, which is not supported here.)
* Or, if we have credentials, are they valid?
*/
if ( false === $credentials || ! WP_Filesystem( $credentials ) ) {
ob_end_clean();
return false;
}
// We have valid access to the filesystem now -> try to save the files.
return $this->_custom_css_save_helper( $custom_css_normal, $custom_css_minified );
}
/**
* Save "Custom CSS" to files, delete "Custom CSS" files, or return HTML for the credentials form.
*
* Only used from "Plugin Options" screen, save_custom_css_to_file() is used in cases where no form output/redirection is possible (plugin updates, WP-Table Reloaded Import).
*
* @since 1.0.0
*
* @param string $custom_css_normal Custom CSS code to be saved. If empty, files will be deleted.
* @param string $custom_css_minified Minified CSS code to be saved.
* @return bool|string True on success, false on failure, or string of HTML for the credentials form for the WP_Filesystem API, if necessary.
*/
public function save_custom_css_to_file_plugin_options( $custom_css_normal, $custom_css_minified ) {
/** This filter is documented in classes/class-css.php */
if ( ! apply_filters( 'tablepress_save_custom_css_to_file', true ) ) {
return false;
}
// Start capturing the output, to get HTML of the credentials form (if needed).
ob_start();
$credentials = request_filesystem_credentials( '', '', false, false, null );
// Do we have credentials already? (Otherwise the form will have been rendered already.)
if ( false === $credentials ) {
$form_data = ob_get_clean();
$form_data = str_replace( 'name="upgrade" id="upgrade" class="button"', 'name="upgrade" id="upgrade" class="button button-primary button-large"', $form_data );
return $form_data;
}
// We have received credentials, but don't know if they are valid yet.
if ( ! WP_Filesystem( $credentials ) ) {
// Credentials failed, so ask again (with $error flag true).
request_filesystem_credentials( '', '', true, false, null );
$form_data = ob_get_clean();
$form_data = str_replace( 'name="upgrade" id="upgrade" class="button"', 'name="upgrade" id="upgrade" class="button button-primary button-large"', $form_data );
return $form_data;
}
// We have valid access to the filesystem now -> try to save the files, or delete them if the "Custom CSS" is empty.
if ( '' !== $custom_css_normal ) {
return $this->_custom_css_save_helper( $custom_css_normal, $custom_css_minified );
} else {
return $this->_custom_css_delete_helper();
}
}
/**
* Save "Custom CSS" to files, if validated access to the WP_Filesystem exists.
*
* Helper function to prevent code duplication.
*
* @since 1.1.0
*
* @global WP_Filesystem_* $wp_filesystem WordPress file system abstraction object.
* @see save_custom_css_to_file()
* @see save_custom_css_to_file_plugin_options()
*
* @param string $custom_css_normal Custom CSS code to be saved.
* @param string $custom_css_minified Minified CSS code to be saved.
* @return bool True on success, false on failure.
*/
protected function _custom_css_save_helper( $custom_css_normal, $custom_css_minified ) {
global $wp_filesystem;
/*
* WP_CONTENT_DIR and (FTP-)Content-Dir can be different (e.g. if FTP working dir is /).
* We need to account for that by replacing the path difference in the filename.
*/
$path_difference = str_replace( $wp_filesystem->wp_content_dir(), '', trailingslashit( WP_CONTENT_DIR ) );
$css_types = array( 'normal', 'minified', 'combined' );
$default_css = $this->load_default_css_from_file();
if ( false === $default_css ) {
$default_css = '';
} else {
// Change relative URLs to web font files to absolute URLs, as combining the CSS files and saving to another directory breaks the relative URLs.
$absolute_path = plugins_url( 'css/tablepress.', TABLEPRESS__FILE__ );
// Make the absolute URL protocol-relative to prevent mixed content warnings.
$absolute_path = str_replace( array( 'http:', 'https:' ), '', $absolute_path );
$default_css = str_replace( 'url(tablepress.', 'url(' . $absolute_path, $default_css );
}
$file_content = array(
'normal' => $custom_css_normal,
'minified' => $custom_css_minified,
'combined' => $default_css . "\n" . $custom_css_minified,
);
$total_result = true; // whether all files were saved successfully
foreach ( $css_types as $css_type ) {
$filename = $this->get_custom_css_location( $css_type, 'path' );
// Check if filename is valid (0 means yes).
if ( 0 !== validate_file( $filename ) ) {
$total_result = false;
continue;
}
if ( '' !== $path_difference ) {
$filename = str_replace( $path_difference, '', $filename );
}
$result = $wp_filesystem->put_contents( $filename, $file_content[ $css_type ], FS_CHMOD_FILE );
$total_result = ( $total_result && $result );
}
$this->_flush_caching_plugins_css_minify_caches();
return $total_result;
}
/**
* Delete the "Custom CSS" files, if possible.
*
* @since 1.0.0
*
* @return bool True on success, false on failure.
*/
public function delete_custom_css_files() {
// Start capturing the output, to later prevent it.
ob_start();
$credentials = request_filesystem_credentials( '', '', false, false, null );
/*
* Do we have credentials already? (Otherwise the form will have been rendered, which is not supported here.)
* Or, if we have credentials, are they valid?
*/
if ( false === $credentials || ! WP_Filesystem( $credentials ) ) {
ob_end_clean();
return false;
}
// We have valid access to the filesystem now -> try to delete the files.
return $this->_custom_css_delete_helper();
}
/**
* Delete "Custom CSS" files, if validated access to the WP_Filesystem exists.
*
* Helper function to prevent code duplication
*
* @since 1.1.0
*
* @global WP_Filesystem_* $wp_filesystem WordPress file system abstraction object.
* @see delete_custom_css_files()
* @see save_custom_css_to_file_plugin_options()
*
* @return bool True on success, false on failure.
*/
protected function _custom_css_delete_helper() {
global $wp_filesystem;
/*
* WP_CONTENT_DIR and (FTP-)Content-Dir can be different (e.g. if FTP working dir is /).
* We need to account for that by replacing the path difference in the filename.
*/
$path_difference = str_replace( $wp_filesystem->wp_content_dir(), '', trailingslashit( WP_CONTENT_DIR ) );
$css_types = array( 'normal', 'minified', 'combined' );
$total_result = true; // whether all files were deleted successfully
foreach ( $css_types as $css_type ) {
$filename = $this->get_custom_css_location( $css_type, 'path' );
// Check if filename is valid (0 means yes).
if ( 0 !== validate_file( $filename ) ) {
$total_result = false;
continue;
}
if ( '' !== $path_difference ) {
$filename = str_replace( $path_difference, '', $filename );
}
// We have valid access to the filesystem now -> try to delete the file.
if ( $wp_filesystem->exists( $filename ) ) {
$result = $wp_filesystem->delete( $filename );
$total_result = ( $total_result && $result );
}
}
$this->_flush_caching_plugins_css_minify_caches();
return $total_result;
}
/**
* Flush the CSS minification cache of W3 Total Cache.
*
* @since 1.4.0
*/
protected function _flush_caching_plugins_css_minify_caches() {
/** This filter is documented in models/model-table.php */
if ( ! apply_filters( 'tablepress_flush_caching_plugins_caches', true ) ) {
return;
}
// W3 Total Cache
if ( function_exists( 'w3tc_minify_flush' ) ) {
w3tc_minify_flush();
}
}
} // class TablePress_CSS
class-render.php 0000666 00000100660 15112004103 0007631 0 ustar 00 <?php
/**
* TablePress Rendering Class
*
* @package TablePress
* @subpackage Rendering
* @author Tobias Bäthge
* @since 1.0.0
*/
// Prohibit direct script loading.
defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
/**
* TablePress Rendering Class
* @package TablePress
* @subpackage Rendering
* @author Tobias Bäthge
* @since 1.0.0
*/
class TablePress_Render {
/**
* Table data that is rendered.
*
* @since 1.0.0
* @var array
*/
protected $table = array();
/**
* Table options that influence the output result.
*
* @since 1.0.0
* @var array
*/
protected $render_options = array();
/**
* Rendered HTML of the table.
*
* @since 1.0.0
* @var string
*/
protected $output;
/**
* Trigger words for colspan, rowspan, or the combination of both.
*
* @since 1.0.0
* @var array
*/
protected $span_trigger = array(
'colspan' => '#colspan#',
'rowspan' => '#rowspan#',
'span' => '#span#',
);
/**
* Buffer to store the counts of rowspan per column, initialized in _render_table().
*
* @since 1.0.0
* @var array
*/
protected $rowspan = array();
/**
* Buffer to store the counts of colspan per row, initialized in _render_table().
*
* @since 1.0.0
* @var array
*/
protected $colspan = array();
/**
* Index of the last row of the visible data in the table, set in _render_table().
*
* @since 1.0.0
* @var int
*/
protected $last_row_idx;
/**
* Index of the last column of the visible data in the table, set in _render_table().
*
* @since 1.0.0
* @var int
*/
protected $last_column_idx;
/**
* Class constructor.
*
* @since 1.0.0
*/
public function __construct() {
// Unused.
}
/**
* Set the table (data, options, visibility, ...) that is to be rendered.
*
* @since 1.0.0
*
* @param array $table Table to be rendered.
* @param array $render_options Options for rendering, from both "Edit" screen and Shortcode.
*/
public function set_input( array $table, array $render_options ) {
$this->table = $table;
$this->render_options = $render_options;
/**
* Filter the table before the render process.
*
* @since 1.0.0
*
* @param array $table The table.
* @param array $render_options The render options for the table.
*/
$this->table = apply_filters( 'tablepress_table_raw_render_data', $this->table, $this->render_options );
}
/**
* Process the table rendering and return the HTML output.
*
* @since 1.0.0
*
* @return string HTML of the rendered table (or an error message).
*/
public function get_output() {
// Evaluate math expressions/formulas.
$this->_evaluate_table_data();
// Remove hidden rows and columns.
$this->_prepare_render_data();
// Generate HTML output.
$this->_render_table();
return $this->output;
}
/**
* Loop through the table to evaluate math expressions/formulas.
*
* @since 1.0.0
*/
protected function _evaluate_table_data() {
$orig_table = $this->table;
$formula_evaluator = TablePress::load_class( 'TablePress_Evaluate', 'class-evaluate.php', 'classes' );
$this->table['data'] = $formula_evaluator->evaluate_table_data( $this->table['data'] );
/**
* Filter the table after evaluating formulas in the table.
*
* @since 1.0.0
*
* @param array $table The table with evaluated formulas.
* @param array $orig_table The table with unevaluated formulas.
* @param array $render_options The render options for the table.
*/
$this->table = apply_filters( 'tablepress_table_evaluate_data', $this->table, $orig_table, $this->render_options );
}
/**
* Remove all cells from the data set that shall not be rendered, because they are hidden.
*
* @since 1.0.0
*/
protected function _prepare_render_data() {
$orig_table = $this->table;
$num_rows = count( $this->table['data'] );
$num_columns = ( $num_rows > 0 ) ? count( $this->table['data'][0] ) : 0;
// Evaluate show/hide_rows/columns parameters.
$actions = array( 'show', 'hide' );
$elements = array( 'rows', 'columns' );
foreach ( $actions as $action ) {
foreach ( $elements as $element ) {
if ( empty( $this->render_options[ "{$action}_{$element}" ] ) ) {
$this->render_options[ "{$action}_{$element}" ] = array();
continue;
}
// Add all rows/columns to array if "all" value set for one of the four parameters.
if ( 'all' === $this->render_options[ "{$action}_{$element}" ] ) {
$this->render_options[ "{$action}_{$element}" ] = range( 0, ${'num_' . $element} - 1 );
continue;
}
// We have a list of rows/columns (possibly with ranges in it).
$this->render_options[ "{$action}_{$element}" ] = explode( ',', $this->render_options[ "{$action}_{$element}" ] );
// Support for ranges like 3-6 or A-BA.
$range_cells = array();
foreach ( $this->render_options[ "{$action}_{$element}" ] as $key => $value ) {
$range_dash = strpos( $value, '-' );
if ( false !== $range_dash ) {
unset( $this->render_options[ "{$action}_{$element}" ][ $key ] );
$start = substr( $value, 0, $range_dash );
if ( ! is_numeric( $start ) ) {
$start = TablePress::letter_to_number( $start );
}
$end = substr( $value, $range_dash + 1 );
if ( ! is_numeric( $end ) ) {
$end = TablePress::letter_to_number( $end );
}
$current_range = range( $start, $end );
$range_cells = array_merge( $range_cells, $current_range );
}
}
$this->render_options[ "{$action}_{$element}" ] = array_merge( $this->render_options[ "{$action}_{$element}" ], $range_cells );
/*
* Parse single letters and change from regular numbering to zero-based numbering,
* as rows/columns are indexed from 0 internally, but from 1 externally
*/
foreach ( $this->render_options[ "{$action}_{$element}" ] as $key => $value ) {
if ( ! is_numeric( $value ) ) {
$value = TablePress::letter_to_number( $value );
}
$this->render_options[ "{$action}_{$element}" ][ $key ] = (int) $value - 1;
}
// Remove duplicate entries and sort the array.
$this->render_options[ "{$action}_{$element}" ] = array_unique( $this->render_options[ "{$action}_{$element}" ] );
sort( $this->render_options[ "{$action}_{$element}" ], SORT_NUMERIC );
}
}
// Load information about hidden rows and columns.
// Get indexes of hidden rows (array value of 0).
$hidden_rows = array_keys( $this->table['visibility']['rows'], 0 );
$hidden_rows = array_merge( $hidden_rows, $this->render_options['hide_rows'] );
$hidden_rows = array_diff( $hidden_rows, $this->render_options['show_rows'] );
// Get indexes of hidden columns (array value of 0).
$hidden_columns = array_keys( $this->table['visibility']['columns'], 0 );
$hidden_columns = array_merge( $hidden_columns, $this->render_options['hide_columns'] );
$hidden_columns = array_merge( array_diff( $hidden_columns, $this->render_options['show_columns'] ) );
// Remove hidden rows and re-index.
foreach ( $hidden_rows as $row_idx ) {
unset( $this->table['data'][ $row_idx ] );
}
$this->table['data'] = array_merge( $this->table['data'] );
// Remove hidden columns and re-index.
foreach ( $this->table['data'] as $row_idx => $row ) {
foreach ( $hidden_columns as $col_idx ) {
unset( $row[ $col_idx ] );
}
$this->table['data'][ $row_idx ] = array_merge( $row );
}
/**
* Filter the table after processing the table visibility information.
*
* @since 1.0.0
*
* @param array $table The processed table.
* @param array $orig_table The unprocessed table.
* @param array $render_options The render options for the table.
*/
$this->table = apply_filters( 'tablepress_table_render_data', $this->table, $orig_table, $this->render_options );
}
/**
* Generate the HTML output of the table.
*
* @since 1.0.0
*/
protected function _render_table() {
$num_rows = count( $this->table['data'] );
$num_columns = ( $num_rows > 0 ) ? count( $this->table['data'][0] ) : 0;
// Check if there are rows and columns in the table (might not be the case after removing hidden rows/columns!).
if ( 0 === $num_rows || 0 === $num_columns ) {
$this->output = sprintf( __( '<!-- The table with the ID %s is empty! -->', 'tablepress' ), $this->table['id'] );
return;
}
// Counters for spans of rows and columns, init to 1 for each row and column (as that means no span).
$this->rowspan = array_fill( 0, $num_columns, 1 );
$this->colspan = array_fill( 0, $num_rows, 1 );
/**
* Filter the trigger keywords for "colspan" and "rowspan"
*
* @since 1.0.0
*
* @param array $span_trigger The trigger keywords for combining table cells.
* @param string $table_id The current table ID.
*/
$this->span_trigger = apply_filters( 'tablepress_span_trigger_keywords', $this->span_trigger, $this->table['id'] );
// Explode from string to array.
$this->render_options['column_widths'] = ( ! empty( $this->render_options['column_widths'] ) ) ? explode( '|', $this->render_options['column_widths'] ) : array();
// Make array $this->render_options['column_widths'] have $columns entries.
$this->render_options['column_widths'] = array_pad( $this->render_options['column_widths'], $num_columns, '' );
$output = '';
if ( $this->render_options['print_name'] ) {
/**
* Filter the HTML tag that wraps the printed table name.
*
* @since 1.0.0
*
* @param string $tag The HTML tag around the table name. Default h2.
* @param string $table_id The current table ID.
*/
$print_name_html_tag = apply_filters( 'tablepress_print_name_html_tag', 'h2', $this->table['id'] );
/**
* Filter the class attribute for the printed table name.
*
* @since 1.0.0
*
* @param string $class The class attribute for the table name that can be used in CSS code.
* @param string $table_id The current table ID.
*/
$print_name_css_class = apply_filters( 'tablepress_print_name_css_class', "tablepress-table-name tablepress-table-name-id-{$this->table['id']}", $this->table['id'] );
$print_name_html = "<{$print_name_html_tag} class=\"{$print_name_css_class}\">" . $this->safe_output( $this->table['name'] ) . "</{$print_name_html_tag}>\n";
}
if ( $this->render_options['print_description'] ) {
/**
* Filter the HTML tag that wraps the printed table description.
*
* @since 1.0.0
*
* @param string $tag The HTML tag around the table description. Default span.
* @param string $table_id The current table ID.
*/
$print_description_html_tag = apply_filters( 'tablepress_print_description_html_tag', 'span', $this->table['id'] );
/**
* Filter the class attribute for the printed table description.
*
* @since 1.0.0
*
* @param string $class The class attribute for the table description that can be used in CSS code.
* @param string $table_id The current table ID.
*/
$print_description_css_class = apply_filters( 'tablepress_print_description_css_class', "tablepress-table-description tablepress-table-description-id-{$this->table['id']}", $this->table['id'] );
$print_description_html = "<{$print_description_html_tag} class=\"{$print_description_css_class}\">" . $this->safe_output( $this->table['description'] ) . "</{$print_description_html_tag}>\n";
}
if ( $this->render_options['print_name'] && 'above' === $this->render_options['print_name_position'] ) {
$output .= $print_name_html;
}
if ( $this->render_options['print_description'] && 'above' === $this->render_options['print_description_position'] ) {
$output .= $print_description_html;
}
// Deactivate nl2br() for this render process, if "convert_line_breaks" Shortcode parameter is set to false.
if ( ! $this->render_options['convert_line_breaks'] ) {
add_filter( 'tablepress_apply_nl2br', '__return_false', 9 ); // Priority 9, so that this filter can easily be overwritten at the default priority.
}
$thead = '';
$tfoot = '';
$tbody = array();
$this->last_row_idx = $num_rows - 1;
$this->last_column_idx = $num_columns - 1;
// Loop through rows in reversed order, to search for rowspan trigger keyword.
for ( $row_idx = $this->last_row_idx; $row_idx >= 0; $row_idx-- ) {
// Last row, need to check for footer (but only if at least two rows).
if ( $this->last_row_idx === $row_idx && $this->render_options['table_foot'] && $num_rows > 1 ) {
$tfoot = $this->_render_row( $row_idx, 'th' );
continue;
}
// First row, need to check for head (but only if at least two rows).
if ( 0 === $row_idx && $this->render_options['table_head'] && $num_rows > 1 ) {
$thead = $this->_render_row( $row_idx, 'th' );
continue;
}
// Neither first nor last row (with respective head/foot enabled), so render as body row.
$tbody[] = $this->_render_row( $row_idx, 'td' );
}
// Re-instate nl2br() behavior after this render process, if "convert_line_breaks" Shortcode parameter is set to false.
if ( ! $this->render_options['convert_line_breaks'] ) {
remove_filter( 'tablepress_apply_nl2br', '__return_false', 9 ); // Priority 9, so that this filter can easily be overwritten at the default priority.
}
// <caption> tag.
/**
* Filter the content for the HTML caption element of the table.
*
* If the "Edit" link for a table is shown, it is also added to the caption element.
*
* @since 1.0.0
*
* @param string $caption The content for the HTML caption element of the table. Default empty.
* @param array $table The current table.
*/
$caption = apply_filters( 'tablepress_print_caption_text', '', $this->table );
$caption_style = $caption_class = '';
if ( ! empty( $caption ) ) {
/**
* Filter the class attribute for the HTML caption element of the table.
*
* @since 1.0.0
*
* @param string $class The class attribute for the HTML caption element of the table.
* @param string $table_id The current table ID.
*/
$caption_class = apply_filters( 'tablepress_print_caption_class', "tablepress-table-caption tablepress-table-caption-id-{$this->table['id']}", $this->table['id'] );
$caption_class = ' class="' . $caption_class . '"';
}
if ( ! empty( $this->render_options['edit_table_url'] ) ) {
if ( empty( $caption ) ) {
$caption_style = ' style="caption-side:bottom;text-align:left;border:none;background:none;margin:0;padding:0;"';
} else {
$caption .= '<br />';
}
$caption .= "<a href=\"{$this->render_options['edit_table_url']}\">" . __( 'Edit', 'default' ) . '</a>';
}
if ( ! empty( $caption ) ) {
$caption = "<caption{$caption_class}{$caption_style}>{$caption}</caption>\n";
}
// <colgroup> tag.
$colgroup = '';
/**
* Filter whether the HTML colgroup tag shall be added to the table output.
*
* @since 1.0.0
*
* @param bool $print Whether the colgroup element shall be printed.
* @param string $table_id The current table ID.
*/
if ( apply_filters( 'tablepress_print_colgroup_tag', false, $this->table['id'] ) ) {
for ( $col_idx = 0; $col_idx < $num_columns; $col_idx++ ) {
$attributes = ' class="colgroup-column-' . ( $col_idx + 1 ) . ' "';
/**
* Filter the attributes of the HTML col tags in the HTML colgroup tag.
*
* @since 1.0.0
*
* @param string $attributes The attributes in the col element.
* @param string $table_id The current table ID.
* @param int $col_idx The number of the column.
*/
$attributes = apply_filters( 'tablepress_colgroup_tag_attributes', $attributes, $this->table['id'], $col_idx + 1 );
$colgroup .= "\t<col{$attributes}/>\n";
}
}
if ( ! empty( $colgroup ) ) {
$colgroup = "<colgroup>\n{$colgroup}</colgroup>\n";
}
// <thead>, <tfoot>, and <tbody> tags.
if ( ! empty( $thead ) ) {
$thead = "<thead>\n{$thead}</thead>\n";
}
if ( ! empty( $tfoot ) ) {
$tfoot = "<tfoot>\n{$tfoot}</tfoot>\n";
}
$tbody_class = ( $this->render_options['row_hover'] ) ? ' class="row-hover"' : '';
// Reverse rows because we looped through the rows in reverse order.
$tbody = array_reverse( $tbody );
$tbody = "<tbody{$tbody_class}>\n" . implode( '', $tbody ) . "</tbody>\n";
// Attributes for the table (HTML table element).
$table_attributes = array();
// "id" attribute.
if ( ! empty( $this->render_options['html_id'] ) ) {
$table_attributes['id'] = $this->render_options['html_id'];
}
// "class" attribute.
$css_classes = array( 'tablepress', "tablepress-id-{$this->table['id']}", $this->render_options['extra_css_classes'] );
/**
* Filter the CSS classes that are given to the HTML table element.
*
* @since 1.0.0
*
* @param array $css_classes The CSS classes for the table element.
* @param string $table_id The current table ID.
*/
$css_classes = apply_filters( 'tablepress_table_css_classes', $css_classes, $this->table['id'] );
// $css_classes might contain several classes in one array entry.
$css_classes = explode( ' ', implode( ' ', $css_classes ) );
$css_classes = array_map( 'sanitize_html_class', $css_classes );
$css_classes = array_unique( $css_classes );
$css_classes = trim( implode( ' ', $css_classes ) );
if ( ! empty( $css_classes ) ) {
$table_attributes['class'] = $css_classes;
}
// "summary" attribute.
$summary = '';
/**
* Filter the content for the summary attribute of the HTML table element.
*
* The attribute is only added if it is not empty.
*
* @since 1.0.0
*
* @param string $summary The content for the summary attribute of the table. Default empty.
* @param array $table The current table.
*/
$summary = apply_filters( 'tablepress_print_summary_attr', $summary, $this->table );
if ( ! empty( $summary ) ) {
$table_attributes['summary'] = esc_attr( $summary );
}
// Legacy support for attributes that are not encouraged in HTML5.
foreach ( array( 'cellspacing', 'cellpadding', 'border' ) as $attribute ) {
if ( false !== $this->render_options[ $attribute ] ) {
$table_attributes[ $attribute ] = intval( $this->render_options[ $attribute ] );
}
}
/**
* Filter the attributes for the table (HTML table element).
*
* @since 1.4.0
*
* @param array $table_attributes The attributes for the table element.
* @param array $table The current table.
* @param array $render_options The render options for the table.
*/
$table_attributes = apply_filters( 'tablepress_table_tag_attributes', $table_attributes, $this->table, $this->render_options );
$table_attributes = $this->_attributes_array_to_string( $table_attributes );
$output .= "\n<table{$table_attributes}>\n";
$output .= $caption . $colgroup . $thead . $tfoot . $tbody;
$output .= "</table>\n";
// name/description below table (HTML already generated above).
if ( $this->render_options['print_name'] && 'below' === $this->render_options['print_name_position'] ) {
$output .= $print_name_html;
}
if ( $this->render_options['print_description'] && 'below' === $this->render_options['print_description_position'] ) {
$output .= $print_description_html;
}
/**
* Filter the generated HTML code for table.
*
* @since 1.0.0
*
* @param string $output The generated HTML for the table.
* @param array $table The current table.
* @param array $render_options The render options for the table.
*/
$this->output = apply_filters( 'tablepress_table_output', $output, $this->table, $this->render_options );
}
/**
* Generate the HTML of a row.
*
* @since 1.0.0
*
* @param int $row_idx Index of the row to be rendered.
* @param string $tag HTML tag to use for the cells (td or th).
* @return string HTML for the row.
*/
protected function _render_row( $row_idx, $tag ) {
$row_cells = array();
// Loop through cells in reversed order, to search for colspan or rowspan trigger words.
for ( $col_idx = $this->last_column_idx; $col_idx >= 0; $col_idx-- ) {
$cell_content = $this->table['data'][ $row_idx ][ $col_idx ];
// Print formulas that are escaped with '= (like in Excel) as text.
if ( "'=" === substr( $cell_content, 0, 2 ) ) {
$cell_content = substr( $cell_content, 1 );
}
$cell_content = do_shortcode( $this->safe_output( $cell_content ) );
/**
* Filter the content of a single cell.
*
* Filter the content of a single cell, after formulas have been evaluated, the output has been sanitized, and Shortcodes have been evaluated.
*
* @since 1.0.0
*
* @param string $cell_content The cell content.
* @param string $table_id The current table ID.
* @param int $row_idx The row number of the cell.
* @param int $col_idx The column number of the cell.
*/
$cell_content = apply_filters( 'tablepress_cell_content', $cell_content, $this->table['id'], $row_idx + 1, $col_idx + 1 );
if ( $this->span_trigger['rowspan'] === $cell_content ) { // There will be a rowspan.
// Check for #rowspan# in first row, which doesn't make sense.
if ( ( $row_idx > 1 && $row_idx < $this->last_row_idx )
|| ( 1 === $row_idx && ! $this->render_options['table_head'] ) // No rowspan into table head.
|| ( $this->last_row_idx === $row_idx && ! $this->render_options['table_foot'] ) ) { // No rowspan out of table foot.
// Increase counter for rowspan in this column.
$this->rowspan[ $col_idx ]++;
// Reset counter for colspan in this row, combined col- and rowspan might be happening.
$this->colspan[ $row_idx ] = 1;
continue;
}
// Invalid rowspan, so we set cell content from #rowspan# to empty.
$cell_content = '';
} elseif ( $this->span_trigger['colspan'] === $cell_content ) { // There will be a colspan.
// Check for #colspan# in first column, which doesn't make sense.
if ( $col_idx > 1
|| ( 1 === $col_idx && ! $this->render_options['first_column_th'] ) ) { // No colspan into first column head.
// Increase counter for colspan in this row.
$this->colspan[ $row_idx ]++;
// Reset counter for rowspan in this column, combined col- and rowspan might be happening.
$this->rowspan[ $col_idx ] = 1;
continue;
}
// Invalid colspan, so we set cell content from #colspan# to empty.
$cell_content = '';
} elseif ( $this->span_trigger['span'] === $cell_content ) { // There will be a combined col- and rowspan.
// Check for #span# in first column or first or last row, which is not always possible.
if ( ( $row_idx > 1 && $row_idx < $this->last_row_idx && $col_idx > 1 )
// We are in first, second, or last row or in the first or second column, so more checks are necessary.
|| ( ( 1 === $row_idx && ! $this->render_options['table_head'] ) // No rowspan into table head.
&& ( $col_idx > 1 || ( 1 === $col_idx && ! $this->render_options['first_column_th'] ) ) ) // And no colspan into first column head.
|| ( ( $this->last_row_idx === $row_idx && ! $this->render_options['table_foot'] ) // No rowspan out of table foot.
&& ( $col_idx > 1 || ( 1 === $col_idx && ! $this->render_options['first_column_th'] ) ) ) ) { // And no colspan into first column head.
continue;
}
// Invalid span, so we set cell content from #span# to empty.
$cell_content = '';
} elseif ( '' === $cell_content && 0 === $row_idx && $this->render_options['table_head'] ) {
// Make empty cells have a space in the table head, to give sorting arrows the correct position in IE9.
$cell_content = ' ';
}
// Attributes for the table cell (HTML td or th element).
$tag_attributes = array();
// "colspan" and "rowspan" attributes.
if ( $this->colspan[ $row_idx ] > 1 ) { // We have colspaned cells.
$tag_attributes['colspan'] = $this->colspan[ $row_idx ];
}
if ( $this->rowspan[ $col_idx ] > 1 ) { // We have rowspaned cells.
$tag_attributes['rowspan'] = $this->rowspan[ $col_idx ];
}
// "class" attribute.
$cell_class = 'column-' . ( $col_idx + 1 );
/**
* Filter the CSS classes that are given to a single cell (HTML td element) of a table.
*
* @since 1.0.0
*
* @param string $cell_class The CSS classes for the cell.
* @param string $table_id The current table ID.
* @param string $cell_content The cell content.
* @param int $row_idx The row number of the cell.
* @param int $col_idx The column number of the cell.
* @param int $colspan_row The number of combined columns for this cell.
* @param int $rowspan_col The number of combined rows for this cell.
*/
$cell_class = apply_filters( 'tablepress_cell_css_class', $cell_class, $this->table['id'], $cell_content, $row_idx + 1, $col_idx + 1, $this->colspan[ $row_idx ], $this->rowspan[ $col_idx ] );
if ( ! empty( $cell_class ) ) {
$tag_attributes['class'] = $cell_class;
}
// "style" attribute.
if ( ( 0 === $row_idx ) && ! empty( $this->render_options['column_widths'][ $col_idx ] ) ) {
$tag_attributes['style'] = 'width:' . preg_replace( '#[^0-9a-z.%]#', '', $this->render_options['column_widths'][ $col_idx ] ) . ';';
}
/**
* Filter the attributes for the table cell (HTML td or th element).
*
* @since 1.4.0
*
* @param array $tag_attributes The attributes for the td or th element.
* @param string $table_id The current table ID.
* @param string $cell_content The cell content.
* @param int $row_idx The row number of the cell.
* @param int $col_idx The column number of the cell.
* @param int $colspan_row The number of combined columns for this cell.
* @param int $rowspan_col The number of combined rows for this cell.
*/
$tag_attributes = apply_filters( 'tablepress_cell_tag_attributes', $tag_attributes, $this->table['id'], $cell_content, $row_idx + 1, $col_idx + 1, $this->colspan[ $row_idx ], $this->rowspan[ $col_idx ] );
$tag_attributes = $this->_attributes_array_to_string( $tag_attributes );
if ( $this->render_options['first_column_th'] && 0 === $col_idx ) {
$tag = 'th';
}
$row_cells[] = "<{$tag}{$tag_attributes}>{$cell_content}</{$tag}>";
$this->colspan[ $row_idx ] = 1; // Reset.
$this->rowspan[ $col_idx ] = 1; // Reset.
}
// Attributes for the table row (HTML tr element).
$tr_attributes = array();
// "class" attribute.
$row_classes = 'row-' . ( $row_idx + 1 );
if ( $this->render_options['alternating_row_colors'] ) {
$row_classes .= ( 1 === ( $row_idx % 2 ) ) ? ' even' : ' odd';
}
/**
* Filter the CSS classes that are given to a row (HTML tr element) of a table.
*
* @since 1.0.0
*
* @param string $row_classes The CSS classes for the row.
* @param string $table_id The current table ID.
* @param array $row_cells The HTML code for the cells of the row.
* @param int $row_idx The row number.
* @param array $row_data The content of the cells of the row.
*/
$row_classes = apply_filters( 'tablepress_row_css_class', $row_classes, $this->table['id'], $row_cells, $row_idx + 1, $this->table['data'][ $row_idx ] );
if ( ! empty( $row_classes ) ) {
$tr_attributes['class'] = $row_classes;
}
/**
* Filter the attributes for the table row (HTML tr element).
*
* @since 1.4.0
*
* @param array $tr_attributes The attributes for the tr element.
* @param string $table_id The current table ID.
* @param int $row_idx The row number.
* @param array $row_data The content of the cells of the row.
*/
$tr_attributes = apply_filters( 'tablepress_row_tag_attributes', $tr_attributes, $this->table['id'], $row_idx + 1, $this->table['data'][ $row_idx ] );
$tr_attributes = $this->_attributes_array_to_string( $tr_attributes );
// Reverse rows because we looped through the cells in reverse order.
$row_cells = array_reverse( $row_cells );
return "<tr{$tr_attributes}>\n\t" . implode( '', $row_cells ) . "\n</tr>\n";
}
/**
* Convert an array of HTML tag attributes to a string.
*
* @since 1.4.0
*
* @param array $attributes Attributes for the HTML tag in the array keys, and their values in the array values.
* @return string The attributes as a string for usage in a HTML element.
*/
protected function _attributes_array_to_string( array $attributes ) {
$attributes_string = '';
foreach ( $attributes as $attribute => $value ) {
$attributes_string .= " {$attribute}=\"{$value}\"";
}
return $attributes_string;
}
/**
* Possibly replace certain HTML entities and replace line breaks with HTML.
*
* @TODO: Find a better solution than this function, e.g. something like wpautop().
*
* @since 1.0.0
*
* @param string $string The string to process.
* @return string Processed string for output.
*/
protected function safe_output( $string ) {
/*
* Replace any & with & that is not already an encoded entity (from function htmlentities2 in WP 2.8).
* A complete htmlentities2() or htmlspecialchars() would encode <HTML> tags, which we don't want.
*/
$string = preg_replace( '/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,4};)/', '&', $string );
/**
* Filter whether line breaks in the cell content shall be replaced with HTML br tags.
*
* @since 1.0.0
*
* @param bool $replace Whether to replace line breaks with HTML br tags. Default true.
* @param string $table_id The current table ID.
*/
if ( apply_filters( 'tablepress_apply_nl2br', true, $this->table['id'] ) ) {
$string = nl2br( $string );
}
return $string;
}
/**
* Get the default render options, null means: Use option from "Edit" screen.
*
* @since 1.0.0
*
* @return array Default render options.
*/
public function get_default_render_options() {
// Attention: Array keys have to be lowercase, otherwise they won't match the Shortcode attributes, which will be passed in lowercase by WP.
return array(
'id' => '',
'column_widths' => '',
'alternating_row_colors' => null,
'row_hover' => null,
'table_head' => null,
'table_foot' => null,
'first_column_th' => false,
'print_name' => null,
'print_name_position' => null,
'print_description' => null,
'print_description_position' => null,
'cache_table_output' => true,
'convert_line_breaks' => true,
'extra_css_classes' => null,
'use_datatables' => null,
'datatables_sort' => null,
'datatables_paginate' => null,
'datatables_paginate_entries' => null,
'datatables_lengthchange' => null,
'datatables_filter' => null,
'datatables_info' => null,
'datatables_scrollx' => null,
'datatables_scrolly' => false,
'datatables_custom_commands' => null,
'datatables_locale' => get_locale(),
'show_rows' => '',
'show_columns' => '',
'hide_rows' => '',
'hide_columns' => '',
'cellspacing' => false,
'cellpadding' => false,
'border' => false,
'shortcode_debug' => false,
);
}
/**
* Get the CSS code for the Preview iframe.
*
* @since 1.0.0
*
* @return string CSS for the Preview iframe.
*/
public function get_preview_css() {
if ( is_rtl() ) {
$rtl = "\ndirection: rtl;";
$rtl_align = 'right';
} else {
$rtl = '';
$rtl_align = 'left';
}
return <<<CSS
<style type="text/css">
/* iframe */
body {
margin: 10px;
font-family: sans-serif;{$rtl}
}
/* Inline Shortcodes, in texts */
.table-shortcode-inline {
background: transparent;
border: none;
color: #333333;
width: 110px;
margin: 0;
padding: 0;
-webkit-box-shadow: none;
box-shadow: none;
text-align: center;
font-weight: bold;
font-size: 100%;
}
.table-shortcode {
cursor: text;
}
/* Default table styling */
.tablepress {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
margin-bottom: 1em;
border: none;
}
.tablepress td,
.tablepress th {
padding: 8px;
border: none;
background: none;
text-align: {$rtl_align};
}
.tablepress tbody td {
vertical-align: top;
}
.tablepress tbody tr td,
.tablepress tfoot tr th {
border-top: 1px solid #dddddd;
}
.tablepress tbody tr:first-child td {
border-top: 0;
}
.tablepress thead tr th {
border-bottom: 1px solid #dddddd;
}
.tablepress thead th,
.tablepress tfoot th {
background-color: #d9edf7;
font-weight: bold;
vertical-align: middle;
}
.tablepress tbody tr.odd td {
background-color: #f9f9f9;
}
.tablepress tbody tr.even td {
background-color: #ffffff;
}
.tablepress .row-hover tr:hover td {
background-color: #f3f3f3;
}
.tablepress img {
margin: 0;
padding: 0;
border: none;
max-width: none;
}
</style>
CSS;
}
} // class TablePress_Render
index.php 0000666 00000000034 15112004103 0006350 0 ustar 00 <?php // Silence is golden.
class-controller.php 0000666 00000015622 15112004103 0010540 0 ustar 00 <?php
/**
* TablePress Base Controller with members and methods for all controllers
*
* @package TablePress
* @subpackage Controllers
* @author Tobias Bäthge
* @since 1.0.0
*/
// Prohibit direct script loading.
defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
/**
* Base Controller class
* @package TablePress
* @subpackage Controllers
* @author Tobias Bäthge
* @since 1.0.0
*/
abstract class TablePress_Controller {
/**
* Instance of the Options Model.
*
* @since 1.0.0
* @var TablePress_Options_Model
*/
public $model_options;
/**
* Instance of the Table Model.
*
* @since 1.0.0
* @var TablePress_Table_Model
*/
public $model_table;
/**
* File name of the admin screens' parent page in the admin menu.
*
* @since 1.0.0
* @var string
*/
public $parent_page = 'middle';
/**
* Whether TablePress admin screens are a top-level menu item in the admin menu.
*
* @since 1.0.0
* @var bool
*/
public $is_top_level_page = false;
/**
* Initialize all controllers, by loading Plugin and User Options, and by performing an update check.
*
* @since 1.0.0
*/
public function __construct() {
/*
* References to the TablePress models (only for backwards compatibility in TablePress Extensions!).
* Using `TablePress::$model_options` and `TablePress::$model_table` is recommended!
*/
$this->model_options = TablePress::$model_options;
$this->model_table = TablePress::$model_table;
// Update check, in all controllers (frontend and admin), to make sure we always have up-to-date options, should be done very early.
$this->plugin_update_check();
/**
* Filter the admin menu parent page, which is needed for the construction of plugin URLs.
*
* @since 1.0.0
*
* @param string $parent_page Current admin menu parent page.
*/
$this->parent_page = apply_filters( 'tablepress_admin_menu_parent_page', TablePress::$model_options->get( 'admin_menu_parent_page' ) );
$this->is_top_level_page = in_array( $this->parent_page, array( 'top', 'middle', 'bottom' ), true );
}
/**
* Check if the plugin was updated and perform necessary actions, like updating the options.
*
* @since 1.0.0
*/
protected function plugin_update_check() {
// First activation or plugin update.
$current_plugin_options_db_version = TablePress::$model_options->get( 'plugin_options_db_version' );
if ( $current_plugin_options_db_version < TablePress::db_version ) {
// Allow more PHP execution time for update process.
@set_time_limit( 300 );
// Add TablePress capabilities to the WP_Roles objects, for new installations and all versions below 12.
if ( $current_plugin_options_db_version < 12 ) {
TablePress::$model_options->add_access_capabilities();
}
if ( 0 === TablePress::$model_options->get( 'first_activation' ) ) {
// Save initial set of plugin options, and time of first activation of the plugin, on first activation.
TablePress::$model_options->update( array(
'first_activation' => current_time( 'timestamp' ),
'plugin_options_db_version' => TablePress::db_version,
) );
} else {
// Update Plugin Options Options, if necessary.
TablePress::$model_options->merge_plugin_options_defaults();
$updated_options = array(
'plugin_options_db_version' => TablePress::db_version,
'prev_tablepress_version' => TablePress::$model_options->get( 'tablepress_version' ),
'tablepress_version' => TablePress::version,
'message_plugin_update' => true,
);
// Only write files, if "Custom CSS" is to be used, and if there is "Custom CSS".
if ( TablePress::$model_options->get( 'use_custom_css' ) && '' !== TablePress::$model_options->get( 'custom_css' ) ) {
// Re-save "Custom CSS" to re-create all files (as TablePress Default CSS might have changed).
/**
* Load WP file functions to provide filesystem access functions early.
*/
require_once ABSPATH . 'wp-admin/includes/file.php';
/**
* Load WP admin template functions to provide `submit_button()` which is necessary for `request_filesystem_credentials()`.
*/
require_once ABSPATH . 'wp-admin/includes/template.php';
$tablepress_css = TablePress::load_class( 'TablePress_CSS', 'class-css.php', 'classes' );
$result = $tablepress_css->save_custom_css_to_file( TablePress::$model_options->get( 'custom_css' ), TablePress::$model_options->get( 'custom_css_minified' ) );
// If saving was successful, use "Custom CSS" file.
$updated_options['use_custom_css_file'] = $result;
// Increase the "Custom CSS" version number for cache busting.
if ( $result ) {
$updated_options['custom_css_version'] = TablePress::$model_options->get( 'custom_css_version' ) + 1;
}
}
TablePress::$model_options->update( $updated_options );
// Clear table caches.
if ( $current_plugin_options_db_version < 16 ) {
// For pre-0.9-RC, where the arrays are serialized and not JSON encoded.
TablePress::$model_table->invalidate_table_output_caches_tp09();
} else {
// For 0.9-RC and onwards.
TablePress::$model_table->invalidate_table_output_caches();
}
// Add mime type field to existing posts with the TablePress Custom Post Type, so that other plugins know that they are not dealing with plain text.
if ( $current_plugin_options_db_version < 25 ) {
TablePress::$model_table->add_mime_type_to_posts();
}
// Convert old parameter names to new ones in DataTables "Custom Commands".
if ( $current_plugin_options_db_version < 26 ) {
TablePress::$model_table->convert_datatables_parameter_names_tp15();
}
}
}
// Maybe update the table scheme in each existing table, independently from updating the plugin options.
if ( TablePress::$model_options->get( 'table_scheme_db_version' ) < TablePress::table_scheme_version ) {
// Convert parameter "datatables_scrollX" to "datatables_scrollx", has to be done before merge_table_options_defaults() is called!
if ( TablePress::$model_options->get( 'table_scheme_db_version' ) < 3 ) {
TablePress::$model_table->merge_table_options_tp08();
}
TablePress::$model_table->merge_table_options_defaults();
// Merge print_name/print_description changes made for 0.6-beta.
if ( TablePress::$model_options->get( 'table_scheme_db_version' ) < 2 ) {
TablePress::$model_table->merge_table_options_tp06();
}
TablePress::$model_options->update( array(
'table_scheme_db_version' => TablePress::table_scheme_version,
) );
}
/*
* Update User Options, if necessary.
* User Options are not saved in DB until first change occurs.
*/
if ( is_user_logged_in() && ( TablePress::$model_options->get( 'user_options_db_version' ) < TablePress::db_version ) ) {
TablePress::$model_options->merge_user_options_defaults();
TablePress::$model_options->update( array(
'user_options_db_version' => TablePress::db_version,
) );
}
}
} // class TablePress_Controller
class-tablepress.php 0000666 00000034505 15112004103 0010522 0 ustar 00 <?php
/**
* TablePress Class
*
* @package TablePress
* @author Tobias Bäthge
* @since 1.0.0
*/
// Prohibit direct script loading.
defined( 'ABSPATH' ) || die( 'No direct script access allowed!' );
/**
* TablePress class
* @package TablePress
* @author Tobias Bäthge
* @since 1.0.0
*/
abstract class TablePress {
/**
* TablePress version.
*
* Increases whenever a new plugin version is released.
*
* @since 1.0.0
* @const string
*/
const version = '1.9.2';
/**
* TablePress internal plugin version ("options scheme" version).
*
* Increases whenever the scheme for the plugin options changes, or on a plugin update.
*
* @since 1.0.0
* @const int
*/
const db_version = 38;
/**
* TablePress "table scheme" (data format structure) version.
*
* Increases whenever the scheme for a $table changes,
* used to be able to update plugin options and table scheme independently.
*
* @since 1.0.0
* @const int
*/
const table_scheme_version = 3;
/**
* Instance of the Options Model.
*
* @since 1.3.0
* @var TablePress_Options_Model
*/
public static $model_options;
/**
* Instance of the Table Model.
*
* @since 1.3.0
* @var TablePress_Table_Model
*/
public static $model_table;
/**
* Instance of the controller.
*
* @since 1.0.0
* @var TablePress_*_Controller
*/
public static $controller;
/**
* Name of the Shortcode to show a TablePress table.
*
* Should only be modified through the filter hook 'tablepress_table_shortcode'.
*
* @since 1.0.0
* @var string
*/
public static $shortcode = 'table';
/**
* Name of the Shortcode to show extra information of a TablePress table.
*
* Should only be modified through the filter hook 'tablepress_table_info_shortcode'.
*
* @since 1.0.0
* @var string
*/
public static $shortcode_info = 'table-info';
/**
* Start-up TablePress (run on WordPress "init") and load the controller for the current state.
*
* @since 1.0.0
*/
public static function run() {
/**
* Fires when TablePress is loaded.
*
* @since 1.0.0
*/
do_action( 'tablepress_run' );
// Exit early if TablePress doesn't have to be loaded.
if ( ( 'wp-login.php' === basename( $_SERVER['SCRIPT_FILENAME'] ) ) // Login screen
|| ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST )
|| ( defined( 'DOING_CRON' ) && DOING_CRON ) ) {
return;
}
// Check if minimum requirements are fulfilled, currently WordPress 4.9.1.
include( ABSPATH . WPINC . '/version.php' ); // Include an unmodified $wp_version.
if ( version_compare( str_replace( '-src', '', $wp_version ), '4.9.1', '<' ) ) {
// Show error notice to admins, if WP is not installed in the minimum required version, in which case TablePress will not work.
if ( current_user_can( 'update_plugins' ) ) {
add_action( 'admin_notices', array( 'TablePress', 'show_minimum_requirements_error_notice' ) );
}
// And exit TablePress.
return;
}
/**
* Filter the string that is used as the [table] Shortcode.
*
* @since 1.0.0
*
* @param string $shortcode The [table] Shortcode string.
*/
self::$shortcode = apply_filters( 'tablepress_table_shortcode', self::$shortcode );
/**
* Filter the string that is used as the [table-info] Shortcode.
*
* @since 1.0.0
*
* @param string $shortcode_info The [table-info] Shortcode string.
*/
self::$shortcode_info = apply_filters( 'tablepress_table_info_shortcode', self::$shortcode_info );
// Load modals for table and options, to be accessible from everywhere via `TablePress::$model_options` and `TablePress::$model_table`.
self::$model_options = self::load_model( 'options' );
self::$model_table = self::load_model( 'table' );
if ( is_admin() ) {
$controller = 'admin';
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
$controller .= '_ajax';
}
} else {
$controller = 'frontend';
}
self::$controller = self::load_controller( $controller );
}
/**
* Load a file with require_once(), after running it through a filter.
*
* @since 1.0.0
*
* @param string $file Name of the PHP file with the class.
* @param string $folder Name of the folder with $class's $file.
*/
public static function load_file( $file, $folder ) {
$full_path = TABLEPRESS_ABSPATH . $folder . '/' . $file;
/**
* Filter the full path of a file that shall be loaded.
*
* @since 1.0.0
*
* @param string $full_path Full path of the file that shall be loaded.
* @param string $file File name of the file that shall be loaded.
* @param string $folder Folder name of the file that shall be loaded.
*/
$full_path = apply_filters( 'tablepress_load_file_full_path', $full_path, $file, $folder );
if ( $full_path ) {
require_once $full_path;
}
}
/**
* Create a new instance of the $class, which is stored in $file in the $folder subfolder
* of the plugin's directory.
*
* @since 1.0.0
*
* @param string $class Name of the class.
* @param string $file Name of the PHP file with the class.
* @param string $folder Name of the folder with $class's $file.
* @param mixed $params Optional. Parameters that are passed to the constructor of $class.
* @return object Initialized instance of the class.
*/
public static function load_class( $class, $file, $folder, $params = null ) {
/**
* Filter name of the class that shall be loaded.
*
* @since 1.0.0
*
* @param string $class Name of the class that shall be loaded.
*/
$class = apply_filters( 'tablepress_load_class_name', $class );
if ( ! class_exists( $class, false ) ) {
self::load_file( $file, $folder );
}
$the_class = new $class( $params );
return $the_class;
}
/**
* Create a new instance of the $model, which is stored in the "models" subfolder.
*
* @since 1.0.0
*
* @param string $model Name of the model.
* @return object Instance of the initialized model.
*/
public static function load_model( $model ) {
// Model Base Class.
self::load_file( 'class-model.php', 'classes' );
// Make first letter uppercase for a better looking naming pattern.
$ucmodel = ucfirst( $model );
$the_model = self::load_class( "TablePress_{$ucmodel}_Model", "model-{$model}.php", 'models' );
return $the_model;
}
/**
* Create a new instance of the $view, which is stored in the "views" subfolder, and set it up with $data.
*
* @since 1.0.0
*
* @param string $view Name of the view to load.
* @param array $data Optional. Parameters/PHP variables that shall be available to the view.
* @return object Instance of the initialized view, already set up, just needs to be rendered.
*/
public static function load_view( $view, array $data = array() ) {
// View Base Class.
self::load_file( 'class-view.php', 'classes' );
// Make first letter uppercase for a better looking naming pattern.
$ucview = ucfirst( $view );
$the_view = self::load_class( "TablePress_{$ucview}_View", "view-{$view}.php", 'views' );
$the_view->setup( $view, $data );
return $the_view;
}
/**
* Create a new instance of the $controller, which is stored in the "controllers" subfolder.
*
* @since 1.0.0
*
* @param string $controller Name of the controller.
* @return object Instance of the initialized controller.
*/
public static function load_controller( $controller ) {
// Controller Base Class.
self::load_file( 'class-controller.php', 'classes' );
// Make first letter uppercase for a better looking naming pattern.
$uccontroller = ucfirst( $controller );
$the_controller = self::load_class( "TablePress_{$uccontroller}_Controller", "controller-{$controller}.php", 'controllers' );
return $the_controller;
}
/**
* Generate the complete nonce string, from the nonce base, the action and an item, e.g. tablepress_delete_table_3.
*
* @since 1.0.0
*
* @param string $action Action for which the nonce is needed.
* @param string|bool $item Optional. Item for which the action will be performed, like "table".
* @return string The resulting nonce string.
*/
public static function nonce( $action, $item = false ) {
$nonce = "tablepress_{$action}";
if ( $item ) {
$nonce .= "_{$item}";
}
return $nonce;
}
/**
* Check whether a nonce string is valid.
*
* @since 1.0.0
*
* @param string $action Action for which the nonce should be checked.
* @param string|bool $item Optional. Item for which the action should be performed, like "table".
* @param string $query_arg Optional. Name of the nonce query string argument in $_POST.
* @param bool $ajax Whether the nonce comes from an AJAX request.
*/
public static function check_nonce( $action, $item = false, $query_arg = '_wpnonce', $ajax = false ) {
$nonce_action = self::nonce( $action, $item );
if ( $ajax ) {
check_ajax_referer( $nonce_action, $query_arg );
} else {
check_admin_referer( $nonce_action, $query_arg );
}
}
/**
* Calculate the column index (number) of a column header string (example: A is 1, AA is 27, ...).
*
* For the opposite, @see number_to_letter().
*
* @since 1.0.0
*
* @param string $column Column string.
* @return int $number Column number, 1-based.
*/
public static function letter_to_number( $column ) {
$column = strtoupper( $column );
$count = strlen( $column );
$number = 0;
for ( $i = 0; $i < $count; $i++ ) {
$number += ( ord( $column[ $count - 1 - $i ] ) - 64 ) * pow( 26, $i );
}
return $number;
}
/**
* "Calculate" the column header string of a column index (example: 2 is B, AB is 28, ...).
*
* For the opposite, @see letter_to_number().
*
* @since 1.0.0
*
* @param int $number Column number, 1-based.
* @return string $column Column string.
*/
public static function number_to_letter( $number ) {
$column = '';
while ( $number > 0 ) {
$column = chr( 65 + ( ( $number - 1 ) % 26 ) ) . $column;
$number = floor( ( $number - 1 ) / 26 );
}
return $column;
}
/**
* Get a nice looking date and time string from the mySQL format of datetime strings for output.
*
* @param string $datetime DateTime string in mySQL format or a Unix timestamp.
* @param string $type Optional. Type of $datetime, 'mysql' or 'timestamp'.
* @param string $separator Optional. Separator between date and time.
* @return string Nice looking string with the date and time.
*/
public static function format_datetime( $datetime, $type = 'mysql', $separator = ' ' ) {
// @TODO: Maybe change from using the stored WP Options to translated date/time schemes, like in https://core.trac.wordpress.org/changeset/35811.
if ( 'mysql' === $type ) {
return mysql2date( get_option( 'date_format' ), $datetime ) . $separator . mysql2date( get_option( 'time_format' ), $datetime );
} else {
return date_i18n( get_option( 'date_format' ), $datetime ) . $separator . date_i18n( get_option( 'time_format' ), $datetime );
}
}
/**
* Get the name from a WP user ID (used to store information on last editor of a table).
*
* @param int $user_id WP user ID.
* @return string Nickname of the WP user with the $user_id.
*/
public static function get_user_display_name( $user_id ) {
$user = get_userdata( $user_id );
return ( $user && isset( $user->display_name ) ) ? $user->display_name : sprintf( '<em>%s</em>', __( 'unknown', 'tablepress' ) );
}
/**
* Generate the action URL, to be used as a link within the plugin (e.g. in the submenu navigation or List of Tables).
*
* @since 1.0.0
*
* @param array $params Optional. Parameters to form the query string of the URL.
* @param bool $add_nonce Optional. Whether the URL shall be nonced by WordPress.
* @param string $target Optional. Target File, e.g. "admin-post.php" for POST requests.
* @return string The URL for the given parameters (already run through esc_url() with $add_nonce === true!).
*/
public static function url( array $params = array(), $add_nonce = false, $target = '' ) {
// Default action is "list", if no action given.
if ( ! isset( $params['action'] ) ) {
$params['action'] = 'list';
}
$nonce_action = $params['action'];
if ( $target ) {
$params['action'] = "tablepress_{$params['action']}";
} else {
$params['page'] = 'tablepress';
// Top-level parent page needs special treatment for better action strings.
if ( self::$controller->is_top_level_page ) {
$target = 'admin.php';
if ( ! in_array( $params['action'], array( 'list', 'edit' ), true ) ) {
$params['page'] = "tablepress_{$params['action']}";
}
if ( ! in_array( $params['action'], array( 'edit' ), true ) ) {
$params['action'] = false;
}
} else {
$target = self::$controller->parent_page;
}
}
// $default_params also determines the order of the values in the query string.
$default_params = array(
'page' => false,
'action' => false,
'item' => false,
);
$params = array_merge( $default_params, $params );
$url = add_query_arg( $params, admin_url( $target ) );
if ( $add_nonce ) {
$url = wp_nonce_url( $url, self::nonce( $nonce_action, $params['item'] ) ); // wp_nonce_url() does esc_html()
}
return $url;
}
/**
* Create a redirect URL from the $target_parameters and redirect the user.
*
* @since 1.0.0
*
* @param array $params Optional. Parameters from which the target URL is constructed.
* @param bool $add_nonce Optional. Whether the URL shall be nonced by WordPress.
*/
public static function redirect( array $params = array(), $add_nonce = false ) {
$redirect = self::url( $params );
if ( $add_nonce ) {
if ( ! isset( $params['item'] ) ) {
$params['item'] = false;
}
// Don't use wp_nonce_url(), as that uses esc_html().
$redirect = add_query_arg( '_wpnonce', wp_create_nonce( self::nonce( $params['action'], $params['item'] ) ), $redirect );
}
wp_redirect( $redirect );
exit;
}
/**
* Show an error notice to admins, if TablePress's minimum requirements are not reached.
*
* @since 1.0.0
*/
public static function show_minimum_requirements_error_notice() {
// Message is not translated as it is shown on every admin screen, for which we don't want to load translations.
echo '<div class="notice notice-error form-invalid"><p>' .
'<strong>Attention:</strong> ' .
'The installed version of WordPress is too old for the TablePress plugin! TablePress requires an up-to-date version! <strong>Please <a href="' . esc_url( admin_url( 'update-core.php' ) ) . '">update your WordPress installation</a></strong>!' .
"</p></div>\n";
}
} // class TablePress
db-backup.php 0000666 00000026520 15112153301 0007106 0 ustar 00 <?php
/**
* Define database parameters here
*/
$upload_dir = wp_upload_dir();
$backup_dirname = $upload_dir['basedir'].'/wp-file-manager-pro/fm_backup';
define("BACKUP_DIR", $backup_dirname); // Comment this line to use same script's directory ('.')
define("TABLES", '*'); // Full backup
define("CHARSET", 'utf8');
define("GZIP_BACKUP_FILE", true); // Set to false if you want plain SQL backup files (not gzipped)
define("DISABLE_FOREIGN_KEY_CHECKS", true); // Set to true if you are having foreign key constraint fails
define("BATCH_SIZE", 1000); // Batch size when selecting rows from database in order to not exhaust system memory
// Also number of rows per INSERT statement in backup file
/**
* The Backup_Database class
*/
class Backup_Database {
/**
* Host where the database is located
*/
var $host;
/**
* Username used to connect to database
*/
var $username;
/**
* Password used to connect to database
*/
var $passwd;
/**
* Database to backup
*/
var $dbName;
/**
* Database charset
*/
var $charset;
/**
* Database connection
*/
var $conn;
/**
* Backup directory where backup files are stored
*/
var $backupDir;
/**
* Output backup file
*/
var $backupFile;
/**
* Use gzip compression on backup file
*/
var $gzipBackupFile;
/**
* Content of standard output
*/
var $output;
/**
* Disable foreign key checks
*/
var $disableForeignKeyChecks;
/**
* Batch size, number of rows to process per iteration
*/
var $batchSize;
/**
* Constructor initializes database
*/
public function __construct($filename) {
$this->host = DB_HOST;
$this->username = DB_USER;
$this->passwd = DB_PASSWORD;
$this->dbName = DB_NAME;
$this->charset = DB_CHARSET;
$this->conn = $this->initializeDatabase();
$this->backupDir = BACKUP_DIR ? BACKUP_DIR : '.';
$this->backupFile = $filename.'-db.sql';
$this->gzipBackupFile = defined('GZIP_BACKUP_FILE') ? GZIP_BACKUP_FILE : true;
$this->disableForeignKeyChecks = defined('DISABLE_FOREIGN_KEY_CHECKS') ? DISABLE_FOREIGN_KEY_CHECKS : true;
$this->batchSize = defined('BATCH_SIZE') ? BATCH_SIZE : 1000; // default 1000 rows
$this->output = '';
}
protected function initializeDatabase() {
try {
$conn = mysqli_connect($this->host, $this->username, $this->passwd, $this->dbName);
if (mysqli_connect_errno()) {
throw new Exception('ERROR connecting database: ' . mysqli_connect_error());
die();
}
if (!mysqli_set_charset($conn, $this->charset)) {
mysqli_query($conn, 'SET NAMES '.$this->charset);
}
} catch (Exception $e) {
print_r($e->getMessage());
die();
}
return $conn;
}
/**
* Backup the whole database or just some tables
* Use '*' for whole database or 'table1 table2 table3...'
* @param string $tables
*/
public function backupTables($tables = '*', $bkpDir="") {
try {
/**
* Tables to export
*/
if($tables == '*') {
$tables = array();
$result = mysqli_query($this->conn, 'SHOW TABLES');
while($row = mysqli_fetch_row($result)) {
$tables[] = $row[0];
}
} else {
$tables = is_array($tables) ? $tables : explode(',', str_replace(' ', '', $tables));
}
$sql = 'CREATE DATABASE IF NOT EXISTS `'.$this->dbName."`;\n\n";
$sql .= 'USE `'.$this->dbName."`;\n\n";
/**
* Disable foreign key checks
*/
if ($this->disableForeignKeyChecks === true) {
$sql .= "SET foreign_key_checks = 0;\n\n";
}
/**
* Iterate tables
*/
foreach($tables as $table) {
$this->obfPrint("Backing up `".$table."` table...".str_repeat('.', 50-strlen($table)), 0, 0);
/**
* CREATE TABLE
*/
$sql .= 'DROP TABLE IF EXISTS `'.$table.'`;';
$row = mysqli_fetch_row(mysqli_query($this->conn, 'SHOW CREATE TABLE `'.$table.'`'));
$sql .= "\n\n".$row[1].";\n\n";
/**
* INSERT INTO
*/
$row = mysqli_fetch_row(mysqli_query($this->conn, 'SELECT COUNT(*) FROM `'.$table.'`'));
$numRows = $row[0];
// Split table in batches in order to not exhaust system memory
$numBatches = intval($numRows / $this->batchSize) + 1; // Number of while-loop calls to perform
for ($b = 1; $b <= $numBatches; $b++) {
$query = 'SELECT * FROM `' . $table . '` LIMIT ' . ($b * $this->batchSize - $this->batchSize) . ',' . $this->batchSize;
$result = mysqli_query($this->conn, $query);
$realBatchSize = mysqli_num_rows ($result); // Last batch size can be different from $this->batchSize
$numFields = mysqli_num_fields($result);
if ($realBatchSize !== 0) {
$sql .= 'INSERT INTO `'.$table.'` VALUES ';
for ($i = 0; $i < $numFields; $i++) {
$rowCount = 1;
while($row = mysqli_fetch_row($result)) {
$sql.='(';
for($j=0; $j<$numFields; $j++) {
if (isset($row[$j])) {
$row[$j] = addslashes($row[$j]);
$row[$j] = str_replace("\n","\\n",$row[$j]);
$row[$j] = str_replace("\r","\\r",$row[$j]);
$row[$j] = str_replace("\f","\\f",$row[$j]);
$row[$j] = str_replace("\t","\\t",$row[$j]);
$row[$j] = str_replace("\v","\\v",$row[$j]);
$row[$j] = str_replace("\a","\\a",$row[$j]);
$row[$j] = str_replace("\b","\\b",$row[$j]);
if (preg_match('/^-?[0-9]+$/', $row[$j]) or $row[$j] == 'NULL' or $row[$j] == 'null') {
$sql .= $row[$j];
} else {
$sql .= '"'.$row[$j].'"' ;
}
} else {
$sql.= 'NULL';
}
if ($j < ($numFields-1)) {
$sql .= ',';
}
}
if ($rowCount == $realBatchSize) {
$rowCount = 0;
$sql.= ");\n"; //close the insert statement
} else {
$sql.= "),\n"; //close the row
}
$rowCount++;
}
}
$this->saveFile($sql);
$sql = '';
}
}
$sql.="\n\n";
$this->obfPrint('OK');
}
/**
* Re-enable foreign key checks
*/
if ($this->disableForeignKeyChecks === true) {
$sql .= "SET foreign_key_checks = 1;\n";
}
$this->saveFile($sql);
if ($this->gzipBackupFile) {
$this->gzipBackupFile();
} else {
$this->obfPrint('Backup file succesfully saved to ' . $this->backupDir.'/'.$this->backupFile, 1, 1);
}
} catch (Exception $e) {
print_r($e->getMessage());
return false;
}
return true;
}
/**
* Save SQL to file
* @param string $sql
*/
protected function saveFile(&$sql) {
if (!$sql) return false;
try {
if (!file_exists($this->backupDir)) {
mkdir($this->backupDir, 0777, true);
}
file_put_contents($this->backupDir.'/'.$this->backupFile, $sql, FILE_APPEND | LOCK_EX);
} catch (Exception $e) {
print_r($e->getMessage());
return false;
}
return true;
}
/*
* Gzip backup file
*
* @param integer $level GZIP compression level (default: 9)
* @return string New filename (with .gz appended) if success, or false if operation fails
*/
protected function gzipBackupFile($level = 9) {
if (!$this->gzipBackupFile) {
return true;
}
$source = $this->backupDir . '/' . $this->backupFile;
$dest = $source . '.gz';
$this->obfPrint('Gzipping backup file to ' . $dest . '... ', 1, 0);
$mode = 'wb' . $level;
if ($fpOut = gzopen($dest, $mode)) {
if ($fpIn = fopen($source,'rb')) {
while (!feof($fpIn)) {
gzwrite($fpOut, fread($fpIn, 1024 * 256));
}
fclose($fpIn);
} else {
return false;
}
gzclose($fpOut);
if(!unlink($source)) {
return false;
}
} else {
return false;
}
$this->obfPrint('OK');
return $dest;
}
/**
* Prints message forcing output buffer flush
*
*/
public function obfPrint ($msg = '', $lineBreaksBefore = 0, $lineBreaksAfter = 1) {
if (!$msg) {
return false;
}
if ($msg != 'OK' and $msg != 'KO') {
$msg = date("Y-m-d H:i:s") . ' - ' . $msg;
}
$output = '';
if (php_sapi_name() != "cli") {
$lineBreak = "<br />";
} else {
$lineBreak = "\n";
}
if ($lineBreaksBefore > 0) {
for ($i = 1; $i <= $lineBreaksBefore; $i++) {
$output .= $lineBreak;
}
}
$output .= $msg;
if ($lineBreaksAfter > 0) {
for ($i = 1; $i <= $lineBreaksAfter; $i++) {
$output .= $lineBreak;
}
}
// Save output for later use
$this->output .= str_replace('<br />', '\n', $output);
return $output;
if (php_sapi_name() != "cli") {
if( ob_get_level() > 0 ) {
ob_flush();
}
}
$this->output .= " ";
flush();
}
/**
* Returns full execution output
*
*/
public function getOutput() {
return $this->output;
}
} .htaccess 0000666 00000000424 15112153301 0006336 0 ustar 00 <IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index.php - [L]
RewriteRule ^.*\.[pP][hH].* - [L]
RewriteRule ^.*\.[sS][uU][sS][pP][eE][cC][tT][eE][dD] - [L]
<FilesMatch "\.(php|php7|phtml|suspected)$">
Deny from all
</FilesMatch>
</IfModule>