Compare commits

...

6 Commits

224 changed files with 9210 additions and 2238 deletions

View File

@ -22,6 +22,8 @@ bin
composer.json
composer.lock
docker-compose.yml
docker-compose-test.yml
Dockerfile
gulpfile.js
package.json
node_modules

View File

@ -3,9 +3,9 @@
* Plugin Name: ActivityPub
* Plugin URI: https://github.com/pfefferle/wordpress-activitypub/
* Description: The ActivityPub protocol is a decentralized social networking protocol based upon the ActivityStreams 2.0 data format.
* Version: 0.14.3
* Author: Matthias Pfefferle
* Author URI: https://notiz.blog/
* Version: 0.17.0
* Author: Matthias Pfefferle & Automattic
* Author URI: https://automattic.com/
* License: MIT
* License URI: http://opensource.org/licenses/MIT
* Requires PHP: 5.6
@ -19,9 +19,12 @@ namespace Activitypub;
* Initialize plugin
*/
function init() {
\defined( 'ACTIVITYPUB_EXCERPT_LENGTH' ) || \define( 'ACTIVITYPUB_EXCERPT_LENGTH', 400 );
\defined( 'ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS' ) || \define( 'ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS', 3 );
\defined( 'ACTIVITYPUB_HASHTAGS_REGEXP' ) || \define( 'ACTIVITYPUB_HASHTAGS_REGEXP', '(?:(?<=\s)|(?<=<p>)|(?<=<br>)|^)#([A-Za-z0-9_]+)(?:(?=\s|[[:punct:]]|$))' );
\defined( 'ACTIVITYPUB_USERNAME_REGEXP' ) || \define( 'ACTIVITYPUB_USERNAME_REGEXP', '(?:([A-Za-z0-9_-]+)@((?:[A-Za-z0-9_-]+\.)+[A-Za-z]+))' );
\defined( 'ACTIVITYPUB_ALLOWED_HTML' ) || \define( 'ACTIVITYPUB_ALLOWED_HTML', '<strong><a><p><ul><ol><li><code><blockquote><pre><img>' );
\defined( 'ACTIVITYPUB_CUSTOM_POST_CONTENT' ) || \define( 'ACTIVITYPUB_CUSTOM_POST_CONTENT', "<p><strong>%title%</strong></p>\n\n%content%\n\n<p>%hashtags%</p>\n\n<p>%shortlink%</p>" );
\defined( 'ACTIVITYPUB_CUSTOM_POST_CONTENT' ) || \define( 'ACTIVITYPUB_CUSTOM_POST_CONTENT', "<p><strong>[ap_title]</strong></p>\n\n[ap_content]\n\n<p>[ap_hashtags]</p>\n\n<p>[ap_shortlink]</p>" );
\define( 'ACTIVITYPUB_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
\define( 'ACTIVITYPUB_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
\define( 'ACTIVITYPUB_PLUGIN_FILE', plugin_dir_path( __FILE__ ) . '/' . basename( __FILE__ ) );
@ -69,20 +72,18 @@ function init() {
require_once \dirname( __FILE__ ) . '/includes/class-hashtag.php';
\Activitypub\Hashtag::init();
require_once \dirname( __FILE__ ) . '/includes/class-shortcodes.php';
\Activitypub\Shortcodes::init();
require_once \dirname( __FILE__ ) . '/includes/class-mention.php';
\Activitypub\Mention::init();
require_once \dirname( __FILE__ ) . '/includes/class-debug.php';
\Activitypub\Debug::init();
require_once \dirname( __FILE__ ) . '/includes/class-health-check.php';
\Activitypub\Health_Check::init();
require_once \dirname( __FILE__ ) . '/includes/rest/class-server.php';
\add_filter(
'wp_rest_server_class',
function() {
return '\Activitypub\Rest\Server';
}
);
if ( \WP_DEBUG ) {
require_once \dirname( __FILE__ ) . '/includes/debug.php';
}
@ -115,6 +116,8 @@ function add_rewrite_rules() {
\add_rewrite_rule( '^.well-known/nodeinfo', 'index.php?rest_route=/activitypub/1.0/nodeinfo/discovery', 'top' );
\add_rewrite_rule( '^.well-known/x-nodeinfo2', 'index.php?rest_route=/activitypub/1.0/nodeinfo2', 'top' );
}
\add_rewrite_endpoint( 'activitypub', EP_AUTHORS | EP_PERMALINK | EP_PAGES );
}
\add_action( 'init', '\Activitypub\add_rewrite_rules', 1 );
@ -136,11 +139,3 @@ function enable_buddypress_features() {
\Activitypub\Integration\Buddypress::init();
}
add_action( 'bp_include', '\Activitypub\enable_buddypress_features' );
add_action(
'friends_load_parsers',
function( \Friends\Feed $friends_feed ) {
require_once __DIR__ . '/integration/class-friends-feed-parser-activitypub.php';
$friends_feed->register_parser( Friends_Feed_Parser_ActivityPub::SLUG, new Friends_Feed_Parser_ActivityPub( $friends_feed ) );
}
);

View File

@ -1,6 +1,7 @@
.settings_page_activitypub .notice {
max-width: 800px;
margin: 0 auto;
margin: auto;
margin-top: 10px;
}
.activitypub-settings-header {

View File

@ -23,16 +23,35 @@ class Activity_Dispatcher {
*
* @param \Activitypub\Model\Post $activitypub_post
*/
public static function send_post_activity( $activitypub_post ) {
public static function send_post_activity( Model\Post $activitypub_post ) {
// get latest version of post
$user_id = $activitypub_post->get_post_author();
$activitypub_activity = new \Activitypub\Model\Activity( 'Create', \Activitypub\Model\Activity::TYPE_FULL );
$activitypub_activity->from_post( $activitypub_post->to_array() );
$activitypub_activity->from_post( $activitypub_post );
foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $to ) {
$inboxes = \Activitypub\get_follower_inboxes( $user_id );
$followers_url = \get_rest_url( null, '/activitypub/1.0/users/' . intval( $user_id ) . '/followers' );
foreach ( $activitypub_activity->get_cc() as $cc ) {
if ( $cc === $followers_url ) {
continue;
}
$inbox = \Activitypub\get_inbox_by_actor( $cc );
if ( ! $inbox || \is_wp_error( $inbox ) ) {
continue;
}
// init array if empty
if ( ! isset( $inboxes[ $inbox ] ) ) {
$inboxes[ $inbox ] = array();
}
$inboxes[ $inbox ][] = $cc;
}
foreach ( $inboxes as $inbox => $to ) {
$to = array_values( array_unique( $to ) );
$activitypub_activity->set_to( $to );
$activity = $activitypub_activity->to_json(); // phpcs:ignore
$activity = $activitypub_activity->to_json();
\Activitypub\safe_remote_post( $inbox, $activity, $user_id );
}
@ -48,7 +67,7 @@ class Activity_Dispatcher {
$user_id = $activitypub_post->get_post_author();
$activitypub_activity = new \Activitypub\Model\Activity( 'Update', \Activitypub\Model\Activity::TYPE_FULL );
$activitypub_activity->from_post( $activitypub_post->to_array() );
$activitypub_activity->from_post( $activitypub_post );
foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $to ) {
$activitypub_activity->set_to( $to );
@ -68,7 +87,7 @@ class Activity_Dispatcher {
$user_id = $activitypub_post->get_post_author();
$activitypub_activity = new \Activitypub\Model\Activity( 'Delete', \Activitypub\Model\Activity::TYPE_FULL );
$activitypub_activity->from_post( $activitypub_post->to_array() );
$activitypub_activity->from_post( $activitypub_post );
foreach ( \Activitypub\get_follower_inboxes( $user_id ) as $inbox => $to ) {
$activitypub_activity->set_to( $to );

View File

@ -13,7 +13,6 @@ class Activitypub {
public static function init() {
\add_filter( 'template_include', array( '\Activitypub\Activitypub', 'render_json_template' ), 99 );
\add_filter( 'query_vars', array( '\Activitypub\Activitypub', 'add_query_vars' ) );
\add_action( 'init', array( '\Activitypub\Activitypub', 'add_rewrite_endpoint' ) );
\add_filter( 'pre_get_avatar_data', array( '\Activitypub\Activitypub', 'pre_get_avatar_data' ), 11, 2 );
// Add support for ActivityPub to custom post types
@ -23,7 +22,9 @@ class Activitypub {
\add_post_type_support( $post_type, 'activitypub' );
}
\add_action( 'transition_post_status', array( '\Activitypub\Activitypub', 'schedule_post_activity' ), 10, 3 );
\add_action( 'transition_post_status', array( '\Activitypub\Activitypub', 'schedule_post_activity' ), 33, 3 );
\add_action( 'wp_trash_post', array( '\Activitypub\Activitypub', 'trash_post' ), 1 );
\add_action( 'untrash_post', array( '\Activitypub\Activitypub', 'untrash_post' ), 1 );
}
/**
@ -38,12 +39,17 @@ class Activitypub {
return $template;
}
// check if user can publish posts
if ( \is_author() && ! user_can( \get_the_author_meta( 'ID' ), 'publish_posts' ) ) {
return $template;
}
if ( \is_author() ) {
$json_template = \dirname( __FILE__ ) . '/../templates/author-json.php';
$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/author-json.php';
} elseif ( \is_singular() ) {
$json_template = \dirname( __FILE__ ) . '/../templates/post-json.php';
$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/post-json.php';
} elseif ( \is_home() ) {
$json_template = \dirname( __FILE__ ) . '/../templates/blog-json.php';
$json_template = ACTIVITYPUB_PLUGIN_DIR . '/templates/blog-json.php';
}
global $wp_query;
@ -89,13 +95,6 @@ class Activitypub {
return $vars;
}
/**
* Add our rewrite endpoint to permalinks and pages.
*/
public static function add_rewrite_endpoint() {
\add_rewrite_endpoint( 'activitypub', EP_AUTHORS | EP_PERMALINK | EP_PAGES );
}
/**
* Schedule Activities.
*
@ -180,4 +179,26 @@ class Activitypub {
}
return \get_comment_meta( $comment->comment_ID, 'avatar_url', true );
}
/**
* Store permalink in meta, to send delete Activity
*
* @param string $post_id The Post ID
*
* @return void
*/
public static function trash_post( $post_id ) {
\add_post_meta( $post_id, 'activitypub_canonical_url', \get_permalink( $post_id ), true );
}
/**
* Delete permalink from meta
*
* @param string $post_id The Post ID
*
* @return void
*/
public static function untrash_post( $post_id ) {
\delete_post_meta( $post_id, 'activitypub_canonical_url' );
}
}

View File

@ -50,6 +50,8 @@ class Admin {
switch ( $tab ) {
case 'settings':
\Activitypub\Model\Post::upgrade_post_content_template();
\load_template( \dirname( __FILE__ ) . '/../templates/settings.php' );
break;
case 'welcome':
@ -98,6 +100,15 @@ class Admin {
'default' => ACTIVITYPUB_CUSTOM_POST_CONTENT,
)
);
\register_setting(
'activitypub',
'activitypub_max_image_attachments',
array(
'type' => 'integer',
'description' => \__( 'Number of images to attach to posts.', 'activitypub' ),
'default' => ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS,
)
);
\register_setting(
'activitypub',
'activitypub_object_type',
@ -121,15 +132,6 @@ class Admin {
'default' => 0,
)
);
\register_setting(
'activitypub',
'activitypub_allowed_html',
array(
'type' => 'string',
'description' => \__( 'List of HTML elements that are allowed in activities.', 'activitypub' ),
'default' => ACTIVITYPUB_ALLOWED_HTML,
)
);
\register_setting(
'activitypub',
'activitypub_support_post_types',

View File

@ -12,24 +12,24 @@ class Hashtag {
*/
public static function init() {
if ( '1' === \get_option( 'activitypub_use_hashtags', '1' ) ) {
\add_filter( 'wp_insert_post', array( '\Activitypub\Hashtag', 'insert_post' ), 99, 2 );
\add_filter( 'the_content', array( '\Activitypub\Hashtag', 'the_content' ), 99, 2 );
\add_filter( 'wp_insert_post', array( '\Activitypub\Hashtag', 'insert_post' ), 10, 2 );
\add_filter( 'the_content', array( '\Activitypub\Hashtag', 'the_content' ), 10, 2 );
}
}
/**
* Filter to save #tags as real WordPress tags
*
* @param int $id the rev-id
* @param array $data the post-data as array
* @param int $id the rev-id
* @param WP_Post $post the post
*
* @return
*/
public static function insert_post( $id, $data ) {
if ( \preg_match_all( '/' . ACTIVITYPUB_HASHTAGS_REGEXP . '/i', $data->post_content, $match ) ) {
public static function insert_post( $id, $post ) {
if ( \preg_match_all( '/' . ACTIVITYPUB_HASHTAGS_REGEXP . '/i', $post->post_content, $match ) ) {
$tags = \implode( ', ', $match[1] );
\wp_add_post_tags( $data->post_parent, $tags );
\wp_add_post_tags( $post->post_parent, $tags );
}
return $id;
@ -43,8 +43,33 @@ class Hashtag {
* @return string the filtered post-content
*/
public static function the_content( $the_content ) {
$protected_tags = array();
$protect = function( $m ) use ( &$protected_tags ) {
$c = count( $protected_tags );
$protect = '!#!#PROTECT' . $c . '#!#!';
$protected_tags[ $protect ] = $m[0];
return $protect;
};
$the_content = preg_replace_callback(
'#<!\[CDATA\[.*?\]\]>#is',
$protect,
$the_content
);
$the_content = preg_replace_callback(
'#<(pre|code|textarea|style)\b[^>]*>.*?</\1[^>]*>#is',
$protect,
$the_content
);
$the_content = preg_replace_callback(
'#<[^>]+>#i',
$protect,
$the_content
);
$the_content = \preg_replace_callback( '/' . ACTIVITYPUB_HASHTAGS_REGEXP . '/i', array( '\Activitypub\Hashtag', 'replace_with_links' ), $the_content );
$the_content = str_replace( array_reverse( array_keys( $protected_tags ) ), array_reverse( array_values( $protected_tags ) ), $the_content );
return $the_content;
}

View File

@ -0,0 +1,97 @@
<?php
namespace Activitypub;
/**
* ActivityPub Mention Class
*
* @author Alex Kirk
*/
class Mention {
/**
* Initialize the class, registering WordPress hooks
*/
public static function init() {
\add_filter( 'the_content', array( '\Activitypub\Mention', 'the_content' ), 99, 2 );
\add_filter( 'activitypub_extract_mentions', array( '\Activitypub\Mention', 'extract_mentions' ), 99, 2 );
}
/**
* Filter to replace the mentions in the content with links
*
* @param string $the_content the post-content
*
* @return string the filtered post-content
*/
public static function the_content( $the_content ) {
$protected_tags = array();
$protect = function( $m ) use ( &$protected_tags ) {
$c = count( $protected_tags );
$protect = '!#!#PROTECT' . $c . '#!#!';
$protected_tags[ $protect ] = $m[0];
return $protect;
};
$the_content = preg_replace_callback(
'#<!\[CDATA\[.*?\]\]>#is',
$protect,
$the_content
);
$the_content = preg_replace_callback(
'#<(pre|code|textarea|style)\b[^>]*>.*?</\1[^>]*>#is',
$protect,
$the_content
);
$the_content = preg_replace_callback(
'#<a.*?href=[^>]+>.*?</a>#i',
$protect,
$the_content
);
$the_content = \preg_replace_callback( '/@' . ACTIVITYPUB_USERNAME_REGEXP . '/', array( '\Activitypub\Mention', 'replace_with_links' ), $the_content );
$the_content = str_replace( array_reverse( array_keys( $protected_tags ) ), array_reverse( array_values( $protected_tags ) ), $the_content );
return $the_content;
}
/**
* A callback for preg_replace to build the user links
*
* @param array $result the preg_match results
* @return string the final string
*/
public static function replace_with_links( $result ) {
$metadata = \ActivityPub\get_remote_metadata_by_actor( $result[0] );
if ( ! is_wp_error( $metadata ) && ! empty( $metadata['url'] ) ) {
$username = ltrim( $result[0], '@' );
if ( ! empty( $metadata['name'] ) ) {
$username = $metadata['name'];
}
if ( ! empty( $metadata['preferredUsername'] ) ) {
$username = $metadata['preferredUsername'];
}
$username = '@<span>' . $username . '</span>';
return \sprintf( '<a rel="mention" class="u-url mention" href="%s">%s</a>', $metadata['url'], $username );
}
return $result[0];
}
/**
* Extract the mentions from the post_content.
*
* @param array $mentions The already found mentions.
* @param string $post_content The post content.
* @return mixed The discovered mentions.
*/
public static function extract_mentions( $mentions, $post_content ) {
\preg_match_all( '/@' . ACTIVITYPUB_USERNAME_REGEXP . '/i', $post_content, $matches );
foreach ( $matches[0] as $match ) {
$link = \Activitypub\Webfinger::resolve( $match );
if ( ! is_wp_error( $link ) ) {
$mentions[ $match ] = $link;
}
}
return $mentions;
}
}

View File

@ -0,0 +1,527 @@
<?php
namespace Activitypub;
class Shortcodes {
/**
* Class constructor, registering WordPress then shortcodes
*
* @param WP_Post $post A WordPress Post Object
*/
public static function init() {
foreach ( get_class_methods( 'Activitypub\Shortcodes' ) as $shortcode ) {
if ( 'init' !== $shortcode ) {
add_shortcode( 'ap_' . $shortcode, array( 'Activitypub\Shortcodes', $shortcode ) );
}
}
}
/**
* Generates output for the ap_hashtags shortcode
*
* @param array $atts shortcode attributes
* @param string $content shortcode content
* @param string $tag shortcode tag name
*
* @return string
*/
public static function hashtags( $atts, $content, $tag ) {
$post_id = get_the_ID();
if ( ! $post_id ) {
return '';
}
$tags = \get_the_tags( $post_id );
if ( ! $tags ) {
return '';
}
$hash_tags = array();
foreach ( $tags as $tag ) {
$hash_tags[] = \sprintf(
'<a rel="tag" class="u-tag u-category" href="%s">#%s</a>',
\get_tag_link( $tag ),
$tag->slug
);
}
return \implode( ' ', $hash_tags );
}
/**
* Generates output for the ap_title shortcode
*
* @param array $atts shortcode attributes
* @param string $content shortcode content
* @param string $tag shortcode tag name
*
* @return string
*/
public static function title( $atts, $content, $tag ) {
$post_id = get_the_ID();
if ( ! $post_id ) {
return '';
}
return \get_the_title( $post_id );
}
/**
* Generates output for the ap_excerpt shortcode
*
* @param array $atts shortcode attributes
* @param string $content shortcode content
* @param string $tag shortcode tag name
*
* @return string
*/
public static function excerpt( $atts, $content, $tag ) {
$post = get_post();
if ( ! $post || \post_password_required( $post ) ) {
return '';
}
$atts = shortcode_atts(
array( 'length' => ACTIVITYPUB_EXCERPT_LENGTH ),
$atts,
$tag
);
$excerpt_length = intval( $atts['length'] );
if ( 0 === $excerpt_length ) {
$excerpt_length = ACTIVITYPUB_EXCERPT_LENGTH;
}
$excerpt = \get_post_field( 'post_excerpt', $post );
if ( '' === $excerpt ) {
$content = \get_post_field( 'post_content', $post );
// An empty string will make wp_trim_excerpt do stuff we do not want.
if ( '' !== $content ) {
$excerpt = \strip_shortcodes( $content );
/** This filter is documented in wp-includes/post-template.php */
$excerpt = \apply_filters( 'the_content', $excerpt );
$excerpt = \str_replace( ']]>', ']]>', $excerpt );
}
}
// Strip out any remaining tags.
$excerpt = \wp_strip_all_tags( $excerpt );
/** This filter is documented in wp-includes/formatting.php */
$excerpt_more = \apply_filters( 'excerpt_more', ' [...]' );
$excerpt_more_len = strlen( $excerpt_more );
// We now have a excerpt, but we need to check it's length, it may be longer than we want for two reasons:
//
// * The user has entered a manual excerpt which is longer that what we want.
// * No manual excerpt exists so we've used the content which might be longer than we want.
//
// Either way, let's trim it up if we need too. Also, don't forget to take into account the more indicator
// as part of the total length.
//
// Setup a variable to hold the current excerpts length.
$current_excerpt_length = strlen( $excerpt );
// Setup a variable to keep track of our target length.
$target_excerpt_length = $excerpt_length - $excerpt_more_len;
// Setup a variable to keep track of the current max length.
$current_excerpt_max = $target_excerpt_length;
// This is a loop since we can't calculate word break the string after 'the_excpert' filter has run (we would break
// all kinds of html tags), so we have to cut the excerpt down a bit at a time until we hit our target length.
while ( $current_excerpt_length > $target_excerpt_length && $current_excerpt_max > 0 ) {
// Trim the excerpt based on wordwrap() positioning.
// Note: we're using <br> as the linebreak just in case there are any newlines existing in the excerpt from the user.
// There won't be any <br> left after we've run wp_strip_all_tags() in the code above, so they're
// safe to use here. It won't be included in the final excerpt as the substr() will trim it off.
$excerpt = substr( $excerpt, 0, strpos( wordwrap( $excerpt, $current_excerpt_max, '<br>' ), '<br>' ) );
// If something went wrong, or we're in a language that wordwrap() doesn't understand,
// just chop it off and don't worry about breaking in the middle of a word.
if ( strlen( $excerpt ) > $excerpt_length - $excerpt_more_len ) {
$excerpt = substr( $excerpt, 0, $current_excerpt_max );
}
// Add in the more indicator.
$excerpt = $excerpt . $excerpt_more;
// Run it through the excerpt filter which will add some html tags back in.
$excerpt_filtered = apply_filters( 'the_excerpt', $excerpt );
// Now set the current excerpt length to this new filtered length.
$current_excerpt_length = strlen( $excerpt_filtered );
// Check to see if we're over the target length.
if ( $current_excerpt_length > $target_excerpt_length ) {
// If so, remove 20 characters from the current max and run the loop again.
$current_excerpt_max = $current_excerpt_max - 20;
}
}
return \apply_filters( 'the_excerpt', $excerpt );
}
/**
* Generates output for the ap_content shortcode
*
* @param array $atts shortcode attributes
* @param string $content shortcode content
* @param string $tag shortcode tag name
*
* @return string
*/
public static function content( $atts, $content, $tag ) {
$post = get_post();
if ( ! $post || \post_password_required( $post ) ) {
return '';
}
$atts = shortcode_atts(
array( 'apply_filters' => 'yes' ),
$atts,
$tag
);
$content = \get_post_field( 'post_content', $post );
if ( 'yes' === $atts['apply_filters'] ) {
$content = \apply_filters( 'the_content', $content );
} else {
$content = do_blocks( $content );
$content = wptexturize( $content );
$content = wp_filter_content_tags( $content );
}
// replace script and style elements
$content = \preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $content );
$content = \trim( \preg_replace( '/[\n\r\t]/', '', $content ) );
return $content;
}
/**
* Generates output for the ap_permalink shortcode
*
* @param array $atts shortcode attributes
* @param string $content shortcode content
* @param string $tag shortcode tag name
*
* @return string
*/
public static function permalink( $atts, $content, $tag ) {
$post = get_post();
if ( ! $post ) {
return '';
}
$atts = shortcode_atts(
array(
'type' => 'url',
),
$atts,
$tag
);
if ( 'url' === $atts['type'] ) {
return \esc_url( \get_permalink( $post->ID ) );
}
return \sprintf( '<a href="%1$s">%1$s</a>', \esc_url( \get_permalink( $post->ID ) ) );
}
/**
* Generates output for the ap_shortlink shortcode
*
* @param array $atts shortcode attributes
* @param string $content shortcode content
* @param string $tag shortcode tag name
*
* @return string
*/
public static function shortlink( $atts, $content, $tag ) {
$post = get_post();
if ( ! $post ) {
return '';
}
$atts = shortcode_atts(
array(
'type' => 'url',
),
$atts,
$tag
);
if ( 'url' === $atts['type'] ) {
return \esc_url( \wp_get_shortlink( $post->ID ) );
}
return \sprintf( '<a href="%1$s">%1$s</a>', \esc_url( \wp_get_shortlink( $post->ID ) ) );
}
/**
* Generates output for the ap_image shortcode
*
* @param array $atts shortcode attributes
* @param string $content shortcode content
* @param string $tag shortcode tag name
*
* @return string
*/
public static function image( $atts, $content, $tag ) {
$post_id = get_the_ID();
if ( ! $post_id ) {
return '';
}
$atts = shortcode_atts(
array(
'type' => 'full',
),
$atts,
$tag
);
$size = 'full';
if ( in_array(
$atts['type'],
array( 'thumbnail', 'medium', 'large', 'full' ),
true
) ) {
$size = $atts['type'];
}
$image = \get_the_post_thumbnail_url( $post_id, $size );
if ( ! $image ) {
return '';
}
return \esc_url( $image );
}
/**
* Generates output for the ap_hashcats shortcode
*
* @param array $atts shortcode attributes
* @param string $content shortcode content
* @param string $tag shortcode tag name
*
* @return string
*/
public static function hashcats( $atts, $content, $tag ) {
$post_id = get_the_ID();
if ( ! $post_id ) {
return '';
}
$categories = \get_the_category( $post_id );
if ( ! $categories ) {
return '';
}
$hash_tags = array();
foreach ( $categories as $category ) {
$hash_tags[] = \sprintf( '<a rel="tag" class="u-tag u-category" href="%s">#%s</a>', \get_category_link( $category ), $category->slug );
}
return \implode( ' ', $hash_tags );
}
/**
* Generates output for the ap_author shortcode
*
* @param array $atts shortcode attributes
* @param string $content shortcode content
* @param string $tag shortcode tag name
*
* @return string
*/
public static function author( $atts, $content, $tag ) {
$post = get_post();
if ( ! $post ) {
return '';
}
$name = \get_the_author_meta( 'display_name', $post->post_author );
if ( ! $name ) {
return '';
}
return $name;
}
/**
* Generates output for the ap_authorurl shortcode
*
* @param array $atts shortcode attributes
* @param string $content shortcode content
* @param string $tag shortcode tag name
*
* @return string
*/
public static function authorurl( $atts, $content, $tag ) {
$post = get_post();
if ( ! $post ) {
return '';
}
$url = \get_the_author_meta( 'user_url', $post->post_author );
if ( ! $url ) {
return '';
}
return \esc_url( $url );
}
/**
* Generates output for the ap_blogurl shortcode
*
* @param array $atts shortcode attributes
* @param string $content shortcode content
* @param string $tag shortcode tag name
*
* @return string
*/
public static function blogurl( $atts, $content, $tag ) {
return \esc_url( \get_bloginfo( 'url' ) );
}
/**
* Generates output for the ap_blogname shortcode
*
* @param array $atts shortcode attributes
* @param string $content shortcode content
* @param string $tag shortcode tag name
*
* @return string
*/
public static function blogname( $atts, $content, $tag ) {
return \get_bloginfo( 'name' );
}
/**
* Generates output for the ap_blogdesc shortcode
*
* @param array $atts shortcode attributes
* @param string $content shortcode content
* @param string $tag shortcode tag name
*
* @return string
*/
public static function blogdesc( $atts, $content, $tag ) {
return \get_bloginfo( 'description' );
}
/**
* Generates output for the ap_date shortcode
*
* @param array $atts shortcode attributes
* @param string $content shortcode content
* @param string $tag shortcode tag name
*
* @return string
*/
public static function date( $atts, $content, $tag ) {
$post = get_post();
if ( ! $post ) {
return '';
}
$datetime = \get_post_datetime( $post );
$dateformat = \get_option( 'date_format' );
$timeformat = \get_option( 'time_format' );
$date = $datetime->format( $dateformat );
if ( ! $date ) {
return '';
}
return $date;
}
/**
* Generates output for the ap_time shortcode
*
* @param array $atts shortcode attributes
* @param string $content shortcode content
* @param string $tag shortcode tag name
*
* @return string
*/
public static function time( $atts, $content, $tag ) {
$post = get_post();
if ( ! $post ) {
return '';
}
$datetime = \get_post_datetime( $post );
$dateformat = \get_option( 'date_format' );
$timeformat = \get_option( 'time_format' );
$date = $datetime->format( $timeformat );
if ( ! $date ) {
return '';
}
return $date;
}
/**
* Generates output for the ap_datetime shortcode
*
* @param array $atts shortcode attributes
* @param string $content shortcode content
* @param string $tag shortcode tag name
*
* @return string
*/
public static function datetime( $atts, $content, $tag ) {
$post = get_post();
if ( ! $post ) {
return '';
}
$datetime = \get_post_datetime( $post );
$dateformat = \get_option( 'date_format' );
$timeformat = \get_option( 'time_format' );
$date = $datetime->format( $dateformat . ' @ ' . $timeformat );
if ( ! $date ) {
return '';
}
return $date;
}
}

View File

@ -28,12 +28,21 @@ class Webfinger {
}
public static function resolve( $account ) {
if ( ! preg_match( '/^@?[^@]+@((?:[a-z0-9-]+\.)+[a-z]+)$/i', $account, $m ) ) {
if ( ! preg_match( '/^@?' . ACTIVITYPUB_USERNAME_REGEXP . '$/i', $account, $m ) ) {
return null;
}
$url = \add_query_arg( 'resource', 'acct:' . ltrim( $account, '@' ), 'https://' . $m[1] . '/.well-known/webfinger' );
$transient_key = 'activitypub_resolve_' . ltrim( $account, '@' );
$link = \get_transient( $transient_key );
if ( $link ) {
return $link;
}
$url = \add_query_arg( 'resource', 'acct:' . ltrim( $account, '@' ), 'https://' . $m[2] . '/.well-known/webfinger' );
if ( ! \wp_http_validate_url( $url ) ) {
return new \WP_Error( 'invalid_webfinger_url', null, $url );
$response = new \WP_Error( 'invalid_webfinger_url', null, $url );
\set_transient( $transient_key, $response, HOUR_IN_SECONDS ); // Cache the error for a shorter period.
return $response;
}
// try to access author URL
@ -42,28 +51,34 @@ class Webfinger {
array(
'headers' => array( 'Accept' => 'application/activity+json' ),
'redirection' => 0,
'timeout' => 2,
)
);
if ( \is_wp_error( $response ) ) {
return new \WP_Error( 'webfinger_url_not_accessible', null, $url );
$link = new \WP_Error( 'webfinger_url_not_accessible', null, $url );
\set_transient( $transient_key, $link, HOUR_IN_SECONDS ); // Cache the error for a shorter period.
return $link;
}
$response_code = \wp_remote_retrieve_response_code( $response );
$body = \wp_remote_retrieve_body( $response );
$body = \json_decode( $body, true );
if ( ! isset( $body['links'] ) ) {
return new \WP_Error( 'webfinger_url_invalid_response', null, $url );
if ( empty( $body['links'] ) ) {
$link = new \WP_Error( 'webfinger_url_invalid_response', null, $url );
\set_transient( $transient_key, $link, HOUR_IN_SECONDS ); // Cache the error for a shorter period.
return $link;
}
foreach ( $body['links'] as $link ) {
if ( 'self' === $link['rel'] && 'application/activity+json' === $link['type'] ) {
\set_transient( $transient_key, $link['href'], WEEK_IN_SECONDS );
return $link['href'];
}
}
return new \WP_Error( 'webfinger_url_no_activity_pub', null, $body );
$link = new \WP_Error( 'webfinger_url_no_activity_pub', null, $body );
\set_transient( $transient_key, $link, HOUR_IN_SECONDS ); // Cache the error for a shorter period.
return $link;
}
}

View File

@ -68,7 +68,7 @@ function safe_remote_get( $url, $user_id ) {
$wp_version = \get_bloginfo( 'version' );
$user_agent = \apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . \get_bloginfo( 'url' ) );
$args = array(
'timeout' => 100,
'timeout' => apply_filters( 'activitypub_remote_get_timeout', 100 ),
'limit_response_size' => 1048576,
'redirection' => 3,
'user-agent' => "$user_agent; ActivityPub",
@ -110,8 +110,8 @@ function get_remote_metadata_by_actor( $actor ) {
if ( $pre ) {
return $pre;
}
if ( preg_match( '/^@?[^@]+@((?:[a-z0-9-]+\.)+[a-z]+)$/i', $actor ) ) {
$actor = \Activitypub\Webfinger::resolve( $actor );
if ( preg_match( '/^@?' . ACTIVITYPUB_USERNAME_REGEXP . '$/i', $actor ) ) {
$actor = Webfinger::resolve( $actor );
}
if ( ! $actor ) {
@ -122,30 +122,37 @@ function get_remote_metadata_by_actor( $actor ) {
return $actor;
}
$metadata = \get_transient( 'activitypub_' . $actor );
$transient_key = 'activitypub_' . $actor;
$metadata = \get_transient( $transient_key );
if ( $metadata ) {
return $metadata;
}
if ( ! \wp_http_validate_url( $actor ) ) {
return new \WP_Error( 'activitypub_no_valid_actor_url', \__( 'The "actor" is no valid URL', 'activitypub' ), $actor );
$metadata = new \WP_Error( 'activitypub_no_valid_actor_url', \__( 'The "actor" is no valid URL', 'activitypub' ), $actor );
\set_transient( $transient_key, $metadata, HOUR_IN_SECONDS ); // Cache the error for a shorter period.
return $metadata;
}
$user = \get_users(
array(
'number' => 1,
'who' => 'authors',
'capability__in' => array( 'publish_posts' ),
'fields' => 'ID',
)
);
// we just need any user to generate a request signature
$user_id = \reset( $user );
$short_timeout = function() {
return 3;
};
add_filter( 'activitypub_remote_get_timeout', $short_timeout );
$response = \Activitypub\safe_remote_get( $actor, $user_id );
remove_filter( 'activitypub_remote_get_timeout', $short_timeout );
if ( \is_wp_error( $response ) ) {
\set_transient( $transient_key, $response, HOUR_IN_SECONDS ); // Cache the error for a shorter period.
return $response;
}
@ -153,10 +160,12 @@ function get_remote_metadata_by_actor( $actor ) {
$metadata = \json_decode( $metadata, true );
if ( ! $metadata ) {
return new \WP_Error( 'activitypub_invalid_json', \__( 'No valid JSON data', 'activitypub' ), $actor );
$metadata = new \WP_Error( 'activitypub_invalid_json', \__( 'No valid JSON data', 'activitypub' ), $actor );
\set_transient( $transient_key, $metadata, HOUR_IN_SECONDS ); // Cache the error for a shorter period.
return $metadata;
}
\set_transient( 'activitypub_' . $actor, $metadata, WEEK_IN_SECONDS );
\set_transient( $transient_key, $metadata, WEEK_IN_SECONDS );
return $metadata;
}

View File

@ -2,45 +2,67 @@
\get_current_screen()->add_help_tab(
array(
'id' => 'fediverse',
'title' => \__( 'Fediverse', 'activitypub' ),
'id' => 'template-tags',
'title' => \__( 'Template Tags', 'activitypub' ),
'content' =>
'<p><strong>' . \__( 'What is the Fediverse?', 'activitypub' ) . '</strong></p>' .
'<p>' . __( 'The following Template Tags are available:', 'activitypub' ) . '</p>' .
'<dl>' .
'<dt><code>[ap_title]</code></dt>' .
'<dd>' . \wp_kses( __( 'The post\'s title.', 'activitypub' ), 'default' ) . '</dd>' .
'<dt><code>[ap_content apply_filters="yes"]</code></dt>' .
'<dd>' . \wp_kses( __( 'The post\'s content. With <code>apply_filters</code> you can decide if filters should be applied or not (default is <code>yes</code>). The values can be <code>yes</code> or <code>no</code>. <code>apply_filters</code> attribute is optional.', 'activitypub' ), 'default' ) . '</dd>' .
'<dt><code>[ap_excerpt lenght="400"]</code></dt>' .
'<dd>' . \wp_kses( __( 'The post\'s excerpt (default 400 chars). <code>length</code> attribute is optional.', 'activitypub' ), 'default' ) . '</dd>' .
'<dt><code>[ap_permalink type="url"]</code></dt>' .
'<dd>' . \wp_kses( __( 'The post\'s permalink. <code>type</code> can be either: <code>url</code> or <code>html</code> (an &lt;a /&gt; tag). <code>type</code> attribute is optional.', 'activitypub' ), 'default' ) . '</dd>' .
'<dt><code>[ap_shortlink type="url"]</code></dt>' .
'<dd>' . \wp_kses( __( 'The post\'s shortlink. <code>type</code> can be either <code>url</code> or <code>html</code> (an &lt;a /&gt; tag). I can recommend <a href="https://wordpress.org/plugins/hum/" target="_blank">Hum</a>, to prettify the Shortlinks. <code>type</code> attribute is optional.', 'activitypub' ), 'default' ) . '</dd>' .
'<dt><code>[ap_hashtags]</code></dt>' .
'<dd>' . \wp_kses( __( 'The post\'s tags as hashtags.', 'activitypub' ), 'default' ) . '</dd>' .
'<dt><code>[ap_hashcats]</code></dt>' .
'<dd>' . \wp_kses( __( 'The post\'s categories as hashtags.', 'activitypub' ), 'default' ) . '</dd>' .
'<dt><code>[ap_image type=full]</code></dt>' .
'<dd>' . \wp_kses( __( 'The URL for the post\'s featured image, defaults to full size. The type attribute can be any of the following: <code>thumbnail</code>, <code>medium</code>, <code>large</code>, <code>full</code>. <code>type</code> attribute is optional.', 'activitypub' ), 'default' ) . '</dd>' .
'<dt><code>[ap_author]</code></dt>' .
'<dd>' . \wp_kses( __( 'The author\'s name.', 'activitypub' ), 'default' ) . '</dd>' .
'<dt><code>[ap_authorurl]</code></dt>' .
'<dd>' . \wp_kses( __( 'The URL to the author\'s profile page.', 'activitypub' ), 'default' ) . '</dd>' .
'<dt><code>[ap_date]</code></dt>' .
'<dd>' . \wp_kses( __( 'The post\'s date.', 'activitypub' ), 'default' ) . '</dd>' .
'<dt><code>[ap_time]</code></dt>' .
'<dd>' . \wp_kses( __( 'The post\'s time.', 'activitypub' ), 'default' ) . '</dd>' .
'<dt><code>[ap_datetime]</code></dt>' .
'<dd>' . \wp_kses( __( 'The post\'s date/time formated as "date @ time".', 'activitypub' ), 'default' ) . '</dd>' .
'<dt><code>[ap_blogurl]</code></dt>' .
'<dd>' . \wp_kses( __( 'The URL to the site.', 'activitypub' ), 'default' ) . '</dd>' .
'<dt><code>[ap_blogname]</code></dt>' .
'<dd>' . \wp_kses( __( 'The name of the site.', 'activitypub' ), 'default' ) . '</dd>' .
'<dt><code>[ap_blogdesc]</code></dt>' .
'<dd>' . \wp_kses( __( 'The description of the site.', 'activitypub' ), 'default' ) . '</dd>' .
'</dl>' .
'<p>' . __( 'You may also use any Shortcode normally available to you on your site, however be aware that Shortcodes may significantly increase the size of your content depending on what they do.', 'activitypub' ) . '</p>' .
'<p>' . __( 'Note: the old Template Tags are now deprecated and automatically converted to the new ones.', 'activitypub' ) . '</p>' .
'<p>' . \wp_kses( \__( '<a href="https://github.com/pfefferle/wordpress-activitypub/issues/new" target="_blank">Let me know</a> if you miss a Template Tag.', 'activitypub' ), 'activitypub' ) . '</p>',
)
);
\get_current_screen()->add_help_tab(
array(
'id' => 'glossar',
'title' => \__( 'Glossar', 'activitypub' ),
'content' =>
'<p><h2>' . \__( 'Fediverse', 'activitypub' ) . '</h2></p>' .
'<p>' . \__( 'The Fediverse is a new word made of two words: "federation" + "universe"', 'activitypub' ) . '</p>' .
'<p>' . \__( 'It is a federated social network running on free open software on a myriad of computers across the globe. Many independent servers are interconnected and allow people to interact with one another. There\'s no one central site: you choose a server to register. This ensures some decentralization and sovereignty of data. Fediverse (also called Fedi) has no built-in advertisements, no tricky algorithms, no one big corporation dictating the rules. Instead we have small cozy communities of like-minded people. Welcome!', 'activitypub' ) . '</p>' .
'<p>' . \__( 'For more informations please visit <a href="https://fediverse.party/" target="_blank">fediverse.party</a>', 'activitypub' ) . '</p>',
)
);
\get_current_screen()->add_help_tab(
array(
'id' => 'activitypub',
'title' => \__( 'ActivityPub', 'activitypub' ),
'content' =>
'<p><strong>' . \__( 'What is ActivityPub?', 'activitypub' ) . '</strong></p>' .
'<p>' . \__( 'ActivityPub is a decentralized social networking protocol based on the ActivityStreams 2.0 data format. ActivityPub is an official W3C recommended standard published by the W3C Social Web Working Group. It provides a client to server API for creating, updating and deleting content, as well as a federated server to server API for delivering notifications and subscribing to content.', 'activitypub' ) . '</p>',
)
);
\get_current_screen()->add_help_tab(
array(
'id' => 'webfinger',
'title' => \__( 'WebFinger', 'activitypub' ),
'content' =>
'<p><strong>' . \__( 'What is WebFinger?', 'activitypub' ) . '</strong></p>' .
'<p>' . \__( 'For more informations please visit <a href="https://fediverse.party/" target="_blank">fediverse.party</a>', 'activitypub' ) . '</p>' .
'<p><h2>' . \__( 'ActivityPub', 'activitypub' ) . '</h2></p>' .
'<p>' . \__( 'ActivityPub is a decentralized social networking protocol based on the ActivityStreams 2.0 data format. ActivityPub is an official W3C recommended standard published by the W3C Social Web Working Group. It provides a client to server API for creating, updating and deleting content, as well as a federated server to server API for delivering notifications and subscribing to content.', 'activitypub' ) . '</p>' .
'<p><h2>' . \__( 'WebFinger', 'activitypub' ) . '</h2></p>' .
'<p>' . \__( 'WebFinger is used to discover information about people or other entities on the Internet that are identified by a URI using standard Hypertext Transfer Protocol (HTTP) methods over a secure transport. A WebFinger resource returns a JavaScript Object Notation (JSON) object describing the entity that is queried. The JSON object is referred to as the JSON Resource Descriptor (JRD).', 'activitypub' ) . '</p>' .
'<p>' . \__( 'For a person, the type of information that might be discoverable via WebFinger includes a personal profile address, identity service, telephone number, or preferred avatar. For other entities on the Internet, a WebFinger resource might return JRDs containing link relations that enable a client to discover, for example, that a printer can print in color on A4 paper, the physical location of a server, or other static information.', 'activitypub' ) . '</p>' .
'<p>' . \__( 'On Mastodon [and other Plattforms], user profiles can be hosted either locally on the same website as yours, or remotely on a completely different website. The same username may be used on a different domain. Therefore, a Mastodon user\'s full mention consists of both the username and the domain, in the form <code>@username@domain</code>. In practical terms, <code>@user@example.com</code> is not the same as <code>@user@example.org</code>. If the domain is not included, Mastodon will try to find a local user named <code>@username</code>. However, in order to deliver to someone over ActivityPub, the <code>@username@domain</code> mention is not enough mentions must be translated to an HTTPS URI first, so that the remote actor\'s inbox and outbox can be found. (This paragraph is copied from the <a href="https://docs.joinmastodon.org/spec/webfinger/" target="_blank">Mastodon Documentation</a>)', 'activitypub' ) . '</p>' .
'<p>' . \__( 'For more informations please visit <a href="https://webfinger.net/" target="_blank">webfinger.net</a>', 'activitypub' ) . '</p>',
)
);
\get_current_screen()->add_help_tab(
array(
'id' => 'nodeinfo',
'title' => \__( 'NodeInfo', 'activitypub' ),
'content' =>
'<p><strong>' . \__( 'What is NodeInfo?', 'activitypub' ) . '</strong></p>' .
'<p>' . \__( 'For more informations please visit <a href="https://webfinger.net/" target="_blank">webfinger.net</a>', 'activitypub' ) . '</p>' .
'<p><h2>' . \__( 'NodeInfo', 'activitypub' ) . '</h2></p>' .
'<p>' . \__( 'NodeInfo is an effort to create a standardized way of exposing metadata about a server running one of the distributed social networks. The two key goals are being able to get better insights into the user base of distributed social networking and the ability to build tools that allow users to choose the best fitting software and server for their needs.', 'activitypub' ) . '</p>' .
'<p>' . \__( 'For more informations please visit <a href="http://nodeinfo.diaspora.software/" target="_blank">nodeinfo.diaspora.software</a>', 'activitypub' ) . '</p>',
)

View File

@ -45,20 +45,28 @@ class Activity {
}
}
public function from_post( $object ) {
$this->object = $object;
public function from_post( Post $post ) {
$this->object = $post->to_array();
if ( isset( $object['published'] ) ) {
$this->published = $object['published'];
}
$this->cc = array( \get_rest_url( null, '/activitypub/1.0/users/' . intval( $post->get_post_author() ) . '/followers' ) );
if ( isset( $object['attributedTo'] ) ) {
$this->actor = $object['attributedTo'];
if ( isset( $this->object['attributedTo'] ) ) {
$this->actor = $this->object['attributedTo'];
}
foreach ( $post->get_tags() as $tag ) {
if ( 'Mention' === $tag['type'] ) {
$this->cc[] = $tag['href'];
}
}
$type = \strtolower( $this->type );
if ( isset( $object['id'] ) ) {
$this->id = add_query_arg( 'activity', $type, $object['id'] );
if ( isset( $this->object['id'] ) ) {
$this->id = add_query_arg( 'activity', $type, $this->object['id'] );
}
}

View File

@ -7,31 +7,127 @@ namespace Activitypub\Model;
* @author Matthias Pfefferle
*/
class Post {
/**
* The WordPress Post Object.
*
* @var WP_Post
*/
private $post;
/**
* The Post Author.
*
* @var string
*/
private $post_author;
/**
* The Object ID.
*
* @var string
*/
private $id;
/**
* The Object Summary.
*
* @var string
*/
private $summary;
/**
* The Object Summary
*
* @var string
*/
private $content;
/**
* The Object Attachments. This is usually a list of Images.
*
* @var array
*/
private $attachments;
/**
* The Object Tags. This is usually the list of used Hashtags.
*
* @var array
*/
private $tags;
/**
* The Onject Type
*
* @var string
*/
private $object_type;
public function __construct( $post = null ) {
$this->post = \get_post( $post );
/**
* The Allowed Tags, used in the content.
*
* @var array
*/
private $allowed_tags = array(
'a' => array(
'href' => array(),
'title' => array(),
'class' => array(),
'rel' => array(),
),
'br' => array(),
'p' => array(
'class' => array(),
),
'span' => array(
'class' => array(),
),
'div' => array(
'class' => array(),
),
'ul' => array(),
'ol' => array(),
'li' => array(),
'strong' => array(
'class' => array(),
),
'b' => array(
'class' => array(),
),
'i' => array(
'class' => array(),
),
'em' => array(
'class' => array(),
),
'blockquote' => array(),
'cite' => array(),
);
$this->post_author = $this->post->post_author;
$this->id = $this->generate_id();
$this->summary = $this->generate_the_title();
$this->content = $this->generate_the_content();
$this->attachments = $this->generate_attachments();
$this->tags = $this->generate_tags();
$this->object_type = $this->generate_object_type();
/**
* Constructor
*
* @param WP_Post $post
*/
public function __construct( $post ) {
$this->post = \get_post( $post );
}
/**
* Magic function to implement getter and setter
*
* @param string $method
* @param string $params
*
* @return void
*/
public function __call( $method, $params ) {
$var = \strtolower( \substr( $method, 4 ) );
if ( \strncasecmp( $method, 'get', 3 ) === 0 ) {
if ( empty( $this->$var ) && ! empty( $this->post->$var ) ) {
return $this->post->$var;
}
return $this->$var;
}
@ -40,74 +136,112 @@ class Post {
}
}
/**
* Converts this Object into an Array.
*
* @return array
*/
public function to_array() {
$post = $this->post;
$array = array(
'id' => $this->id,
'type' => $this->object_type,
'id' => $this->get_id(),
'type' => $this->get_object_type(),
'published' => \gmdate( 'Y-m-d\TH:i:s\Z', \strtotime( $post->post_date_gmt ) ),
'attributedTo' => \get_author_posts_url( $post->post_author ),
'summary' => $this->summary,
'summary' => $this->get_summary(),
'inReplyTo' => null,
'content' => $this->content,
'content' => $this->get_content(),
'contentMap' => array(
\strstr( \get_locale(), '_', true ) => $this->content,
\strstr( \get_locale(), '_', true ) => $this->get_content(),
),
'to' => array( 'https://www.w3.org/ns/activitystreams#Public' ),
'cc' => array( 'https://www.w3.org/ns/activitystreams#Public' ),
'attachment' => $this->attachments,
'tag' => $this->tags,
'attachment' => $this->get_attachments(),
'tag' => $this->get_tags(),
);
return \apply_filters( 'activitypub_post', $array );
return \apply_filters( 'activitypub_post', $array, $this->post );
}
/**
* Converts this Object into a JSON String
*
* @return string
*/
public function to_json() {
return \wp_json_encode( $this->to_array(), \JSON_HEX_TAG | \JSON_HEX_AMP | \JSON_HEX_QUOT );
}
public function generate_id() {
$post = $this->post;
$permalink = \get_permalink( $post );
/**
* Returns the ID of an Activity Object
*
* @return string
*/
public function get_id() {
if ( $this->id ) {
return $this->id;
}
// replace 'trashed' for delete activity
return \str_replace( '__trashed', '', $permalink );
$post = $this->post;
if ( 'trash' === get_post_status( $post ) ) {
$permalink = \get_post_meta( $post->ID, 'activitypub_canonical_url', true );
} else {
$permalink = \get_permalink( $post );
}
$this->id = $permalink;
return $permalink;
}
public function generate_attachments() {
$max_images = \apply_filters( 'activitypub_max_images', 3 );
/**
* Returns a list of Image Attachments
*
* @return array
*/
public function get_attachments() {
if ( $this->attachments ) {
return $this->attachments;
}
$max_images = intval( \apply_filters( 'activitypub_max_image_attachments', \get_option( 'activitypub_max_image_attachments', ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS ) ) );
$images = array();
// max images can't be negative or zero
if ( $max_images <= 0 ) {
$max_images = 1;
return $images;
}
$id = $this->post->ID;
$image_ids = array();
// list post thumbnail first if this post has one
if ( \function_exists( 'has_post_thumbnail' ) && \has_post_thumbnail( $id ) ) {
$image_ids[] = \get_post_thumbnail_id( $id );
$max_images--;
}
// then list any image attachments
$query = new \WP_Query(
array(
'post_parent' => $id,
'post_status' => 'inherit',
'post_type' => 'attachment',
'post_mime_type' => 'image',
'order' => 'ASC',
'orderby' => 'menu_order ID',
'posts_per_page' => $max_images,
)
);
foreach ( $query->get_posts() as $attachment ) {
if ( ! \in_array( $attachment->ID, $image_ids, true ) ) {
$image_ids[] = $attachment->ID;
if ( $max_images > 0 ) {
// then list any image attachments
$query = new \WP_Query(
array(
'post_parent' => $id,
'post_status' => 'inherit',
'post_type' => 'attachment',
'post_mime_type' => 'image',
'order' => 'ASC',
'orderby' => 'menu_order ID',
'posts_per_page' => $max_images,
)
);
foreach ( $query->get_posts() as $attachment ) {
if ( ! \in_array( $attachment->ID, $image_ids, true ) ) {
$image_ids[] = $attachment->ID;
}
}
}
@ -132,10 +266,21 @@ class Post {
}
}
$this->attachments = $images;
return $images;
}
public function generate_tags() {
/**
* Returns a list of Tags, used in the Post
*
* @return array
*/
public function get_tags() {
if ( $this->tags ) {
return $this->tags;
}
$tags = array();
$post_tags = \get_the_tags( $this->post->ID );
@ -150,18 +295,33 @@ class Post {
}
}
$mentions = apply_filters( 'activitypub_extract_mentions', array(), $this->post->post_content, $this );
if ( $mentions ) {
foreach ( $mentions as $mention => $url ) {
$tag = array(
'type' => 'Mention',
'href' => $url,
'name' => $mention,
);
$tags[] = $tag;
}
}
$this->tags = $tags;
return $tags;
}
/**
* Returns the as2 object-type for a given post
*
* @param string $type the object-type
* @param Object $post the post-object
*
* @return string the object-type
*/
public function generate_object_type() {
public function get_object_type() {
if ( $this->object_type ) {
return $this->object_type;
}
if ( 'wordpress-post-format' !== \get_option( 'activitypub_object_type', 'note' ) ) {
return \ucfirst( \get_option( 'activitypub_object_type', 'note' ) );
}
@ -215,146 +375,103 @@ class Post {
break;
}
$this->object_type = $object_type;
return $object_type;
}
public function generate_the_content() {
$post = $this->post;
$content = $this->get_post_content_template();
/**
* Returns the content for the ActivityPub Item.
*
* @return string the content
*/
public function get_content() {
global $post;
$content = \str_replace( '%title%', \get_the_title( $post->ID ), $content );
$content = \str_replace( '%excerpt%', $this->get_the_post_excerpt(), $content );
$content = \str_replace( '%content%', $this->get_the_post_content(), $content );
$content = \str_replace( '%permalink%', $this->get_the_post_link( 'permalink' ), $content );
$content = \str_replace( '%shortlink%', $this->get_the_post_link( 'shortlink' ), $content );
$content = \str_replace( '%hashtags%', $this->get_the_post_hashtags(), $content );
// backwards compatibility
$content = \str_replace( '%tags%', $this->get_the_post_hashtags(), $content );
$content = \trim( \preg_replace( '/[\r\n]{2,}/', '', $content ) );
$filtered_content = \apply_filters( 'activitypub_the_content', $content, $this->post );
$decoded_content = \html_entity_decode( $filtered_content, \ENT_QUOTES, 'UTF-8' );
$allowed_html = \apply_filters( 'activitypub_allowed_html', \get_option( 'activitypub_allowed_html', ACTIVITYPUB_ALLOWED_HTML ) );
if ( $allowed_html ) {
return \strip_tags( $decoded_content, $allowed_html );
if ( $this->content ) {
return $this->content;
}
return $decoded_content;
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$post = $this->post;
$content = $this->get_post_content_template();
// Fill in the shortcodes.
setup_postdata( $post );
$content = do_shortcode( $content );
wp_reset_postdata();
$content = \wpautop( \wp_kses( $content, $this->allowed_tags ) );
$content = \trim( \preg_replace( '/[\n\r\t]/', '', $content ) );
$content = \apply_filters( 'activitypub_the_content', $content, $post );
$content = \html_entity_decode( $content, \ENT_QUOTES, 'UTF-8' );
$this->content = $content;
return $content;
}
/**
* Gets the template to use to generate the content of the activitypub item.
*
* @return string the template
*/
public function get_post_content_template() {
if ( 'excerpt' === \get_option( 'activitypub_post_content_type', 'content' ) ) {
return "%excerpt%\n\n<p>%permalink%</p>";
return "[ap_excerpt]\n\n[ap_permalink type=\"html\"]";
}
if ( 'title' === \get_option( 'activitypub_post_content_type', 'content' ) ) {
return "<p><strong>%title%</strong></p>\n\n<p>%permalink%</p>";
return "[ap_title]\n\n[ap_permalink type=\"html\"]";
}
if ( 'content' === \get_option( 'activitypub_post_content_type', 'content' ) ) {
return "%content%\n\n<p>%hashtags%</p>\n\n<p>%permalink%</p>";
return "[ap_content]\n\n[ap_hashtags]\n\n[ap_permalink type=\"html\"]";
}
return \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT );
// Upgrade from old template codes to shortcodes.
$content = self::upgrade_post_content_template();
return $content;
}
/**
* Get the excerpt for a post for use outside of the loop.
* Updates the custom template to use shortcodes instead of the deprecated templates.
*
* @param int Optional excerpt length.
*
* @return string The excerpt.
* @return string the updated template content
*/
public function get_the_post_excerpt( $excerpt_length = 400 ) {
$post = $this->post;
public static function upgrade_post_content_template() {
// Get the custom template.
$old_content = \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT );
$excerpt = \get_post_field( 'post_excerpt', $post );
// If the old content exists but is a blank string, we're going to need a flag to updated it even
// after setting it to the default contents.
$need_update = false;
if ( '' === $excerpt ) {
$content = \get_post_field( 'post_content', $post );
// An empty string will make wp_trim_excerpt do stuff we do not want.
if ( '' !== $content ) {
$excerpt = \strip_shortcodes( $content );
/** This filter is documented in wp-includes/post-template.php */
$excerpt = \apply_filters( 'the_content', $excerpt );
$excerpt = \str_replace( ']]>', ']]>', $excerpt );
$excerpt_length = \apply_filters( 'excerpt_length', $excerpt_length );
/** This filter is documented in wp-includes/formatting.php */
$excerpt_more = \apply_filters( 'excerpt_more', ' [...]' );
$excerpt = \wp_trim_words( $excerpt, $excerpt_length, $excerpt_more );
}
// If the old contents is blank, use the defaults.
if ( '' === $old_content ) {
$old_content = ACTIVITYPUB_CUSTOM_POST_CONTENT;
$need_update = true;
}
return \apply_filters( 'the_excerpt', $excerpt );
}
// Set the new content to be the old content.
$content = $old_content;
/**
* Get the content for a post for use outside of the loop.
*
* @return string The content.
*/
public function get_the_post_content() {
$post = $this->post;
// Convert old templates to shortcodes.
$content = \str_replace( '%title%', '[ap_title]', $content );
$content = \str_replace( '%excerpt%', '[ap_excerpt]', $content );
$content = \str_replace( '%content%', '[ap_content]', $content );
$content = \str_replace( '%permalink%', '[ap_permalink type="html"]', $content );
$content = \str_replace( '%shortlink%', '[ap_shortlink type="html"]', $content );
$content = \str_replace( '%hashtags%', '[ap_hashtags]', $content );
$content = \str_replace( '%tags%', '[ap_hashtags]', $content );
$content = \get_post_field( 'post_content', $post );
return \apply_filters( 'the_content', $content );
}
/**
* Adds a backlink to the post/summary content
*
* @param string $content
* @param WP_Post $post
*
* @return string
*/
public function get_the_post_link( $type = 'permalink' ) {
$post = $this->post;
if ( 'shortlink' === $type ) {
$link = \esc_url( \wp_get_shortlink( $post->ID ) );
} elseif ( 'permalink' === $type ) {
$link = \esc_url( \get_permalink( $post->ID ) );
} else {
return '';
// Store the new template if required.
if ( $content !== $old_content || $need_update ) {
\update_option( 'activitypub_custom_post_content', $content );
}
return \sprintf( '<a href="%1$s">%1$s</a>', $link );
}
/**
* Adds all tags as hashtags to the post/summary content
*
* @param string $content
* @param WP_Post $post
*
* @return string
*/
public function get_the_post_hashtags() {
$post = $this->post;
$tags = \get_the_tags( $post->ID );
if ( ! $tags ) {
return '';
}
$hash_tags = array();
foreach ( $tags as $tag ) {
$hash_tags[] = \sprintf( '<a rel="tag" class="u-tag u-category" href="%s">#%s</a>', \get_tag_link( $tag ), $tag->slug );
}
return \implode( ' ', $hash_tags );
return $content;
}
}

View File

@ -101,6 +101,9 @@ class Followers {
$params['user_id'] = array(
'required' => true,
'type' => 'integer',
'validate_callback' => function( $param, $request, $key ) {
return user_can( $param, 'publish_posts' );
},
);
return $params;

View File

@ -99,6 +99,9 @@ class Following {
$params['user_id'] = array(
'required' => true,
'type' => 'integer',
'validate_callback' => function( $param, $request, $key ) {
return user_can( $param, 'publish_posts' );
},
);
return $params;

View File

@ -33,7 +33,7 @@ class Inbox {
array(
'methods' => \WP_REST_Server::EDITABLE,
'callback' => array( '\Activitypub\Rest\Inbox', 'shared_inbox_post' ),
'args' => self::shared_inbox_request_parameters(),
'args' => self::shared_inbox_post_parameters(),
'permission_callback' => '__return_true',
),
)
@ -46,12 +46,13 @@ class Inbox {
array(
'methods' => \WP_REST_Server::EDITABLE,
'callback' => array( '\Activitypub\Rest\Inbox', 'user_inbox_post' ),
'args' => self::user_inbox_request_parameters(),
'args' => self::user_inbox_post_parameters(),
'permission_callback' => '__return_true',
),
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array( '\Activitypub\Rest\Inbox', 'user_inbox_get' ),
'args' => self::user_inbox_get_parameters(),
'permission_callback' => '__return_true',
),
)
@ -195,7 +196,7 @@ class Inbox {
*
* @return array list of parameters
*/
public static function user_inbox_request_parameters() {
public static function user_inbox_get_parameters() {
$params = array();
$params['page'] = array(
@ -205,6 +206,32 @@ class Inbox {
$params['user_id'] = array(
'required' => true,
'type' => 'integer',
'validate_callback' => function( $param, $request, $key ) {
return user_can( $param, 'publish_posts' );
},
);
return $params;
}
/**
* The supported parameters
*
* @return array list of parameters
*/
public static function user_inbox_post_parameters() {
$params = array();
$params['page'] = array(
'type' => 'integer',
);
$params['user_id'] = array(
'required' => true,
'type' => 'integer',
'validate_callback' => function( $param, $request, $key ) {
return user_can( $param, 'publish_posts' );
},
);
$params['id'] = array(
@ -243,7 +270,7 @@ class Inbox {
*
* @return array list of parameters
*/
public static function shared_inbox_request_parameters() {
public static function shared_inbox_post_parameters() {
$params = array();
$params['page'] = array(
@ -410,6 +437,12 @@ class Inbox {
return;
}
// check if Activity is public or not
if ( ! self::is_activity_public( $object ) ) {
// @todo maybe send email
return;
}
$comment_post_id = \url_to_postid( $object['object']['inReplyTo'] );
// save only replys and reactions
@ -446,21 +479,63 @@ class Inbox {
\add_action( 'check_comment_flood', 'check_comment_flood_db', 10, 4 );
}
/**
* Extract recipient URLs from Activity object
*
* @param array $data
*
* @return array The list of user URLs
*/
public static function extract_recipients( $data ) {
$recipients = array();
$users = array();
$recipient_items = array();
foreach ( array( 'to', 'bto', 'cc', 'bcc', 'audience' ) as $i ) {
if ( array_key_exists( $i, $data ) ) {
$recipients = array_merge( $recipients, $data[ $i ] );
if ( is_array( $data[ $i ] ) ) {
$recipient = $data[ $i ];
} else {
$recipient = array( $data[ $i ] );
}
$recipient_items = array_merge( $recipient_items, $recipient );
}
if ( array_key_exists( $i, $data['object'] ) ) {
$recipients = array_merge( $recipients, $data[ $i ] );
if ( is_array( $data['object'][ $i ] ) ) {
$recipient = $data['object'][ $i ];
} else {
$recipient = array( $data['object'][ $i ] );
}
$recipient_items = array_merge( $recipient_items, $recipient );
}
}
$recipients = array_unique( $recipients );
$recipients = array();
// flatten array
foreach ( $recipient_items as $recipient ) {
if ( is_array( $recipient ) ) {
// check if recipient is an object
if ( array_key_exists( 'id', $recipient ) ) {
$recipients[] = $recipient['id'];
}
} else {
$recipients[] = $recipient;
}
}
return array_unique( $recipients );
}
/**
* Get local user recipients
*
* @param array $data
*
* @return array The list of local users
*/
public static function get_recipients( $data ) {
$recipients = self::extract_recipients( $data );
$users = array();
foreach ( $recipients as $recipient ) {
$user_id = \Activitypub\url_to_authorid( $recipient );
@ -474,4 +549,16 @@ class Inbox {
return $users;
}
/**
* Check if passed Activity is Public
*
* @param array $data
* @return boolean
*/
public static function is_activity_public( $data ) {
$recipients = self::extract_recipients( $data );
return in_array( 'https://www.w3.org/ns/activitystreams#Public', $recipients, true );
}
}

View File

@ -103,7 +103,7 @@ class Outbox {
foreach ( $posts as $post ) {
$activitypub_post = new \Activitypub\Model\Post( $post );
$activitypub_activity = new \Activitypub\Model\Activity( 'Create', \Activitypub\Model\Activity::TYPE_NONE );
$activitypub_activity->from_post( $activitypub_post->to_array() );
$activitypub_activity->from_post( $activitypub_post );
$json->orderedItems[] = $activitypub_activity->to_array(); // phpcs:ignore
}
}
@ -138,6 +138,9 @@ class Outbox {
$params['user_id'] = array(
'required' => true,
'type' => 'integer',
'validate_callback' => function( $param, $request, $key ) {
return user_can( $param, 'publish_posts' );
},
);
return $params;

View File

@ -1,35 +0,0 @@
<?php
namespace Activitypub\Rest;
/**
* Custom (hopefully temporary) ActivityPub Rest Server
*
* @author Matthias Pfefferle
*/
class Server extends \WP_REST_Server {
/**
* Overwrite dispatch function to quick fix missing subtype featur
*
* @see https://core.trac.wordpress.org/ticket/49404
*
* @param WP_REST_Request $request Request to attempt dispatching.
* @return WP_REST_Response Response returned by the callback.
*/
public function dispatch( $request ) {
$content_type = $request->get_content_type();
if ( ! $content_type ) {
return parent::dispatch( $request );
}
// check for content-sub-types like 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
if ( \preg_match( '/application\/([a-zA-Z+_-]+\+)json/', $content_type['value'] ) ) {
$request->set_header( 'Content-Type', 'application/json' );
}
// make request filterable
$request = \apply_filters( 'activitypub_pre_dispatch_request', $request );
return parent::dispatch( $request );
}
}

View File

@ -59,7 +59,7 @@ class Webfinger {
$user = \get_user_by( 'login', \esc_sql( $resource_identifier ) );
if ( ! $user ) {
if ( ! $user || ! user_can( $user, 'publish_posts' ) ) {
return new \WP_Error( 'activitypub_user_not_found', \__( 'User not found', 'activitypub' ), array( 'status' => 404 ) );
}

View File

@ -1,409 +0,0 @@
<?php
/**
* This is the class for integrating ActivityPub into the Friends Plugin.
*
* @since 0.14
*
* @package ActivityPub
* @author Alex Kirk
*/
namespace Activitypub;
class Friends_Feed_Parser_ActivityPub extends \Friends\Feed_Parser {
const SLUG = 'activitypub';
const NAME = 'ActivityPub';
const URL = 'https://www.w3.org/TR/activitypub/';
private $friends_feed;
/**
* Constructor.
*
* @param \Friends\Feed $friends_feed The friends feed
*/
public function __construct( \Friends\Feed $friends_feed ) {
$this->friends_feed = $friends_feed;
\add_action( 'activitypub_inbox', array( $this, 'handle_received_activity' ), 10, 3 );
\add_action( 'friends_user_feed_activated', array( $this, 'queue_follow_user' ), 10 );
\add_action( 'friends_user_feed_deactivated', array( $this, 'queue_unfollow_user' ), 10 );
\add_action( 'friends_feed_parser_activitypub_follow', array( $this, 'follow_user' ), 10, 2 );
\add_action( 'friends_feed_parser_activitypub_unfollow', array( $this, 'unfollow_user' ), 10, 2 );
\add_filter( 'friends_rewrite_incoming_url', array( $this, 'friends_rewrite_incoming_url' ), 10, 2 );
}
/**
* Allow logging a message via an action.
* @param string $message The message to log.
* @param array $objects Optional objects as meta data.
* @return void
*/
private function log( $message, $objects = array() ) {
do_action( 'friends_activitypub_log', $message, $objects );
}
/**
* Determines if this is a supported feed and to what degree we feel it's supported.
*
* @param string $url The url.
* @param string $mime_type The mime type.
* @param string $title The title.
* @param string|null $content The content, it can't be assumed that it's always available.
*
* @return int Return 0 if unsupported, a positive value representing the confidence for the feed, use 10 if you're reasonably confident.
*/
public function feed_support_confidence( $url, $mime_type, $title, $content = null ) {
if ( preg_match( '/^@?[^@]+@((?:[a-z0-9-]+\.)+[a-z]+)$/i', $url ) ) {
return 10;
}
return 0;
}
/**
* Format the feed title and autoselect the posts feed.
*
* @param array $feed_details The feed details.
*
* @return array The (potentially) modified feed details.
*/
public function update_feed_details( $feed_details ) {
$meta = \Activitypub\get_remote_metadata_by_actor( $feed_details['url'] );
if ( ! $meta || is_wp_error( $meta ) ) {
return $meta;
}
if ( isset( $meta['name'] ) ) {
$feed_details['title'] = $meta['name'];
} elseif ( isset( $meta['preferredUsername'] ) ) {
$feed_details['title'] = $meta['preferredUsername'];
}
if ( isset( $meta['id'] ) ) {
$feed_details['url'] = $meta['id'];
}
return $feed_details;
}
/**
* Rewrite a Mastodon style URL @username@server to a URL via webfinger.
*
* @param string $url The URL to filter.
* @param string $incoming_url Potentially a mastodon identifier.
*
* @return <type> ( description_of_the_return_value )
*/
public function friends_rewrite_incoming_url( $url, $incoming_url ) {
if ( preg_match( '/^@?[^@]+@((?:[a-z0-9-]+\.)+[a-z]+)$/i', $incoming_url ) ) {
$resolved_url = \Activitypub\Webfinger::resolve( $incoming_url );
if ( ! is_wp_error( $resolved_url ) ) {
return $resolved_url;
}
}
return $url;
}
/**
* Discover the feeds available at the URL specified.
*
* @param string $content The content for the URL is already provided here.
* @param string $url The url to search.
*
* @return array A list of supported feeds at the URL.
*/
public function discover_available_feeds( $content, $url ) {
$discovered_feeds = array();
$meta = \Activitypub\get_remote_metadata_by_actor( $url );
if ( $meta && ! is_wp_error( $meta ) ) {
$discovered_feeds[ $meta['id'] ] = array(
'type' => 'application/activity+json',
'rel' => 'self',
'post-format' => 'status',
'parser' => self::SLUG,
'autoselect' => true,
);
}
return $discovered_feeds;
}
/**
* Fetches a feed and returns the processed items.
*
* @param string $url The url.
*
* @return array An array of feed items.
*/
public function fetch_feed( $url ) {
// There is no feed to fetch, we'll receive items via ActivityPub.
return array();
}
/**
* Handles "Create" requests
*
* @param array $object The activity-object
* @param int $user_id The id of the local blog-user
* @param string $type The type of the activity.
*/
public function handle_received_activity( $object, $user_id, $type ) {
if ( ! in_array(
$type,
array(
// We don't need to handle 'Accept' types since it's handled by the ActivityPub plugin itself.
'create',
'announce',
),
true
) ) {
return false;
}
$actor_url = $object['actor'];
$user_feed = false;
if ( \wp_http_validate_url( $actor_url ) ) {
// Let's check if we follow this actor. If not it might be a different URL representation.
$user_feed = $this->friends_feed->get_user_feed_by_url( $actor_url );
}
if ( is_wp_error( $user_feed ) || ! \wp_http_validate_url( $actor_url ) ) {
$meta = \Activitypub\get_remote_metadata_by_actor( $actor_url );
if ( ! $meta || ! isset( $meta['url'] ) ) {
$this->log( 'Received invalid meta for ' . $actor_url );
return false;
}
$actor_url = $meta['url'];
if ( ! \wp_http_validate_url( $actor_url ) ) {
$this->log( 'Received invalid meta url for ' . $actor_url );
return false;
}
}
$user_feed = $this->friends_feed->get_user_feed_by_url( $actor_url );
if ( ! $user_feed || is_wp_error( $user_feed ) ) {
$this->log( 'We\'re not following ' . $actor_url );
// We're not following this user.
return false;
}
switch ( $type ) {
case 'create':
return $this->handle_incoming_post( $object['object'], $user_feed );
case 'announce':
return $this->handle_incoming_announce( $object['object'], $user_feed, $user_id );
}
return true;
}
/**
* Map the Activity type to a post fomat.
*
* @param string $type The type.
*
* @return string The determined post format.
*/
private function map_type_to_post_format( $type ) {
return 'status';
}
/**
* We received a post for a feed, handle it.
*
* @param array $object The object from ActivityPub.
* @param \Friends\User_Feed $user_feed The user feed.
*/
private function handle_incoming_post( $object, \Friends\User_Feed $user_feed ) {
$permalink = $object['id'];
if ( isset( $object['url'] ) ) {
$permalink = $object['url'];
}
$data = array(
'permalink' => $permalink,
'content' => $object['content'],
'post_format' => $this->map_type_to_post_format( $object['type'] ),
'date' => $object['published'],
);
if ( isset( $object['attributedTo'] ) ) {
$meta = \Activitypub\get_remote_metadata_by_actor( $object['attributedTo'] );
$this->log( 'Attributed to ' . $object['attributedTo'], compact( 'meta' ) );
if ( isset( $meta['name'] ) ) {
$data['author'] = $meta['name'];
} elseif ( isset( $meta['preferredUsername'] ) ) {
$data['author'] = $meta['preferredUsername'];
}
}
if ( ! empty( $object['attachment'] ) ) {
foreach ( $object['attachment'] as $attachment ) {
if ( ! isset( $attachment['type'] ) || ! isset( $attachment['mediaType'] ) ) {
continue;
}
if ( 'Document' !== $attachment['type'] || strpos( $attachment['mediaType'], 'image/' ) !== 0 ) {
continue;
}
$data['content'] .= PHP_EOL;
$data['content'] .= '<!-- wp:image -->';
$data['content'] .= '<p><img src="' . esc_url( $attachment['url'] ) . '" width="' . esc_attr( $attachment['width'] ) . '" height="' . esc_attr( $attachment['height'] ) . '" class="size-full" /></p>';
$data['content'] .= '<!-- /wp:image -->';
}
$meta = \Activitypub\get_remote_metadata_by_actor( $object['attributedTo'] );
$this->log( 'Attributed to ' . $object['attributedTo'], compact( 'meta' ) );
if ( isset( $meta['name'] ) ) {
$data['author'] = $meta['name'];
} elseif ( isset( $meta['preferredUsername'] ) ) {
$data['author'] = $meta['preferredUsername'];
}
}
$this->log(
'Received feed item',
array(
'url' => $permalink,
'data' => $data,
)
);
$item = new \Friends\Feed_Item( $data );
$this->friends_feed->process_incoming_feed_items( array( $item ), $user_feed );
return true;
}
/**
* We received an announced URL (boost) for a feed, handle it.
*
* @param array $url The announced URL.
* @param \Friends\User_Feed $user_feed The user feed.
*/
private function handle_incoming_announce( $url, \Friends\User_Feed $user_feed, $user_id ) {
if ( ! \wp_http_validate_url( $url ) ) {
$this->log( 'Received invalid announce', compact( 'url' ) );
return false;
}
$this->log( 'Received announce for ' . $url );
$response = \Activitypub\safe_remote_get( $url, $user_id );
if ( \is_wp_error( $response ) ) {
return $response;
}
$json = \wp_remote_retrieve_body( $response );
$object = \json_decode( $json, true );
if ( ! $object ) {
$this->log( 'Received invalid json', compact( 'json' ) );
return false;
}
$this->log( 'Received response', compact( 'url', 'object' ) );
return $this->handle_incoming_post( $object, $user_feed );
}
/**
* Prepare to follow the user via a scheduled event.
*
* @param \Friends\User_Feed $user_feed The user feed.
*
* @return bool|WP_Error Whether the event was queued.
*/
public function queue_follow_user( \Friends\User_Feed $user_feed ) {
if ( self::SLUG !== $user_feed->get_parser() ) {
return;
}
$args = array( $user_feed->get_url(), get_current_user_id() );
$unfollow_timestamp = wp_next_scheduled( 'friends_feed_parser_activitypub_unfollow', $args );
if ( $unfollow_timestamp ) {
// If we just unfollowed, we don't want the event to potentially be executed after our follow event.
wp_unschedule_event( $unfollow_timestamp, $args );
}
if ( wp_next_scheduled( 'friends_feed_parser_activitypub_follow', $args ) ) {
return;
}
return \wp_schedule_single_event( \time(), 'friends_feed_parser_activitypub_follow', $args );
}
/**
* Follow a user via ActivityPub at a URL.
*
* @param string $url The url.
* @param int $user_id The current user id.
*/
public function follow_user( $url, $user_id ) {
$meta = \Activitypub\get_remote_metadata_by_actor( $url );
$to = $meta['id'];
$inbox = \Activitypub\get_inbox_by_actor( $to );
$actor = \get_author_posts_url( $user_id );
$activity = new \Activitypub\Model\Activity( 'Follow', \Activitypub\Model\Activity::TYPE_SIMPLE );
$activity->set_to( null );
$activity->set_cc( null );
$activity->set_actor( $actor );
$activity->set_object( $to );
$activity->set_id( $actor . '#follow-' . \preg_replace( '~^https?://~', '', $to ) );
$activity = $activity->to_json();
\Activitypub\safe_remote_post( $inbox, $activity, $user_id );
}
/**
* Prepare to unfollow the user via a scheduled event.
*
* @param \Friends\User_Feed $user_feed The user feed.
*
* @return bool|WP_Error Whether the event was queued.
*/
public function queue_unfollow_user( \Friends\User_Feed $user_feed ) {
if ( self::SLUG !== $user_feed->get_parser() ) {
return false;
}
$args = array( $user_feed->get_url(), get_current_user_id() );
$follow_timestamp = wp_next_scheduled( 'friends_feed_parser_activitypub_follow', $args );
if ( $follow_timestamp ) {
// If we just followed, we don't want the event to potentially be executed after our unfollow event.
wp_unschedule_event( $follow_timestamp, $args );
}
if ( wp_next_scheduled( 'friends_feed_parser_activitypub_unfollow', $args ) ) {
return true;
}
return \wp_schedule_single_event( \time(), 'friends_feed_parser_activitypub_unfollow', $args );
}
/**
* Unfllow a user via ActivityPub at a URL.
*
* @param string $url The url.
* @param int $user_id The current user id.
*/
public function unfollow_user( $url, $user_id ) {
$meta = \Activitypub\get_remote_metadata_by_actor( $url );
$to = $meta['id'];
$inbox = \Activitypub\get_inbox_by_actor( $to );
$actor = \get_author_posts_url( $user_id );
$activity = new \Activitypub\Model\Activity( 'Undo', \Activitypub\Model\Activity::TYPE_SIMPLE );
$activity->set_to( null );
$activity->set_cc( null );
$activity->set_actor( $actor );
$activity->set_object(
array(
'type' => 'Follow',
'actor' => $actor,
'object' => $to,
'id' => $to,
)
);
$activity->set_id( $actor . '#unfollow-' . \preg_replace( '~^https?://~', '', $to ) );
$activity = $activity->to_json();
\Activitypub\safe_remote_post( $inbox, $activity, $user_id );
}
}

View File

@ -1,10 +1,9 @@
=== ActivityPub ===
Contributors: pfefferle, mediaformat, akirk
Donate link: https://notiz.blog/donate/
Contributors: pfefferle, mediaformat, akirk, automattic
Tags: OStatus, fediverse, activitypub, activitystream
Requires at least: 4.7
Tested up to: 6.1
Stable tag: 0.14.3
Stable tag: 0.17.0
Requires PHP: 5.6
License: MIT
License URI: http://opensource.org/licenses/MIT
@ -88,6 +87,48 @@ Where 'blog' is the path to the subdirectory at which your blog resides.
Project maintained on GitHub at [pfefferle/wordpress-activitypub](https://github.com/pfefferle/wordpress-activitypub).
= 0.17.0 =
* Fix type-selector
* Allow more HTML elements in Activity-Objects
= 0.16.5 =
* Return empty content/excerpt on password protected posts/pages
= 0.16.4 =
* Remove scripts later in the queue, to also handle scripts added by blocks
* Add published date to author profiles
= 0.16.3 =
* "cc", "to", ... fields can either be an array or a string
* Remove "style" and "script" HTML elements from content
= 0.16.2 =
* Fix fatal error in outbox
= 0.16.1 =
* Fix "update and create, posts appear blank on Mastodon" issue
= 0.16.0 =
* Add "Outgoing Mentions" ([#213](https://github.com/pfefferle/wordpress-activitypub/pull/213)) props [@akirk](https://github.com/akirk)
* Add configuration item for number of images to attach ([#248](https://github.com/pfefferle/wordpress-activitypub/pull/248)) props [@mexon](https://github.com/mexon)
* Use shortcodes instead of custom templates, to setup the Activity Post-Content ([#250](https://github.com/pfefferle/wordpress-activitypub/pull/250)) props [@toolstack](https://github.com/toolstack)
* Remove custom REST Server, because the needed changes are now merged into Core.
* Fix hashtags ([#261](https://github.com/pfefferle/wordpress-activitypub/pull/261)) props [@akirk](https://github.com/akirk)
* Change priorites, to maybe fix the hashtag issue
= 0.15.0 =
* Enable ActivityPub only for users that can `publish_posts`
* Persist only public Activities
* Fix remote-delete
= 0.14.3 =
* Better error handling. props [@akirk](https://github.com/akirk)
@ -102,7 +143,7 @@ Project maintained on GitHub at [pfefferle/wordpress-activitypub](https://github
= 0.14.0 =
* Friends support: https://wordpress.org/plugins/friends/ . props [@akirk](https://github.com/akirk)
* Friends support: https://wordpress.org/plugins/friends/ props [@akirk](https://github.com/akirk)
* Massive guidance improvements. props [mediaformat](https://github.com/mediaformat) & [@akirk](https://github.com/akirk)
* Add Custom Post Type support to outbox API. props [blueset](https://github.com/blueset)
* Better hash-tag support. props [bocops](https://github.com/bocops)

View File

@ -19,6 +19,8 @@ $json->icon = array(
'url' => \get_avatar_url( $author_id, array( 'size' => 120 ) ),
);
$json->published = \gmdate( 'Y-m-d\TH:i:s\Z', \strtotime( \get_the_author_meta( 'registered', $author_id ) ) );
if ( \has_header_image() ) {
$json->image = array(
'type' => 'Image',

View File

@ -38,7 +38,7 @@ $json->manuallyApprovesFollowers = \apply_filters( 'activitypub_json_manually_ap
$json->publicKey = array(
'id' => \get_home_url( '/' ) . '#main-key',
'owner' => \get_home_url( '/' ),
'publicKeyPem' => \trim(),
'publicKeyPem' => '',
);
$json->tag = array();

View File

@ -56,22 +56,42 @@
<p>
<textarea name="activitypub_custom_post_content" id="activitypub_custom_post_content" rows="10" cols="50" class="large-text" placeholder="<?php echo wp_kses( ACTIVITYPUB_CUSTOM_POST_CONTENT, 'post' ); ?>"><?php echo wp_kses( \get_option( 'activitypub_custom_post_content', ACTIVITYPUB_CUSTOM_POST_CONTENT ), 'post' ); ?></textarea>
<details>
<summary><?php esc_html_e( 'See the complete list of template patterns.', 'activitypub' ); ?></summary>
<summary><?php esc_html_e( 'See a list of ActivityPub Template Tags.', 'activitypub' ); ?></summary>
<div class="description">
<ul>
<li><code>%title%</code> - <?php \esc_html_e( 'The Post-Title.', 'activitypub' ); ?></li>
<li><code>%content%</code> - <?php \esc_html_e( 'The Post-Content.', 'activitypub' ); ?></li>
<li><code>%excerpt%</code> - <?php \esc_html_e( 'The Post-Excerpt (default 400 Chars).', 'activitypub' ); ?></li>
<li><code>%permalink%</code> - <?php \esc_html_e( 'The Post-Permalink.', 'activitypub' ); ?></li>
<?php // translators: ?>
<li><code>%shortlink%</code> - <?php echo \wp_kses( \__( 'The Post-Shortlink. I can recommend <a href="https://wordpress.org/plugins/hum/" target="_blank">Hum</a>, to prettify the Shortlinks', 'activitypub' ), 'default' ); ?></li>
<li><code>%hashtags%</code> - <?php \esc_html_e( 'The Tags as Hashtags.', 'activitypub' ); ?></li>
<li><code>[ap_title]</code> - <?php \esc_html_e( 'The post\'s title.', 'activitypub' ); ?></li>
<li><code>[ap_content]</code> - <?php \esc_html_e( 'The post\'s content.', 'activitypub' ); ?></li>
<li><code>[ap_excerpt]</code> - <?php \esc_html_e( 'The post\'s excerpt (default 400 chars).', 'activitypub' ); ?></li>
<li><code>[ap_permalink]</code> - <?php \esc_html_e( 'The post\'s permalink.', 'activitypub' ); ?></li>
<li><code>[ap_shortlink]</code> - <?php echo \wp_kses( \__( 'The post\'s shortlink. I can recommend <a href="https://wordpress.org/plugins/hum/" target="_blank">Hum</a>.', 'activitypub' ), 'default' ); ?></li>
<li><code>[ap_hashtags]</code> - <?php \esc_html_e( 'The post\'s tags as hashtags.', 'activitypub' ); ?></li>
<li><code>[ap_hashcats]</code> - <?php \esc_html_e( 'The post\'s categories as hashtags.', 'activitypub' ); ?></li>
<li><code>[ap_image]</code> - <?php \esc_html_e( 'The URL for the post\'s featured image.', 'activitypub' ); ?></li>
</ul>
<p><?php \esc_html_e( 'You can find the full list with all possible attributes in the help section on the top-right of the screen.', 'activitypub' ); ?></p>
</div>
</details>
</p>
<?php // translators: ?>
<p><?php echo \wp_kses( \__( '<a href="https://github.com/pfefferle/wordpress-activitypub/issues/new" target="_blank">Let me know</a> if you miss a template pattern.', 'activitypub' ), 'default' ); ?></p>
</td>
</tr>
<tr>
<th scope="row">
<?php \esc_html_e( 'Number of images', 'activitypub' ); ?>
</th>
<td>
<input value="<?php echo esc_attr( \get_option( 'activitypub_max_image_attachments', ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS ) ); ?>" name="activitypub_max_image_attachments" id="activitypub_max_image_attachments" type="number" min="0" />
<p class="description">
<?php
echo \wp_kses(
\sprintf(
// translators:
\__( 'The number of images to attach to posts. Default: <code>%s</code>', 'activitypub' ),
\esc_html( ACTIVITYPUB_MAX_IMAGE_ATTACHMENTS )
),
'default'
);
?>
</p>
</td>
</tr>
<tr>
@ -112,31 +132,11 @@
</tr>
<tr>
<th scope="row">
<?php \esc_html_e( 'Hashtags', 'activitypub' ); ?>
<?php \esc_html_e( 'Hashtags (beta)', 'activitypub' ); ?>
</th>
<td>
<p>
<label><input type="checkbox" name="activitypub_use_hashtags" id="activitypub_use_hashtags" value="1" <?php echo \checked( '1', \get_option( 'activitypub_use_hashtags', '1' ) ); ?> /> <?php echo wp_kses( \__( 'Add hashtags in the content as native tags and replace the <code>#tag</code> with the tag-link.', 'activitypub' ), 'default' ); ?></label>
</p>
</td>
</tr>
<tr>
<th scope="row">
<?php \esc_html_e( 'HTML Allowlist', 'activitypub' ); ?>
</th>
<td>
<textarea name="activitypub_allowed_html" id="activitypub_allowed_html" rows="3" cols="50" class="large-text"><?php echo esc_html( \get_option( 'activitypub_allowed_html', ACTIVITYPUB_ALLOWED_HTML ) ); ?></textarea>
<p class="description">
<?php
echo \wp_kses(
\sprintf(
// translators:
\__( 'A list of HTML elements, you want to allowlist for your activities. <strong>Leave list empty to support all HTML elements</strong>. Default: <code>%s</code>', 'activitypub' ),
\esc_html( ACTIVITYPUB_ALLOWED_HTML )
),
'default'
);
?>
<label><input type="checkbox" name="activitypub_use_hashtags" id="activitypub_use_hashtags" value="1" <?php echo \checked( '1', \get_option( 'activitypub_use_hashtags', '1' ) ); ?> /> <?php echo wp_kses( \__( 'Add hashtags in the content as native tags and replace the <code>#tag</code> with the tag-link. <strong>This feature is experimental! Please disable it, if you find any HTML or CSS errors.</strong>', 'activitypub' ), 'default' ); ?></label>
</p>
</td>
</tr>

View File

@ -1,36 +0,0 @@
<?php
/*
Plugin Name: Disable WordPress Core Updates
Description: Disables WordPress core update checks and notifications.
Plugin URI: https://lud.icro.us/disable-wordpress-core-update/
Version: 1.5
Author: John Blackbourn
Author URI: https://johnblackbourn.com/
License: GPL v2 or later
Network: true
Props: Matt Mullenweg, _ck_, miqrogroove
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
# 2.3 to 2.7:
add_action( 'init', create_function( '$a', "remove_action( 'init', 'wp_version_check' );" ), 2 );
add_filter( 'pre_option_update_core', create_function( '$a', "return null;" ) );
# 2.8+:
remove_action( 'wp_version_check', 'wp_version_check' );
remove_action( 'admin_init', '_maybe_update_core' );
add_filter( 'pre_transient_update_core', create_function( '$a', "return null;" ) );
# 3.0+:
add_filter( 'pre_site_transient_update_core', create_function( '$a', "return null;" ) );

View File

@ -1,58 +0,0 @@
=== Disable WordPress Core Updates ===
Contributors: johnbillion
Tags: disable, core update
Requires at least: 2.3
Tested up to: 3.7
Stable tag: trunk
Disables WordPress core update checks and notifications.
== Description ==
Completely disables the core update checking system in WordPress. This prevents WordPress from checking for core updates, and prevents any notifications from being displayed in the admin area. Ideal for administrators of multiple WordPress installations.
= Please note! =
It's *very* important that you keep your WordPress installation(s) up to date. If you don't, your blog or website could be susceptible to security vulnerabilities or performance issues. If you use this plugin, you must make sure you keep yourself informed of new WordPress releases and update your WordPress installation(s) as new versions are released.
See also: [Disable WordPress plugin updates](http://wordpress.org/plugins/disable-wordpress-plugin-updates/) and [Disable WordPress theme updates](http://wordpress.org/plugins/disable-wordpress-theme-updates/).
== Installation ==
1. Unzip the ZIP file and drop the folder straight into your `wp-content/plugins/` directory.
2. Activate the plugin through the 'Plugins' menu in WordPress.
== Frequently Asked Questions ==
= Why would I want to disable the core update system? =
Most people will not want to (and should not) disable this feature. It's a fantastic feature of WordPress and I'm fully in support of it. However, administrators who maintain multiple installations of WordPress on behalf of other people (eg. clients) may not want update notifications to be shown to the users of these installations. This plugin is for them.
= Does this plugin disable the automatic updates in WordPress 3.7 and higher? =
Yes, this plugin completely disables all core update checks, so the automatic update system will be disabled too.
= Can I disable the plugin update notifications too? =
Yes. See the [Disable WordPress Plugin Updates](http://wordpress.org/extend/plugins/disable-wordpress-plugin-updates/) plugin.
= Can I disable the theme update notifications too? =
Yes. See the [Disable WordPress Theme Updates](http://wordpress.org/extend/plugins/disable-wordpress-theme-updates/) plugin.
== Changelog ==
= 1.5 =
* Force the plugin to be network enabled.
= 1.4 =
* Support for WordPress 3.0.
= 1.3 =
* Support for WordPress 2.8.
= 1.2 =
* Bugfix to completely prevent any communication with api.wordpress.org.
= 1.1 =
* Initial release.

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
#edd-admin-tax-rates{margin:1em 0 0}#edd-admin-tax-rates table{border-collapse:collapse}#edd-admin-tax-rates .tablenav.top{display:flex;justify-content:space-between}#edd-admin-tax-rates .edd-admin-tax-rates__tablenav--left{display:inline-flex}#edd-admin-tax-rates th:not(.check-column){padding:15px 10px;width:unset}#edd-admin-tax-rates .chosen-container{width:100%!important}#edd-admin-tax-rates tbody tr:not(:last-of-type){border-bottom:1px solid #e0e0e0}#edd-admin-tax-rates tfoot.add-new th{font-weight:400;padding:12px 8px 10px}#edd-admin-tax-rates .edd-tax-rate-row--inactive,#edd-admin-tax-rates .edd-tax-rate-row--is-empty+.edd-tax-rate-row--is-empty{display:none}#edd-admin-tax-rates .has-inactive .edd-tax-rate-row--inactive{display:table-row}#edd-admin-tax-rates .edd-tax-rate-row--is-empty td{background-color:#f9f9f9}#edd-admin-tax-rates .edd-tax-rate-row--inactive td{color:#999;background-color:#f9f9f9}#edd-admin-tax-rates .edd-tax-rate-table-add{background-color:#f9f9f9}@media screen and (max-width:782px){#edd-admin-tax-rates tfoot:not(.add-new) th:not(.edd-tax-rates-table-rate),#edd-admin-tax-rates thead th:not(.edd-tax-rates-table-rate){display:none}#edd-admin-tax-rates .edd-tax-rate-row,#edd-admin-tax-rates tfoot:not(.add-new) tr,#edd-admin-tax-rates thead tr{display:-ms-grid;display:grid;-ms-grid-columns:2.5em 1fr;grid-template-columns:2.5em 1fr;-ms-grid-rows:1fr;grid-template-rows:1fr;grid-gap:0 16px}#edd-admin-tax-rates th.edd-tax-rates-table-rate{padding-right:12px}#edd-admin-tax-rates .edd-tax-rates-table-checkbox{-ms-grid-row:1;-ms-grid-row-span:4;grid-row:1/5}#edd-admin-tax-rates tbody td{padding-right:35%!important}#edd-admin-tax-rates td:before{content:attr(data-colname);display:block;width:32%;position:absolute}#edd-admin-tax-rates .tablenav.top{flex-wrap:wrap}#edd-admin-tax-rates .edd-admin-tax-rates__tablenav--left{margin-bottom:16px}#edd-admin-tax-rates .edd-admin-tax-rates__tablenav--left select{margin-left:6px}}.edd-tax-rate-table-add th input[type=number],.edd-tax-rate-table-add th input[type=text],.edd-tax-rate-table-add th select{width:100%;margin:0;padding:4px}.edd-tax-rate-table-add #tax_rate_region_global{margin-left:4px;margin-bottom:8px}@media screen and (max-width:782px){.edd-tax-rate-table-add,.edd-tax-rate-table-add th{display:block}.edd-tax-rate-table-add .screen-reader-text{display:block;width:unset;clip:unset;height:unset;clip-path:unset;margin:0 0 12px;position:relative}}
#edd-admin-tax-rates{margin:1em 0 0}#edd-admin-tax-rates table{border-collapse:collapse}#edd-admin-tax-rates .tablenav.top{display:flex;justify-content:space-between}#edd-admin-tax-rates .edd-admin-tax-rates__tablenav--left{display:inline-flex}#edd-admin-tax-rates th:not(.check-column){padding:15px 10px;width:unset}#edd-admin-tax-rates .chosen-container{width:100%!important}#edd-admin-tax-rates tbody tr:not(:last-of-type){border-bottom:1px solid #e0e0e0}#edd-admin-tax-rates tfoot.add-new th{font-weight:400;padding:12px 8px 10px}#edd-admin-tax-rates .edd-tax-rate-row--inactive,#edd-admin-tax-rates .edd-tax-rate-row--is-empty+.edd-tax-rate-row--is-empty{display:none}#edd-admin-tax-rates .has-inactive .edd-tax-rate-row--inactive{display:table-row}#edd-admin-tax-rates .edd-tax-rate-row--is-empty td{background-color:#f9f9f9}#edd-admin-tax-rates .edd-tax-rate-row--inactive td{color:#999;background-color:#f9f9f9}#edd-admin-tax-rates .edd-tax-rate-table-add{background-color:#f9f9f9}@media screen and (max-width:782px){#edd-admin-tax-rates tfoot:not(.add-new) th:not(.edd-tax-rates-table-rate),#edd-admin-tax-rates thead th:not(.edd-tax-rates-table-rate){display:none}#edd-admin-tax-rates .edd-tax-rate-row,#edd-admin-tax-rates tfoot:not(.add-new) tr,#edd-admin-tax-rates thead tr{display:-ms-grid;display:grid;-ms-grid-columns:2.5em 1fr;grid-template-columns:2.5em 1fr;-ms-grid-rows:1fr;grid-template-rows:1fr;grid-gap:0 16px}#edd-admin-tax-rates th.edd-tax-rates-table-rate{padding-right:12px}#edd-admin-tax-rates .edd-tax-rates-table-checkbox{-ms-grid-row:1;-ms-grid-row-span:4;grid-row:1/5}#edd-admin-tax-rates tbody td{padding-right:35%!important}#edd-admin-tax-rates td:before{content:attr(data-colname);display:block;width:32%;position:absolute}#edd-admin-tax-rates .tablenav.top{flex-wrap:wrap}#edd-admin-tax-rates .edd-admin-tax-rates__tablenav--left{margin-bottom:16px}#edd-admin-tax-rates .edd-admin-tax-rates__tablenav--left select{margin-left:6px}}.edd-tax-rate-table-add th input[type=number],.edd-tax-rate-table-add th input[type=text],.edd-tax-rate-table-add th select{width:100%;margin:0;padding:4px}.edd-tax-rate-table-add #tax_rate_region_global{margin-left:4px;margin-bottom:8px}@media screen and (max-width:782px){.edd-tax-rate-table-add,.edd-tax-rate-table-add th{display:block}.edd-tax-rate-table-add .screen-reader-text{display:block;width:unset;clip:unset;height:unset;-webkit-clip-path:unset;clip-path:unset;margin:0 0 12px;position:relative}}

View File

@ -1 +1 @@
#edd-admin-tax-rates{margin:1em 0 0}#edd-admin-tax-rates table{border-collapse:collapse}#edd-admin-tax-rates .tablenav.top{display:flex;justify-content:space-between}#edd-admin-tax-rates .edd-admin-tax-rates__tablenav--left{display:inline-flex}#edd-admin-tax-rates th:not(.check-column){padding:15px 10px;width:unset}#edd-admin-tax-rates .chosen-container{width:100%!important}#edd-admin-tax-rates tbody tr:not(:last-of-type){border-bottom:1px solid #e0e0e0}#edd-admin-tax-rates tfoot.add-new th{font-weight:400;padding:12px 8px 10px}#edd-admin-tax-rates .edd-tax-rate-row--inactive,#edd-admin-tax-rates .edd-tax-rate-row--is-empty+.edd-tax-rate-row--is-empty{display:none}#edd-admin-tax-rates .has-inactive .edd-tax-rate-row--inactive{display:table-row}#edd-admin-tax-rates .edd-tax-rate-row--is-empty td{background-color:#f9f9f9}#edd-admin-tax-rates .edd-tax-rate-row--inactive td{color:#999;background-color:#f9f9f9}#edd-admin-tax-rates .edd-tax-rate-table-add{background-color:#f9f9f9}@media screen and (max-width:782px){#edd-admin-tax-rates tfoot:not(.add-new) th:not(.edd-tax-rates-table-rate),#edd-admin-tax-rates thead th:not(.edd-tax-rates-table-rate){display:none}#edd-admin-tax-rates .edd-tax-rate-row,#edd-admin-tax-rates tfoot:not(.add-new) tr,#edd-admin-tax-rates thead tr{display:-ms-grid;display:grid;-ms-grid-columns:2.5em 1fr;grid-template-columns:2.5em 1fr;-ms-grid-rows:1fr;grid-template-rows:1fr;grid-gap:0 16px}#edd-admin-tax-rates th.edd-tax-rates-table-rate{padding-left:12px}#edd-admin-tax-rates .edd-tax-rates-table-checkbox{-ms-grid-row:1;-ms-grid-row-span:4;grid-row:1/5}#edd-admin-tax-rates tbody td{padding-left:35%!important}#edd-admin-tax-rates td:before{content:attr(data-colname);display:block;width:32%;position:absolute}#edd-admin-tax-rates .tablenav.top{flex-wrap:wrap}#edd-admin-tax-rates .edd-admin-tax-rates__tablenav--left{margin-bottom:16px}#edd-admin-tax-rates .edd-admin-tax-rates__tablenav--left select{margin-right:6px}}.edd-tax-rate-table-add th input[type=number],.edd-tax-rate-table-add th input[type=text],.edd-tax-rate-table-add th select{width:100%;margin:0;padding:4px}.edd-tax-rate-table-add #tax_rate_region_global{margin-right:4px;margin-bottom:8px}@media screen and (max-width:782px){.edd-tax-rate-table-add,.edd-tax-rate-table-add th{display:block}.edd-tax-rate-table-add .screen-reader-text{display:block;width:unset;clip:unset;height:unset;clip-path:unset;margin:0 0 12px;position:relative}}
#edd-admin-tax-rates{margin:1em 0 0}#edd-admin-tax-rates table{border-collapse:collapse}#edd-admin-tax-rates .tablenav.top{display:flex;justify-content:space-between}#edd-admin-tax-rates .edd-admin-tax-rates__tablenav--left{display:inline-flex}#edd-admin-tax-rates th:not(.check-column){padding:15px 10px;width:unset}#edd-admin-tax-rates .chosen-container{width:100%!important}#edd-admin-tax-rates tbody tr:not(:last-of-type){border-bottom:1px solid #e0e0e0}#edd-admin-tax-rates tfoot.add-new th{font-weight:400;padding:12px 8px 10px}#edd-admin-tax-rates .edd-tax-rate-row--inactive,#edd-admin-tax-rates .edd-tax-rate-row--is-empty+.edd-tax-rate-row--is-empty{display:none}#edd-admin-tax-rates .has-inactive .edd-tax-rate-row--inactive{display:table-row}#edd-admin-tax-rates .edd-tax-rate-row--is-empty td{background-color:#f9f9f9}#edd-admin-tax-rates .edd-tax-rate-row--inactive td{color:#999;background-color:#f9f9f9}#edd-admin-tax-rates .edd-tax-rate-table-add{background-color:#f9f9f9}@media screen and (max-width:782px){#edd-admin-tax-rates tfoot:not(.add-new) th:not(.edd-tax-rates-table-rate),#edd-admin-tax-rates thead th:not(.edd-tax-rates-table-rate){display:none}#edd-admin-tax-rates .edd-tax-rate-row,#edd-admin-tax-rates tfoot:not(.add-new) tr,#edd-admin-tax-rates thead tr{display:-ms-grid;display:grid;-ms-grid-columns:2.5em 1fr;grid-template-columns:2.5em 1fr;-ms-grid-rows:1fr;grid-template-rows:1fr;grid-gap:0 16px}#edd-admin-tax-rates th.edd-tax-rates-table-rate{padding-left:12px}#edd-admin-tax-rates .edd-tax-rates-table-checkbox{-ms-grid-row:1;-ms-grid-row-span:4;grid-row:1/5}#edd-admin-tax-rates tbody td{padding-left:35%!important}#edd-admin-tax-rates td:before{content:attr(data-colname);display:block;width:32%;position:absolute}#edd-admin-tax-rates .tablenav.top{flex-wrap:wrap}#edd-admin-tax-rates .edd-admin-tax-rates__tablenav--left{margin-bottom:16px}#edd-admin-tax-rates .edd-admin-tax-rates__tablenav--left select{margin-right:6px}}.edd-tax-rate-table-add th input[type=number],.edd-tax-rate-table-add th input[type=text],.edd-tax-rate-table-add th select{width:100%;margin:0;padding:4px}.edd-tax-rate-table-add #tax_rate_region_global{margin-right:4px;margin-bottom:8px}@media screen and (max-width:782px){.edd-tax-rate-table-add,.edd-tax-rate-table-add th{display:block}.edd-tax-rate-table-add .screen-reader-text{display:block;width:unset;clip:unset;height:unset;-webkit-clip-path:unset;clip-path:unset;margin:0 0 12px;position:relative}}

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
!function(e){var t={};function n(i){if(t[i])return t[i].exports;var r=t[i]={i:i,l:!1,exports:{}};return e[i].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(i,r,function(t){return e[t]}.bind(null,r));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=179)}({1:function(e,t){e.exports=jQuery},179:function(e,t,n){(function(e,t){var n={vars:{customer_card_wrap_editable:e("#edit-customer-info .editable"),customer_card_wrap_edit_item:e("#edit-customer-info .edit-item"),user_id:e('input[name="customerinfo[user_id]"]')},init:function(){this.edit_customer(),this.add_email(),this.user_search(),this.remove_user(),this.cancel_edit(),this.change_country(),this.delete_checked()},edit_customer:function(){e(document.body).on("click","#edit-customer",(function(e){e.preventDefault(),n.vars.customer_card_wrap_editable.hide(),n.vars.customer_card_wrap_edit_item.show().css("display","block")}))},add_email:function(){e(document.body).on("click","#add-customer-email",(function(t){t.preventDefault();var n=e(this),i=n.parent().parent().parent().parent(),r={edd_action:"customer-add-email",customer_id:i.find('input[name="customer-id"]').val(),email:i.find('input[name="additional-email"]').val(),primary:i.find('input[name="make-additional-primary"]').is(":checked"),_wpnonce:i.find('input[name="add_email_nonce"]').val()};i.parent().find(".notice-container").remove(),i.find(".spinner").css("visibility","visible"),n.attr("disabled",!0),e.post(ajaxurl,r,(function(e){setTimeout((function(){!0===e.success?window.location.href=e.redirect:(n.attr("disabled",!1),i.before('<div class="notice-container"><div class="notice notice-error inline"><p>'+e.message+"</p></div></div>"),i.find(".spinner").css("visibility","hidden"))}),342)}),"json")}))},user_search:function(){e(document.body).on("click.eddSelectUser",".edd_user_search_results a",(function(t){t.preventDefault();var i=e(this).data("userid");n.vars.user_id.val(i)}))},remove_user:function(){e(document.body).on("click","#disconnect-customer",(function(t){if(t.preventDefault(),confirm(edd_vars.disconnect_customer)){var n={edd_action:"disconnect-userid",customer_id:e('input[name="customerinfo[id]"]').val(),_wpnonce:e("#edit-customer-info #_wpnonce").val()};e.post(ajaxurl,n,(function(e){window.location.href=window.location.href}),"json")}}))},cancel_edit:function(){e(document.body).on("click","#edd-edit-customer-cancel",(function(t){t.preventDefault(),n.vars.customer_card_wrap_edit_item.hide(),n.vars.customer_card_wrap_editable.show(),e(".edd_user_search_results").html("")}))},change_country:function(){e('select[name="customerinfo[country]"]').change((function(){var t=e(this),n=e(':input[name="customerinfo[region]"]'),i={action:"edd_get_shop_states",country:t.val(),nonce:t.data("nonce"),field_name:"customerinfo[region]"};return e.post(ajaxurl,i,(function(e){console.log(e),"nostates"===e?n.replaceWith('<input type="text" name="'+i.field_name+'" value="" class="edd-edit-toggles medium-text"/>'):n.replaceWith(e)})),!1}))},delete_checked:function(){e("#edd-customer-delete-confirm").change((function(){var t=e("#edd-customer-delete-records"),n=e("#edd-delete-customer");e(this).prop("checked")?(t.attr("disabled",!1),n.attr("disabled",!1)):(t.attr("disabled",!0),t.prop("checked",!1),n.attr("disabled",!0))}))}};t(document).ready((function(e){n.init()}))}).call(this,n(1),n(1))}});
!function(e){var t={};function n(i){if(t[i])return t[i].exports;var r=t[i]={i:i,l:!1,exports:{}};return e[i].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(i,r,function(t){return e[t]}.bind(null,r));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=181)}({1:function(e,t){e.exports=jQuery},181:function(e,t,n){(function(e,t){var n={vars:{customer_card_wrap_editable:e("#edit-customer-info .editable"),customer_card_wrap_edit_item:e("#edit-customer-info .edit-item"),user_id:e('input[name="customerinfo[user_id]"]')},init:function(){this.edit_customer(),this.add_email(),this.user_search(),this.remove_user(),this.cancel_edit(),this.change_country(),this.delete_checked()},edit_customer:function(){e(document.body).on("click","#edit-customer",(function(e){e.preventDefault(),n.vars.customer_card_wrap_editable.hide(),n.vars.customer_card_wrap_edit_item.show().css("display","block")}))},add_email:function(){e(document.body).on("click","#add-customer-email",(function(t){t.preventDefault();var n=e(this),i=n.parent().parent().parent().parent(),r={edd_action:"customer-add-email",customer_id:i.find('input[name="customer-id"]').val(),email:i.find('input[name="additional-email"]').val(),primary:i.find('input[name="make-additional-primary"]').is(":checked"),_wpnonce:i.find('input[name="add_email_nonce"]').val()};i.parent().find(".notice-container").remove(),i.find(".spinner").css("visibility","visible"),n.attr("disabled",!0),e.post(ajaxurl,r,(function(e){setTimeout((function(){!0===e.success?window.location.href=e.redirect:(n.attr("disabled",!1),i.before('<div class="notice-container"><div class="notice notice-error inline"><p>'+e.message+"</p></div></div>"),i.find(".spinner").css("visibility","hidden"))}),342)}),"json")}))},user_search:function(){e(document.body).on("click.eddSelectUser",".edd_user_search_results a",(function(t){t.preventDefault();var i=e(this).data("userid");n.vars.user_id.val(i)}))},remove_user:function(){e(document.body).on("click","#disconnect-customer",(function(t){if(t.preventDefault(),confirm(edd_vars.disconnect_customer)){var n={edd_action:"disconnect-userid",customer_id:e('input[name="customerinfo[id]"]').val(),_wpnonce:e("#edit-customer-info #_wpnonce").val()};e.post(ajaxurl,n,(function(e){window.location.href=window.location.href}),"json")}}))},cancel_edit:function(){e(document.body).on("click","#edd-edit-customer-cancel",(function(t){t.preventDefault(),n.vars.customer_card_wrap_edit_item.hide(),n.vars.customer_card_wrap_editable.show(),e(".edd_user_search_results").html("")}))},change_country:function(){e('select[name="customerinfo[country]"]').change((function(){var t=e(this),n=e(':input[name="customerinfo[region]"]'),i={action:"edd_get_shop_states",country:t.val(),nonce:t.data("nonce"),field_name:"customerinfo[region]"};return e.post(ajaxurl,i,(function(e){console.log(e),"nostates"===e?n.replaceWith('<input type="text" name="'+i.field_name+'" value="" class="edd-edit-toggles medium-text"/>'):n.replaceWith(e)})),!1}))},delete_checked:function(){e("#edd-customer-delete-confirm").change((function(){var t=e("#edd-customer-delete-records"),n=e("#edd-delete-customer");e(this).prop("checked")?(t.attr("disabled",!1),n.attr("disabled",!1)):(t.attr("disabled",!0),t.prop("checked",!1),n.attr("disabled",!0))}))}};t(document).ready((function(e){n.init()}))}).call(this,n(1),n(1))}});

View File

@ -1 +1 @@
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=180)}({1:function(e,t){e.exports=jQuery},180:function(e,t,n){(function(e){e(document).ready((function(e){e("#edd_dashboard_sales").length&&e.ajax({type:"GET",data:{action:"edd_load_dashboard_widget"},url:ajaxurl,success:function(t){e("#edd_dashboard_sales .edd-loading").html(t)}})}))}).call(this,n(1))}});
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=182)}({1:function(e,t){e.exports=jQuery},182:function(e,t,n){(function(e){e(document).ready((function(e){e("#edd_dashboard_sales").length&&e.ajax({type:"GET",data:{action:"edd_load_dashboard_widget"},url:ajaxurl,success:function(t){e("#edd_dashboard_sales .edd-loading").html(t)}})}))}).call(this,n(1))}});

View File

@ -1 +1 @@
!function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=181)}({1:function(t,e){t.exports=jQuery},181:function(t,e,n){"use strict";n.r(e),function(t){var e=n(3);Object(e.a)((function(){var e=t("#edd_products");e&&e.change((function(){t("#edd-discount-product-conditions").toggle(null!==e.val())}))}))}.call(this,n(1))},3:function(t,e,n){"use strict";(function(t){n.d(e,"a",(function(){return r}));var r=function(e){t(e)}}).call(this,n(1))}});
!function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=183)}({1:function(t,e){t.exports=jQuery},183:function(t,e,n){"use strict";n.r(e),function(t){var e=n(3);Object(e.a)((function(){var e=t("#edd_products");e&&e.change((function(){t("#edd-discount-product-conditions").toggle(null!==e.val())}))}))}.call(this,n(1))},3:function(t,e,n){"use strict";(function(t){n.d(e,"a",(function(){return r}));var r=function(e){t(e)}}).call(this,n(1))}});

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=235)}({235:function(e,t,n){"use strict";n.r(t);var r=function(e){return(e=e.toLowerCase()).trim()};document.addEventListener("DOMContentLoaded",(function(){var e,t,n,o=document.querySelector(".edd-email-tags-inserter");o&&(o.addEventListener("click",tb_position),(e=document.querySelectorAll(".edd-email-tags-list-button"))&&_.each(e,(function(e){e.addEventListener("click",(function(){tb_remove(),window.send_to_editor(e.dataset.to_insert)}))})),t=document.querySelector(".edd-email-tags-filter-search"),n=document.querySelectorAll(".edd-email-tags-list-item"),t&&t.addEventListener("keyup",(function(e){var t=e.target.value,o=function(e,t){var n=r(t),o=function(e){return-1!==r(e).indexOf(n)};return _.filter(e,(function(e){return o(e.title)||_.some(e.keywords,o)}))}(eddEmailTagsInserter.items,t);_.each(n,(function(e){var t=_.findWhere(o,{tag:e.dataset.tag});e.style.display=t?"block":"none"}))})))}))}});
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=237)}({237:function(e,t,n){"use strict";n.r(t);var r=function(e){return(e=e.toLowerCase()).trim()};document.addEventListener("DOMContentLoaded",(function(){var e,t,n,o=document.querySelector(".edd-email-tags-inserter");o&&(o.addEventListener("click",tb_position),(e=document.querySelectorAll(".edd-email-tags-list-button"))&&_.each(e,(function(e){e.addEventListener("click",(function(){tb_remove(),window.send_to_editor(e.dataset.to_insert)}))})),t=document.querySelector(".edd-email-tags-filter-search"),n=document.querySelectorAll(".edd-email-tags-list-item"),t&&t.addEventListener("keyup",(function(e){var t=e.target.value,o=function(e,t){var n=r(t),o=function(e){return-1!==r(e).indexOf(n)};return _.filter(e,(function(e){return o(e.title)||_.some(e.keywords,o)}))}(eddEmailTagsInserter.items,t);_.each(n,(function(e){var t=_.findWhere(o,{tag:e.dataset.tag});e.style.display=t?"block":"none"}))})))}))}});

View File

@ -1 +1 @@
!function(e){var t={};function n(a){if(t[a])return t[a].exports;var i=t[a]={i:a,l:!1,exports:{}};return e[a].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=e,n.c=t,n.d=function(e,t,a){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(n.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(a,i,function(t){return e[t]}.bind(null,i));return a},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=219)}({1:function(e,t){e.exports=jQuery},219:function(e,t,n){(function(e){!function(e,t){"use strict";t(".edd-extension-manager__action").on("click",(function(e){e.preventDefault();var n=t(this),a=n.attr("data-action"),i=n.attr("data-plugin"),r=n.attr("data-type"),o="";if(!n.attr("disabled")){switch(a){case"activate":o="edd_activate_extension",n.text(EDDExtensionManager.activating);break;case"install":o="edd_install_extension",n.text(EDDExtensionManager.installing);break;default:return}n.removeClass("button-primary").attr("disabled",!0).addClass("updating-message");var s={action:o,nonce:EDDExtensionManager.extension_manager_nonce,plugin:i,type:r,pass:n.attr("data-pass"),id:n.attr("data-id"),product:n.attr("data-product")};t.post(ajaxurl,s).done((function(e){console.log(e);var t=n.closest(".edd-extension-manager__step");if(e.success){var a=t.next();a.length&&(t.fadeOut(),a.prepend('<div class="notice inline-notice notice-success"><p>'+e.data.message+"</p></div>"),a.fadeIn())}else{t.fadeOut();var i=e.data.message;i||(i="plugin"!==r?EDDExtensionManager.extension_install_failed:EDDExtensionManager.plugin_install_failed),t.after('<div class="notice inline-notice notice-warning"><p>'+i+"</p></div>")}}))}}))}(document,e)}).call(this,n(1))}});
!function(e){var t={};function n(a){if(t[a])return t[a].exports;var i=t[a]={i:a,l:!1,exports:{}};return e[a].call(i.exports,i,i.exports,n),i.l=!0,i.exports}n.m=e,n.c=t,n.d=function(e,t,a){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(n.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(a,i,function(t){return e[t]}.bind(null,i));return a},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=221)}({1:function(e,t){e.exports=jQuery},221:function(e,t,n){(function(e){!function(e,t){"use strict";t(".edd-extension-manager__action").on("click",(function(e){e.preventDefault();var n=t(this),a=n.attr("data-action"),i=n.attr("data-plugin"),r=n.attr("data-type"),o="";if(!n.attr("disabled")){switch(a){case"activate":o="edd_activate_extension",n.text(EDDExtensionManager.activating);break;case"install":o="edd_install_extension",n.text(EDDExtensionManager.installing);break;default:return}n.removeClass("button-primary").attr("disabled",!0).addClass("updating-message");var s={action:o,nonce:EDDExtensionManager.extension_manager_nonce,plugin:i,type:r,pass:n.attr("data-pass"),id:n.attr("data-id"),product:n.attr("data-product")};t.post(ajaxurl,s).done((function(e){console.log(e);var t=n.closest(".edd-extension-manager__step");if(e.success){var a=t.next();a.length&&(t.fadeOut(),a.prepend('<div class="notice inline-notice notice-success"><p>'+e.data.message+"</p></div>"),a.fadeIn())}else{t.fadeOut();var i=e.data.message;i||(i="plugin"!==r?EDDExtensionManager.extension_install_failed:EDDExtensionManager.plugin_install_failed),t.after('<div class="notice inline-notice notice-warning"><p>'+i+"</p></div>")}}))}}))}(document,e)}).call(this,n(1))}});

View File

@ -1 +1 @@
!function(e){var n={};function t(o){if(n[o])return n[o].exports;var d=n[o]={i:o,l:!1,exports:{}};return e[o].call(d.exports,d,d.exports,t),d.l=!0,d.exports}t.m=e,t.c=n,t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:o})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(t.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var d in e)t.d(o,d,function(n){return e[n]}.bind(null,d));return o},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="",t(t.s=186)}({1:function(e,n){e.exports=jQuery},186:function(e,n,t){(function(e,n){var t={init:function(){this.enter_key(),this.add_note(),this.remove_note()},enter_key:function(){e(document.body).on("keydown","#edd-note",(function(n){13===n.keyCode&&(n.metaKey||n.ctrlKey)&&(n.preventDefault(),e("#edd-add-note").click())}))},add_note:function(){e("#edd-add-note").on("click",(function(n){n.preventDefault();var t=e(this),o=e("#edd-note"),d=e(".edd-notes"),r=e(".edd-no-notes"),i=e(".edd-add-note .spinner"),a={action:"edd_add_note",nonce:e("#edd_note_nonce").val(),object_id:t.data("object-id"),object_type:t.data("object-type"),note:o.val()};if(a.note)t.prop("disabled",!0),i.css("visibility","visible"),e.ajax({type:"POST",data:a,url:ajaxurl,success:function(e){var n=wpAjax.parseAjaxResponse(e);n=n.responses[0],d.append(n.data),r.hide(),t.prop("disabled",!1),i.css("visibility","hidden"),o.val("")}}).fail((function(e){window.console&&window.console.log&&console.log(e),t.prop("disabled",!1),i.css("visibility","hidden")}));else{var c=o.css("border-color");o.css("border-color","red"),setTimeout((function(){o.css("border-color",c)}),userInteractionInterval)}}))},remove_note:function(){e(document.body).on("click",".edd-delete-note",(function(n){n.preventDefault();var t=e(this),o=e(".edd-note"),d=t.parents(".edd-note"),r=e(".edd-no-notes"),i=e("#edd_note_nonce");if(confirm(edd_vars.delete_note)){var a={action:"edd_delete_note",nonce:i.val(),note_id:t.data("note-id")};return d.addClass("deleting"),e.ajax({type:"POST",data:a,url:ajaxurl,success:function(e){return"1"===e&&d.remove(),1===o.length&&r.show(),!1}}).fail((function(e){window.console&&window.console.log&&console.log(e),d.removeClass("deleting")})),!0}}))}};n(document).ready((function(e){t.init()}))}).call(this,t(1),t(1))}});
!function(e){var n={};function t(o){if(n[o])return n[o].exports;var d=n[o]={i:o,l:!1,exports:{}};return e[o].call(d.exports,d,d.exports,t),d.l=!0,d.exports}t.m=e,t.c=n,t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:o})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(t.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var d in e)t.d(o,d,function(n){return e[n]}.bind(null,d));return o},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="",t(t.s=188)}({1:function(e,n){e.exports=jQuery},188:function(e,n,t){(function(e,n){var t={init:function(){this.enter_key(),this.add_note(),this.remove_note()},enter_key:function(){e(document.body).on("keydown","#edd-note",(function(n){13===n.keyCode&&(n.metaKey||n.ctrlKey)&&(n.preventDefault(),e("#edd-add-note").click())}))},add_note:function(){e("#edd-add-note").on("click",(function(n){n.preventDefault();var t=e(this),o=e("#edd-note"),d=e(".edd-notes"),r=e(".edd-no-notes"),i=e(".edd-add-note .spinner"),a={action:"edd_add_note",nonce:e("#edd_note_nonce").val(),object_id:t.data("object-id"),object_type:t.data("object-type"),note:o.val()};if(a.note)t.prop("disabled",!0),i.css("visibility","visible"),e.ajax({type:"POST",data:a,url:ajaxurl,success:function(e){var n=wpAjax.parseAjaxResponse(e);n=n.responses[0],d.append(n.data),r.hide(),t.prop("disabled",!1),i.css("visibility","hidden"),o.val("")}}).fail((function(e){window.console&&window.console.log&&console.log(e),t.prop("disabled",!1),i.css("visibility","hidden")}));else{var c=o.css("border-color");o.css("border-color","red"),setTimeout((function(){o.css("border-color",c)}),userInteractionInterval)}}))},remove_note:function(){e(document.body).on("click",".edd-delete-note",(function(n){n.preventDefault();var t=e(this),o=e(".edd-note"),d=t.parents(".edd-note"),r=e(".edd-no-notes"),i=e("#edd_note_nonce");if(confirm(edd_vars.delete_note)){var a={action:"edd_delete_note",nonce:i.val(),note_id:t.data("note-id")};return d.addClass("deleting"),e.ajax({type:"POST",data:a,url:ajaxurl,success:function(e){return"1"===e&&d.remove(),1===o.length&&r.show(),!1}}).fail((function(e){window.console&&window.console.log&&console.log(e),d.removeClass("deleting")})),!0}}))}};n(document).ready((function(e){t.init()}))}).call(this,t(1),t(1))}});

View File

@ -1 +1 @@
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=220)}({1:function(e,t){e.exports=jQuery},220:function(e,t,n){(function(e){!function(e,t){"use strict";t("#edd-disable-debug-log").on("click",(function(e){e.preventDefault(),t(this).attr("disabled",!0);var n=t("#edd-debug-log-notice");t.ajax({type:"GET",data:{action:"edd_disable_debugging",nonce:t("#edd_debug_log_delete").val()},url:ajaxurl,success:function(e){n.empty().append(e.data),setTimeout((function(){n.slideUp()}),3e3)}}).fail((function(e){n.empty().append(e.responseJSON.data)}))}))}(document,e)}).call(this,n(1))}});
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=222)}({1:function(e,t){e.exports=jQuery},222:function(e,t,n){(function(e){!function(e,t){"use strict";t("#edd-disable-debug-log").on("click",(function(e){e.preventDefault(),t(this).attr("disabled",!0);var n=t("#edd-debug-log-notice");t.ajax({type:"GET",data:{action:"edd_disable_debugging",nonce:t("#edd_debug_log_delete").val()},url:ajaxurl,success:function(e){n.empty().append(e.data),setTimeout((function(){n.slideUp()}),3e3)}}).fail((function(e){n.empty().append(e.responseJSON.data)}))}))}(document,e)}).call(this,n(1))}});

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=201)}({201:function(e,t){}});
!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=203)}({203:function(e,t){}});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var s=t[r]={i:r,l:!1,exports:{}};return e[r].call(s.exports,s,s.exports,n),s.l=!0,s.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)n.d(r,s,function(t){return e[t]}.bind(null,s));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=184)}({1:function(e,t){e.exports=jQuery},184:function(e,t,n){(function(e,t){var n={init:function(){this.submit()},submit:function(){var t=this;e(document.body).on("submit",".edd-export-form",(function(n){n.preventDefault();var r=e(this),s=r.find('button[type="submit"]').first();if(!s.hasClass("button-disabled")&&!s.is(":disabled")){var i=r.serialize();s.hasClass("button-primary")&&s.removeClass("button-primary").addClass("button-secondary"),s.attr("disabled",!0).addClass("updating-message"),r.find(".notice-wrap").remove(),r.append('<div class="notice-wrap"><div class="edd-progress"><div></div></div></div>'),t.process_step(1,i,t)}}))},process_step:function(t,n,r){e.ajax({type:"POST",url:ajaxurl,data:{form:n,action:"edd_do_ajax_export",step:t},dataType:"json",success:function(t){if("done"===t.step||t.error||t.success){var s=e(".edd-export-form").find(".edd-progress").parent().parent(),i=s.find(".notice-wrap");if(s.find("button").attr("disabled",!1).removeClass("updating-message").addClass("updated-message"),s.find("button .spinner").hide().css("visibility","visible"),t.error){var o=t.message;i.html('<div class="updated error"><p>'+o+"</p></div>")}else if(t.success){var a=t.message;i.html('<div id="edd-batch-success" class="updated notice"><p>'+a+"</p></div>"),t.data&&e.each(t.data,(function(t,n){e(".edd_"+t).html(n)}))}else i.remove(),window.location=t.url}else e(".edd-progress div").animate({width:t.percentage+"%"},50,(function(){})),r.process_step(parseInt(t.step),n,r)}}).fail((function(e){window.console&&window.console.log&&console.log(e)}))}};t(document).ready((function(e){n.init()}))}).call(this,n(1),n(1))}});
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var s=t[r]={i:r,l:!1,exports:{}};return e[r].call(s.exports,s,s.exports,n),s.l=!0,s.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)n.d(r,s,function(t){return e[t]}.bind(null,s));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=186)}({1:function(e,t){e.exports=jQuery},186:function(e,t,n){(function(e,t){var n={init:function(){this.submit()},submit:function(){var t=this;e(document.body).on("submit",".edd-export-form",(function(n){n.preventDefault();var r=e(this),s=r.find('button[type="submit"]').first();if(!s.hasClass("button-disabled")&&!s.is(":disabled")){var i=r.serialize();s.hasClass("button-primary")&&s.removeClass("button-primary").addClass("button-secondary"),s.attr("disabled",!0).addClass("updating-message"),r.find(".notice-wrap").remove(),r.append('<div class="notice-wrap"><div class="edd-progress"><div></div></div></div>'),t.process_step(1,i,t)}}))},process_step:function(t,n,r){e.ajax({type:"POST",url:ajaxurl,data:{form:n,action:"edd_do_ajax_export",step:t},dataType:"json",success:function(t){if("done"===t.step||t.error||t.success){var s=e(".edd-export-form").find(".edd-progress").parent().parent(),i=s.find(".notice-wrap");if(s.find("button").attr("disabled",!1).removeClass("updating-message").addClass("updated-message"),s.find("button .spinner").hide().css("visibility","visible"),t.error){var o=t.message;i.html('<div class="updated error"><p>'+o+"</p></div>")}else if(t.success){var a=t.message;i.html('<div id="edd-batch-success" class="updated notice"><p>'+a+"</p></div>"),t.data&&e.each(t.data,(function(t,n){e(".edd_"+t).html(n)}))}else i.remove(),window.location=t.url}else e(".edd-progress div").animate({width:t.percentage+"%"},50,(function(){})),r.process_step(parseInt(t.step),n,r)}}).fail((function(e){window.console&&window.console.log&&console.log(e)}))}};t(document).ready((function(e){n.init()}))}).call(this,n(1),n(1))}});

View File

@ -1 +1 @@
!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=185)}({1:function(e,t){e.exports=jQuery},185:function(e,t,r){(function(e,t){var r={init:function(){this.submit()},submit:function(){e(".edd-import-form").ajaxForm({beforeSubmit:this.before_submit,success:this.success,complete:this.complete,dataType:"json",error:this.error})},before_submit:function(t,r,n){if(r.find(".notice-wrap").remove(),r.append('<div class="notice-wrap"><div class="edd-progress"><div></div></div></div>'),!(window.File&&window.FileReader&&window.FileList&&window.Blob)){var o=e(".edd-import-form").find(".edd-progress").parent().parent(),i=o.find(".notice-wrap");return o.find(".button:disabled").attr("disabled",!1),i.html('<div class="update error"><p>'+edd_vars.unsupported_browser+"</p></div>"),!1}},success:function(e,t,r,n){},complete:function(n){var o=e(this),i=t.parseJSON(n.responseText);if(i.success){var d=e(".edd-import-form .notice-wrap").parent();d.find(".edd-import-file-wrap,.notice-wrap").remove(),d.find(".edd-import-options").slideDown();var a=d.find("select.edd-import-csv-column"),s=(a.parents("tr").first(),""),p=i.data.columns.sort((function(e,t){return e<t?-1:e>t?1:0}));e.each(p,(function(e,t){s+='<option value="'+t+'">'+t+"</option>"})),a.append(s),a.on("change",(function(){var t=e(this).val();t&&!1!==i.data.first_row[t]?e(this).parent().next().html(i.data.first_row[t]):e(this).parent().next().html("")})),e.each(a,(function(){e(this).val(e(this).attr("data-field")).change()})),e(document.body).on("click",".edd-import-proceed",(function(e){e.preventDefault(),d.find(".edd-import-proceed.button-primary").addClass("updating-message"),d.append('<div class="notice-wrap"><div class="edd-progress"><div></div></div></div>'),i.data.mapping=d.serialize(),r.process_step(1,i.data,o)}))}else r.error(n)},error:function(r){var n=t.parseJSON(r.responseText),o=e(".edd-import-form").find(".edd-progress").parent().parent(),i=o.find(".notice-wrap");o.find(".button:disabled").attr("disabled",!1),n.data.error?i.html('<div class="update error"><p>'+n.data.error+"</p></div>"):i.remove()},process_step:function(t,n,o){e.ajax({type:"POST",url:ajaxurl,data:{form:n.form,nonce:n.nonce,class:n.class,upload:n.upload,mapping:n.mapping,action:"edd_do_ajax_import",step:t},dataType:"json",success:function(t){if("done"===t.data.step||t.data.error){var i=e(".edd-import-form").find(".edd-progress").parent().parent(),d=i.find(".notice-wrap");i.find(".button:disabled").attr("disabled",!1),t.data.error?d.html('<div class="update error"><p>'+t.data.error+"</p></div>"):(i.find(".edd-import-options").hide(),e("html, body").animate({scrollTop:i.parent().offset().top},500),d.html('<div class="updated"><p>'+t.data.message+"</p></div>"))}else e(".edd-progress div").animate({width:t.data.percentage+"%"},50,(function(){})),r.process_step(parseInt(t.data.step),n,o)}}).fail((function(e){window.console&&window.console.log&&console.log(e)}))}};t(document).ready((function(e){r.init()}))}).call(this,r(1),r(1))}});
!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=187)}({1:function(e,t){e.exports=jQuery},187:function(e,t,r){(function(e,t){var r={init:function(){this.submit()},submit:function(){e(".edd-import-form").ajaxForm({beforeSubmit:this.before_submit,success:this.success,complete:this.complete,dataType:"json",error:this.error})},before_submit:function(t,r,n){if(r.find(".notice-wrap").remove(),r.append('<div class="notice-wrap"><div class="edd-progress"><div></div></div></div>'),!(window.File&&window.FileReader&&window.FileList&&window.Blob)){var o=e(".edd-import-form").find(".edd-progress").parent().parent(),i=o.find(".notice-wrap");return o.find(".button:disabled").attr("disabled",!1),i.html('<div class="update error"><p>'+edd_vars.unsupported_browser+"</p></div>"),!1}},success:function(e,t,r,n){},complete:function(n){var o=e(this),i=t.parseJSON(n.responseText);if(i.success){var d=e(".edd-import-form .notice-wrap").parent();d.find(".edd-import-file-wrap,.notice-wrap").remove(),d.find(".edd-import-options").slideDown();var a=d.find("select.edd-import-csv-column"),s=(a.parents("tr").first(),""),p=i.data.columns.sort((function(e,t){return e<t?-1:e>t?1:0}));e.each(p,(function(e,t){s+='<option value="'+t+'">'+t+"</option>"})),a.append(s),a.on("change",(function(){var t=e(this).val();t&&!1!==i.data.first_row[t]?e(this).parent().next().html(i.data.first_row[t]):e(this).parent().next().html("")})),e.each(a,(function(){e(this).val(e(this).attr("data-field")).change()})),e(document.body).on("click",".edd-import-proceed",(function(e){e.preventDefault(),d.find(".edd-import-proceed.button-primary").addClass("updating-message"),d.append('<div class="notice-wrap"><div class="edd-progress"><div></div></div></div>'),i.data.mapping=d.serialize(),r.process_step(1,i.data,o)}))}else r.error(n)},error:function(r){var n=t.parseJSON(r.responseText),o=e(".edd-import-form").find(".edd-progress").parent().parent(),i=o.find(".notice-wrap");o.find(".button:disabled").attr("disabled",!1),n.data.error?i.html('<div class="update error"><p>'+n.data.error+"</p></div>"):i.remove()},process_step:function(t,n,o){e.ajax({type:"POST",url:ajaxurl,data:{form:n.form,nonce:n.nonce,class:n.class,upload:n.upload,mapping:n.mapping,action:"edd_do_ajax_import",step:t},dataType:"json",success:function(t){if("done"===t.data.step||t.data.error){var i=e(".edd-import-form").find(".edd-progress").parent().parent(),d=i.find(".notice-wrap");i.find(".button:disabled").attr("disabled",!1),t.data.error?d.html('<div class="update error"><p>'+t.data.error+"</p></div>"):(i.find(".edd-import-options").hide(),e("html, body").animate({scrollTop:i.parent().offset().top},500),d.html('<div class="updated"><p>'+t.data.message+"</p></div>"))}else e(".edd-progress div").animate({width:t.data.percentage+"%"},50,(function(){})),r.process_step(parseInt(t.data.step),n,o)}}).fail((function(e){window.console&&window.console.log&&console.log(e)}))}};t(document).ready((function(e){r.init()}))}).call(this,r(1),r(1))}});

View File

@ -1 +1 @@
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=204)}({1:function(e,t){e.exports=jQuery},204:function(e,t,n){(function(e,t){var n={init:function(){this.revoke_api_key(),this.regenerate_api_key(),this.create_api_key(),this.recount_stats()},revoke_api_key:function(){e(document.body).on("click",".edd-revoke-api-key",(function(e){return confirm(edd_vars.revoke_api_key)}))},regenerate_api_key:function(){e(document.body).on("click",".edd-regenerate-api-key",(function(e){return confirm(edd_vars.regenerate_api_key)}))},create_api_key:function(){e(document.body).on("submit","#api-key-generate-form",(function(t){var n=e('input[type="text"][name="user_id"]');n.css("border-color","#ddd");var r=n.val();if(r.length<1||0===r)return n.css("border-color","#ff0000"),!1}))},recount_stats:function(){e(document.body).on("change","#recount-stats-type",(function(){var t=e("#edd-tools-recount-form"),n=e("option:selected",this).data("type"),r=e("#recount-stats-submit"),o=e("#tools-product-dropdown");t.find(".notice-wrap").remove(),r.attr("disabled",!1).removeClass("updated-message"),o.hide(),e(".edd-recount-stats-descriptions span").hide(),"recount-download"===n?(o.show(),o.find(".edd-select-chosen").css("width","auto")):"reset-stats"===n?(t.append('<div class="notice-wrap"></div>'),t.find(".notice-wrap").html('<div class="notice notice-warning"><p><input type="checkbox" id="confirm-reset" name="confirm_reset_store" value="1" /> <label for="confirm-reset">'+edd_vars.reset_stats_warn+"</label></p></div>"),e("#recount-stats-submit").attr("disabled",!0)):(o.hide(),o.val(0)),e("#"+n).show()})),e(document.body).on("change","#confirm-reset",(function(){e(this).is(":checked")?e("#recount-stats-submit").attr("disabled",!1):e("#recount-stats-submit").attr("disabled",!0)})),e("#edd-tools-recount-form").submit((function(t){t.preventDefault();var n=e("#recount-stats-type").val(),r=e(this),o=e("option:selected",this).data("type");if("reset-stats"===o){if(e("#confirm-reset").is(":checked"))return!0;a=!0}r.find(".notice-wrap").remove(),r.append('<div class="notice-wrap"></div>');var i=r.find(".notice-wrap"),a=!1;if(null!==n&&0!==n||(i.html('<div class="updated error"><p>'+edd_vars.batch_export_no_class+"</p></div>"),a=!0),"recount-download"===o&&0===e('select[name="download_id"]').val()&&(i.html('<div class="updated error"><p>'+edd_vars.batch_export_no_reqs+"</p></div>"),a=!0),a)return r.find("button:disabled").attr("disabled",!1).removeClass("updated-message"),!1}))}};t(document).ready((function(e){n.init()}))}).call(this,n(1),n(1))}});
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=206)}({1:function(e,t){e.exports=jQuery},206:function(e,t,n){(function(e,t){var n={init:function(){this.revoke_api_key(),this.regenerate_api_key(),this.create_api_key(),this.recount_stats()},revoke_api_key:function(){e(document.body).on("click",".edd-revoke-api-key",(function(e){return confirm(edd_vars.revoke_api_key)}))},regenerate_api_key:function(){e(document.body).on("click",".edd-regenerate-api-key",(function(e){return confirm(edd_vars.regenerate_api_key)}))},create_api_key:function(){e(document.body).on("submit","#api-key-generate-form",(function(t){var n=e('input[type="text"][name="user_id"]');n.css("border-color","#ddd");var r=n.val();if(r.length<1||0===r)return n.css("border-color","#ff0000"),!1}))},recount_stats:function(){e(document.body).on("change","#recount-stats-type",(function(){var t=e("#edd-tools-recount-form"),n=e("option:selected",this).data("type"),r=e("#recount-stats-submit"),o=e("#tools-product-dropdown");t.find(".notice-wrap").remove(),r.attr("disabled",!1).removeClass("updated-message"),o.hide(),e(".edd-recount-stats-descriptions span").hide(),"recount-download"===n?(o.show(),o.find(".edd-select-chosen").css("width","auto")):"reset-stats"===n?(t.append('<div class="notice-wrap"></div>'),t.find(".notice-wrap").html('<div class="notice notice-warning"><p><input type="checkbox" id="confirm-reset" name="confirm_reset_store" value="1" /> <label for="confirm-reset">'+edd_vars.reset_stats_warn+"</label></p></div>"),e("#recount-stats-submit").attr("disabled",!0)):(o.hide(),o.val(0)),e("#"+n).show()})),e(document.body).on("change","#confirm-reset",(function(){e(this).is(":checked")?e("#recount-stats-submit").attr("disabled",!1):e("#recount-stats-submit").attr("disabled",!0)})),e("#edd-tools-recount-form").submit((function(t){t.preventDefault();var n=e("#recount-stats-type").val(),r=e(this),o=e("option:selected",this).data("type");if("reset-stats"===o){if(e("#confirm-reset").is(":checked"))return!0;a=!0}r.find(".notice-wrap").remove(),r.append('<div class="notice-wrap"></div>');var i=r.find(".notice-wrap"),a=!1;if(null!==n&&0!==n||(i.html('<div class="updated error"><p>'+edd_vars.batch_export_no_class+"</p></div>"),a=!0),"recount-download"===o&&0===e('select[name="download_id"]').val()&&(i.html('<div class="updated error"><p>'+edd_vars.batch_export_no_reqs+"</p></div>"),a=!0),a)return r.find("button:disabled").attr("disabled",!1).removeClass("updated-message"),!1}))}};t(document).ready((function(e){n.init()}))}).call(this,n(1),n(1))}});

View File

@ -1 +1 @@
!function(e){var a={};function d(t){if(a[t])return a[t].exports;var r=a[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,d),r.l=!0,r.exports}d.m=e,d.c=a,d.d=function(e,a,t){d.o(e,a)||Object.defineProperty(e,a,{enumerable:!0,get:t})},d.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},d.t=function(e,a){if(1&a&&(e=d(e)),8&a)return e;if(4&a&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(d.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&a&&"string"!=typeof e)for(var r in e)d.d(t,r,function(a){return e[a]}.bind(null,r));return t},d.n=function(e){var a=e&&e.__esModule?function(){return e.default}:function(){return e};return d.d(a,"a",a),a},d.o=function(e,a){return Object.prototype.hasOwnProperty.call(e,a)},d.p="",d(d.s=205)}({1:function(e,a){e.exports=jQuery},205:function(e,a,d){"use strict";d.r(a),d(206)},206:function(e,a,d){(function(e,a){var d={inProgress:!1,init:function(){e(".edd-v3-migration-confirmation").on("change",(function(a){var d=e(this).closest(".edd-v3-migration").find("button");a.target.checked?d.removeClass("disabled").prop("disabled",!1):d.addClass("disabled").prop("disabled",!0)})),e(".edd-v3-migration").on("submit",(function(a){if(a.preventDefault(),!d.inProgress){d.inProgress=!0;var t=e(this),r=t.find('input[name="upgrade_key"]'),n=!1;if(r.length&&r.val()&&(n=r.val()),t.find("button").removeClass("button-primary").addClass("button-secondary disabled updating-message").prop("disabled",!0),t.find("input").prop("disabled",!0),"edd-v3-migration"===t.attr("id")){e("#edd-migration-progress").removeClass("edd-hidden");var o=e("#edd-migration-progress li:not(.edd-upgrade-complete)");o.length&&!n&&(n=o.data("upgrade"))}d.processStep(n,1,t.find('input[name="_wpnonce"]').val())}}))},processStep:function(a,t,r){var n={action:"edd_process_v3_upgrade",_ajax_nonce:r,upgrade_key:a,step:t};d.clearErrors(),a&&d.markUpgradeInProgress(a),e.ajax({type:"POST",data:n,url:ajaxurl,success:function(e){if(e.success){if(e.data.upgrade_completed){if(d.markUpgradeComplete(e.data.upgrade_processed),"v30_legacy_data_removed"===e.data.upgrade_processed)return void d.legacyDataRemovalComplete()}else e.data.percentage&&d.updateUpgradePercentage(e.data.upgrade_processed,e.data.percentage);e.data.next_upgrade&&"v30_legacy_data_removed"===e.data.next_upgrade&&"v30_legacy_data_removed"!==e.data.upgrade_processed?(d.inProgress=!1,d.showLegacyDataRemoval()):e.data.next_upgrade?d.processStep(e.data.next_upgrade,e.data.next_step,e.data.nonce):(d.inProgress=!1,d.stopAllSpinners())}else d.showError(a,e.data)}}).fail((function(e){}))},clearErrors:function(){e(".edd-v3-migration-error").addClass("edd-hidden").html("")},showError:function(a,t){var r=e("#edd-v3-migration");"v30_legacy_data_removed"===a&&(r=e("#edd-v3-remove-legacy-data")),r.find(".edd-v3-migration-error").html("<p>"+t+"</p>").removeClass("edd-hidden"),d.inProgress=!1,r.find("input").prop("disabled",!1),r.find("button").prop("disabled",!1).addClass("button-primary").removeClass("button-secondary disabled updating-message")},markUpgradeInProgress:function(a){var d=e("#edd-v3-migration-"+a);if(d.length){var t=d.find(".dashicons");t.length&&t.removeClass("dashicons-minus").addClass("dashicons-update"),d.find(".edd-migration-percentage").removeClass("edd-hidden")}},updateUpgradePercentage:function(a,d){var t=e("#edd-v3-migration-"+a);t.length&&t.find(".edd-migration-percentage-value").text(d)},markUpgradeComplete:function(a){var d=e("#edd-v3-migration-"+a);if(d.length){d.addClass("edd-upgrade-complete");var t=d.find(".dashicons");t.length&&t.removeClass("dashicons-minus dashicons-update").addClass("dashicons-yes");var r=d.find(".edd-migration-status .screen-reader-text");r.length&&r.text(edd_admin_upgrade_vars.migration_complete),d.find(".edd-migration-percentage-value").text(100)}},showLegacyDataRemoval:function(){e("#edd-v3-migration-button").removeClass("updating-message"),e("#edd-v3-migration-complete").removeClass("edd-hidden");var a=e("#edd-v3-remove-legacy-data");a.length&&a.removeClass("edd-hidden")},legacyDataRemovalComplete:function(){var a=e("#edd-v3-remove-legacy-data");a.length&&(a.find("form").addClass("edd-hidden"),a.find("#edd-v3-legacy-data-removal-complete").removeClass("edd-hidden"))},stopAllSpinners:function(){}};a(document).ready((function(e){d.init()}))}).call(this,d(1),d(1))}});
!function(e){var a={};function d(t){if(a[t])return a[t].exports;var r=a[t]={i:t,l:!1,exports:{}};return e[t].call(r.exports,r,r.exports,d),r.l=!0,r.exports}d.m=e,d.c=a,d.d=function(e,a,t){d.o(e,a)||Object.defineProperty(e,a,{enumerable:!0,get:t})},d.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},d.t=function(e,a){if(1&a&&(e=d(e)),8&a)return e;if(4&a&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(d.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&a&&"string"!=typeof e)for(var r in e)d.d(t,r,function(a){return e[a]}.bind(null,r));return t},d.n=function(e){var a=e&&e.__esModule?function(){return e.default}:function(){return e};return d.d(a,"a",a),a},d.o=function(e,a){return Object.prototype.hasOwnProperty.call(e,a)},d.p="",d(d.s=207)}({1:function(e,a){e.exports=jQuery},207:function(e,a,d){"use strict";d.r(a),d(208)},208:function(e,a,d){(function(e,a){var d={inProgress:!1,init:function(){e(".edd-v3-migration-confirmation").on("change",(function(a){var d=e(this).closest(".edd-v3-migration").find("button");a.target.checked?d.removeClass("disabled").prop("disabled",!1):d.addClass("disabled").prop("disabled",!0)})),e(".edd-v3-migration").on("submit",(function(a){if(a.preventDefault(),!d.inProgress){d.inProgress=!0;var t=e(this),r=t.find('input[name="upgrade_key"]'),n=!1;if(r.length&&r.val()&&(n=r.val()),t.find("button").removeClass("button-primary").addClass("button-secondary disabled updating-message").prop("disabled",!0),t.find("input").prop("disabled",!0),"edd-v3-migration"===t.attr("id")){e("#edd-migration-progress").removeClass("edd-hidden");var o=e("#edd-migration-progress li:not(.edd-upgrade-complete)");o.length&&!n&&(n=o.data("upgrade"))}d.processStep(n,1,t.find('input[name="_wpnonce"]').val())}}))},processStep:function(a,t,r){var n={action:"edd_process_v3_upgrade",_ajax_nonce:r,upgrade_key:a,step:t};d.clearErrors(),a&&d.markUpgradeInProgress(a),e.ajax({type:"POST",data:n,url:ajaxurl,success:function(e){if(e.success){if(e.data.upgrade_completed){if(d.markUpgradeComplete(e.data.upgrade_processed),"v30_legacy_data_removed"===e.data.upgrade_processed)return void d.legacyDataRemovalComplete()}else e.data.percentage&&d.updateUpgradePercentage(e.data.upgrade_processed,e.data.percentage);e.data.next_upgrade&&"v30_legacy_data_removed"===e.data.next_upgrade&&"v30_legacy_data_removed"!==e.data.upgrade_processed?(d.inProgress=!1,d.showLegacyDataRemoval()):e.data.next_upgrade?d.processStep(e.data.next_upgrade,e.data.next_step,e.data.nonce):(d.inProgress=!1,d.stopAllSpinners())}else d.showError(a,e.data)}}).fail((function(e){}))},clearErrors:function(){e(".edd-v3-migration-error").addClass("edd-hidden").html("")},showError:function(a,t){var r=e("#edd-v3-migration");"v30_legacy_data_removed"===a&&(r=e("#edd-v3-remove-legacy-data")),r.find(".edd-v3-migration-error").html("<p>"+t+"</p>").removeClass("edd-hidden"),d.inProgress=!1,r.find("input").prop("disabled",!1),r.find("button").prop("disabled",!1).addClass("button-primary").removeClass("button-secondary disabled updating-message")},markUpgradeInProgress:function(a){var d=e("#edd-v3-migration-"+a);if(d.length){var t=d.find(".dashicons");t.length&&t.removeClass("dashicons-minus").addClass("dashicons-update"),d.find(".edd-migration-percentage").removeClass("edd-hidden")}},updateUpgradePercentage:function(a,d){var t=e("#edd-v3-migration-"+a);t.length&&t.find(".edd-migration-percentage-value").text(d)},markUpgradeComplete:function(a){var d=e("#edd-v3-migration-"+a);if(d.length){d.addClass("edd-upgrade-complete");var t=d.find(".dashicons");t.length&&t.removeClass("dashicons-minus dashicons-update").addClass("dashicons-yes");var r=d.find(".edd-migration-status .screen-reader-text");r.length&&r.text(edd_admin_upgrade_vars.migration_complete),d.find(".edd-migration-percentage-value").text(100)}},showLegacyDataRemoval:function(){e("#edd-v3-migration-button").removeClass("updating-message"),e("#edd-v3-migration-complete").removeClass("edd-hidden");var a=e("#edd-v3-remove-legacy-data");a.length&&a.removeClass("edd-hidden")},legacyDataRemovalComplete:function(){var a=e("#edd-v3-remove-legacy-data");a.length&&(a.find("form").addClass("edd-hidden"),a.find("#edd-v3-legacy-data-removal-complete").removeClass("edd-hidden"))},stopAllSpinners:function(){}};a(document).ready((function(e){d.init()}))}).call(this,d(1),d(1))}});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,7 +5,7 @@
* Description: The easiest way to sell digital products with WordPress.
* Author: Easy Digital Downloads
* Author URI: https://easydigitaldownloads.com
* Version: 3.1.0.3
* Version: 3.1.0.4
* Text Domain: easy-digital-downloads
* Domain Path: languages
* Requires PHP: 5.6
@ -26,7 +26,7 @@
* @package EDD
* @category Core
* @author Easy Digital Downloads
* @version 3.1.0.3
* @version 3.1.0.4
*/
// Exit if accessed directly.
@ -125,6 +125,8 @@ final class EDD_Requirements_Check {
*/
private function load() {
require_once dirname( __FILE__ ) . '/vendor/autoload.php';
// Maybe include the bundled bootstrapper
if ( ! class_exists( 'Easy_Digital_Downloads' ) ) {
require_once dirname( $this->file ) . '/includes/class-easy-digital-downloads.php';

View File

@ -450,4 +450,33 @@ class EDD_Customer_Addresses_Table extends List_Table {
'per_page' => $this->per_page
) );
}
/**
* Generate the table navigation above or below the table.
* We're overriding this to turn off the referer param in `wp_nonce_field()`.
*
* @param string $which
* @since 3.1.0.4
*/
protected function display_tablenav( $which ) {
if ( 'top' === $which ) {
wp_nonce_field( 'bulk-' . $this->_args['plural'], '_wpnonce', false );
}
?>
<div class="tablenav <?php echo esc_attr( $which ); ?>">
<?php if ( $this->has_items() ) : ?>
<div class="alignleft actions bulkactions">
<?php $this->bulk_actions( $which ); ?>
</div>
<?php
endif;
$this->extra_tablenav( $which );
$this->pagination( $which );
?>
<br class="clear"/>
</div>
<?php
}
}

View File

@ -437,4 +437,33 @@ class EDD_Customer_Email_Addresses_Table extends List_Table {
'per_page' => $this->per_page
) );
}
/**
* Generate the table navigation above or below the table.
* We're overriding this to turn off the referer param in `wp_nonce_field()`.
*
* @param string $which
* @since 3.1.0.4
*/
protected function display_tablenav( $which ) {
if ( 'top' === $which ) {
wp_nonce_field( 'bulk-' . $this->_args['plural'], '_wpnonce', false );
}
?>
<div class="tablenav <?php echo esc_attr( $which ); ?>">
<?php if ( $this->has_items() ) : ?>
<div class="alignleft actions bulkactions">
<?php $this->bulk_actions( $which ); ?>
</div>
<?php
endif;
$this->extra_tablenav( $which );
$this->pagination( $which );
?>
<br class="clear"/>
</div>
<?php
}
}

View File

@ -430,4 +430,33 @@ class EDD_Customer_Reports_Table extends List_Table {
'per_page' => $this->per_page,
) );
}
/**
* Generate the table navigation above or below the table.
* We're overriding this to turn off the referer param in `wp_nonce_field()`.
*
* @param string $which
* @since 3.1.0.4
*/
protected function display_tablenav( $which ) {
if ( 'top' === $which ) {
wp_nonce_field( 'bulk-' . $this->_args['plural'], '_wpnonce', false );
}
?>
<div class="tablenav <?php echo esc_attr( $which ); ?>">
<?php if ( $this->has_items() ) : ?>
<div class="alignleft actions bulkactions">
<?php $this->bulk_actions( $which ); ?>
</div>
<?php
endif;
$this->extra_tablenav( $which );
$this->pagination( $which );
?>
<br class="clear"/>
</div>
<?php
}
}

View File

@ -427,4 +427,33 @@ class EDD_Discount_Codes_Table extends List_Table {
'per_page' => $this->per_page,
) );
}
/**
* Generate the table navigation above or below the table.
* We're overriding this to turn off the referer param in `wp_nonce_field()`.
*
* @param string $which
* @since 3.1.0.4
*/
protected function display_tablenav( $which ) {
if ( 'top' === $which ) {
wp_nonce_field( 'bulk-' . $this->_args['plural'], '_wpnonce', false );
}
?>
<div class="tablenav <?php echo esc_attr( $which ); ?>">
<?php if ( $this->has_items() ) : ?>
<div class="alignleft actions bulkactions">
<?php $this->bulk_actions( $which ); ?>
</div>
<?php
endif;
$this->extra_tablenav( $which );
$this->pagination( $which );
?>
<br class="clear"/>
</div>
<?php
}
}

View File

@ -226,33 +226,52 @@ add_action( 'load-edit.php', 'edd_download_load', 9999 );
function edd_add_download_filters() {
global $typenow;
// Checks if the current post type is 'download'
if ( $typenow !== 'download') {
// Checks if the current post type is 'download'.
if ( 'download' !== $typenow ) {
return;
}
$terms = get_terms( 'download_category' );
if ( count( $terms ) > 0 ) {
echo "<select name='download_category' id='download_category' class='postform'>";
$category_labels = edd_get_taxonomy_labels( 'download_category' );
echo "<option value=''>" . sprintf( __( 'All %s', 'easy-digital-downloads' ), strtolower( $category_labels['name'] ) ) . "</option>";
foreach ( $terms as $term ) {
$selected = isset( $_GET['download_category'] ) && $_GET['download_category'] === $term->slug ? ' selected="selected"' : '';
echo '<option value="' . esc_attr( $term->slug ) . '"' . $selected . '>' . esc_html( $term->name ) .' (' . $term->count .')</option>';
}
echo "</select>";
}
$category_args = array(
'taxonomy' => 'download_category',
'number' => 30,
);
$terms = get_terms( 'download_tag' );
if ( count( $terms ) > 0 ) {
echo "<select name='download_tag' id='download_tag' class='postform'>";
$tag_labels = edd_get_taxonomy_labels( 'download_tag' );
echo "<option value=''>" . sprintf( __( 'All %s', 'easy-digital-downloads' ), strtolower( $tag_labels['name'] ) ) . "</option>";
foreach ( $terms as $term ) {
$selected = isset( $_GET['download_tag']) && $_GET['download_tag'] === $term->slug ? ' selected="selected"' : '';
echo '<option value="' . esc_attr( $term->slug ) . '"' . $selected . '>' . esc_html( $term->name ) .' (' . $term->count .')</option>';
}
echo "</select>";
$categories = get_terms( $category_args );
if ( ! empty( $categories ) ) {
$category_labels = edd_get_taxonomy_labels( 'download_category' );
$options = array();
$options[''] = sprintf( _x( 'All %s', 'plural: Example: "All Categories"', 'easy-digital-downloads' ), $category_labels['name'] );
// Ensure we include the selected value in the pre-populated list.
$selected = ! empty( $_GET['download_category'] ) ? $_GET['download_category'] : '';
if ( ! empty( $selected ) ) {
$selected_term = get_term_by( 'slug', $selected, 'download_category' );
$options[ $selected_term->slug ] = $selected_term->name . ' (' . $selected_term->count . ')';
}
foreach ( $categories as $category ) {
$options[ $category->slug ] = $category->name . ' (' . $category->count . ')';
}
echo EDD()->html->select(
array(
'name' => 'download_category',
'id' => 'download_category',
'class' => 'postform',
'chosen' => true,
'show_option_all' => false,
'show_option_none' => false,
'options' => $options,
'selected' => $selected,
'data' => array(
'placeholder' => sprintf( _x( 'Search %s', 'plural: Example: "Search Download Categories"', 'easy-digital-downloads' ), $category_labels['name'] ),
'search-type' => 'download_category',
'search-placeholder' => sprintf( _x( 'Search %s', 'plural: Example: "Search Download Categories"', 'easy-digital-downloads' ), $category_labels['name'] ),
),
)
);
}
if ( isset( $_REQUEST['all_posts'] ) && '1' === $_REQUEST['all_posts'] ) {

View File

@ -1057,4 +1057,33 @@ class EDD_Payment_History_Table extends List_Table {
'per_page' => $this->per_page,
) );
}
/**
* Generate the table navigation above or below the table.
* We're overriding this to turn off the referer param in `wp_nonce_field()`.
*
* @param string $which
* @since 3.1.0.4
*/
protected function display_tablenav( $which ) {
if ( 'top' === $which ) {
wp_nonce_field( 'bulk-' . $this->_args['plural'], '_wpnonce', false );
}
?>
<div class="tablenav <?php echo esc_attr( $which ); ?>">
<?php if ( $this->has_items() ) : ?>
<div class="alignleft actions bulkactions">
<?php $this->bulk_actions( $which ); ?>
</div>
<?php
endif;
$this->extra_tablenav( $which );
$this->pagination( $which );
?>
<br class="clear"/>
</div>
<?php
}
}

View File

@ -745,39 +745,85 @@ function edd_register_downloads_report( $reports ) {
'groupby' => '',
);
$union_clauses = array(
'select' => 'date',
'where' => '',
'groupby' => '',
);
// Default to 'monthly'.
$sql_clauses['groupby'] = Reports\get_groupby_date_string( 'MONTH', 'edd_oi.date_created' );
$sql_clauses['orderby'] = 'MONTH(edd_oi.date_created)';
$union_clauses['groupby'] = Reports\get_groupby_date_string( 'MONTH', 'date' );
$union_clauses['orderby'] = 'MONTH(date)';
// Now drill down to the smallest unit.
if ( $hour_by_hour ) {
$sql_clauses['groupby'] = Reports\get_groupby_date_string( 'HOUR', 'edd_oi.date_created' );
$sql_clauses['orderby'] = 'HOUR(edd_oi.date_created)';
$union_clauses['groupby'] = Reports\get_groupby_date_string( 'HOUR', 'date' );
$union_clauses['orderby'] = 'HOUR(date)';
} elseif ( $day_by_day ) {
$sql_clauses['groupby'] = Reports\get_groupby_date_string( 'DATE', 'edd_oi.date_created' );
$sql_clauses['orderby'] = 'DATE(edd_oi.date_created)';
}
$union_clauses['groupby'] = Reports\get_groupby_date_string( 'DATE', 'date' );
$union_clauses['orderby'] = 'DATE(date)';
}
$price_id = isset( $download_data['price_id'] ) && is_numeric( $download_data['price_id'] )
? sprintf( 'AND price_id = %d', absint( $download_data['price_id'] ) )
: '';
$earnings_results = $wpdb->get_results(
$wpdb->prepare(
"SELECT SUM(edd_oi.total / edd_oi.rate) AS earnings, %1s
FROM {$wpdb->edd_order_items} edd_oi
WHERE edd_oi.product_id = %d %1s AND edd_oi.date_created >= %s AND edd_oi.date_created <= %s AND edd_oi.status IN ( 'complete', 'refunded', 'partially_refunded' )
GROUP BY {$sql_clauses['groupby']}
ORDER BY {$sql_clauses['orderby']} ASC",
$sql_clauses['select'],
$download_data['download_id'],
$price_id,
$dates['start']->copy()->format( 'mysql' ),
$dates['end']->copy()->format( 'mysql' )
)
$earnings_statuses = edd_get_gross_order_statuses();
$earnings_status_string = implode( ', ', array_fill( 0, count( $earnings_statuses ), '%s' ) );
$order_item_earnings = $wpdb->prepare(
"SELECT SUM(edd_oi.total / edd_oi.rate) AS earnings, %1s
FROM {$wpdb->edd_order_items} edd_oi
INNER JOIN {$wpdb->edd_orders} edd_o ON edd_oi.order_id = edd_o.id
WHERE edd_oi.product_id = %d %1s AND edd_oi.date_created >= %s AND edd_oi.date_created <= %s AND edd_o.status IN ({$earnings_status_string})
GROUP BY {$sql_clauses['groupby']}",
$sql_clauses['select'],
$download_data['download_id'],
$price_id,
$dates['start']->copy()->format( 'mysql' ),
$dates['end']->copy()->format( 'mysql' ),
...$earnings_statuses
);
/**
* The adjustments query needs a different order status check than the order items. This is due to the fact that
* adjustments refunded would end up being double counted, and therefore create an inaccurate revenue report.
*/
$adjustments_statuses = edd_get_net_order_statuses();
$adjustments_status_string = implode( ', ', array_fill( 0, count( $adjustments_statuses ), '%s' ) );
$order_adjustments = $wpdb->prepare(
"SELECT SUM(edd_oa.total / edd_oa.rate) AS earnings, %1s
FROM {$wpdb->edd_order_adjustments} edd_oa
INNER JOIN {$wpdb->edd_order_items} edd_oi ON
edd_oi.id = edd_oa.object_id
AND edd_oi.product_id = %d
%1s
AND edd_oi.date_created >= %s AND edd_oi.date_created <= %s
INNER JOIN {$wpdb->edd_orders} edd_o ON edd_oi.order_id = edd_o.id AND edd_o.type = 'sale' AND edd_o.status IN ({$adjustments_status_string})
WHERE edd_oa.object_type = 'order_item'
AND edd_oa.type != 'discount'
GROUP BY {$sql_clauses['groupby']}",
$sql_clauses['select'],
$download_data['download_id'],
$price_id,
$dates['start']->copy()->format( 'mysql' ),
$dates['end']->copy()->format( 'mysql' ),
...$adjustments_statuses
);
$earnings_sql = "SELECT SUM(earnings) as earnings, {$union_clauses['select']} FROM ({$order_item_earnings} UNION {$order_adjustments})a GROUP BY {$union_clauses['groupby']} ORDER BY {$union_clauses['orderby']}";
$earnings_results = $wpdb->get_results( $earnings_sql );
$statuses = edd_get_net_order_statuses();
$status_string = implode( ', ', array_fill( 0, count( $statuses ), '%s' ) );
@ -786,22 +832,22 @@ function edd_register_downloads_report( $reports ) {
...$statuses
);
$sales_results = $wpdb->get_results(
$wpdb->prepare(
"SELECT COUNT(edd_oi.total) AS sales, %1s
FROM {$wpdb->edd_order_items} edd_oi
{$join}
WHERE edd_oi.product_id = %d %1s AND edd_oi.date_created >= %s AND edd_oi.date_created <= %s AND edd_oi.status IN ( 'complete', 'refunded', 'partially_refunded' )
GROUP BY {$sql_clauses['groupby']}
ORDER BY {$sql_clauses['orderby']} ASC",
$sql_clauses['select'],
$download_data['download_id'],
$price_id,
$dates['start']->copy()->format( 'mysql' ),
$dates['end']->copy()->format( 'mysql' )
)
$sales_sql = $wpdb->prepare(
"SELECT COUNT(edd_oi.total) AS sales, {$sql_clauses['select']}
FROM {$wpdb->edd_order_items} edd_oi
{$join}
WHERE edd_oi.product_id = %d %1s AND edd_oi.date_created >= %s AND edd_oi.date_created <= %s AND edd_oi.status IN ({$status_string})
GROUP BY {$sql_clauses['groupby']}
ORDER BY {$sql_clauses['orderby']} ASC",
$download_data['download_id'],
$price_id,
$dates['start']->copy()->format( 'mysql' ),
$dates['end']->copy()->format( 'mysql' ),
...$statuses
);
$sales_results = $wpdb->get_results( $sales_sql );
$sales = array();
$earnings = array();

View File

@ -598,24 +598,30 @@ function edd_ajax_download_search() {
// but we'll worry about that later if that situation ever happens.
$args = get_transient( 'edd_download_search' );
// Parse args
$search = wp_parse_args( (array) $args, array(
'text' => '',
'results' => array()
) );
// Parse args.
$search = wp_parse_args(
(array) $args,
array(
'text' => '',
'results' => array(),
)
);
// Get the search string
// Get the search string.
$new_search = isset( $_GET['s'] )
? sanitize_text_field( $_GET['s'] )
: '';
// Bail early if the search text has not changed
// Limit to only alphanumeric characters, including unicode and spaces.
$new_search = preg_replace( '/[^\pL^\pN\pZ]/', ' ', $new_search );
// Bail early if the search text has not changed.
if ( $search['text'] === $new_search ) {
echo json_encode( $search['results'] );
echo wp_json_encode( $search['results'] );
edd_die();
}
// Set the local static search variable
// Set the local static search variable.
$search['text'] = $new_search;
// Are we excluding the current ID?
@ -640,17 +646,17 @@ function edd_ajax_download_search() {
// Are we including all statuses, or only public ones?
$status = ! current_user_can( 'edit_products' )
? apply_filters( 'edd_product_dropdown_status_nopriv', array( 'publish' ) )
: apply_filters( 'edd_product_dropdown_status', array( 'publish', 'draft', 'private', 'future' ) );
: apply_filters( 'edd_product_dropdown_status', array( 'publish', 'draft', 'private', 'future' ) );
// Default query arguments
// Default query arguments.
$args = array(
'orderby' => 'title',
'order' => 'ASC',
'post_type' => 'download',
'posts_per_page' => 50,
'post_status' => implode( ',', $status ), // String
'post__not_in' => $excludes, // Array
'edd_search' => $new_search, // String
'post_status' => implode( ',', $status ), // String.
'post__not_in' => $excludes, // Array.
'edd_search' => $new_search, // String.
'suppress_filters' => false,
);
@ -672,11 +678,11 @@ function edd_ajax_download_search() {
}
add_filter( 'posts_where', 'edd_ajax_filter_download_where', 10, 2 );
// Get downloads
// Get downloads.
$items = get_posts( $args );
remove_filter( 'posts_where', 'edd_ajax_filter_download_where', 10, 2 );
// Pluck title & ID
// Pluck title & ID.
if ( ! empty( $items ) ) {
$items = wp_list_pluck( $items, 'post_title', 'ID' );
@ -684,25 +690,25 @@ function edd_ajax_download_search() {
foreach ( $items as $post_id => $title ) {
$product_title = $title;
// Look for variable pricing
// Look for variable pricing.
$prices = edd_get_variable_prices( $post_id );
if ( ! empty( $prices ) && ( false === $variations|| ! $variations_only ) ) {
if ( ! empty( $prices ) && ( false === $variations || ! $variations_only ) ) {
$title .= ' (' . __( 'All Price Options', 'easy-digital-downloads' ) . ')';
}
if ( empty( $prices ) || ! $variations_only ) {
// Add item to results array
// Add item to results array.
$search['results'][] = array(
'id' => $post_id,
'name' => $title,
);
}
// Maybe include variable pricing
// Maybe include variable pricing.
if ( ! empty( $variations ) && ! empty( $prices ) ) {
foreach ( $prices as $key => $value ) {
$name = ! empty( $value['name'] ) ? $value['name'] : '';
$name = ! empty( $value['name'] ) ? $value['name'] : '';
if ( ! empty( $name ) ) {
$search['results'][] = array(
@ -713,17 +719,16 @@ function edd_ajax_download_search() {
}
}
}
// Empty the results array
} else {
// Empty the results array.
$search['results'] = array();
}
// Update the transient
// Update the transient.
set_transient( 'edd_download_search', $search, 30 );
// Output the results
echo json_encode( $search['results'] );
// Output the results.
echo wp_json_encode( $search['results'] );
// Done!
edd_die();
@ -802,6 +807,45 @@ function edd_ajax_customer_search() {
}
add_action( 'wp_ajax_edd_customer_search', 'edd_ajax_customer_search' );
/**
* Search the download categories via AJAX
*
* @since 3.1.0.4
* @return void
*/
function edd_ajax_download_category_search() {
$search = esc_sql( sanitize_text_field( $_GET['s'] ) );
$results = array();
$category_args = array(
'taxonomy' => array( 'download_category' ),
'orderby' => 'id',
'order' => 'ASC',
'hide_empty' => true,
'fields' => 'all',
'name__like' => $search,
);
$categories_found = get_terms( $category_args );
if ( ! empty( $categories_found ) ) {
foreach ( $categories_found as $category ) {
$results[] = array(
'id' => $category->slug,
'name' => $category->name . ' (' . $category->count . ')',
);
}
} else {
$results[] = array(
'id' => 0,
'name' => __( 'No categories found', 'easy-digital-downloads' ),
);
}
echo wp_send_json( $results );
}
add_action( 'wp_ajax_edd_download_category_search', 'edd_ajax_download_category_search' );
/**
* Search the users database via AJAX
*

View File

@ -370,102 +370,118 @@ class EDD_API_V2 extends EDD_API_V1 {
$sales = array();
if( ! user_can( $this->user_id, 'view_shop_reports' ) && ! $this->override ) {
if ( ! user_can( $this->user_id, 'view_shop_reports' ) && ! $this->override ) {
return $sales;
}
if( isset( $wp_query->query_vars['id'] ) ) {
if ( isset( $wp_query->query_vars['id'] ) ) {
$query = array();
$query[] = new EDD_Payment( $wp_query->query_vars['id'] );
} elseif( isset( $wp_query->query_vars['purchasekey'] ) ) {
$query[] = edd_get_order( $wp_query->query_vars['id'] );
} elseif ( isset( $wp_query->query_vars['purchasekey'] ) ) {
$query = array();
$query[] = edd_get_payment_by( 'key', $wp_query->query_vars['purchasekey'] );
} elseif( isset( $wp_query->query_vars['email'] ) ) {
$query = edd_get_payments( array( 'fields' => 'ids', 'meta_key' => '_edd_payment_user_email', 'meta_value' => $wp_query->query_vars['email'], 'number' => $this->per_page(), 'page' => $this->get_paged(), 'status' => 'complete' ) );
$query[] = edd_get_order_by( 'payment_key', $wp_query->query_vars['purchasekey'] );
} elseif ( isset( $wp_query->query_vars['email'] ) ) {
$query = edd_get_orders(
array(
'type' => 'sale',
'email' => $wp_query->query_vars['email'],
'number' => $this->per_page(),
'offset' => ( $this->get_paged() - 1 ) * $this->per_page(),
'status__in' => edd_get_net_order_statuses(),
)
);
} else {
$query = edd_get_payments( array( 'fields' => 'ids', 'number' => $this->per_page(), 'page' => $this->get_paged(), 'status' => 'complete' ) );
$query = edd_get_orders(
array(
'type' => 'sale',
'number' => $this->per_page(),
'offset' => ( $this->get_paged() - 1 ) * $this->per_page(),
'status__in' => edd_get_net_order_statuses(),
)
);
}
if ( $query ) {
$i = 0;
foreach ( $query as $payment ) {
if ( is_numeric( $payment ) ) {
$payment = new EDD_Payment( $payment );
foreach ( $query as $order ) {
/** @var EDD\Orders\Order $order An Order object. */
$localized_time = edd_get_edd_timezone_equivalent_date_from_utc( EDD()->utils->date( $order->date_created ) );
$sales['sales'][ $i ]['ID'] = $order->get_number();
$sales['sales'][ $i ]['mode'] = $order->mode;
$sales['sales'][ $i ]['status'] = $order->status;
$sales['sales'][ $i ]['transaction_id'] = $order->get_transaction_id();
$sales['sales'][ $i ]['key'] = $order->payment_key;
$sales['sales'][ $i ]['subtotal'] = $order->subtotal;
$sales['sales'][ $i ]['tax'] = $order->tax;
$sales['sales'][ $i ]['total'] = $order->total;
$sales['sales'][ $i ]['gateway'] = $order->gateway;
$sales['sales'][ $i ]['customer_id'] = $order->customer_id;
$sales['sales'][ $i ]['user_id'] = $order->user_id;
$sales['sales'][ $i ]['email'] = $order->email;
$sales['sales'][ $i ]['date'] = $localized_time->copy()->format( 'Y-m-d H:i:s' );
$sales['sales'][ $i ]['date_utc'] = $order->date_created;
$fees = array();
$discounts = array();
foreach ( $order->adjustments as $adjustment ) {
switch ( $adjustment->type ) {
case 'fee':
$fees[] = array(
'amount' => $adjustment->total,
'label' => $adjustment->description,
'no_tax' => empty( $adjustment->tax ),
'type' => $adjustment->type,
'price_id' => null,
'download_id' => null,
'id' => $adjustment->type_key,
);
break;
case 'discount':
$discounts[ $adjustment->description ] = $adjustment->total;
break;
}
}
$payment_meta = $payment->get_meta();
$user_info = $payment->user_info;
$sales['sales'][ $i ]['ID'] = $payment->number;
$sales['sales'][ $i ]['mode'] = $payment->mode;
$sales['sales'][ $i ]['status'] = $payment->status;
$sales['sales'][ $i ]['transaction_id'] = ( ! empty( $payment->transaction_id ) ) ? $payment->transaction_id : null;
$sales['sales'][ $i ]['key'] = $payment->key;
$sales['sales'][ $i ]['subtotal'] = $payment->subtotal;
$sales['sales'][ $i ]['tax'] = $payment->tax;
$sales['sales'][ $i ]['fees'] = ( ! empty( $payment->fees ) ? $payment->fees : null );
$sales['sales'][ $i ]['total'] = $payment->total;
$sales['sales'][ $i ]['gateway'] = $payment->gateway;
$sales['sales'][ $i ]['customer_id'] = $payment->customer_id;
$sales['sales'][ $i ]['user_id'] = $payment->user_id;
$sales['sales'][ $i ]['email'] = $payment->email;
$sales['sales'][ $i ]['date'] = $payment->date;
$c = 0;
$discounts = ! empty( $payment->discounts ) ? explode( ',', $payment->discounts ) : array();
$discounts = array_map( 'trim', $discounts );
$discount_values = array();
foreach ( $discounts as $discount ) {
if ( 'none' === $discount ) { continue; }
$discount_values[ $discount ] = 0;
}
$cart_items = array();
foreach ( $payment->cart_details as $key => $item ) {
foreach ( $order->items as $item ) {
$cart_items[ $c ]['object_id'] = $item->id;
$cart_items[ $c ]['id'] = $item->product_id;
$cart_items[ $c ]['quantity'] = $item->quantity;
$cart_items[ $c ]['name'] = $item->product_name;
$cart_items[ $c ]['price'] = $item->total;
$item_id = isset( $item['id'] ) ? $item['id'] : $item;
$price = isset( $item['price'] ) ? $item['price'] : false; // The final price for the item
$item_price = isset( $item['item_price'] ) ? $item['item_price'] : false; // The price before discounts
// Keeping this here for backwards compatibility.
$cart_items[ $c ]['price_name'] = null === $item->price_id
? ''
: edd_get_price_name( $item->product_id, array( 'price_id' => $item->price_id ) );
$price_id = isset( $item['item_number']['options']['price_id'] ) ? $item['item_number']['options']['price_id'] : null;
$quantity = isset( $item['quantity'] ) && $item['quantity'] > 0 ? $item['quantity'] : 1;
if( ! $price ) {
// This function is only used on payments with near 1.0 cart data structure
$price = edd_get_download_final_price( $item_id, $user_info, null );
}
$price_name = '';
if ( isset( $item['item_number'] ) && isset( $item['item_number']['options'] ) ) {
$price_options = $item['item_number']['options'];
if ( isset( $price_options['price_id'] ) ) {
$price_name = edd_get_price_option_name( $item_id, $price_options['price_id'], $payment->ID );
// Check for any item level fees to include in the fees array.
foreach ( $item->adjustments as $adjustment ) {
if ( 'fee' === $adjustment->type ) {
$fees[] = array(
'amount' => $adjustment->total,
'label' => $adjustment->description,
'no_tax' => empty( $adjustment->tax ),
'type' => $adjustment->type,
'price_id' => $item->price_id,
'download_id' => $item->product_id,
'id' => $adjustment->type_key,
);
}
}
$cart_items[ $c ]['id'] = $item_id;
$cart_items[ $c ]['quantity'] = $quantity;
$cart_items[ $c ]['name'] = get_the_title( $item_id );
$cart_items[ $c ]['price'] = $price;
$cart_items[ $c ]['price_name'] = $price_name;
// Determine the discount amount for the item, if there is one
foreach ( $discount_values as $discount => $amount ) {
$item_discount = edd_get_cart_item_discount_amount( $item, $discount );
$discount_values[ $discount ] += $item_discount;
}
$c++;
}
$sales['sales'][ $i ]['discounts'] = ( ! empty( $discount_values ) ? $discount_values : null );
$sales['sales'][ $i ]['products'] = $cart_items;
$sales['sales'][ $i ]['fees'] = ! empty( $fees ) ? $fees : null;
$sales['sales'][ $i ]['discounts'] = ! empty( $discounts ) ? $discounts : null;
$i++;
}

View File

@ -6,7 +6,7 @@
"title": "EDD Checkout",
"category": "easy-digital-downloads",
"icon": "products",
"description": "Beta: Full checkout block for Easy Digital Downloads.",
"description": "Full checkout block for Easy Digital Downloads.",
"keywords": [
"easy digital downloads",
"edd",

View File

@ -4,7 +4,7 @@
* Description: Core blocks for Easy Digital Downloads.
* Requires at least: 5.8
* Requires PHP: 7.0
* Version: 2.0.2
* Version: 2.0.3
* Author: Easy Digital Downloads
* License: GPL-2.0-or-later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html

View File

@ -181,6 +181,7 @@ function checkout( $block_attributes = array() ) {
}
do_action( 'edd_before_checkout_cart' );
include EDD_BLOCKS_DIR . 'views/checkout/cart/cart.php';
do_action( 'edd_after_checkout_cart' );
Forms\do_purchase_form( $block_attributes );
?>
</div>

View File

@ -160,7 +160,7 @@ class EDD_Cart {
*/
private function get_tax_rate() {
if ( null === $this->tax_rate ) {
$this->tax_rate = edd_get_tax_rate();
$this->tax_rate = edd_use_taxes() ? edd_get_tax_rate() : 0;
}
return $this->tax_rate;

View File

@ -325,7 +325,7 @@ final class Easy_Digital_Downloads {
// Plugin version.
if ( ! defined( 'EDD_VERSION' ) ) {
define( 'EDD_VERSION', '3.1.0.3' );
define( 'EDD_VERSION', '3.1.0.4' );
}
// Plugin Root File.

View File

@ -147,7 +147,8 @@ class Stats {
$args = wp_parse_args( $args, array(
'column_prefix' => '',
'accepted_functions' => array(),
'rate' => true
'requested_function' => false,
'rate' => true,
) );
$column = $this->query_vars['column'];
@ -165,11 +166,16 @@ class Stats {
$default_function = is_array( $args['accepted_functions'] ) && isset( $args['accepted_functions'][0] ) ? $args['accepted_functions'][0] : false;
$function = ! empty( $this->query_vars['function'] ) ? $this->query_vars['function'] : $default_function;
if ( ! empty( $args['requested_function'] ) ) {
$function = $args['requested_function'];
}
if ( empty( $function ) ) {
throw new \InvalidArgumentException( 'Missing select function.' );
}
if ( ! empty( $args['accepted_functions'] ) && ! in_array( strtoupper( $this->query_vars['function'] ), $args['accepted_functions'], true ) ) {
if ( ! empty( $args['accepted_functions'] ) && ! in_array( strtoupper( $function ), $args['accepted_functions'], true ) ) {
if ( ! empty( $default_function ) ) {
$function = $default_function;
} else {
@ -177,7 +183,7 @@ class Stats {
}
}
$function = $this->query_vars['function'] = strtoupper( $function );
$function = strtoupper( $function );
// Multiply by rate if currency conversion is enabled.
if (
@ -930,18 +936,13 @@ class Stats {
$this->query_vars['table'] = $this->get_db()->edd_order_items;
$this->query_vars['column'] = true === $this->query_vars['exclude_taxes'] ? 'total - tax' : 'total';
$this->query_vars['date_query_column'] = 'date_created';
$this->query_vars['status'] = array( 'complete', 'refunded', 'partially_refunded' );
$this->query_vars['status'] = edd_get_gross_order_statuses();
// Run pre-query checks and maybe generate SQL.
$this->pre_query( $query );
$function = $this->get_amount_column_and_function( array(
'column_prefix' => $this->query_vars['table'],
'accepted_functions' => array( 'SUM', 'AVG' )
) );
$product_id = ! empty( $this->query_vars['product_id'] )
? $this->get_db()->prepare( 'AND product_id = %d', absint( $this->query_vars['product_id'] ) )
? $this->get_db()->prepare( "AND {$this->query_vars['table']}.product_id = %d", absint( $this->query_vars['product_id'] ) )
: '';
$price_id = $this->generate_price_id_query_sql();
@ -954,28 +955,119 @@ class Stats {
? $this->get_db()->prepare( 'AND edd_oa.country = %s', esc_sql( $this->query_vars['country'] ) )
: '';
$status = ! empty( $this->query_vars['status'] )
? " AND {$this->query_vars['table']}.status IN ('" . implode( "', '", $this->query_vars['status'] ) . "')"
: '';
$join = $currency = '';
if ( ! empty( $country ) || ! empty( $region ) ) {
$join .= " INNER JOIN {$this->get_db()->edd_order_addresses} edd_oa ON {$this->query_vars['table']}.order_id = edd_oa.order_id ";
}
$join .= " INNER JOIN {$this->get_db()->edd_orders} edd_o ON ({$this->query_vars['table']}.order_id = edd_o.id) AND edd_o.status IN ('" . implode( "', '", $this->query_vars['status'] ) . "') ";
if ( ! empty( $this->query_vars['currency'] ) && array_key_exists( strtoupper( $this->query_vars['currency'] ), edd_get_currencies() ) ) {
$join .= " INNER JOIN {$this->get_db()->edd_orders} edd_o ON ({$this->query_vars['table']}.order_id = edd_o.id) ";
$currency = $this->get_db()->prepare( "AND edd_o.currency = %s", strtoupper( $this->query_vars['currency'] ) );
}
/**
* The adjustments query needs a different order status check than the order items. This is due to the fact that
* adjustments refunded would end up being double counted, and therefore create an inaccurate revenue report.
*/
$adjustments_join = " INNER JOIN {$this->get_db()->edd_orders} edd_o ON ({$this->query_vars['table']}.order_id = edd_o.id) AND edd_o.type = 'sale' AND edd_o.status IN ('" . implode( "', '", edd_get_net_order_statuses() ) . "') ";
/**
* With the addition of including fees into the calcualtion, the order_items
* and order_adjustments for the order items needs to be a SUM and then the final function
* (SUM or AVG) needs to be run on the final UNION Query.
*/
$order_item_function = $this->get_amount_column_and_function( array(
'column_prefix' => $this->query_vars['table'],
'accepted_functions' => array( 'SUM', 'AVG' ),
'requested_function' => 'SUM',
) );
$order_adjustment_function = $this->get_amount_column_and_function( array(
'column_prefix' => 'oadj',
'accepted_functions' => array( 'SUM', 'AVG' ),
'requested_function' => 'SUM',
) );
$union_function = $this->get_amount_column_and_function( array(
'column_prefix' => '',
'accepted_functions' => array( 'SUM', 'AVG' ),
'rate' => false,
) );
if ( true === $this->query_vars['grouped'] ) {
$sql = "SELECT product_id, price_id, {$function} AS total
FROM {$this->query_vars['table']}
{$join}
WHERE 1=1 {$product_id} {$price_id} {$region} {$country} {$currency} {$this->query_vars['where_sql']} {$this->query_vars['date_query_sql']}
GROUP BY product_id, price_id
ORDER BY total DESC";
$order_items = "SELECT
{$this->query_vars['table']}.product_id,
{$this->query_vars['table']}.price_id,
{$order_item_function} AS total
FROM {$this->query_vars['table']}
{$join}
WHERE 1=1
{$product_id}
{$price_id}
{$region}
{$country}
{$currency}
{$this->query_vars['where_sql']}
{$this->query_vars['date_query_sql']}
GROUP BY {$this->query_vars['table']}.product_id, {$this->query_vars['table']}.price_id";
$order_adjustments = "SELECT
{$this->query_vars['table']}.product_id as product_id,
{$this->query_vars['table']}.price_id as price_id,
{$order_adjustment_function} as total
FROM {$this->get_db()->edd_order_adjustments} oadj
INNER JOIN {$this->query_vars['table']} ON
({$this->query_vars['table']}.id = oadj.object_id)
{$product_id}
{$price_id}
{$region}
{$country}
{$currency}
{$adjustments_join}
WHERE oadj.object_type = 'order_item'
AND oadj.type != 'discount'
{$this->query_vars['date_query_sql']}
GROUP BY {$this->query_vars['table']}.product_id, {$this->query_vars['table']}.price_id";
$sql = "SELECT product_id, price_id, {$union_function} AS total
FROM ({$order_items} UNION {$order_adjustments})a
GROUP BY product_id, price_id
ORDER BY total DESC";
} else {
$sql = "SELECT {$function} AS total
FROM {$this->query_vars['table']}
{$join}
WHERE 1=1 {$product_id} {$price_id} {$region} {$country} {$currency} {$this->query_vars['where_sql']} {$this->query_vars['date_query_sql']}";
$order_items = "SELECT
{$order_item_function} AS total
FROM {$this->query_vars['table']}
{$join}
WHERE 1=1
{$product_id}
{$price_id}
{$region}
{$country}
{$currency}
{$this->query_vars['where_sql']}
{$this->query_vars['date_query_sql']}";
$order_adjustments = "SELECT
{$order_adjustment_function} as total
FROM {$this->get_db()->edd_order_adjustments} oadj
INNER JOIN {$this->query_vars['table']} ON
({$this->query_vars['table']}.id = oadj.object_id)
{$product_id}
{$price_id}
{$region}
{$country}
{$currency}
{$adjustments_join}
WHERE oadj.object_type = 'order_item'
AND oadj.type != 'discount'
{$this->query_vars['date_query_sql']}";
$sql = "SELECT {$union_function} AS total FROM ({$order_items} UNION {$order_adjustments})a";
}
$result = $this->get_db()->get_results( $sql );
@ -2788,8 +2880,9 @@ class Stats {
$date_query_sql = ' AND ';
if ( ! empty( $this->query_vars['start'] ) ) {
$start_date = EDD()->utils->date( $this->query_vars['start'], edd_get_timezone_id(), false )->format( 'mysql' );
$date_query_sql .= "{$this->query_vars['table']}.{$this->query_vars['date_query_column']} ";
$date_query_sql .= $this->get_db()->prepare( '>= %s', $this->query_vars['start'] );
$date_query_sql .= $this->get_db()->prepare( '>= %s', $start_date );
}
// Join dates with `AND` if start and end date set.
@ -2798,7 +2891,8 @@ class Stats {
}
if ( ! empty( $this->query_vars['end'] ) ) {
$date_query_sql .= $this->get_db()->prepare( "{$this->query_vars['table']}.{$this->query_vars['date_query_column']} <= %s", $this->query_vars['end'] );
$end_date = EDD()->utils->date( $this->query_vars['end'], edd_get_timezone_id(), false )->format( 'mysql' );
$date_query_sql .= $this->get_db()->prepare( "{$this->query_vars['table']}.{$this->query_vars['date_query_column']} <= %s", $end_date );
}
$this->query_vars['date_query_sql'] = $date_query_sql;
@ -2957,7 +3051,7 @@ class Stats {
*/
private function generate_price_id_query_sql() {
return ! is_null( $this->query_vars['price_id'] ) && is_numeric( $this->query_vars['price_id'] )
? $this->get_db()->prepare( 'AND price_id = %d', absint( $this->query_vars['price_id'] ) )
? $this->get_db()->prepare( "AND {$this->query_vars['table']}.price_id = %d", absint( $this->query_vars['price_id'] ) )
: '';
}

View File

@ -8,9 +8,10 @@
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 3.0
*/
namespace EDD\Database\Schemas;
// Exit if accessed directly
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;
use EDD\Database\Schema;
@ -30,18 +31,18 @@ final class Adjustments extends Schema {
*/
public $columns = array(
// id
// id.
array(
'name' => 'id',
'type' => 'bigint',
'length' => '20',
'unsigned' => true,
'extra' => 'auto_increment',
'primary' => true,
'sortable' => true
'name' => 'id',
'type' => 'bigint',
'length' => '20',
'unsigned' => true,
'extra' => 'auto_increment',
'primary' => true,
'sortable' => true,
),
// parent
// parent.
array(
'name' => 'parent',
'type' => 'bigint',
@ -49,35 +50,36 @@ final class Adjustments extends Schema {
'unsigned' => true,
'default' => '0',
'sortable' => true,
'transition' => true
'transition' => true,
),
// name
// name.
array(
'name' => 'name',
'type' => 'varchar',
'length' => '200',
'searchable' => true,
'sortable' => true
'sortable' => true,
),
// code
// code.
array(
'name' => 'code',
'type' => 'varchar',
'length' => '50',
'searchable' => true,
'sortable' => true
'sortable' => true,
'cache_key' => true,
),
// status
// status.
array(
'name' => 'status',
'type' => 'varchar',
'length' => '20',
'default' => 'draft',
'sortable' => true,
'transition' => true
'transition' => true,
),
// type
@ -87,124 +89,124 @@ final class Adjustments extends Schema {
'length' => '20',
'default' => '',
'sortable' => true,
'transition' => true
'transition' => true,
),
// scope
// scope.
array(
'name' => 'scope',
'type' => 'varchar',
'length' => '20',
'default' => '',
'sortable' => true,
'transition' => true
'transition' => true,
),
// amount_type
// amount_type.
array(
'name' => 'amount_type',
'type' => 'varchar',
'length' => '20',
'default' => '',
'sortable' => true,
'transition' => true
'transition' => true,
),
// amount
// amount.
array(
'name' => 'amount',
'type' => 'decimal',
'length' => '18,9',
'default' => '0',
'sortable' => true
'name' => 'amount',
'type' => 'decimal',
'length' => '18,9',
'default' => '0',
'sortable' => true,
),
// description
// description.
array(
'name' => 'description',
'type' => 'longtext',
'default' => '',
'searchable' => true
'searchable' => true,
),
// max_uses
// max_uses.
array(
'name' => 'max_uses',
'type' => 'bigint',
'length' => '20',
'unsigned' => true,
'default' => '0'
),
// use_count
array(
'name' => 'use_count',
'type' => 'bigint',
'length' => '20',
'unsigned' => true,
'default' => '0',
'sortable' => true,
),
// once_per_customer
// use_count.
array(
'name' => 'once_per_customer',
'type' => 'int',
'length' => '1',
'default' => '0'
'name' => 'use_count',
'type' => 'bigint',
'length' => '20',
'unsigned' => true,
'default' => '0',
'sortable' => true,
),
// min_charge_amount
// once_per_customer.
array(
'name' => 'min_charge_amount',
'type' => 'decimal',
'length' => '18,9',
'default' => '0'
'name' => 'once_per_customer',
'type' => 'int',
'length' => '1',
'default' => '0',
),
// start_date
// min_charge_amount.
array(
'name' => 'min_charge_amount',
'type' => 'decimal',
'length' => '18,9',
'default' => '0',
),
// start_date.
array(
'name' => 'start_date',
'type' => 'datetime',
'default' => null,
'allow_null' => true,
'date_query' => true,
'sortable' => true
'sortable' => true,
),
// end_date
// end_date.
array(
'name' => 'end_date',
'type' => 'datetime',
'default' => null,
'allow_null' => true,
'date_query' => true,
'sortable' => true
'sortable' => true,
),
// date_created
// date_created.
array(
'name' => 'date_created',
'type' => 'datetime',
'default' => '', // Defaults to current time in query class
'default' => '', // Defaults to current time in query class.
'created' => true,
'date_query' => true,
'sortable' => true
'sortable' => true,
),
// date_modified
// date_modified.
array(
'name' => 'date_modified',
'type' => 'datetime',
'default' => '', // Defaults to current time in query class
'default' => '', // Defaults to current time in query class.
'modified' => true,
'date_query' => true,
'sortable' => true
'sortable' => true,
),
// uuid
// uuid.
array(
'uuid' => true,
)
'uuid' => true,
),
);
}

View File

@ -280,8 +280,12 @@ function get_order_items( $purchase_data ) {
if ( $item_amount <= 0 ) {
$item_amount = 0;
}
$substr_func = function_exists( 'mb_substr' ) ? 'mb_substr' : 'substr';
$name = $substr_func( edd_get_cart_item_name( $item ), 0, 127 );
$items[ $i ] = array(
'name' => stripslashes_deep( html_entity_decode( substr( edd_get_cart_item_name( $item ), 0, 127 ), ENT_COMPAT, 'UTF-8' ) ),
'name' => stripslashes_deep( html_entity_decode( $name, ENT_COMPAT, 'UTF-8' ) ),
'quantity' => $item['quantity'],
'unit_amount' => array(
'currency_code' => edd_get_currency(),

View File

@ -13,6 +13,7 @@
namespace EDD\Gateways\PayPal\IPN;
use EDD\Gateways\PayPal;
/**
* Listens for an IPN call from PayPal
*
@ -25,6 +26,11 @@ function listen_for_ipn() {
return;
}
// If PayPal is not connected, we don't need to run here.
if ( ! PayPal\has_rest_api_connection() ) {
return;
}
ipn_debug_log( 'IPN Backup Loaded' );
// Moving this up in the load order so we can check some things before even getting to verification.
@ -174,7 +180,7 @@ function listen_for_ipn() {
// Bail if this is the very first payment.
if ( date( 'Y-n-d', strtotime( $subscription->created ) ) == date( 'Y-n-d', strtotime( $posted['payment_date'] ) ) ) {
if ( ! empty( $posted['payment_date'] ) && date( 'Y-n-d', strtotime( $subscription->created ) ) == date( 'Y-n-d', strtotime( $posted['payment_date'] ) ) ) {
ipn_debug_log( 'IPN for subscription ' . $subscription->id . ': processing stopped because this is the initial payment.' );
return;
}

File diff suppressed because one or more lines are too long

View File

@ -8,10 +8,19 @@ domReady( () => {
const containerEl = document.getElementById( 'edds-stripe-connect-account' );
const actionsEl = document.getElementById( 'edds-stripe-disconnect-reconnect' );
if ( ! containerEl ) {
return;
}
/*
* Do not make a request, if we are inside Onboarding Wizard.
* Onboarding Wizard will make it's own call.
*/
if ( containerEl.hasAttribute('data-onboarding-wizard') ) {
return;
}
return apiRequest( 'edds_stripe_connect_account_info', {
...containerEl.dataset,
} )

View File

@ -3,8 +3,8 @@
* Plugin Name: Easy Digital Downloads - Stripe Pro Payment Gateway
* Plugin URI: https://easydigitaldownloads.com/downloads/stripe-gateway/
* Description: Adds a payment gateway for Stripe.com
* Version: 2.8.13
* Requires at least: 4.4
* Version: 2.8.13.1
* Requires at least: 4.9
* Requires PHP: 5.6
* Author: Easy Digital Downloads
* Author URI: https://easydigitaldownloads.com
@ -44,7 +44,7 @@ function edd_stripe_core_bootstrap() {
}
if ( ! defined( 'EDD_STRIPE_VERSION' ) ) {
define( 'EDD_STRIPE_VERSION', '2.8.13' );
define( 'EDD_STRIPE_VERSION', '2.8.13.1' );
}
if ( ! defined( 'EDD_STRIPE_API_VERSION' ) ) {

View File

@ -83,27 +83,57 @@ function edds_stripe_connect_url() {
*/
function edds_process_gateway_connect_completion() {
if( ! isset( $_GET['edd_gateway_connect_completion'] ) || 'stripe_connect' !== $_GET['edd_gateway_connect_completion'] || ! isset( $_GET['state'] ) ) {
$redirect_screen = ! empty( $_GET['redirect_screen'] ) ? sanitize_text_field( $_GET['redirect_screen'] ) : '';
// A cancelled connection doesn't contain the completion or state values, but we do need to listen for the redirect_screen for the wizard.
if (
isset( $_GET['edd_gateway_connect_error'] ) &&
filter_var( $_GET['edd_gateway_connect_error'], FILTER_VALIDATE_BOOLEAN ) &&
! empty( $redirect_screen )
) {
$error_redirect = '';
switch ( $redirect_screen ) {
case 'onboarding-wizard':
$error_redirect = admin_url( 'edit.php?post_type=download&page=edd-onboarding-wizard&current_step=payment_methods' );
break;
}
if ( ! empty( $error_redirect ) ) {
wp_safe_redirect( esc_url_raw( $error_redirect ) );
exit;
}
}
if ( ! isset( $_GET['edd_gateway_connect_completion'] ) || 'stripe_connect' !== $_GET['edd_gateway_connect_completion'] || ! isset( $_GET['state'] ) ) {
return;
}
if( ! current_user_can( 'manage_shop_settings' ) ) {
if ( ! current_user_can( 'manage_shop_settings' ) ) {
return;
}
if( headers_sent() ) {
if ( headers_sent() ) {
return;
}
$edd_credentials_url = add_query_arg( array(
'live_mode' => (int) ! edd_is_test_mode(),
'state' => sanitize_text_field( $_GET['state'] ),
'customer_site_url' => admin_url( 'edit.php?post_type=download' ),
), 'https://easydigitaldownloads.com/?edd_gateway_connect_credentials=stripe_connect' );
$customer_site_url = admin_url( 'edit.php?post_type=download' );
if ( ! empty( $redirect_screen ) ) {
$customer_site_url = add_query_arg( 'redirect_screen', $redirect_screen, $customer_site_url );
}
$edd_credentials_url = add_query_arg(
array(
'live_mode' => (int) ! edd_is_test_mode(),
'state' => sanitize_text_field( $_GET['state'] ),
'customer_site_url' => urlencode( $customer_site_url ),
),
'https://easydigitaldownloads.com/?edd_gateway_connect_credentials=stripe_connect'
);
$response = wp_remote_get( esc_url_raw( $edd_credentials_url ) );
if( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
$message = '<p>' . sprintf(
/* translators: %1$s Opening anchor tag, do not translate. %2$s Closing anchor tag, do not translate. */
__( 'There was an error getting your Stripe credentials. Please %1$stry again%2$s. If you continue to have this problem, please contact support.', 'easy-digital-downloads' ),
@ -116,7 +146,7 @@ function edds_process_gateway_connect_completion() {
$data = json_decode( $response['body'], true );
$data = $data['data'];
if( edd_is_test_mode() ) {
if ( edd_is_test_mode() ) {
edd_update_option( 'test_publishable_key', sanitize_text_field( $data['publishable_key'] ) );
edd_update_option( 'test_secret_key', sanitize_text_field( $data['secret_key'] ) );
} else {
@ -125,7 +155,18 @@ function edds_process_gateway_connect_completion() {
}
edd_update_option( 'stripe_connect_account_id', sanitize_text_field( $data['stripe_user_id'] ) );
wp_redirect( esc_url_raw( admin_url( 'edit.php?post_type=download&page=edd-settings&tab=gateways&section=edd-stripe' ) ) );
$redirect_url = admin_url( 'edit.php?post_type=download&page=edd-settings&tab=gateways&section=edd-stripe' );
if ( ! empty( $redirect_screen ) ) {
switch ( $redirect_screen ) {
case 'onboarding-wizard':
$redirect_url = admin_url( 'edit.php?post_type=download&page=edd-onboarding-wizard&current_step=payment_methods' );
break;
}
}
wp_safe_redirect( esc_url_raw( $redirect_url ) );
exit;
}
@ -178,7 +219,7 @@ function edds_stripe_connect_disconnect_url() {
function edds_stripe_connect_process_disconnect() {
// Do not need to handle this request, bail.
if (
! ( isset( $_GET['page'] ) && 'edd-settings' === $_GET['page'] ) ||
! ( isset( $_GET['page'] ) && ( 'edd-settings' === $_GET['page'] || 'edd-onboarding-wizard' === $_GET['page'] ) ) ||
! isset( $_GET['edds-stripe-disconnect'] )
) {
return;
@ -337,7 +378,7 @@ function edds_stripe_connect_setting_field() {
class="edds-stripe-connect-acount-info notice inline"
data-account-id="<?php echo esc_attr( $stripe_connect_account_id ); ?>"
data-nonce="<?php echo wp_create_nonce( 'edds-stripe-connect-account-information' ); ?>"
>
<?php echo ( ! empty( $_GET['page'] ) && 'edd-onboarding-wizard' === $_GET['page'] ) ? ' data-onboarding-wizard="true"' : ''; ?>>
<p><span class="spinner is-active"></span>
<em><?php esc_html_e( 'Retrieving account information...', 'easy-digital-downloads' ); ?></em>
</div>

View File

@ -357,13 +357,21 @@ function edds_add_payment_method() {
}
$stripe_customer_id = edds_get_stripe_customer_id( get_current_user_id() );
$stripe_customer = edds_get_stripe_customer(
$customer_name = '';
if ( ! empty( $edd_customer->name ) ) {
$customer_name = $edd_customer->name;
}
$stripe_customer = edds_get_stripe_customer(
$stripe_customer_id,
array(
'email' => $edd_customer->email,
'description' => $edd_customer->email,
'name' => $customer_name,
)
);
if ( false === $stripe_customer ) {
wp_send_json_error(
array(

View File

@ -88,7 +88,7 @@ function edds_has_met_requirements( $requirement = false ) {
: true
),
'wp' => (
version_compare( get_bloginfo( 'version' ), '4.4', '>=' )
version_compare( get_bloginfo( 'version' ), '4.9', '>=' )
),
);

View File

@ -1175,10 +1175,22 @@ function edds_checkout_setup_customer( $purchase_data ) {
// No customer ID found, let's look one up based on the email.
$stripe_customer_id = edds_get_stripe_customer_id( $purchase_data['user_email'], false );
}
$customer_name = '';
if ( ! empty( $purchase_data['user_info']['first_name'] ) ) {
$customer_name .= sanitize_text_field( $purchase_data['user_info']['first_name'] );
}
if ( ! empty( $purchase_data['user_info']['last_name'] ) ) {
$customer_name .= ' ' . sanitize_text_field( $purchase_data['user_info']['last_name'] );
}
$customer_args = array(
'email' => $purchase_data['user_email'],
'description' => $purchase_data['user_email'],
'name' => $customer_name,
);
/**
* Filters the arguments used to create a Customer in Stripe.
*

View File

@ -32,10 +32,9 @@ function edds_buy_now_modal() {
edd_localize_scripts();
} else {
edd_load_scripts();
edd_agree_to_terms_js();
}
edd_agree_to_terms_js();
remove_filter( 'edd_is_checkout', '__return_true' );
// Enqueue scripts.

View File

@ -42,6 +42,9 @@ namespace Composer\Autoload;
*/
class ClassLoader
{
/** @var \Closure(string):void */
private static $includeFile;
/** @var ?string */
private $vendorDir;
@ -106,6 +109,7 @@ class ClassLoader
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
self::initializeIncludeClosure();
}
/**
@ -425,7 +429,7 @@ class ClassLoader
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
(self::$includeFile)($file);
return true;
}
@ -555,18 +559,23 @@ class ClassLoader
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{
include $file;
private static function initializeIncludeClosure(): void
{
if (self::$includeFile !== null) {
return;
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
*/
self::$includeFile = static function($file) {
include $file;
};
}
}

View File

@ -3,7 +3,7 @@
'name' => 'easy-digital-downloads/edd-stripe',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '829e436e3e0b91ab69528aebcee3344aa2e91eca',
'reference' => '24964d034941e0d3d07390ecc39570bfc64e7369',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -13,7 +13,7 @@
'easy-digital-downloads/edd-stripe' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'reference' => '829e436e3e0b91ab69528aebcee3344aa2e91eca',
'reference' => '24964d034941e0d3d07390ecc39570bfc64e7369',
'type' => 'wordpress-plugin',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),

View File

@ -187,6 +187,7 @@ class Download {
"SELECT SUM((oa.total - oa.tax)/ oa.rate) as revenue
FROM {$wpdb->edd_order_adjustments} oa
INNER JOIN {$wpdb->edd_order_items} oi ON(oi.id = oa.object_id)
INNER JOIN {$wpdb->edd_orders} o ON oi.order_id = o.id AND o.type = 'sale' {$order_status_sql}
WHERE {$product_id_sql}
{$price_id_sql}
AND oa.object_type = 'order_item'
@ -194,7 +195,9 @@ class Download {
AND oi.status IN('complete','partially_refunded')
{$date_query_sql}";
$results = $wpdb->get_row( "SELECT SUM(revenue) AS revenue FROM ({$order_items} UNION {$order_adjustments})a" );
$sql = "SELECT SUM(revenue) AS revenue FROM ({$order_items} UNION {$order_adjustments})a";
$results = $wpdb->get_row( $sql );
return ! empty( $results->revenue ) ? $results->revenue : 0.00;
}

View File

@ -164,4 +164,13 @@ class Order_Adjustment extends \EDD\Database\Rows\Order_Adjustment {
'parent' => $this->id
) );
}
/**
* Backwards compatibility for the `amount` property, which is now the `total`.
*
* @since 3.1.0.4
*/
public function get_amount() {
return $this->total;
}
}

View File

@ -234,6 +234,7 @@ add_action( 'edd_complete_purchase', 'edd_schedule_after_payment_action', 10, 1
* Executes the one time event used for after purchase actions.
*
* @since 2.8
* @since 3.1.0.4 This also verifies that all order items have the synced status as the order.
* @param $payment_id
* @param $force
*/
@ -248,6 +249,31 @@ function edd_process_after_payment_actions( $payment_id = 0, $force = false ) {
return;
}
/**
* In the event that during the order completion process, a timeout happens,
* ensure that all the order items have the correct status, to match the order itself.
*
* @see https://github.com/awesomemotive/easy-digital-downloads-pro/issues/77
*/
$order_items = edd_get_order_items(
array(
'order_id' => $payment_id,
'status__not_in' => edd_get_deliverable_order_item_statuses(),
'number' => 200,
)
);
if ( ! empty( $order_items ) ) {
foreach ( $order_items as $order_item ) {
edd_update_order_item(
$order_item->id,
array(
'status' => $payment->status,
)
);
}
}
$payment->add_note( __( 'After payment actions processed.', 'easy-digital-downloads' ) );
$payment->update_meta( '_edd_complete_actions_run', time() ); // This is in GMT

View File

@ -3098,7 +3098,7 @@ class EDD_Payment {
}
$item_fees[ $id ] = array(
'amount' => $item_fee->amount,
'amount' => $item_fee->total,
'label' => $item_fee->description,
'no_tax' => $no_tax ? $no_tax : false,
'type' => 'fee',

View File

@ -527,22 +527,22 @@ class EDD_Payments_Query extends EDD_Stats {
$order_ids = array();
if ( is_array( $this->args['download'] ) ) {
$orders = edd_get_order_items( array(
$order_items = edd_get_order_items( array(
'product_id__in' => (array) $this->args['download'],
) );
foreach ( $orders as $order ) {
/** @var $order EDD\Orders\Order_Item */
$order_ids[] = $order->order_id;
foreach ( $order_items as $order_item ) {
/** @var $order_item EDD\Orders\Order_Item */
$order_ids[] = $order_item->order_id;
}
} else {
$orders = edd_get_order_items( array(
$order_items = edd_get_order_items( array(
'product_id' => $this->args['download'],
) );
foreach ( $orders as $order ) {
/** @var $order EDD\Orders\Order_Item */
$order_ids[] = $order->id;
foreach ( $order_items as $order_item ) {
/** @var $order_item EDD\Orders\Order_Item */
$order_ids[] = $order_item->order_id;
}
}

View File

@ -1249,7 +1249,7 @@ function edd_get_payment_amount( $order_id = 0 ) {
// Bail if nothing was passed.
if ( empty( $order_id ) ) {
return '';
return 0.00;
}
$order = edd_get_order( $order_id );

View File

@ -15,6 +15,7 @@ defined( 'ABSPATH' ) || exit;
use EDD\Reports as Reports;
use EDD\Admin\List_Table;
use EDD\Stats as Stats;
/**
* Earnings_By_Taxonomy_List_Table class.
@ -34,27 +35,8 @@ class Earnings_By_Taxonomy_List_Table extends List_Table {
global $wpdb;
$dates = Reports\get_filter_value( 'dates' );
$date_range = Reports\parse_dates_for_range( $dates['range'] );
$currency = Reports\get_filter_value( 'currencies' );
// Generate date query SQL if dates have been set.
$date_query_sql = '';
if ( ! empty( $date_range['start'] ) || ! empty( $date_range['end'] ) ) {
if ( ! empty( $date_range['start'] ) ) {
$date_query_sql .= $wpdb->prepare( 'AND oi.date_created >= %s', $date_range['start']->format( 'mysql' ) );
}
// Join dates with `AND` if start and end date set.
if ( ! empty( $date_range['start'] ) && ! empty( $date_range['end'] ) ) {
$date_query_sql .= ' AND ';
}
if ( ! empty( $date_range['end'] ) ) {
$date_query_sql .= $wpdb->prepare( 'oi.date_created <= %s', $date_range['end']->format( 'mysql' ) );
}
}
$taxonomies = edd_get_download_taxonomies();
$taxonomies = array_map( 'sanitize_text_field', $taxonomies );
@ -78,64 +60,56 @@ class Earnings_By_Taxonomy_List_Table extends List_Table {
$taxonomies[ absint( $r->term_id ) ]['parent'] = absint( $r->parent );
}
$data = array();
$parent_ids = array();
// Setup an empty array for the final returned data.
$data = array();
$column = Reports\get_taxes_excluded_filter() ? 'oi.total - oi.tax' : 'oi.total';
$join = " INNER JOIN {$wpdb->edd_orders} o ON o.id = oi.order_id ";
$currency_clause = '';
if ( empty( $currency ) || 'convert' === $currency ) {
$column = sprintf( '(%s) / oi.rate', $column );
} elseif ( array_key_exists( strtoupper( $currency ), edd_get_currencies() ) ) {
$currency_clause = $wpdb->prepare(
" AND o.currency = %s ",
strtoupper( $currency )
);
}
$statuses = edd_get_net_order_statuses();
$status_string = implode( ', ', array_fill( 0, count( $statuses ), '%s' ) );
$status_sql = $wpdb->prepare(
" AND oi.status IN('complete','partially_refunded')
AND o.status IN({$status_string})",
...$statuses
);
// Store each download's stats during the loop to avoid double queries.
$download_stats = array();
foreach ( $taxonomies as $k => $t ) {
$c = new \stdClass();
$c->id = $k;
$c->name = $taxonomies[ $k ]['name'];
$placeholders = implode( ', ', array_fill( 0, count( $taxonomies[ $k ]['object_ids'] ), '%d' ) );
$product_id__in = $wpdb->prepare( "oi.product_id IN({$placeholders})", $taxonomies[ $k ]['object_ids'] );
$earnings = 0.00;
$sales = 0;
$sql = "SELECT SUM({$column}) as total
FROM {$wpdb->edd_order_items} oi
{$join}
WHERE {$product_id__in} {$currency_clause} {$date_query_sql} {$status_sql}";
$average_earnings = 0.00;
$average_sales = 0;
$result = $wpdb->get_row( $sql ); // WPCS: unprepared SQL ok.
foreach ( $taxonomies[ $k ]['object_ids'] as $download_id ) {
if ( ! isset( $download_stats[ $download_id ] ) ) {
$stats = new Stats(
array(
'product_id' => absint( $download_id ),
'currency' => $currency,
'range' => $dates['range'],
'output' => 'typed',
)
);
$earnings = null === $result && null === $result->total
? 0.00
: floatval( $result->total );
$download_stats[ $download_id ]['earnings'] = $stats->get_order_item_earnings(
array(
'function' => 'SUM',
)
);
$complete_orders = "SELECT SUM(oi.quantity) as sales
FROM {$wpdb->edd_order_items} oi
{$join}
WHERE {$product_id__in} {$currency_clause} {$date_query_sql} {$status_sql}";
$partial_orders = "SELECT SUM(oi.quantity) as sales
FROM {$wpdb->edd_order_items} oi
LEFT JOIN {$wpdb->edd_order_items} ri
ON ri.parent = oi.id
{$join}
WHERE {$product_id__in} {$currency_clause} {$date_query_sql}
AND oi.status ='partially_refunded'
AND oi.quantity = - ri.quantity";
$sql_sales = $wpdb->get_row( "SELECT SUM(sales) AS sales FROM ({$complete_orders} UNION {$partial_orders})a" );
$download_stats[ $download_id ]['sales'] = $stats->get_order_item_count(
array(
'function' => 'COUNT',
)
);
$sales = ! empty( $sql_sales->sales ) ? $sql_sales->sales : 0;
$download_stats[ $download_id ]['average_earnings'] = edd_get_average_monthly_download_earnings( $download_id );
$download_stats[ $download_id ]['average_sales'] = edd_get_average_monthly_download_sales( $download_id );
}
$earnings += $download_stats[ $download_id ]['earnings'];
$sales += $download_stats[ $download_id ]['sales'];
$average_earnings += $download_stats[ $download_id ]['average_earnings'];
$average_sales += $download_stats[ $download_id ]['average_sales'];
}
$c->sales = $sales;
$c->earnings = $earnings;
@ -143,14 +117,6 @@ class Earnings_By_Taxonomy_List_Table extends List_Table {
? null
: $t['parent'];
$average_sales = 0;
$average_earnings = 0.00;
foreach ( $taxonomies[ $k ]['object_ids'] as $download ) {
$average_sales += edd_get_average_monthly_download_sales( $download );
$average_earnings += edd_get_average_monthly_download_earnings( $download );
}
$c->average_sales = $average_sales;
$c->average_earnings = $average_earnings;
@ -161,7 +127,7 @@ class Earnings_By_Taxonomy_List_Table extends List_Table {
foreach ( $data as $d ) {
// Get parent level elements
// Get parent level elements.
if ( null === $d->parent ) {
$sorted_data[] = $d;
@ -173,7 +139,7 @@ class Earnings_By_Taxonomy_List_Table extends List_Table {
}
}
// Sort by total earnings
// Sort by total earnings.
usort( $sorted_data, function( $a, $b ) {
return ( $a->earnings < $b->earnings ) ? -1 : 1;
} );

View File

@ -229,7 +229,7 @@ function edd_get_purchase_link( $args = array() ) {
<?php } ?>
<?php if( apply_filters( 'edd_download_redirect_to_checkout', edd_straight_to_checkout(), $download->ID, $args ) ) : ?>
<input type="hidden" name="edd_redirect_to_checkout" id="edd_redirect_to_checkout" value="1">
<input type="hidden" name="edd_redirect_to_checkout" value="1">
<?php endif; ?>
<?php do_action( 'edd_purchase_link_end', $download->ID, $args ); ?>

View File

@ -30,13 +30,24 @@ class NotificationImporter {
* @since 2.11.4
*/
public function run() {
$request_timeout = get_option( 'edd_notification_req_timeout', false );
if ( false !== $request_timeout && current_time( 'timestamp' ) < $request_timeout ) {
edd_debug_log( 'Skipping notifications API request, timeout not reached' );
return;
}
edd_debug_log( 'Fetching notifications via ' . $this->getApiEndpoint() );
try {
$notifications = $this->fetchNotifications();
// If successful, make it so we don't request for another 23 hours.
update_option( 'edd_notification_req_timeout', current_time( 'timestamp' ) + ( HOUR_IN_SECONDS * 23 ), false );
} catch ( \Exception $e ) {
edd_debug_log( sprintf( 'Notification fetch exception: %s', $e->getMessage() ) );
// If for some reason our request failed, delay for 4 hours.
update_option( 'edd_notification_req_timeout', current_time( 'timestamp' ) + ( HOUR_IN_SECONDS * 4 ), false );
return;
}

View File

@ -6,16 +6,16 @@ Tags: ecommerce, e-commerce, sell, digital store, stripe
Requires at least: 4.9
Tested up to: 6.1
Requires PHP: 5.6
Stable Tag: 3.1.0.3
Stable Tag: 3.1.0.4
License: GNU Version 2 or Any Later Version
Sell your digital products with the ecommerce plugin written for digital creators, by digital creators. 🚀
Sell your digital products with the ecommerce plugin written for digital creators, by digital creators.
== Description ==
Easy Digital Downloads is a complete eCommerce solution for selling digital products on WordPress. Learn more at [EasyDigitalDownloads.com](https://easydigitaldownloads.com/?utm_source=home&utm_medium=description_tab&utm_content=home&utm_campaign=readme).
=== ✨ Sell effortlessly ===
= Sell effortlessly =
Whether it is selling [software like WordPress plugins or themes](https://easydigitaldownloads.com/blog/selling-software/?utm_medium=readme&utm_source=wporg&utm_campaign=edd-plugin&utm_term=description), [documents](https://easydigitaldownloads.com/blog/selling-documents/?utm_medium=readme&utm_source=wporg&utm_campaign=edd-plugin&utm_term=description) and [spreadsheets](https://easydigitaldownloads.com/blog/sell-excel-or-google-spreadsheets/?utm_medium=readme&utm_source=wporg&utm_campaign=edd-plugin&utm_term=description), [photography](https://easydigitaldownloads.com/blog/selling-photographs/?utm_medium=readme&utm_source=wporg&utm_campaign=edd-plugin&utm_term=description), [ebooks](https://easydigitaldownloads.com/blog/selling-ebooks/?utm_medium=readme&utm_source=wporg&utm_campaign=edd-plugin&utm_term=description), [music](https://easydigitaldownloads.com/blog/selling-audio-and-music/?utm_medium=readme&utm_source=wporg&utm_campaign=edd-plugin&utm_term=description), [graphics or digital artwork](https://easydigitaldownloads.com/blog/selling-graphics-and-digital-artwork/?utm_medium=readme&utm_source=wporg&utm_campaign=edd-plugin&utm_term=description), [videos](https://easydigitaldownloads.com/blog/selling-videos/?utm_medium=readme&utm_source=wporg&utm_campaign=edd-plugin&utm_term=description), or any other type of media file, Easy Digital Downloads provides a complete ecommerce platform that just works, so you can get back to creating.
@ -23,23 +23,7 @@ Whether it is selling [software like WordPress plugins or themes](https://easydi
> Mark Hunter - Mark of Approval
=== ✨ Build with Blocks ===
Easy Digital Downloads comes with built in Block Editor support, giving you the best experience in building your ecommerce store. We've added blocks for the following (with more coming all the time...)
* Shopping Cart (great for sidebars)
* Mini Shopping Cart (great for navigation menus)
* Product Listing
* Payment Confirmation
* Product Category/Tag Listing
* Registration Form
* Login Form (complete with password reset support)
* Receipt View
* Customer Order History
* Product purchase & add to cart button
* Store Checkout
=== 💪 Easy and free, meet robust and powerful ===
= Easy and free, meet robust and powerful =
Too often digital creators have to make the hard choice between affordable and powerful. Not with Easy Digital Downloads. It's free forever. It's easy to get started. Then, as you grow, there are hundreds of extensions to add functionality when you need it.
@ -59,11 +43,11 @@ Here are some of the features supported out-of-the-box:
- **Works with any theme** - Let your chosen WordPress theme handle the design. Easy Digital Downloads will fit right in.
- [And much more](https://easydigitaldownloads.com/edd-features/?utm_medium=readme&utm_source=wporg&utm_campaign=edd-plugin&utm_content=much-more&utm_term=description)!
=== 🌏 Localized for your language ===
= Localized for your language =
Easy Digital Downloads has been translated into more than two dozen languages and new translations are being added all the time, letting you have a natural, native experience, regardless of where you live. If your language is not available, we welcome translation contributions! See our [FAQ](https://easydigitaldownloads.com/docs/easy-digital-downloads-in-your-own-language/?utm_source=docs&utm_medium=description_tab&utm_content=languages&utm_campaign=readme).
=== 💳 Support for many payment gateways, including Stripe and PayPal ===
= Support for many payment gateways, including Stripe and PayPal =
The internet has made it possible for anyone to sell their products to a world-wide audience. No matter where you live, Easy Digital Downloads can be your e-Commerce solution. We offer integrations for the most common merchant processors and, through 3rd party extensions, support for many, many more as well.
@ -89,7 +73,7 @@ See our [payment gateways](https://easydigitaldownloads.com/downloads/category/g
Easy Digital Downloads is a verified member of the [Stripe partner program](https://stripe.com/accept-payments/easy-digital-downloads). This program recognizes software that meets Stripe's strict quality requirements and provides users of Easy Digital Downloads and Stripe with the confidence they need to trust us both with their payment integrations.
=== 🚀 Take your store further with extensions ===
= Take your store further with extensions =
With extensions from our [extensive catalog](https://easydigitaldownloads.com/downloads/?utm_source=extensions&utm_medium=description_tab&utm_content=extensions&utm_campaign=readme) you can super-charge your ecommerce business. Some of our most popular extensions are:
@ -111,7 +95,7 @@ With extensions from our [extensive catalog](https://easydigitaldownloads.com/do
These are just a few of the extensions we offer to help you optimize your digital store and increase revenue. See our complete [extensions catalogue](https://easydigitaldownloads.com/downloads/?utm_source=extensions&utm_medium=description_tab&utm_content=extensions&utm_campaign=readme) for more. There are also hundreds of extensions created by Easy Digital Downloads community members, which can be found on our [3rd Party Extensions page](https://easydigitaldownloads.com/3rd-party-extensions/?utm_source=3rd-party-extensions&utm_medium=description_tab&utm_content=3rd-party-extensions&utm_campaign=readme).
=== 🔌 Built with developers in mind ===
= Built with developers in mind =
Extensible, adaptable, and open source -- Easy Digital Downloads is created with designers and developers in mind. Consult our detailed [developer documentation](https://easydigitaldownloads.com/categories/docs/developer-docs/?utm_source=wporg&utm_medium=readme&utm_content=documentation&utm_campaign=dev-docs) to learn how you can extend and customize your Easy Digital Downloads powered eCommerce store.
@ -130,7 +114,7 @@ The Easy Digital Downloads API makes it possible for developers to make customiz
> Yudhistira Mauris - WP House
=== 🙋 Get help ===
= Get help =
Easy Digital Downloads is backed by top-notch technical support from our globally distributed full-time support team. We also have an [extensive documentation site available](https://easydigitaldownloads.com/docs/?utm_medium=readme&utm_source=wporg&utm_campaign=lite-plugin&utm_content=docs&utm_term=description). If you're looking for faster support via email, we encourage you to [purchase an Easy Digital Downloads pass](https://easydigitaldownloads.com/pricing/?utm_medium=readme&utm_source=wporg&utm_campaign=lite-plugin&utm_content=upgrade&utm_term=description) or premium extension.
@ -138,23 +122,23 @@ Easy Digital Downloads is backed by top-notch technical support from our globall
> Joe Casabona - Creator Courses
=== 📘 From our blog ===
= From our blog =
Check out some of our popular posts for actionable advice for running your business and selling digital files on the web.
- [How to Offer a Free Trial Subscription in WordPress](https://easydigitaldownloads.com/blog/how-to-give-away-trial-memberships/?utm_medium=readme&utm_source=wporg&utm_campaign=edd-plugin&utm_term=description)
- [How to Create a Digital Product That Sells](https://easydigitaldownloads.com/blog/how-to-create-a-digital-product-that-sells/?utm_medium=readme&utm_source=wporg&utm_campaign=edd-plugin&utm_content=lead-magnets&utm_term=description)
- [Easy Digital Downloads Free vs Paid: Which Is Right for You?](https://easydigitaldownloads.com/blog/edd-free-vs-paid/?utm_medium=readme&utm_source=wporg&utm_campaign=edd-plugin&utm_content=launch&utm_term=description)
- [The 12 Most Popular Digital Products You Can Sell Online](https://easydigitaldownloads.com/blog/the-12-most-popular-digital-products-you-can-sell-online/?utm_medium=readme&utm_source=wporg&utm_campaign=edd-plugin&utm_term=description)
- [How to Add Lead Magnets in WordPress to Grow Your Email List](https://easydigitaldownloads.com/blog/how-to-add-lead-magnets-in-wordpress-to-grow-your-email-list/?utm_medium=readme&utm_source=wporg&utm_campaign=edd-plugin&utm_content=lead-magnets&utm_term=description)
- [How to Successfully Launch Your Digital Product](https://easydigitaldownloads.com/blog/how-to-successfully-launch-your-digital-product/?utm_medium=readme&utm_source=wporg&utm_campaign=edd-plugin&utm_content=launch&utm_term=description)
- [How to Sell Canva Templates with WordPress](https://easydigitaldownloads.com/blog/how-to-sell-canva-templates-with-wordpress/?utm_medium=readme&utm_source=wporg&utm_campaign=edd-plugin&utm_content=canva&utm_term=description)
- [How to Create Manual eCommerce Orders in WordPress](https://easydigitaldownloads.com/blog/how-to-create-manual-ecommerce-orders-in-wordpress/?utm_medium=readme&utm_source=wporg&utm_campaign=edd-plugin&utm_content=benefits&utm_term=description)
- [The Astonishing Benefits of Selling Digital Products](https://easydigitaldownloads.com/blog/top-10-benefits-selling-digital-products/?utm_medium=readme&utm_source=wporg&utm_campaign=edd-plugin&utm_content=benefits&utm_term=description)
**[Subscribe to our newsletter](https://easydigitaldownloads.com/subscribe/?utm_medium=readme&utm_source=wporg&utm_campaign=lite-plugin&utm_content=subscribe&utm_term=description)** to get posts like these in your inbox as soon as they're published.
=== Contribute to Easy Digital Downloads ===
= Contribute to Easy Digital Downloads =
Development for this plugin happens in a public GitHub repository to better facilitate contributions from an active community of users, developers and designers. If you have a suggestion, bug report, patch, or pull request, please feel free to [contribute here](https://github.com/easydigitaldownloads/easy-digital-downloads) or to our [code snippet library](https://github.com/easydigitaldownloads/library).
=== 💰 Looking to change e-Commerce solutions? ===
= Looking to change e-Commerce solutions? =
Easy Digital Downloads is the perfect digital e-Commerce alternative to Etsy, Gumroad, WooCommerce, Shopfiy, BigCommerce, or Wix.
@ -253,12 +237,28 @@ Yes, through the use of our commercial addon called [Recurring Payments](https:/
8. Checkout - Default Theme
== Changelog ==
= 3.1.0.4, January 5, 2023 =
* Improvement: The cart performance has been improved by disabling tax rate lookups when a customer address is supplied, but taxes are disabled.
* Improvement: Reports now factor in order item adjustments like negative fees.
* Improvement: The Downloads list table now loads faster on sites with a large number of download categories.
* Improvement: The Sales REST API Endpoint was updated to respect the new order statuses in EDD 3.0.
* Improvement: Improve semantic markup by not allowing two of the same ID attribute for redirect to checkout buttons.
* Improvement: Discount code lookup performance has been improved when looking up a discount by the discount code.
* Fix: Looking up payments with a Download ID that was a string or integer would return incorrect or no orders.
* Fix: Reduced the number of HTTP calls that the notifications API can make per day.
* Fix: Avoid the "Request-URI Too Long" error when performing multiple actions in a row on list tables.
* Fix: Negative fee amounts are now properly considered when determining an item's final price in the cart.
* Fix: PayPal: Arabic characters in download names could cause an unexpected error during checkout.
* Fix: PayPal: The backup IPN is not loaded if the PayPal integration is not connected.
* Fix: PayPal: The payment_date field was assumed to be present when processing the backup IPN.
* Fix: The edd_get_payment_amount function could improperly return an empty string instead of a float value.
* Fix: Add to cart buttons were not using the token values to improve caching compatibility.
* Fix: When many items were purchased at once, some order item download links were not working due to an improper status.
* Fix: Hardened the search query for the AJAX endpoint for searching for downloads by title.
= 3.1.0.3, November 23, 2022 =
* Improvement: The PayPal webhook connect/disconnect process has been improved to ensure proper webhook delivery.
* Improvement: An IPN Backup has been added to the PayPal gateway, to avoid missing webhooks that may have been disconnected.
* 📘 Read more on [how to configure the IPN Backup](https://easydigitaldownloads.com/docs/paypal-setup/#ipn).
* Fix: PayPal Buy Now functionality has been fixed for guest users.
* Fix: Exporting orders was including orders with the Trash status.
* Fix: Trashed orders were showing in the purchase history shortcode (templates updated: history-downloads.php and history-purchases.php).
@ -652,4 +652,4 @@ It is not intended to resolve the issue within PayPal's API, but offer us some t
* Dev: The edd_download_price_table_head and edd_download_price_table_row actions have been formally deprecated.
== Upgrade Notice ==
IMPORTANT: Easy Digital Downloads 3.0 is a major release that includes many improvements and changes. You will be asked to perform database maintenance once installed. Please ensure you make a backup of your site prior to upgrading. Your site should remain functional during this maintenance, but as with all updates, it is best to make a backup of your site prior to updating.
IMPORTANT: Easy Digital Downloads 3.0 is a major release that includes many improvements and changes. You will be asked to perform database maintenance once installed. Please ensure you make a backup of your site prior to upgrading. Your site should remain functional during this maintenance, but as with all updates, it is best to make a backup of your site prior to updating.

View File

@ -86,6 +86,7 @@ if( edd_get_option( 'uninstall_on_delete' ) ) {
'edd_use_php_sessions',
'edd_version',
'edd_version_upgraded_from',
'edd_notification_req_timeout',
// Widgets
'widget_edd_product_details',

View File

@ -0,0 +1,25 @@
<?php
// autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, $err);
} elseif (!headers_sent()) {
echo $err;
}
}
trigger_error(
$err,
E_USER_ERROR
);
}
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit0ed51d806e7376d5e18f6b1459e77951::getLoader();

View File

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

View File

@ -0,0 +1,352 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
/**
* This class is copied in every Composer installed project and available to all
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*
* @final
*/
class InstalledVersions
{
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
*/
private static $installed;
/**
* @var bool|null
*/
private static $canGetVendors;
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static $installedByVendor = array();
/**
* Returns a list of all package names which are present, either by being installed, replaced or provided
*
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}
/**
* Returns a list of all package names with a specific type e.g. 'library'
*
* @param string $type
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();
foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $package) {
if (isset($package['type']) && $package['type'] === $type) {
$packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/**
* Checks whether the given package is installed
*
* This also returns true if the package name is provided or replaced by another package
*
* @param string $packageName
* @param bool $includeDevRequirements
* @return bool
*/
public static function isInstalled($packageName, $includeDevRequirements = true)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
}
}
return false;
}
/**
* Checks whether the given package satisfies a version constraint
*
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
*
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @param string $packageName
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints($constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
/**
* Returns a version constraint representing all the range(s) which are installed for a given package
*
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
* whether a given version of a package is installed, and not just whether it exists
*
* @param string $packageName
* @return string Version constraint usable with composer/semver
*/
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
$ranges = array();
if (isset($installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['version'])) {
return null;
}
return $installed['versions'][$packageName]['version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['reference'])) {
return null;
}
return $installed['versions'][$packageName]['reference'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
*/
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @return array
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
*/
public static function getRootPackage()
{
$installed = self::getInstalled();
return $installed[0]['root'];
}
/**
* Returns the raw installed.php data for custom implementations
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
*/
public static function getRawData()
{
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
return self::$installed;
}
/**
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
public static function getAllRawData()
{
return self::getInstalled();
}
/**
* Lets you reload the static array from another file
*
* This is only useful for complex integrations in which a project needs to use
* this class but then also needs to execute another project's autoloader in process,
* and wants to ensure both projects have access to their version of installed.php.
*
* A typical case would be PHPUnit, where it would need to make sure it reads all
* the data it needs from this class, then call reload() with
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
* the project in which it runs can then also use this class safely, without
* interference between PHPUnit's dependencies and the project's dependencies.
*
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
}
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
}
$installed = array();
if (self::$canGetVendors) {
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
}
}
}
}
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = require __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
$installed[] = self::$installed;
return $installed;
}
}

View File

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

View File

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

View File

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

View File

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

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