installed plugin Easy Digital Downloads version 3.1.0.3

This commit is contained in:
2022-11-27 15:03:07 +00:00
committed by Gitium
parent 555673545b
commit c5dce2cec6
1200 changed files with 238970 additions and 0 deletions

View File

@ -0,0 +1,181 @@
<?php
/**
* Admin Add-ons
*
* @package EDD
* @subpackage Admin/Add-ons
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.0
*/
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;
/**
* Add-ons Page
*
* Renders the add-ons page content.
*
* @since 1.0
* @return void
*/
function edd_add_ons_page() {
// Filter the add-ons tabs.
$add_ons_tabs = apply_filters( 'edd_add_ons_tabs', array(
'popular' => __( 'Popular', 'easy-digital-downloads' ),
'new' => __( 'New', 'easy-digital-downloads' ),
'all' => __( 'All', 'easy-digital-downloads' )
) );
// Active tab.
$active_tab = isset( $_GET['tab'] ) && array_key_exists( $_GET['tab'], $add_ons_tabs )
? sanitize_key( $_GET['tab'] )
: 'popular';
// Empty tabs array.
$tabs = array();
// Loop through add-ons and make array of tabs.
foreach ( $add_ons_tabs as $tab_id => $tab_name ) {
// "All"
if ( 'all' === $tab_id ) {
$tab_url = edd_link_helper(
'https://easydigitaldownloads.com/downloads/',
array(
'utm_medium' => 'addons-page',
'utm_content' => 'all-extensions',
)
);
// All other tabs besides "All".
} else {
$tab_url = add_query_arg( array(
'settings-updated' => false,
'tab' => sanitize_key( $tab_id ),
) );
}
// Active?
$active = ( $active_tab === $tab_id )
? 'current'
: '';
// Count.
$count = ( 'all' === $tab_id )
? '150+'
: '29';
// The link.
$tab = '<li class="' . esc_attr( $tab_id ) . '">';
$tab .= ( 'all' === $tab_id )
? '<a href="' . esc_url( $tab_url ) . '" class="' . esc_attr( $active ) . '" target="_blank">'
: '<a href="' . esc_url( $tab_url ) . '" class="' . esc_attr( $active ) . '">';
$tab .= esc_html( $tab_name );
$tab .= ' <span class="count">(' . esc_html( $count ) . ')</span>';
// "All" is an external link, so denote it as such.
if ( 'all' === $tab_id ) {
$tab .= '<span class="dashicons dashicons-external"></span>';
}
$tab .= '</a>';
$tab .= '</li>';
// Set the tab.
$tabs[] = $tab;
}
// Start a buffer.
ob_start(); ?>
<div class="wrap" id="edd-add-ons">
<h1>
<?php _e( 'Apps and Integrations for Easy Digital Downloads', 'easy-digital-downloads' ); ?>
<span>
<?php
$url = edd_link_helper(
'https://easydigitaldownloads.com/downloads/',
array(
'utm_medium' => 'addons-page',
'utm_content' => 'browse-all',
)
);
?>
&nbsp;&nbsp;<a href="<?php echo $url; ?>" class="button button-primary" target="_blank"><?php esc_html_e( 'Browse All Integrations', 'easy-digital-downloads' ); ?></a>
</span>
</h1>
<p><?php _e( 'These <em><strong>add functionality</strong></em> to your Easy Digital Downloads powered store.', 'easy-digital-downloads' ); ?></p>
<ul class="subsubsub"><?php echo implode( ' | ', $tabs ); ?></ul>
<div class="edd-add-ons-container">
<?php
// Display all add ons.
echo wp_kses_post( edd_add_ons_get_feed( $active_tab ) );
?>
<div class="clear"></div>
</div>
<div class="edd-add-ons-footer">
<?php
$url = edd_link_helper(
'https://easydigitaldownloads.com/downloads/',
array(
'utm_medium' => 'addons-page',
'utm_content' => 'browse-all',
)
);
?>
<a href="<?php echo $url; ?>" class="button button-primary" target="_blank"><?php esc_html_e( 'Browse All Integrations', 'easy-digital-downloads' ); ?></a>
</div>
</div>
<?php
// Output the current buffer.
echo ob_get_clean();
}
/**
* Add-ons Get Feed
*
* Gets the add-ons page feed.
*
* @since 1.0
* @return void
*/
function edd_add_ons_get_feed( $tab = 'popular' ) {
// Transient.
$trans_key = 'easydigitaldownloads_add_ons_feed_' . $tab;
$cache = get_transient( $trans_key );
// No add ons, so reach out and get some.
if ( false === $cache ) {
$url = 'https://easydigitaldownloads.com/?feed=addons';
// Popular.
if ( 'popular' !== $tab ) {
$url = add_query_arg( array( 'display' => sanitize_key( $tab ) ), $url );
}
// Remote request.
$feed = wp_remote_get( esc_url_raw( $url ), array( 'sslverify' => false ) );
// Handle error.
if ( empty( $feed ) || is_wp_error( $feed ) ) {
$cache = '<div class="error"><p>' . __( 'These extensions could not be retrieved from the server. Please try again later.', 'easy-digital-downloads' ) . '</div>';
// Cache the results.
} elseif ( isset( $feed['body'] ) && strlen( $feed['body'] ) > 0 ) {
$cache = wp_remote_retrieve_body( $feed );
set_transient( $trans_key, $cache, HOUR_IN_SECONDS );
}
}
return $cache;
}

View File

@ -0,0 +1,106 @@
<?php
/**
* Adjustments
*
* These are functions used for displaying discounts, credits, fees, and more.
*
* @package EDD
* @subpackage Admin/Tools
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Shows the adjustments page, containing of all registered & visible adjustment
* types (Discounts|Credits|Fees)
*
* @since 3.0
* @author Daniel J Griffiths
*/
function edd_adjustments_page() {
// Get all tabs
$all_tabs = edd_get_adjustments_tabs();
// Current tab
$active_tab = isset( $_GET['tab'] )
? sanitize_key( $_GET['tab'] )
: 'discount';
// Add new URL
$add_new_url = edd_get_admin_url( array(
'page' => 'edd-discounts',
'edd-action' => 'add_' . sanitize_key( $active_tab ),
) );
// Start the output buffer
ob_start(); ?>
<div class="wrap">
<h1 class="wp-heading-inline"><?php esc_html_e( 'Discounts', 'easy-digital-downloads' ); ?></h1>
<a href="<?php echo esc_url( $add_new_url ); ?>" class="page-title-action"><?php esc_html_e( 'Add New', 'easy-digital-downloads' ); ?></a>
<hr class="wp-header-end">
<?php if ( 1 < count( $all_tabs ) ) : ?>
<nav class="nav-tab-wrapper edd-nav-tab-wrapper" aria-label="<?php esc_attr_e( 'Secondary menu', 'easy-digital-downloads' ); ?>">
<?php
// Loop through all tabs
foreach ( $all_tabs as $tab_id => $tab_name ) :
// Add the tab ID
$tab_url = edd_get_admin_url( array(
'page' => 'edd-discounts',
'tab' => sanitize_key( $tab_id ),
) );
// Remove messages
$tab_url = remove_query_arg( array(
'edd-message',
), $tab_url );
// Setup the selected class
$active = ( $active_tab === $tab_id )
? ' nav-tab-active'
: ''; ?>
<a href="<?php echo esc_url( $tab_url ); ?>" class="nav-tab<?php echo esc_attr( $active ); ?>"><?php echo esc_html( $tab_name ); ?></a>
<?php endforeach; ?>
</nav>
<br>
<?php endif; ?>
<?php do_action( 'edd_adjustments_page_' . esc_attr( $active_tab ) ); ?>
</div><!-- .wrap -->
<?php
// Output the current buffer
echo ob_get_clean();
}
/**
* Retrieve adjustments tabs.
*
* @since 3.0
*
* @return array Tabs for the 'Adjustments' page.
*/
function edd_get_adjustments_tabs() {
// Tabs
$tabs = array(
'discount' => __( 'Discounts', 'easy-digital-downloads' ),
// 'credit' => __( 'Credits', 'easy-digital-downloads' ),
// 'fee' => __( 'Fees', 'easy-digital-downloads' )
);
// Filter & return
return apply_filters( 'edd_adjustments_tabs', $tabs );
}

View File

@ -0,0 +1,272 @@
<?php
/**
* Admin Actions
*
* @package EDD
* @subpackage Admin/Actions
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Processes all EDD actions sent via POST and GET by looking for the 'edd-action'
* request and running do_action() to call the function
*
* @since 1.0
* @return void
*/
function edd_process_actions() {
if ( isset( $_POST['edd-action'] ) ) {
do_action( 'edd_' . $_POST['edd-action'], $_POST );
}
if ( isset( $_GET['edd-action'] ) ) {
do_action( 'edd_' . $_GET['edd-action'], $_GET );
}
}
add_action( 'admin_init', 'edd_process_actions' );
/**
* When the Download list table loads, call the function to view our tabs.
*
* @since 2.8.9
* @since 2.11.3 Unhooked this to revert to standard admin H1 tags.
* @since 3.0 Added back as download categories/tags have been removed from the admin menu.
* @param $views
*
* @return mixed
*/
function edd_products_tabs( $views ) {
edd_display_product_tabs();
return $views;
}
add_filter( 'views_edit-download', 'edd_products_tabs', 10, 1 );
/**
* When the Download list table loads, call the function to view our tabs.
*
* @since 3.0
*
* @return void
*/
function edd_taxonomies_tabs() {
// Bail if not viewing a taxonomy
if ( empty( $_GET['taxonomy'] ) ) {
return;
}
// Get taxonomies
$taxonomy = sanitize_key( $_GET['taxonomy'] );
$taxonomies = get_object_taxonomies( 'download' );
// Bail if current taxonomy is not a download taxonomy
if ( ! in_array( $taxonomy, $taxonomies, true ) ) {
return;
}
// Output the tabs
edd_display_product_tabs();
}
add_action( 'admin_notices', 'edd_taxonomies_tabs', 10, 1 );
/**
* Remove the top level taxonomy submenus.
*
* Since 3.0, these links were moved to horizontal tabs.
*
* @since 3.0
*/
function edd_admin_adjust_submenus() {
// Get taxonomies
$taxonomies = get_object_taxonomies( 'download' );
// Bail if no taxonomies
if ( empty( $taxonomies ) ) {
return;
}
// Loop through each taxonomy and remove the menu
foreach ( $taxonomies as $taxonomy ) {
remove_submenu_page( 'edit.php?post_type=download', 'edit-tags.php?taxonomy=' . $taxonomy . '&amp;post_type=download' );
}
}
add_action( 'admin_menu', 'edd_admin_adjust_submenus', 999 );
/**
* This tells WordPress to highlight the Downloads > Downloads submenu,
* regardless of which actual Downloads Taxonomy screen we are on.
*
* The conditional prevents the override when the user is viewing settings or
* any third-party plugins.
*
* @since 3.0.0
*
* @global string $submenu_file
*/
function edd_taxonomies_modify_menu_highlight() {
global $submenu_file;
// Bail if not viewing a taxonomy
if ( empty( $_GET['taxonomy'] ) ) {
return;
}
// Get taxonomies
$taxonomy = sanitize_key( $_GET['taxonomy'] );
$taxonomies = get_object_taxonomies( 'download' );
// Bail if current taxonomy is not a download taxonomy
if ( ! in_array( $taxonomy, $taxonomies, true ) ) {
return;
}
// Force the submenu file
$submenu_file = 'edit.php?post_type=download';
}
add_filter( 'admin_head', 'edd_taxonomies_modify_menu_highlight', 9999 );
/**
* This tells WordPress to highlight the Downloads > Downloads submenu when
* adding a new product.
*
* @since 3.0.0
*
* @global string $submenu_file
*/
function edd_add_new_modify_menu_highlight() {
global $submenu_file, $pagenow;
// Bail if not viewing the right page or post type
if ( empty( $_GET['post_type'] ) || ( 'post-new.php' !== $pagenow ) ) {
return;
}
// Get post_type
$post_type = sanitize_key( $_GET['post_type'] );
// Bail if current post type is not download
if ( 'download' !== $post_type ) {
return;
}
// Force the submenu file
$submenu_file = 'edit.php?post_type=download';
}
add_filter( 'admin_head', 'edd_add_new_modify_menu_highlight', 9999 );
/**
* Displays the product tabs for Products, Categories, and Tags
*
* @since 2.8.9
*/
function edd_display_product_tabs() {
// Initial tabs
$tabs = array(
'products' => array(
'name' => edd_get_label_plural(),
'url' => edd_get_admin_url(),
),
);
// Get taxonomies
$taxonomies = get_object_taxonomies( 'download', 'objects' );
foreach ( $taxonomies as $tax => $details ) {
$tabs[ $tax ] = array(
'name' => $details->labels->menu_name,
'url' => add_query_arg( array(
'taxonomy' => sanitize_key( $tax ),
'post_type' => 'download'
), admin_url( 'edit-tags.php' ) )
);
}
// Filter the tabs
$tabs = apply_filters( 'edd_add_ons_tabs', $tabs );
// Taxonomies
if ( isset( $_GET['taxonomy'] ) && in_array( $_GET['taxonomy'], array_keys( $taxonomies ), true ) ) {
$active_tab = $_GET['taxonomy'];
// Default to Products
} else {
$active_tab = 'products';
}
// Start a buffer
ob_start();
?>
<nav class="nav-tab-wrapper wp-clearfix" aria-label="<?php esc_attr_e( 'Secondary menu', 'easy-digital-downloads' ); ?>">
<?php
foreach ( $tabs as $tab_id => $tab ) {
$class = 'nav-tab';
if ( $active_tab === $tab_id ) {
$class .= ' nav-tab-active';
}
printf(
'<a href="%s" class="%s">%s</a>',
esc_url( $tab['url'] ),
esc_attr( $class ),
esc_html( $tab['name'] )
);
} ?>
</nav>
<br />
<?php
// Output the current buffer
echo ob_get_clean();
}
/**
* Return array of query arguments that should be removed from URLs.
*
* @since 3.0
*
* @return array
*/
function edd_admin_removable_query_args() {
return apply_filters( 'edd_admin_removable_query_args', array(
'edd-action',
'edd-notice',
'edd-message',
'edd-redirect'
) );
}
/**
* Output payment icons into the admin footer.
*
* Specifically on the "General" tab of the "Payment Gateways" admin page.
*
* @since 3.0
*/
function edd_admin_print_payment_icons() {
// Bail if not the gateways page
if ( ! edd_is_admin_page( 'settings', 'gateways' ) ) {
return;
}
// Output the SVG icons
edd_print_payment_icons( array(
'mastercard',
'visa',
'americanexpress',
'discover',
'paypal',
'amazon'
) );
}
add_action( 'admin_footer', 'edd_admin_print_payment_icons', 9999 );

View File

@ -0,0 +1,251 @@
<?php
/**
* Admin Bar
*
* @package EDD
* @subpackage Admin/Bar
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 3.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Maybe add the store status to the WordPress admin bar
*
* @since 3.0
*/
function edd_maybe_add_store_mode_admin_bar_menu( $wp_admin_bar ) {
// Bail if no admin bar.
if ( empty( $wp_admin_bar ) ) {
return;
}
// Bail if user cannot manage shop settings.
if ( ! current_user_can( 'manage_shop_settings' ) ) {
return;
}
$indicator = '';
$notifications = EDD()->notifications->countActiveNotifications();
if ( $notifications ) {
$count = $notifications < 10 ? $notifications : '!';
$indicator = ' <div class="wp-core-ui wp-ui-notification edd-menu-notification-counter">' . $count . '</div>';
}
// Add the menu
$wp_admin_bar->add_menu(
array(
'id' => 'edd-store-menu',
'title' => 'EDD' . $indicator,
'href' => edd_get_admin_url(
array(
'page' => 'edd-reports',
)
),
)
);
if ( ! empty( $count ) ) {
$wp_admin_bar->add_menu( array(
'parent' => 'edd-store-menu',
'id' => 'edd-store-notifications',
'title' => __( 'Notifications', 'easy-digital-downloads' ) . ' <div class="wp-core-ui wp-ui-notification edd-menu-notification-indicator"></div>',
'href' => edd_get_admin_url(
array(
'page' => 'edd-reports',
'notifications' => 'true',
)
),
) );
}
$wp_admin_bar->add_menu( array(
'parent' => 'edd-store-menu',
'id' => 'edd-store-reports',
'title' => __( 'Store Reports', 'easy-digital-downloads' ),
'href' => edd_get_admin_url(
array(
'page' => 'edd-reports',
)
),
) );
$wp_admin_bar->add_menu( array(
'parent' => 'edd-store-menu',
'id' => 'edd-store-settings',
'title' => __( 'Store Settings', 'easy-digital-downloads' ),
'href' => edd_get_admin_url(
array(
'page' => 'edd-settings',
)
),
) );
$wp_admin_bar->add_menu( array(
'parent' => 'edd-store-menu',
'id' => 'edd-store-prodcuts',
'title' => sprintf( __( 'All %1$s', 'easy-digital-downloads' ), edd_get_label_plural() ),
'href' => edd_get_admin_url(),
) );
// String.
$text = ! edd_is_test_mode()
? __( 'Live', 'easy-digital-downloads' )
: __( 'Test Mode', 'easy-digital-downloads' );
// Mode.
$mode = ! edd_is_test_mode()
? 'live'
: 'test';
$wp_admin_bar->add_menu( array(
'parent' => 'edd-store-menu',
'id' => 'edd-store-status',
'title' => sprintf( __( 'Store Status: %s', 'easy-digital-downloads' ), '<span class="edd-mode edd-mode-' . esc_attr( $mode ) . '">' . $text . '</span>' ),
'href' => edd_get_admin_url(
array(
'page' => 'edd-settings',
'tab' => 'gateways',
)
),
) );
$pass_manager = new \EDD\Admin\Pass_Manager();
if ( false === $pass_manager->has_pass() ) {
$url = edd_link_helper(
'https://easydigitaldownloads.com/lite-upgrade/',
array(
'utm_medium' => 'admin-bar',
'utm_content' => 'upgrade-to-pro',
)
);
$wp_admin_bar->add_menu( array(
'parent' => 'edd-store-menu',
'id' => 'edd-upgrade',
'title' => esc_html__( 'Upgrade to Pro', 'easy-digital-downloads' ),
'href' => $url,
'meta' => array(
'target' => '_blank',
'rel' => 'noopener noreferrer',
),
) );
}
// Is development environment?
$is_dev = edd_is_dev_environment();
if ( ! empty( $is_dev ) ) {
$wp_admin_bar->add_menu( array(
'id' => 'edd-is-dev',
'title' => sprintf( __( 'Development Domain %s', 'easy-digital-downloads' ), '<span class="edd-mode">' . $is_dev . '</span>' ),
'parent' => 'edd-store-menu',
'href' => edd_get_admin_url(
array(
'page' => 'edd-settings',
'tab' => 'gateways',
)
),
) );
}
}
add_action( 'admin_bar_menu', 'edd_maybe_add_store_mode_admin_bar_menu', 9999 );
/**
* Styling for text-mode button
*
* @since 3.0
*/
function edd_store_mode_admin_bar_print_link_styles() {
// Bail if user cannot manage shop settings
if ( ! current_user_can( 'manage_shop_settings' ) ) {
return;
} ?>
<style type="text/css" id="edd-store-menu-styling">
#wp-admin-bar-edd-store-status .edd-mode {
line-height: inherit;
}
#wp-admin-bar-edd-store-status .edd-mode-live {
color: #32CD32;
}
#wp-admin-bar-edd-store-menu .edd-mode-test {
color: #FF8C00;
}
#wpadminbar .edd-menu-notification-counter {
display: inline-block !important;
min-width: 18px !important;
height: 18px !important;
border-radius: 9px !important;
margin: 7px 0 0 2px !important;
vertical-align: top !important;
font-size: 11px !important;
line-height: 1.6 !important;
text-align: center !important;
}
#wpadminbar .edd-menu-notification-indicator {
float: right !important;
margin: 10px 0 0 !important;
width: 8px !important;
height: 8px !important;
border-radius: 4px !important;
}
#wpadminbar .edd-menu-notification-indicator:after {
display: block !important;
content: "";
position: absolute !important;
width: inherit !important;
height: inherit !important;
border-radius: inherit !important;
background-color: inherit !important;
animation: edd-menu-notification-indicator-pulse 1.5s infinite !important;
}
@keyframes edd-menu-notification-indicator-pulse {
0% {
transform: scale(1);
opacity: 1;
}
100% {
transform: scale(3);
opacity: 0;
}
}
#wpadminbar #wp-admin-bar-edd-upgrade a {
background-color: #00a32a;
color: #fff;
margin-top: 5px;
}
#wpadminbar #wp-admin-bar-edd-upgrade a:hover {
background-color: #008a20;
}
#wpadminbar .edd-menu-form-last {
border-bottom: 1px solid #3c4146 !important;
margin-bottom: 6px !important;
padding-bottom: 6px !important;
}
<?php if ( ! is_admin() ) : ?>
#wpadminbar .wp-ui-notification.edd-menu-notification-counter,
#wpadminbar .wp-ui-notification.edd-menu-notification-indicator {
color: #fff;
background-color: #d63638;
}
<?php endif; ?>
</style>
<?php
}
add_action( 'wp_print_styles', 'edd_store_mode_admin_bar_print_link_styles' );
add_action( 'admin_print_styles', 'edd_store_mode_admin_bar_print_link_styles' );

View File

@ -0,0 +1,75 @@
<?php
/**
* Admin Deprecated Functions
*
* All admin functions that have been deprecated.
*
* @package EDD
* @subpackage Deprecated
* @copyright Copyright (c) 2020, Sandhills Development, LLC
* @license https://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 3.0
*/
/**
* Display the ban emails tab
*
* @since 2.0
* @deprecated 3.0 replaced by Order Blocking in settings.
*/
function edd_tools_banned_emails_display() {
_edd_deprecated_function( __FUNCTION__, '3.0' );
if ( ! current_user_can( 'manage_shop_settings' ) ) {
return;
}
do_action( 'edd_tools_banned_emails_before' );
?>
<div class="postbox">
<h3><span><?php esc_html_e( 'Banned Emails', 'easy-digital-downloads' ); ?></span></h3>
<div class="inside">
<p><?php esc_html_e( 'Emails placed in the box below will not be allowed to make purchases.', 'easy-digital-downloads' ); ?></p>
<form method="post"
action="<?php echo esc_url( edd_get_admin_url( array( 'page' => 'edd-tools', 'tab' => 'general' ) ) ); ?>">
<p>
<textarea name="banned_emails" rows="10"
class="large-text"><?php echo esc_textarea( implode( "\n", edd_get_banned_emails() ) ); ?></textarea>
<span class="description"><?php esc_html_e( 'Enter emails and/or domains (starting with "@") and/or TLDs (starting with ".") to disallow, one per line.', 'easy-digital-downloads' ); ?></span>
</p>
<p>
<input type="hidden" name="edd_action" value="save_banned_emails"/>
<?php wp_nonce_field( 'edd_banned_emails_nonce', 'edd_banned_emails_nonce' ); ?>
<?php submit_button( __( 'Save', 'easy-digital-downloads' ), 'secondary', 'submit', false ); ?>
</p>
</form>
</div><!-- .inside -->
</div><!-- .postbox -->
<?php
do_action( 'edd_tools_banned_emails_after' );
do_action( 'edd_tools_after' );
}
/**
* Trigger a Purchase Deletion
*
* @since 1.3.4
* @deprecated 3.0 replaced by edd_trigger_destroy_order.
* @param array $data Arguments passed.
* @return void
*/
function edd_trigger_purchase_delete( $data ) {
if ( wp_verify_nonce( $data['_wpnonce'], 'edd_payment_nonce' ) ) {
$payment_id = absint( $data['purchase_id'] );
if ( ! current_user_can( 'delete_shop_payments', $payment_id ) ) {
wp_die( __( 'You do not have permission to edit this payment record', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
edd_delete_purchase( $payment_id );
edd_redirect( admin_url( 'edit.php?post_type=download&page=edd-payment-history&edd-message=payment_deleted' ) );
}
}
add_action( 'edd_delete_payment', 'edd_trigger_purchase_delete' );

View File

@ -0,0 +1,37 @@
<?php
/**
* Admin Footer
*
* @package EDD
* @subpackage Admin/Footer
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.0
*/
// Exit if accessed directly
if( !defined( 'ABSPATH' ) ) exit;
/**
* Add rating links to the admin dashboard
*
* @since 1.8.5
* @global string $typenow
* @param string $footer_text The existing footer text
* @return string
*/
function edd_admin_rate_us( $footer_text ) {
global $typenow;
if ( $typenow == 'download' ) {
$rate_text = sprintf( __( 'Thank you for using <a href="%1$s" target="_blank">Easy Digital Downloads</a>! Please <a href="%2$s" target="_blank">rate us on WordPress.org</a>', 'easy-digital-downloads' ),
'https://easydigitaldownloads.com',
'https://wordpress.org/support/plugin/easy-digital-downloads/reviews/?rate=5#new-post'
);
return str_replace( '</span>', '', $footer_text ) . ' | ' . $rate_text . '</span>';
} else {
return $footer_text;
}
}
add_filter( 'admin_footer_text', 'edd_admin_rate_us' );

View File

@ -0,0 +1,520 @@
<?php
/**
* Admin Pages
*
* @package EDD
* @subpackage Admin/Pages
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Get the admin pages.
*
* This largely exists for back-compat in edd_is_admin_page(). Maybe eventually
* we'll move away from globals for all of these, but who knows what add-ons are
* doing, so we're keeping these around until we can formally deprecate them.
*
* @since 3.0
*
* @global $edd_discounts_page $edd_discounts_page
* @global $edd_payments_page $edd_payments_page
* @global $edd_settings_page $edd_settings_page
* @global $edd_reports_page $edd_reports_page
* @global type $edd_system_info_page
* @global $edd_add_ons_page $edd_add_ons_page
* @global $edd_settings_export $edd_settings_export
* @global $edd_upgrades_screen $edd_upgrades_screen
* @global $edd_customers_page $edd_customers_page
* @global $edd_reports_page $edd_reports_page
*
* @return array
*/
function edd_get_admin_pages() {
global $edd_discounts_page,
$edd_payments_page,
$edd_settings_page,
$edd_reports_page,
$edd_system_info_page,
$edd_add_ons_page,
$edd_settings_export,
$edd_upgrades_screen,
$edd_customers_page,
$edd_reports_page;
// Filter & return
return (array) apply_filters( 'edd_admin_pages', array(
$edd_discounts_page,
$edd_payments_page,
$edd_settings_page,
$edd_reports_page,
$edd_system_info_page,
$edd_add_ons_page,
$edd_settings_export,
$edd_upgrades_screen,
$edd_customers_page,
$edd_reports_page
) );
}
/**
* Creates the admin submenu pages under the Downloads menu and assigns their
* links to global variables
*
* @since 1.0
*
* @global $edd_discounts_page
* @global $edd_payments_page
* @global $edd_customers_page
* @global $edd_settings_page
* @global $edd_reports_page
* @global $edd_add_ons_page
* @global $edd_settings_export
* @global $edd_upgrades_screen
*/
function edd_add_options_link() {
global $submenu, $edd_discounts_page, $edd_payments_page, $edd_settings_page, $edd_reports_page, $edd_upgrades_screen, $edd_tools_page, $edd_customers_page;
// Filter the "View Customers" role
$customer_view_role = apply_filters( 'edd_view_customers_role', 'view_shop_reports' );
// Setup pages
$edd_payments_page = add_submenu_page( 'edit.php?post_type=download', __( 'Orders', 'easy-digital-downloads' ), __( 'Orders', 'easy-digital-downloads' ), 'edit_shop_payments', 'edd-payment-history', 'edd_payment_history_page' );
$edd_customers_page = add_submenu_page( 'edit.php?post_type=download', __( 'Customers', 'easy-digital-downloads' ), __( 'Customers', 'easy-digital-downloads' ), $customer_view_role, 'edd-customers', 'edd_customers_page' );
$edd_discounts_page = add_submenu_page( 'edit.php?post_type=download', __( 'Discounts', 'easy-digital-downloads' ), __( 'Discounts', 'easy-digital-downloads' ), 'manage_shop_discounts', 'edd-discounts', 'edd_discounts_page' );
$edd_reports_page = add_submenu_page( 'edit.php?post_type=download', __( 'Reports', 'easy-digital-downloads' ), __( 'Reports', 'easy-digital-downloads' ), 'view_shop_reports', 'edd-reports', 'edd_reports_page' );
$edd_settings_page = add_submenu_page( 'edit.php?post_type=download', __( 'EDD Settings', 'easy-digital-downloads' ), __( 'Settings', 'easy-digital-downloads' ), 'manage_shop_settings', 'edd-settings', 'edd_options_page' );
$edd_tools_page = add_submenu_page( 'edit.php?post_type=download', __( 'EDD Tools', 'easy-digital-downloads' ), __( 'Tools', 'easy-digital-downloads' ), 'manage_shop_settings', 'edd-tools', 'edd_tools_page' );
// Setup hidden upgrades page
$edd_upgrades_screen = add_submenu_page( null, __( 'EDD Upgrades', 'easy-digital-downloads' ), __( 'EDD Upgrades', 'easy-digital-downloads' ), 'manage_shop_settings', 'edd-upgrades', 'edd_upgrades_screen' );
// Add our reports link in the main Dashboard menu.
$submenu['index.php'][] = array(
__( 'Store Reports', 'easy-digital-downloads' ),
'view_shop_reports',
'edit.php?post_type=download&page=edd-reports',
);
}
add_action( 'admin_menu', 'edd_add_options_link', 10 );
/**
* Create the Extensions submenu page under the "Downloads" menu
*
* @since 3.0
*
* @global $edd_add_ons_page
*/
function edd_add_extentions_link() {
if ( ! current_user_can( 'manage_shop_settings' ) ) {
return;
}
global $submenu, $edd_add_ons_page;
$edd_add_ons_page = add_submenu_page( 'edit.php?post_type=download', __( 'EDD Extensions', 'easy-digital-downloads' ), __( 'Extensions', 'easy-digital-downloads' ), 'manage_shop_settings', 'edd-addons', 'edd_add_ons_page' );
$pass_manager = new \EDD\Admin\Pass_Manager();
if ( ! $pass_manager->has_pass() ) {
$submenu[ 'edit.php?post_type=download' ][] = array(
'<span class="edd-menu-highlight">' . esc_html__( 'Upgrade to Pro', 'easy-digital-downloads' ) . '</span>',
'manage_shop_settings',
edd_link_helper(
'https://easydigitaldownloads.com/lite-upgrade',
array(
'utm_medium' => 'admin-menu',
'utm_content' => 'upgrade-to-pro',
)
)
);
add_action( 'admin_print_styles', function() {
?>
<style>#menu-posts-download li:last-child {background-color: #1da867;}#menu-posts-download li:last-child a,#menu-posts-download li:last-child a:hover{color: #FFFFFF !important;font-weight: 600;}</style>
<?php
} );
}
}
add_action( 'admin_menu', 'edd_add_extentions_link', 99999 );
/**
* Whether the current admin area page is one that allows the insertion of a
* button to make inserting Downloads easier.
*
* @since 3.0
* @global $pagenow $pagenow
* @global $typenow $typenow
* @return boolean
*/
function edd_is_insertable_admin_page() {
global $pagenow, $typenow;
// Allowed pages
$pages = array(
'post.php',
'page.php',
'post-new.php',
'post-edit.php'
);
// Allowed post types
$types = get_post_types_by_support( 'edd_insert_download' );
// Return if page and type are allowed
return in_array( $pagenow, $pages, true ) && in_array( $typenow, $types, true );
}
/**
* Determines whether the current admin page is a specific EDD admin page.
*
* Only works after the `wp_loaded` hook, & most effective
* starting on `admin_menu` hook. Failure to pass in $view will match all views of $passed_page.
* Failure to pass in $passed_page will return true if on any EDD page
*
* @since 1.9.6
* @since 2.11.3 Added `$include_non_exclusive` parameter.
*
* @param string $passed_page Optional. Main page's slug.
* @param string $passed_view Optional. Page view ( ex: `edit` or `delete` )
* @param bool $include_non_exclusive Optional. If we should consider pages not exclusive to EDD.
* Includes the main dashboard page and custom post types that
* support the "Insert Download" button via the TinyMCE editor.
*
* @return bool True if EDD admin page we're looking for or an EDD page or if $page is empty, any EDD page
*/
function edd_is_admin_page( $passed_page = '', $passed_view = '', $include_non_exclusive = true ) {
global $pagenow, $typenow;
$found = false;
$post_type = isset( $_GET['post_type'] ) ? strtolower( $_GET['post_type'] ) : false;
$action = isset( $_GET['action'] ) ? strtolower( $_GET['action'] ) : false;
$taxonomy = isset( $_GET['taxonomy'] ) ? strtolower( $_GET['taxonomy'] ) : false;
$page = isset( $_GET['page'] ) ? strtolower( $_GET['page'] ) : false;
$view = isset( $_GET['view'] ) ? strtolower( $_GET['view'] ) : false;
$edd_action = isset( $_GET['edd-action'] ) ? strtolower( $_GET['edd-action'] ) : false;
$tab = isset( $_GET['tab'] ) ? strtolower( $_GET['tab'] ) : false;
switch ( $passed_page ) {
case 'download':
switch ( $passed_view ) {
case 'list-table':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' ) {
$found = true;
}
break;
case 'edit':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'post.php' ) {
$found = true;
}
break;
case 'new':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'post-new.php' ) {
$found = true;
}
break;
default:
if ( ( 'download' === $typenow || 'download' === $post_type ) || 'download' === $post_type || ( 'post-new.php' === $pagenow && 'download' === $post_type ) ) {
$found = true;
}
break;
}
break;
case 'categories':
switch ( $passed_view ) {
case 'list-table':
case 'new':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit-tags.php' && 'edit' !== $action && 'download_category' === $taxonomy ) {
$found = true;
}
break;
case 'edit':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit-tags.php' && 'edit' === $action && 'download_category' === $taxonomy ) {
$found = true;
}
break;
default:
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit-tags.php' && 'download_category' === $taxonomy ) {
$found = true;
}
break;
}
break;
case 'tags':
switch ( $passed_view ) {
case 'list-table':
case 'new':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit-tags.php' && 'edit' !== $action && 'download_tax' === $taxonomy ) {
$found = true;
}
break;
case 'edit':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit-tags.php' && 'edit' === $action && 'download_tax' === $taxonomy ) {
$found = true;
}
break;
default:
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit-tags.php' && 'download_tax' === $taxonomy ) {
$found = true;
}
break;
}
break;
case 'payments':
switch ( $passed_view ) {
case 'list-table':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-payment-history' === $page && false === $view ) {
$found = true;
}
break;
case 'edit':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-payment-history' === $page && 'view-order-details' === $view ) {
$found = true;
}
break;
default:
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-payment-history' === $page ) {
$found = true;
}
break;
}
break;
case 'discounts':
switch ( $passed_view ) {
case 'list-table':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-discounts' === $page && false === $edd_action ) {
$found = true;
}
break;
case 'edit':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-discounts' === $page && 'edit_discount' === $edd_action ) {
$found = true;
}
break;
case 'new':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-discounts' === $page && 'add_discount' === $edd_action ) {
$found = true;
}
break;
default:
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-discounts' === $page ) {
$found = true;
}
break;
}
break;
case 'reports':
switch ( $passed_view ) {
// If you want to do something like enqueue a script on a particular report's duration, look at $_GET[ 'range' ]
case 'earnings':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-reports' === $page && ( 'earnings' === $view || '-1' === $view || false === $view ) ) {
$found = true;
}
break;
case 'downloads':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-reports' === $page && 'downloads' === $view ) {
$found = true;
}
break;
case 'customers':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-reports' === $page && 'customers' === $view ) {
$found = true;
}
break;
case 'gateways':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-reports' === $page && 'gateways' === $view ) {
$found = true;
}
break;
case 'taxes':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-reports' === $page && 'taxes' === $view ) {
$found = true;
}
break;
case 'export':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-reports' === $page && 'export' === $view ) {
$found = true;
}
break;
default:
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-reports' === $page ) {
$found = true;
}
break;
}
break;
case 'settings':
switch ( $passed_view ) {
case 'general':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-settings' === $page && ( 'general' === $tab || false === $tab ) ) {
$found = true;
}
break;
case 'gateways':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-settings' === $page && 'gateways' === $tab ) {
$found = true;
}
break;
case 'emails':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-settings' === $page && 'emails' === $tab ) {
$found = true;
}
break;
case 'styles':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-settings' === $page && 'styles' === $tab ) {
$found = true;
}
break;
case 'taxes':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-settings' === $page && 'taxes' === $tab ) {
$found = true;
}
break;
case 'extensions':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-settings' === $page && 'extensions' === $tab ) {
$found = true;
}
break;
case 'licenses':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-settings' === $page && 'licenses' === $tab ) {
$found = true;
}
break;
case 'misc':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-settings' === $page && 'misc' === $tab ) {
$found = true;
}
break;
case 'marketing':
if ( ( 'download' == $typenow || 'download' === $post_type ) && $pagenow == 'edit.php' && 'edd-settings' === $page && 'marketing' === $tab ) {
$found = true;
}
break;
default:
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-settings' === $page ) {
$found = true;
}
break;
}
break;
case 'tools':
switch ( $passed_view ) {
case 'general':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-tools' === $page && ( 'general' === $tab || false === $tab ) ) {
$found = true;
}
break;
case 'api_keys':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-tools' === $page && 'api_keys' === $tab ) {
$found = true;
}
break;
case 'system_info':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-tools' === $page && 'system_info' === $tab ) {
$found = true;
}
break;
case 'logs':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-tools' === $page && 'logs' === $tab ) {
$found = true;
}
break;
case 'import_export':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-tools' === $page && 'import_export' === $tab ) {
$found = true;
}
break;
default:
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-tools' === $page ) {
$found = true;
}
break;
}
break;
case 'addons':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-addons' === $page ) {
$found = true;
}
break;
case 'customers':
switch ( $passed_view ) {
case 'list-table':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-customers' === $page && false === $view ) {
$found = true;
}
break;
case 'overview':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-customers' === $page && 'overview' === $view ) {
$found = true;
}
break;
case 'notes':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-customers' === $page && 'notes' === $view ) {
$found = true;
}
break;
default:
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-customers' === $page ) {
$found = true;
}
break;
}
break;
case 'reports':
if ( ( 'download' === $typenow || 'download' === $post_type ) && $pagenow === 'edit.php' && 'edd-reports' === $page ) {
$found = true;
}
break;
case 'index.php' :
if ( 'index.php' === $pagenow ) {
$found = true;
}
break;
default:
$admin_pages = edd_get_admin_pages();
// Downloads sub-page or Dashboard page
if ( ( 'download' === $typenow ) || ( $include_non_exclusive && 'index.php' === $pagenow ) ) {
$found = true;
// Registered global pages
} elseif ( in_array( $pagenow, $admin_pages, true ) ) {
$found = true;
// Supported post types
} elseif ( $include_non_exclusive && edd_is_insertable_admin_page() ) {
$found = true;
// The EDD settings screen (fallback if mislinked)
} elseif ( 'edd-settings' === $page ) {
$found = true;
}
break;
}
return (bool) apply_filters( 'edd_is_admin_page', $found, $page, $view, $passed_page, $passed_view );
}
/**
* Forces the Cache-Control header on our admin pages to send the no-store header
* which prevents the back-forward cache (bfcache) from storing a copy of this page in local
* cache. This helps make sure that page elements modified via AJAX and DOM manipulations aren't
* incorrectly shown as if they never changed.
*
* @since 3.0
* @param array $headers An array of nocache headers.
*
* @return array
*/
function _edd_bfcache_buster( $headers ) {
if ( ! is_admin() & ! edd_is_admin_page() ) {
return $headers;
}
$headers['Cache-Control'] = 'no-cache, must-revalidate, max-age=0, no-store';
return $headers;
}
add_filter( 'nocache_headers', '_edd_bfcache_buster', 10, 1 );

View File

@ -0,0 +1,294 @@
<?php
/**
* API Key Table Class
*
* @package EDD
* @subpackage Admin/Tools/APIKeys
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
// Load WP_List_Table if not loaded
if ( ! class_exists( 'WP_List_Table' ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}
/**
* EDD_API_Keys_Table Class
*
* Renders the API Keys table
*
* @since 2.0
*/
class EDD_API_Keys_Table extends WP_List_Table {
/**
* Get things started
*
* @since 1.5
* @see WP_List_Table::__construct()
*/
public function __construct() {
parent::__construct( array(
'singular' => __( 'API Key', 'easy-digital-downloads' ),
'plural' => __( 'API Keys', 'easy-digital-downloads' ),
'ajax' => false
) );
$this->query();
}
/**
* Gets the name of the primary column.
*
* @since 2.5
* @access protected
*
* @return string Name of the primary column.
*/
protected function get_primary_column_name() {
return 'user';
}
/**
* This function renders most of the columns in the list table.
*
* @since 2.0
*
* @param array $item Contains all the data of the keys
* @param string $column_name The name of the column
*
* @return string Column Name
*/
public function column_default( $item, $column_name ) {
return $item[ $column_name ];
}
/**
* Displays the public key rows
*
* @since 2.4
*
* @param array $item Contains all the data of the keys
* @param string $column_name The name of the column
*
* @return string Column Name
*/
public function column_key( $item ) {
return '<input readonly="readonly" type="text" class="code" value="' . esc_attr( $item[ 'key' ] ) . '"/>';
}
/**
* Displays the token rows
*
* @since 2.4
*
* @param array $item Contains all the data of the keys
* @param string $column_name The name of the column
*
* @return string Column Name
*/
public function column_token( $item ) {
return '<input readonly="readonly" type="text" class="code" value="' . esc_attr( $item[ 'token' ] ) . '"/>';
}
/**
* Displays the secret key rows
*
* @since 2.4
*
* @param array $item Contains all the data of the keys
* @param string $column_name The name of the column
*
* @return string Column Name
*/
public function column_secret( $item ) {
return '<input readonly="readonly" type="text" class="code" value="' . esc_attr( $item[ 'secret' ] ) . '"/>';
}
/**
* Renders the column for the user field
*
* @since 2.0
* @return void
*/
public function column_user( $item ) {
$actions = array();
if ( apply_filters( 'edd_api_log_requests', true ) ) {
$actions['view'] = sprintf(
'<a href="%s">%s</a>',
esc_url( edd_get_admin_url( array( 'view' => 'api_requests', 'page' => 'edd-tools', 'tab' => 'logs', 's' => rawurlencode( $item['email'] ) ) ) ),
__( 'View Log', 'easy-digital-downloads' )
);
}
$actions['reissue'] = sprintf(
'<a href="%s" class="edd-regenerate-api-key">%s</a>',
esc_url( wp_nonce_url( add_query_arg( array( 'user_id' => absint( $item['id'] ), 'edd_action' => 'process_api_key', 'edd_api_process' => 'regenerate' ) ), 'edd-api-nonce' ) ),
__( 'Reissue', 'easy-digital-downloads' )
);
$actions['revoke'] = sprintf(
'<a href="%s" class="edd-revoke-api-key edd-delete">%s</a>',
esc_url( wp_nonce_url( add_query_arg( array( 'user_id' => absint( $item['id'] ), 'edd_action' => 'process_api_key', 'edd_api_process' => 'revoke' ) ), 'edd-api-nonce' ) ),
__( 'Revoke', 'easy-digital-downloads' )
);
$actions = apply_filters( 'edd_api_row_actions', array_filter( $actions ) );
return sprintf( '%1$s %2$s', $item['user'], $this->row_actions( $actions ) );
}
/**
* Retrieve the table columns
*
* @since 2.0
* @return array $columns Array of all the list table columns
*/
public function get_columns() {
return array(
'user' => __( 'Username', 'easy-digital-downloads' ),
'key' => __( 'Public Key', 'easy-digital-downloads' ),
'token' => __( 'Token', 'easy-digital-downloads' ),
'secret' => __( 'Secret Key', 'easy-digital-downloads' )
);
}
/**
* Display the key generation form
*
* @since 1.5
* @return void
*/
public function bulk_actions( $which = '' ) {
static $edd_api_is_bottom = false;
if ( true === $edd_api_is_bottom ) {
return;
}
if ( 'top' !== $which ) {
return;
}
$edd_api_is_bottom = true; ?>
<form id="api-key-generate-form" method="post" action="<?php echo esc_url( edd_get_admin_url( array( 'page' => 'edd-tools', 'tab' => 'api_keys' ) ) ); ?>">
<input type="hidden" name="edd_action" value="process_api_key" />
<input type="hidden" name="edd_api_process" value="generate" />
<?php wp_nonce_field( 'edd-api-nonce' ); ?>
<?php echo EDD()->html->ajax_user_search(); ?>
<?php submit_button( __( 'Generate New API Keys', 'easy-digital-downloads' ), 'secondary', 'submit', false ); ?>
</form>
<?php
}
/**
* Generate the table navigation above or below the table
*
* @since 3.1.0
* @access protected
* @param string $which
*/
protected function display_tablenav( $which ) {
if ( 'top' === $which ) {
wp_nonce_field( 'bulk-' . $this->_args['plural'] );
} ?>
<div class="tablenav <?php echo esc_attr( $which ); ?>">
<div class="alignleft actions bulkactions">
<?php $this->bulk_actions( $which ); ?>
</div><?php
$this->extra_tablenav( $which );
$this->pagination( $which );
?><br class="clear" />
</div>
<?php
}
/**
* Performs the key query
*
* @since 2.0
* @return void
*/
public function query() {
$users = get_users( array(
'meta_value' => 'edd_user_secret_key',
'number' => $this->per_page,
'offset' => $this->per_page * ( $this->get_paged() - 1 )
) );
$keys = array();
foreach( $users as $user ) {
$keys[$user->ID]['id'] = $user->ID;
$keys[$user->ID]['email'] = $user->user_email;
$keys[$user->ID]['user'] = '<a href="' . esc_url( add_query_arg( 'user_id', urlencode( $user->ID ), 'user-edit.php' ) ) . '"><strong>' . esc_html( $user->user_login ) . '</strong></a>';
$keys[$user->ID]['key'] = EDD()->api->get_user_public_key( $user->ID );
$keys[$user->ID]['secret'] = EDD()->api->get_user_secret_key( $user->ID );
$keys[$user->ID]['token'] = EDD()->api->get_token( $user->ID );
}
return $keys;
}
/**
* Retrieve count of total users with keys
*
* @since 2.0
* @return int
*/
public function total_items() {
global $wpdb;
if ( ! get_transient( 'edd_total_api_keys' ) ) {
$total_items = $wpdb->get_var( "SELECT count(user_id) FROM {$wpdb->usermeta} WHERE meta_value='edd_user_secret_key'" );
set_transient( 'edd_total_api_keys', $total_items, 60 * 60 );
}
return get_transient( 'edd_total_api_keys' );
}
/**
* Setup the final data for the table
*
* @since 2.0
* @return void
*/
public function prepare_items() {
$this->_column_headers = array(
$this->get_columns(),
array(),
array(),
'user'
);
$total_items = $this->total_items();
$this->items = $this->query();
$per_page = ! empty( $this->per_page )
? $this->per_page
: 30;
$this->set_pagination_args( array(
'total_items' => $total_items,
'per_page' => $per_page,
'total_pages' => $total_items > 0
? ceil( $total_items / $per_page )
: 0
) );
}
}

View File

@ -0,0 +1,149 @@
<?php
/**
* Admin / Heartbeat
*
* @package EDD
* @subpackage Admin
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.8
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Heartbeart Class
*
* Hooks into the WP heartbeat API to update various parts of the dashboard as new sales are made
*
* Dashboard components that are effect:
* - Dashboard Summary Widget
*
* @since 1.8
*/
class EDD_Heartbeat {
/**
* Get things started
*
* @since 1.8
* @return void
*/
public static function init() {
add_filter( 'heartbeat_received', array( 'EDD_Heartbeat', 'heartbeat_received' ), 10, 2 );
add_action( 'admin_enqueue_scripts', array( 'EDD_Heartbeat', 'enqueue_scripts' ) );
}
/**
* Tie into the heartbeat and append our stats
*
* @since 1.8
* @return array
*/
public static function heartbeat_received( $response, $data ) {
if ( ! current_user_can( 'view_shop_reports' ) ) {
return $response; // Only modify heartbeat if current user can view show reports
}
// Make sure we only run our query if the edd_heartbeat key is present
if ( ( isset( $data['edd_heartbeat'] ) ) && ( 'dashboard_summary' === $data['edd_heartbeat'] ) ) {
$stats = edd_get_dashboard_sales_widget_data();
$response['edd-total-payments'] = $stats['total']['count'];
$response['edd-total-earnings'] = html_entity_decode( $stats['total']['earnings'] );
$response['edd-payments-month'] = $stats['this_month']['count'];
$response['edd-earnings-month'] = html_entity_decode( $stats['this_month']['earnings'] );
$response['edd-payments-today'] = $stats['today']['count'];
$response['edd-earnings-today'] = html_entity_decode( $stats['today']['earnings'] );
}
return $response;
}
/**
* Load the heartbeat scripts
*
* @since 1.8
* @return array
*/
public static function enqueue_scripts() {
if( ! current_user_can( 'view_shop_reports' ) ) {
return; // Only load heartbeat if current user can view show reports
}
// Make sure the JS part of the Heartbeat API is loaded.
wp_enqueue_script( 'heartbeat' );
add_action( 'admin_print_footer_scripts', array( 'EDD_Heartbeat', 'footer_js' ), 20 );
}
/**
* Inject our JS into the admin footer
*
* @since 1.8
* @return array
*/
public static function footer_js() {
global $pagenow;
// Only proceed if on the dashboard
if( 'index.php' != $pagenow ) {
return;
}
if( ! current_user_can( 'view_shop_reports' ) ) {
return; // Only load heartbeat if current user can view show reports
}
?>
<script>
(function($){
// Hook into the heartbeat-send
$(document).on('heartbeat-send', function(e, data) {
data['edd_heartbeat'] = 'dashboard_summary';
});
// Listen for the custom event "heartbeat-tick" on $(document).
$(document).on( 'heartbeat-tick', function(e, data) {
// Only proceed if our EDD data is present
if ( ! data['edd-total-payments'] )
return;
<?php if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) : ?>
console.log('tick');
<?php endif; ?>
// Update sale count and bold it to provide a highlight
edd_dashboard_heartbeat_update( '.edd_dashboard_widget .table_totals .b.b-earnings', data['edd-total-earnings'] );
edd_dashboard_heartbeat_update( '.edd_dashboard_widget .table_totals .b.b-sales', data['edd-total-payments'] );
edd_dashboard_heartbeat_update( '.edd_dashboard_widget .table_today .b.b-earnings', data['edd-earnings-today'] );
edd_dashboard_heartbeat_update( '.edd_dashboard_widget .table_today .b.b-sales', data['edd-payments-today'] );
edd_dashboard_heartbeat_update( '.edd_dashboard_widget .table_current_month .b-earnings', data['edd-earnings-month'] );
edd_dashboard_heartbeat_update( '.edd_dashboard_widget .table_current_month .b-sales', data['edd-payments-month'] );
// Return font-weight to normal after 2 seconds
setTimeout(function(){
$('.edd_dashboard_widget .b.b-sales,.edd_dashboard_widget .b.b-earnings').css( 'font-weight', 'normal' );
$('.edd_dashboard_widget .table_current_month .b.b-earnings,.edd_dashboard_widget .table_current_month .b.b-sales').css( 'font-weight', 'normal' );
}, 2000);
});
function edd_dashboard_heartbeat_update( selector, new_value ) {
var current_value = $(selector).text();
$(selector).text( new_value );
if ( current_value !== new_value ) {
$(selector).css( 'font-weight', 'bold' );
}
}
}(jQuery));
</script>
<?php
}
}
add_action( 'plugins_loaded', array( 'EDD_Heartbeat', 'init' ) );

View File

@ -0,0 +1,797 @@
<?php
/**
* Admin Notices Class
*
* @package EDD
* @subpackage Admin/Notices
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.3
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Notices Class
*
* @since 2.3
*/
class EDD_Notices {
/**
* @var array Array of notices to output to the current user
*/
private $notices = array();
/**
* Get things started
*
* @since 2.3
*/
public function __construct() {
add_action( 'edd_dismiss_notices', array( $this, 'dismiss_notices' ) );
add_action( 'admin_init', array( $this, 'add_notices' ), 20 );
add_action( 'admin_notices', array( $this, 'display_notices' ), 30 );
add_action( 'wp_ajax_edd_disable_debugging', array( $this, 'edd_disable_debugging' ) );
}
/**
* Add a notice to the notices array
*
* @since 3.0
*
* @param string $id Unique ID for each message
* @param string|WP_Error $message A message to be displayed or {@link WP_Error}
* @param string $class Optional. A class to be added to the message div
* @param bool $is_dismissible Optional. True to dismiss, false to persist
*
* @return void
*/
public function add_notice( $args = array() ) {
// Parse args
$r = wp_parse_args( $args, array(
'id' => '',
'message' => '',
'class' => false,
'is_dismissible' => true
) );
$default_class ='updated';
// One message as string
if ( is_string( $r['message'] ) ) {
$message = '<p>' . $this->esc_notice( $r['message'] ) . '</p>';
} elseif ( is_array( $r['message'] ) ) {
$message = '<p>' . implode( '</p><p>', array_map( array( $this, 'esc_notice' ), $r['message'] ) ) . '</p>';
// Messages as objects
} elseif ( is_wp_error( $r['message'] ) ) {
$default_class = 'is-error';
$errors = $r['message']->get_error_messages();
switch ( count( $errors ) ) {
case 0:
return false;
case 1:
$message = '<p>' . $this->esc_notice( $errors[0] ) . '</p>';
break;
default:
$escaped = array_map( array( $this, 'esc_notice' ), $errors );
$message = '<ul>' . "\n\t" . '<li>' . implode( '</li>' . "\n\t" . '<li>', $escaped ) . '</li>' . "\n" . '</ul>';
break;
}
// Message is an unknown format, so bail
} else {
return false;
}
// CSS Classes
$classes = ! empty( $r['class'] )
? array( $r['class'] )
: array( $default_class );
// Add dismissible class
if ( ! empty( $r['is_dismissible'] ) ) {
array_push( $classes, 'is-dismissible' );
}
// Assemble the message
$message = '<div class="notice ' . implode( ' ', array_map( 'sanitize_html_class', $classes ) ) . '">' . $message . '</div>';
$message = str_replace( "'", "\'", $message );
// Avoid malformed notices variable
if ( ! is_array( $this->notices ) ) {
$this->notices = array();
}
// Add notice to notices array
$this->notices[] = $message;
}
/**
* Add all admin area notices
*
* @since 3.0
*/
public function add_notices() {
// User can edit pages
if ( current_user_can( 'edit_pages' ) ) {
$this->add_page_notices();
}
// User can view shop reports
if ( current_user_can( 'view_shop_reports' ) ) {
$this->add_reports_notices();
}
// User can manage the entire shop
if ( current_user_can( 'manage_shop_settings' ) ) {
$this->add_system_notices();
$this->add_data_notices();
$this->add_settings_notices();
}
// Generic notices
if ( ! empty( $_REQUEST['edd-message'] ) ) {
$this->add_user_action_notices( $_REQUEST['edd-message'] );
}
}
/**
* Dismiss admin notices when dismiss links are clicked
*
* @since 2.3
*/
public function dismiss_notices() {
// Bail if no notices to dismiss
if ( empty( $_GET['edd_notice'] ) || empty( $_GET['_wpnonce'] ) ) {
return;
}
// Construct key we are dismissing
$key = sanitize_key( $_GET['edd_notice'] );
// Bail if sanitized notice is empty
if ( empty( $key ) ) {
return;
}
// Bail if nonce does not verify
if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'edd_notice_nonce' ) ) {
return;
}
// Dismiss notice
update_user_meta( get_current_user_id(), "_edd_{$key}_dismissed", 1 );
edd_redirect( remove_query_arg( array( 'edd_action', 'edd_notice', '_wpnonce' ) ) );
}
/**
* Output all admin area notices
*
* @since 2.6.0 bbPress (r6771)
*/
public function display_notices() {
$this->show_debugging_notice();
// Bail if no notices
if ( empty( $this->notices ) || ! is_array( $this->notices ) ) {
return;
}
// Start an output buffer
ob_start();
// Loop through notices, and add them to buffer
foreach ( $this->notices as $notice ) {
echo $notice;
}
// Output the current buffer
$notices = ob_get_clean();
// Only echo if not empty
if ( ! empty( $notices ) ) {
echo $notices;
}
}
/** Private Methods *******************************************************/
/**
* Notices about missing pages
*
* @since 3.0
*/
private function add_page_notices() {
// Checkout page is missing
$purchase_page = edd_get_option( 'purchase_page', '' );
if ( empty( $purchase_page ) || ( 'trash' === get_post_status( $purchase_page ) ) ) {
$this->add_notice( array(
'id' => 'edd-no-purchase-page',
'message' => sprintf( __( 'No checkout page is configured. Set one in <a href="%s">Settings</a>.', 'easy-digital-downloads' ), esc_url( edd_get_admin_url( array( 'page' => 'edd-settings', 'tab' => 'general', 'section' => 'pages' ) ) ) ),
'class' => 'error',
'is_dismissible' => false
) );
}
}
/**
* Notices about reports
*
* @since 3.0
*/
private function add_reports_notices() {
}
/**
* Notices for the entire shop
*
* @since 3.0
*/
private function add_system_notices() {
// Bail if not an EDD admin page
if ( ! edd_is_admin_page() || edd_is_dev_environment() || edd_is_admin_page( 'index.php' ) ) {
return;
}
// Bail if user cannot manage options
if ( ! current_user_can( 'manage_shop_settings' ) ) {
return;
}
// Bail if uploads directory is protected
if ( edd_is_uploads_url_protected() ) {
return;
}
// Get the upload directory
$upload_directory = edd_get_upload_dir();
// Running NGINX
$show_nginx_notice = apply_filters( 'edd_show_nginx_redirect_notice', true );
if ( $show_nginx_notice && ! empty( $GLOBALS['is_nginx'] ) && ! get_user_meta( get_current_user_id(), '_edd_nginx_redirect_dismissed', true ) ) {
$dismiss_notice_url = wp_nonce_url( add_query_arg( array(
'edd_action' => 'dismiss_notices',
'edd_notice' => 'nginx_redirect'
) ), 'edd_notice_nonce' );
$this->add_notice( array(
'id' => 'edd-nginx',
'class' => 'error',
'is_dismissible' => false,
'message' => array(
sprintf( __( 'The files in %s are not currently protected.', 'easy-digital-downloads' ), '<code>' . $upload_directory . '</code>' ),
__( 'To protect them, you must add this <a href="https://docs.easydigitaldownloads.com/article/682-protected-download-files-on-nginx">NGINX redirect rule</a>.', 'easy-digital-downloads' ),
sprintf( __( 'If you have already done this, or it does not apply to your site, you may permenently %s.', 'easy-digital-downloads' ), '<a href="' . esc_url( $dismiss_notice_url ) . '">' . __( 'dismiss this notice', 'easy-digital-downloads' ) . '</a>' )
)
) );
}
// Running Apache
if ( ! empty( $GLOBALS['is_apache'] ) && ! edd_htaccess_exists() && ! get_user_meta( get_current_user_id(), '_edd_htaccess_missing_dismissed', true ) ) {
$dismiss_notice_url = wp_nonce_url( add_query_arg( array(
'edd_action' => 'dismiss_notices',
'edd_notice' => 'htaccess_missing'
) ), 'edd_notice_nonce' );
$this->add_notice( array(
'id' => 'edd-apache',
'class' => 'error',
'is_dismissible' => false,
'message' => array(
sprintf( __( 'The .htaccess file is missing from: %s', 'easy-digital-downloads' ), '<strong>' . $upload_directory . '</strong>' ),
sprintf( __( 'First, please resave the Misc settings tab a few times. If this warning continues to appear, create a file called ".htaccess" in the %s directory, and copy the following into it:', 'easy-digital-downloads' ), '<strong>' . $upload_directory . '</strong>' ),
sprintf( __( 'If you have already done this, or it does not apply to your site, you may permenently %s.', 'easy-digital-downloads' ), '<a href="' . esc_url( $dismiss_notice_url ) . '">' . __( 'dismiss this notice', 'easy-digital-downloads' ) . '</a>' ),
'<pre>' . edd_get_htaccess_rules() . '</pre>'
)
) );
}
}
/**
* Notices about data (migrations, etc...)
*
* @since 3.0
*/
private function add_data_notices() {
// Recount earnings
if ( class_exists( 'EDD_Recount_Earnings' ) ) {
$this->add_notice( array(
'id' => 'edd-recount-earnings',
'class' => 'error',
'is_dismissible' => false,
'message' => sprintf(
__( 'Easy Digital Downloads 2.5 contains a <a href="%s">built in recount tool</a>. Please <a href="%s">deactivate the Easy Digital Downloads - Recount Earnings plugin</a>', 'easy-digital-downloads' ),
esc_url( edd_get_admin_url( array( 'page' => 'edd-tools', 'tab' => 'general' ) ) ),
esc_url( admin_url( 'plugins.php' ) )
)
) );
}
}
/**
* Adds a notice about the deprecated Default Rate for Taxes.
*
* @since 3.0
* @since 3.0.2 - We've found a way to add default tax rates. Leaving the method in case anyone (for some reason) is calling it.
*/
private function add_tax_rate_notice() {
// Default tax rate not detected.
if ( false === edd_get_option( 'tax_rate' ) ) {
return;
}
// On Rates page, settings notice is shown.
if ( ! empty( $_GET['page'] ) && 'edd-settings' === $_GET['page'] && ! empty( $_GET['section'] ) && 'rates' === $_GET['section'] ) {
return;
}
// URL to fix this
$url = edd_get_admin_url( array(
'page' => 'edd-settings',
'tab' => 'taxes',
'section' => 'rates'
) );
// Link
$link = '<a href="' . esc_url( $url ) . '" class="button button-secondary">' . __( 'Review Tax Rates', 'easy-digital-downloads' ) . '</a>';
// Add the notice
$this->add_notice( array(
'id' => 'edd-default-tax-rate',
'class' => 'error',
/* translators: Link to review existing tax rates. */
'message' => '<strong>' . __( 'A default tax rate was detected.', 'easy-digital-downloads' ) . '</strong></p><p>' . __( 'This setting is no longer used in this version of Easy Digital Downloads. Please confirm your regional tax rates are properly configured and update tax settings to remove this notice.', 'easy-digital-downloads' ) . '</p><p>' . $link,
'is_dismissible' => false
) );
}
/**
* Notices about settings (updating, etc...)
*
* @since 3.0
*/
private function add_settings_notices() {
// Settings area
if ( ! empty( $_GET['page'] ) && ( 'edd-settings' === $_GET['page'] ) ) {
// Settings updated
if ( ! empty( $_GET['settings-updated'] ) ) {
$this->add_notice( array(
'id' => 'edd-notices',
'message' => __( 'Settings updated.', 'easy-digital-downloads' )
) );
}
// No payment gateways are enabled
if ( ! edd_get_option( 'gateways' ) && edd_is_test_mode() ) {
// URL to fix this
$url = edd_get_admin_url(
array(
'page' => 'edd-settings',
'tab' => 'gateways',
)
);
// Link
$link = '<a href="' . esc_url( $url ) . '">' . __( 'Fix this', 'easy-digital-downloads' ) . '</a>';
// Add the notice
$this->add_notice( array(
'id' => 'edd-gateways',
'class' => 'error',
'message' => sprintf( __( 'No payment gateways are enabled. %s.', 'easy-digital-downloads' ), $link ),
'is_dismissible' => false
) );
}
}
}
/**
* Notices about actions that the user has taken
*
* @since 3.0
*
* @param string $notice
*/
private function add_user_action_notices( $notice = '' ) {
// Sanitize notice key
$notice = sanitize_key( $notice );
// Bail if notice is empty
if ( empty( $notice ) ) {
return;
}
// Shop discounts errors
if ( current_user_can( 'manage_shop_discounts' ) ) {
switch ( $notice ) {
case 'discount_added' :
$this->add_notice( array(
'id' => 'edd-discount-added',
'message' => __( 'Discount code added.', 'easy-digital-downloads' )
) );
break;
case 'discount_add_failed' :
$this->add_notice( array(
'id' => 'edd-discount-add-fail',
'message' => __( 'There was a problem adding that discount code, please try again.', 'easy-digital-downloads' ),
'class' => 'error'
) );
break;
case 'discount_exists' :
$this->add_notice( array(
'id' => 'edd-discount-exists',
'message' => __( 'A discount with that code already exists, please use a different code.', 'easy-digital-downloads' ),
'class' => 'error'
) );
break;
case 'discount_updated' :
$this->add_notice( array(
'id' => 'edd-discount-updated',
'message' => __( 'Discount code updated.', 'easy-digital-downloads' )
) );
break;
case 'discount_not_changed' :
$this->add_notice( array(
'id' => 'edd-discount-not-changed',
'message' => __( 'No changes were made to that discount code.', 'easy-digital-downloads' )
) );
break;
case 'discount_update_failed' :
$this->add_notice( array(
'id' => 'edd-discount-updated-fail',
'message' => __( 'There was a problem updating that discount code, please try again.', 'easy-digital-downloads' ),
'class' => 'error'
) );
break;
case 'discount_validation_failed' :
$this->add_notice( array(
'id' => 'edd-discount-validation-fail',
'message' => __( 'The discount code could not be added because one or more of the required fields was empty, please try again.', 'easy-digital-downloads' ),
'class' => 'error'
) );
break;
case 'discount_invalid_code':
$this->add_notice( array(
'id' => 'edd-discount-invalid-code',
'message' => __( 'The discount code entered is invalid; only alphanumeric characters are allowed, please try again.', 'easy-digital-downloads' ),
'class' => 'error'
) );
break;
case 'discount_invalid_amount' :
$this->add_notice( array(
'id' => 'edd-discount-invalid-amount',
'message' => __( 'The discount amount must be a valid percentage or numeric flat amount. Please try again.', 'easy-digital-downloads' ),
'class' => 'error'
) );
break;
case 'discount_deleted':
$this->add_notice( array(
'id' => 'edd-discount-deleted',
'message' => __( 'Discount code deleted.', 'easy-digital-downloads' )
) );
break;
case 'discount_delete_failed':
$this->add_notice( array(
'id' => 'edd-discount-delete-fail',
'message' => __( 'There was a problem deleting that discount code, please try again.', 'easy-digital-downloads' ),
'class' => 'error'
) );
break;
case 'discount_activated':
$this->add_notice( array(
'id' => 'edd-discount-activated',
'message' => __( 'Discount code activated.', 'easy-digital-downloads' )
) );
break;
case 'discount_activation_failed':
$this->add_notice( array(
'id' => 'edd-discount-activation-fail',
'message' => __( 'There was a problem activating that discount code, please try again.', 'easy-digital-downloads' ),
'class' => 'error'
) );
break;
case 'discount_deactivated':
$this->add_notice( array(
'id' => 'edd-discount-deactivated',
'message' => __( 'Discount code deactivated.', 'easy-digital-downloads' )
) );
break;
case 'discount_deactivation_failed':
$this->add_notice( array(
'id' => 'edd-discount-deactivation-fail',
'message' => __( 'There was a problem deactivating that discount code, please try again.', 'easy-digital-downloads' ),
'class' => 'error'
) );
break;
}
}
// Shop reports errors
if ( current_user_can( 'view_shop_reports' ) ) {
switch( $notice ) {
case 'refreshed-reports' :
$this->add_notice( array(
'id' => 'edd-refreshed-reports',
'message' => __( 'The reports have been refreshed.', 'easy-digital-downloads' )
) );
break;
}
}
// Shop settings errors
if ( current_user_can( 'manage_shop_settings' ) ) {
switch( $notice ) {
case 'settings-imported' :
$this->add_notice( array(
'id' => 'edd-settings-imported',
'message' => __( 'The settings have been imported.', 'easy-digital-downloads' )
) );
break;
case 'api-key-generated' :
$this->add_notice( array(
'id' => 'edd-api-key-generated',
'message' => __( 'API keys successfully generated.', 'easy-digital-downloads' )
) );
break;
case 'api-key-exists' :
$this->add_notice( array(
'id' => 'edd-api-key-exists',
'message' => __( 'The specified user already has API keys.', 'easy-digital-downloads' ),
'class' => 'error'
) );
break;
case 'api-key-regenerated' :
$this->add_notice( array(
'id' => 'edd-api-key-regenerated',
'message' => __( 'API keys successfully regenerated.', 'easy-digital-downloads' )
) );
break;
case 'api-key-revoked' :
$this->add_notice( array(
'id' => 'edd-api-key-revoked',
'message' => __( 'API keys successfully revoked.', 'easy-digital-downloads' )
) );
break;
case 'test-purchase-email-sent':
$this->add_notice(
array(
'id' => 'edd-test-purchase-receipt-sent',
'message' => __( 'The test email was sent successfully.', 'easy-digital-downloads' )
)
);
break;
case 'test-summary-email-sent':
$this->add_notice(
array(
'id' => 'edd-test-summary-email-sent',
'message' => __( 'The test email summary was sent successfully.', 'easy-digital-downloads' )
)
);
break;
}
}
// Shop payments errors
if ( current_user_can( 'edit_shop_payments' ) ) {
switch( $notice ) {
case 'note-added' :
$this->add_notice( array(
'id' => 'edd-note-added',
'message' => __( 'The note has been added successfully.', 'easy-digital-downloads' )
) );
break;
case 'payment-updated' :
$this->add_notice( array(
'id' => 'edd-payment-updated',
'message' => __( 'The order has been updated successfully.', 'easy-digital-downloads' )
) );
break;
case 'order_added' :
$this->add_notice( array(
'id' => 'edd-order-added',
'message' => __( 'Order successfully created.', 'easy-digital-downloads' )
) );
break;
case 'order_trashed' :
$this->add_notice( array(
'id' => 'edd-order-trashed',
'message' => __( 'The order has been moved to the trash.', 'easy-digital-downloads' )
) );
break;
case 'order_restored' :
$this->add_notice( array(
'id' => 'edd-order-restored',
'message' => __( 'The order has been restored.', 'easy-digital-downloads' )
) );
break;
case 'payment_deleted' :
$this->add_notice( array(
'id' => 'edd-payment-deleted',
'message' => __( 'The order has been deleted.', 'easy-digital-downloads' )
) );
break;
case 'email_sent' :
$this->add_notice( array(
'id' => 'edd-payment-sent',
'message' => __( 'The purchase receipt has been resent.', 'easy-digital-downloads' )
) );
break;
case 'email_send_failed':
$this->add_notice( array(
'id' => 'edd-payment-sent',
'message' => __( 'Failed to send purchase receipt.', 'easy-digital-downloads' )
) );
break;
case 'payment-note-deleted' :
$this->add_notice( array(
'id' => 'edd-note-deleted',
'message' => __( 'The order note has been deleted.', 'easy-digital-downloads' )
) );
break;
}
}
// Customer Notices
if ( current_user_can( 'edit_shop_payments' ) ) {
switch( $notice ) {
case 'customer-deleted' :
$this->add_notice( array(
'id' => 'edd-customer-deleted',
'message' => __( 'Customer successfully deleted.', 'easy-digital-downloads' ),
) );
break;
case 'user-verified' :
$this->add_notice( array(
'id' => 'edd-user-verified',
'message' => __( 'User successfully verified.', 'easy-digital-downloads' ),
) );
break;
case 'email-added' :
$this->add_notice( array(
'id' => 'edd-customer-email-added',
'message' => __( 'Customer email added.', 'easy-digital-downloads' ),
) );
break;
case 'email-removed' :
$this->add_notice( array(
'id' => 'edd-customer-email-removed',
'message' => __( 'Customer email deleted.', 'easy-digital-downloads' ),
) );
break;
case 'email-remove-failed' :
$this->add_notice( array(
'id' => 'edd-customer-email-remove-failed',
'message' => __( 'Failed to delete customer email.', 'easy-digital-downloads' ),
'class' => 'error',
) );
break;
case 'primary-email-updated' :
$this->add_notice( array(
'id' => 'eddedd-customer-primary-email-updated',
'message' => __( 'Primary email updated for customer.', 'easy-digital-downloads' )
) );
break;
case 'primary-email-failed' :
$this->add_notice( array(
'id' => 'edd-customer-primary-email-failed',
'message' => __( 'Failed to set primary email.', 'easy-digital-downloads' ),
'class' => 'error',
) );
break;
case 'address-removed' :
$this->add_notice( array(
'id' => 'edd-customer-address-removed',
'message' => __( 'Customer address deleted.', 'easy-digital-downloads' )
) );
break;
case 'address-remove-failed' :
$this->add_notice( array(
'id' => 'edd-customer-address-remove-failed',
'message' => __( 'Failed to delete customer address.', 'easy-digital-downloads' ),
'class' => 'error',
) );
break;
}
}
}
/**
* Show a notice if debugging is enabled in the EDD settings.
* Does not show if only the `EDD_DEBUG_MODE` constant is defined.
*
* @since 2.11.5
* @return void
*/
private function show_debugging_notice() {
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
return;
}
if ( ! current_user_can( 'manage_shop_settings' ) ) {
return;
}
if ( ! edd_get_option( 'debug_mode', false ) ) {
return;
}
/**
* The notices JS needs to be output wherever the notice is displayed, not just EDD screens.
* If more notices add to the script then this enqueue will need to be moved.
*
* @since 3.0
*/
wp_enqueue_script( 'edd-admin-notices', EDD_PLUGIN_URL . 'assets/js/edd-admin-notices.js', array( 'jquery' ), EDD_VERSION, true );
$view_url = add_query_arg(
array(
'post_type' => 'download',
'page' => 'edd-tools',
'tab' => 'debug_log',
),
admin_url( 'edit.php' )
);
?>
<div id="edd-debug-log-notice" class="notice notice-warning">
<p>
<?php esc_html_e( 'Easy Digital Downloads debug logging is enabled. Please only leave it enabled for as long as it is needed for troubleshooting.', 'easy-digital-downloads' ); ?>
</p>
<p>
<a class="button button-secondary" href="<?php echo esc_url( $view_url ); ?>"><?php esc_html_e( 'View Debug Log', 'easy-digital-downloads' ); ?></a>
<button class="button button-primary" id="edd-disable-debug-log"><?php esc_html_e( 'Delete Log File and Disable Logging', 'easy-digital-downloads' ); ?></button>
<?php wp_nonce_field( 'edd_debug_log_delete', 'edd_debug_log_delete' ); ?>
</p>
</div>
<?php
}
/**
* Disables the debug log setting and deletes the existing log file.
*
* @since 2.11.5
* @return void
*/
public function edd_disable_debugging() {
$validate_nonce = ! empty( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], 'edd_debug_log_delete' );
if ( ! current_user_can( 'manage_shop_settings' ) || ! $validate_nonce ) {
wp_send_json_error( wpautop( __( 'You do not have permission to perform this action.', 'easy-digital-downloads' ) ), 403 );
}
edd_update_option( 'debug_mode', false );
global $edd_logs;
$edd_logs->clear_log_file();
wp_send_json_success( wpautop( __( 'The debug log has been cleared and logging has been disabled.', 'easy-digital-downloads' ) ) );
}
/**
* Escape message string output
*
* @since 2.6.0 bbPress (r6775)
*
* @param string $message
*
* @return string
*/
private function esc_notice( $message = '' ) {
$tags = wp_kses_allowed_html( 'post' );
$text = wp_kses( $message, $tags );
return $text;
}
}

View File

@ -0,0 +1,244 @@
<?php
/**
* List Table Base Class.
*
* @package EDD
* @subpackage Admin
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 3.0
*/
namespace EDD\Admin;
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
// Load WP_List_Table if not loaded
if ( ! class_exists( 'WP_List_Table' ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}
/**
* Implements a base list table class to be extended by core.
*
* @since 3.0
* @abstract
*/
abstract class List_Table extends \WP_List_Table {
/**
* Arguments for the data set.
*
* @since 3.0
* @var array
*/
public $args = array();
/**
* Number of results to show per page.
*
* @since 3.0
* @var int
*/
public $per_page = 30;
/**
* Counts.
*
* @since 3.0
* @var array
*/
public $counts = array(
'total' => 0
);
/**
* Get a request var, or return the default if not set.
*
* @since 3.0
*
* @param string $var
* @param mixed $default
* @return mixed Un-sanitized request var
*/
public function get_request_var( $var = '', $default = false ) {
return isset( $_REQUEST[ $var ] )
? $_REQUEST[ $var ]
: $default;
}
/**
* Get a status request var, if set.
*
* @since 3.0
*
* @param mixed $default
* @return string
*/
protected function get_status( $default = '' ) {
return sanitize_key( $this->get_request_var( 'status', $default ) );
}
/**
* Retrieve the current page number.
*
* @since 3.0
*
* @return int Current page number.
*/
protected function get_paged() {
return absint( $this->get_request_var( 'paged', 1 ) );
}
/**
* Retrieve the current page number.
*
* @since 3.0
*
* @return int Current page number.
*/
protected function get_search() {
return urldecode( trim( $this->get_request_var( 's', '' ) ) );
}
/**
* Retrieves the data to be populated into the list table.
*
* @since 3.0
*
* @return array Array of list table data.
*/
abstract public function get_data();
/**
* Retrieve the view types
*
* @since 1.4
*
* @return array $views All the views available
*/
public function get_views() {
// Get the current status
$current = $this->get_status();
// Args to remove
$remove = array( 'edd-message', 'status', 'paged', '_wpnonce' );
// Base URL
$url = remove_query_arg( $remove, $this->get_base_url() );
// Is all selected?
$class = in_array( $current, array( '', 'all' ), true )
? ' class="current"'
: '';
// All
$count = '&nbsp;<span class="count">(' . esc_attr( $this->counts['total'] ) . ')</span>';
$label = __( 'All', 'easy-digital-downloads' ) . $count;
$views = array(
'all' => sprintf( '<a href="%s"%s>%s</a>', esc_url( $url ), $class, $label ),
);
// Remove total from counts array
$counts = $this->counts;
unset( $counts['total'] );
// Loop through statuses.
if ( ! empty( $counts ) ) {
foreach ( $counts as $status => $count ) {
$count_url = add_query_arg( array(
'status' => sanitize_key( $status ),
'paged' => false,
), $url );
$class = ( $current === $status )
? ' class="current"'
: '';
$count = '&nbsp;<span class="count">(' . absint( $this->counts[ $status ] ) . ')</span>';
$label = edd_get_status_label( $status ) . $count;
$views[ $status ] = sprintf( '<a href="%s"%s>%s</a>', esc_url( $count_url ), $class, $label );
}
}
return $views;
}
/**
* Parse pagination query arguments into keys & values that the Query class
* can understand and use to retrieve the correct results from the database.
*
* @since 3.0
*
* @param array $args
*
* @return array
*/
public function parse_pagination_args( $args = array() ) {
// Get pagination values
$order = isset( $_GET['order'] ) ? sanitize_text_field( $_GET['order'] ) : 'DESC'; // WPCS: CSRF ok.
$orderby = isset( $_GET['orderby'] ) ? sanitize_text_field( $_GET['orderby'] ) : 'id'; // WPCS: CSRF ok.
$paged = $this->get_paged();
// Only perform paged math if numeric and greater than 1
if ( ! empty( $paged ) && is_numeric( $paged ) && ( $paged > 1 ) ) {
$offset = ceil( $this->per_page * ( $paged - 1 ) );
// Otherwise, default to the first page of results
} else {
$offset = 0;
}
// Parse pagination args into passed args
$r = wp_parse_args( $args, array(
'number' => $this->per_page,
'offset' => $offset,
'order' => $order,
'orderby' => $orderby
) );
// Return args
return array_filter( $r );
}
/**
* Show the search field.
*
* @since 3.0
*
* @param string $text Label for the search box
* @param string $input_id ID of the search box
*/
public function search_box( $text, $input_id ) {
// Bail if no customers and no search
if ( ! $this->get_search() && ! $this->has_items() ) {
return;
}
$orderby = $this->get_request_var( 'orderby' );
$order = $this->get_request_var( 'order' );
$input_id = $input_id . '-search-input';
if ( ! empty( $orderby ) ) {
echo '<input type="hidden" name="orderby" value="' . esc_attr( $orderby ) . '" />';
}
if ( ! empty( $order ) ) {
echo '<input type="hidden" name="order" value="' . esc_attr( $order ) . '" />';
}
?>
<p class="search-box">
<label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"><?php echo esc_html( $text ); ?>:</label>
<input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="s" value="<?php _admin_search_query(); ?>"/>
<?php submit_button( esc_html( $text ), 'button', false, false, array( 'ID' => 'search-submit' ) ); ?>
</p>
<?php
}
}

View File

@ -0,0 +1,277 @@
<?php
/**
* Pass Manager
*
* Tool for determining what kind of pass, if any, is activated on the site.
*
* @package easy-digital-downloads
* @copyright Copyright (c) 2021, Sandhills Development, LLC
* @license GPL2+
* @since 2.10.6
*/
namespace EDD\Admin;
class Pass_Manager {
const PERSONAL_PASS_ID = 1245715;
const EXTENDED_PASS_ID = 1245716;
const PROFESSIONAL_PASS_ID = 1245717;
const ALL_ACCESS_PASS_ID = 1150319;
const ALL_ACCESS_PASS_LIFETIME_ID = 1464807;
/**
* ID of the highest tier pass that's activated.
*
* @var int|null
*/
public $highest_pass_id = null;
/**
* License key of the highest tier pass that's activated.
*
* @var string|null
*/
public $highest_license_key = null;
/**
* Pass data from the database. This will be an array with
* the key being the license key, and the value being another
* array with the `pass_id` and `time_checked`.
*
* @var array|null
*/
public $pass_data;
/**
* Whether or not we've stored any pass data yet.
* If no pass data has been stored, then that means the user
* might have a pass activated, we just haven't figured it out
* yet and we're still waiting for the first cron to run.
*
* @see \EDD_License::weekly_license_check()
* @see \EDD_License::activate_license()
* @see \EDD_License::maybe_set_pass_flag()
*
* @var bool
*/
public $has_pass_data = false;
/**
* Number of license keys entered on this site.
*
* @var int
*/
public $number_license_keys;
/**
* Hierarchy of passes. This helps us determine if one pass
* is "higher" than another.
*
* @see Pass_Manager::pass_compare()
*
* @var int[]
*/
private static $pass_hierarchy = array(
self::PERSONAL_PASS_ID => 10,
self::EXTENDED_PASS_ID => 20,
self::PROFESSIONAL_PASS_ID => 30,
self::ALL_ACCESS_PASS_ID => 40,
self::ALL_ACCESS_PASS_LIFETIME_ID => 50
);
/**
* Pass_Manager constructor.
*/
public function __construct() {
$pass_data = get_option( 'edd_pass_licenses' );
if ( false !== $pass_data ) {
$this->pass_data = json_decode( $pass_data, true );
$this->has_pass_data = true;
}
// Set up the highest pass data.
$this->set_highest_pass_data();
$this->number_license_keys = count( \EDD\Extensions\get_licensed_extension_slugs() );
}
/**
* Gets the highest pass and defines its data.
*
* @since 2.11.4
* @return void
*/
private function set_highest_pass_data() {
if ( ! $this->has_pass_data || ! is_array( $this->pass_data ) ) {
return;
}
$highest_license_key = null;
$highest_pass_id = null;
foreach ( $this->pass_data as $license_key => $pass_data ) {
/*
* If this pass was last verified more than 2 months ago, we're not using it.
* This ensures we never deal with a "stale" record for a pass that's no longer
* actually activated, but still exists in our DB array for some reason.
*
* Our cron job should always be updating with active data once per week.
*/
if ( empty( $pass_data['time_checked'] ) || strtotime( '-2 months' ) > $pass_data['time_checked'] ) {
continue;
}
// We need a pass ID.
if ( empty( $pass_data['pass_id'] ) ) {
continue;
}
// If we don't yet have a "highest pass", then this one is it automatically.
if ( empty( $highest_pass_id ) ) {
$highest_license_key = $license_key;
$highest_pass_id = intval( $pass_data['pass_id'] );
continue;
}
// Otherwise, this pass only takes over the highest pass if it's actually higher.
if ( self::pass_compare( (int) $pass_data['pass_id'], $highest_pass_id, '>' ) ) {
$highest_license_key = $license_key;
$highest_pass_id = intval( $pass_data['pass_id'] );
}
}
$this->highest_license_key = $highest_license_key;
$this->highest_pass_id = $highest_pass_id;
}
/**
* Whether or not a pass is activated.
*
* @since 2.10.6
*
* @return bool
*/
public function has_pass() {
return ! empty( $this->highest_pass_id );
}
/**
* If this is a "free install". That means there are no à la carte or pass licenses activated.
*
* @since 2.11.4
*
* @return bool
*/
public function isFree() {
return 0 === $this->number_license_keys;
}
/**
* If this is a "pro install". This means they have the pro version of EDD installed.
*
* @since 3.1
*
* @return bool
*/
public static function isPro() {
return false;
}
/**
* If this site has an individual product license active (à la carte), but no pass active.
*
* @since 2.11.4
*
* @return bool
*/
public function hasIndividualLicense() {
return ! $this->isFree() && ! $this->has_pass();
}
/**
* If this site has a Personal Pass active.
*
* @since 2.11.4
*
* @return bool
*/
public function hasPersonalPass() {
try {
return self::pass_compare( $this->highest_pass_id, self::PERSONAL_PASS_ID, '=' );
} catch ( \Exception $e ) {
return false;
}
}
/**
* If this site has an Extended Pass active.
*
* @since 2.11.4
*
* @return bool
*/
public function hasExtendedPass() {
try {
return self::pass_compare( $this->highest_pass_id, self::EXTENDED_PASS_ID, '=' );
} catch ( \Exception $e ) {
return false;
}
}
/**
* If this site has a Professional Pass active.
*
* @since 2.11.4
*
* @return bool
*/
public function hasProfessionalPass() {
try {
return self::pass_compare( $this->highest_pass_id, self::PROFESSIONAL_PASS_ID, '=' );
} catch( \Exception $e ) {
return false;
}
}
/**
* If this site has an All Access Pass active.
* Note: This uses >= to account for both All Access and lifetime All Access.
*
* @since 2.11.4
*
* @return bool
*/
public function hasAllAccessPass() {
try {
return self::pass_compare( $this->highest_pass_id, self::ALL_ACCESS_PASS_ID, '>=' );
} catch( \Exception $e ) {
return false;
}
}
/**
* Compares two passes with each other according to the supplied operator.
*
* @since 2.10.6
*
* @param int $pass_1 ID of the first pass.
* @param int $pass_2 ID of the second pass
* @param string $comparison Comparison operator.
*
* @return bool
* @throws \InvalidArgumentException
*/
public static function pass_compare( $pass_1, $pass_2, $comparison = '>' ) {
if ( ! array_key_exists( $pass_1, self::$pass_hierarchy ) ) {
throw new \InvalidArgumentException( 'Invalid pass 1: ' . $pass_1 );
}
if ( ! array_key_exists( $pass_2, self::$pass_hierarchy ) ) {
throw new \InvalidArgumentException( 'Invalid pass 2: ' . $pass_2 );
}
return version_compare( self::$pass_hierarchy[ $pass_1 ], self::$pass_hierarchy[ $pass_2 ], $comparison );
}
}

View File

@ -0,0 +1,324 @@
<?php
/**
* Sections Class
*
* @package EDD
* @subpackage Admin
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 3.0
*/
namespace EDD\Admin;
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Main class for creating a vertically tabbed UI
*
* @since 3.0
*/
class Sections {
/**
* Section ID
*
* @since 3.0
*
* @var array
*/
protected $id = 'edd_general_';
/**
* Sections
*
* @since 3.0
*
* @var array
*/
protected $sections = array();
/**
* ID of the currently selected section
*
* @since 3.0
*
* @var string
*/
public $current_section = 'general';
/**
* Whether to use JavaScript should
*
* @since 3.0
*
* @var string
*/
public $use_js = true;
/**
* Base URL for each section
*
* @since 3.0
*
* @var string
*/
public $base_url = '';
/**
* The object, if any
*
* @since 3.0
*
* @var object
*/
public $item = false;
/** Public Methods ********************************************************/
/**
* Constructor
*
* Might be useful someday
*
* @since 3.0
*/
public function __construct() {
}
/**
* Setup default sections
*
* @since 3.0
*/
public function set_sections( $sections = array() ) {
if ( empty( $sections ) ) {
$this->add_section( array(
'id' => 'general',
'label' => esc_html__( 'General', 'easy-digital-downloads' ),
'callback' => array( $this, 'section_general' )
) );
} elseif ( count( $sections ) ) {
foreach ( $sections as $section ) {
$this->add_section( $section );
}
}
}
/**
* Setup the sections for the current item
*
* @since 3.0
*
* @param object $item
*/
public function set_item( $item = false ) {
$this->item = $item;
}
/**
* Output the contents
*
* @since 3.0
*/
public function display() {
$use_js = ! empty( $this->use_js )
? ' use-js'
: '';
$role = $this->use_js ? 'tablist' : 'menu';
ob_start(); ?>
<div class="edd-sections-wrap">
<div class="edd-vertical-sections<?php echo $use_js; ?>">
<ul class="section-nav" role="<?php echo esc_attr( $role ); ?>">
<?php echo $this->get_all_section_links(); ?>
</ul>
<div class="section-wrap">
<?php echo $this->get_all_section_contents(); ?>
</div>
<br class="clear" />
</div>
<?php $this->nonce_field();
if ( ! empty( $this->item ) ) : ?>
<input type="hidden" name="edd-item-id" value="<?php echo esc_attr( $this->item->id ); ?>" />
<?php endif; ?>
</div>
<?php
// Output current buffer
echo ob_get_clean();
}
/** Private Methods *******************************************************/
/**
* Add a section
*
* @since 3.0
*
* @param array $section
*/
private function add_section( $section = array() ) {
$this->sections[] = (object) wp_parse_args( $section, array(
'id' => '',
'label' => '',
'icon' => 'admin-settings',
'callback' => ''
) );
}
/**
* Is a section the current section?
*
* @since 3.0
*
* @param string $section_id
*
* @return bool
*/
private function is_current_section( $section_id = '' ) {
return ( $section_id === $this->current_section );
}
/**
* Output the nonce field for the meta box
*
* @since 3.0
*/
protected function nonce_field() {
wp_nonce_field(
'edd_' . $this->id . '_sections_nonce',
'edd_' . $this->id . 'nonce_sections',
true
);
}
/**
* Get all section links
*
* @since 3.0
*
* @return string
*/
protected function get_all_section_links() {
ob_start();
// Loop through sections
foreach ( $this->sections as $section ) :
$url = $this->use_js
? '#' . esc_attr( $this->id . $section->id )
: add_query_arg( 'view', sanitize_key( $section->id ), $this->base_url );
// Special selected section
$selected = ! $this->use_js && $this->is_current_section( $section->id )
? 'aria-current="true"'
: '';
$class = $this->is_current_section( $section->id ) ? 'section-title section-title--is-active' : 'section-title';
$aria_attributes = $this->use_js ? 'role="tab" aria-controls="' . esc_attr( $this->id . $section->id ) . '"' : 'role="menuitem"';
?>
<li class="<?php echo esc_attr( $class ); ?>" <?php echo $aria_attributes . ' ' . $selected; ?>>
<a href="<?php echo esc_url( $url ); ?>">
<span class="dashicons dashicons-<?php echo esc_attr( $section->icon ); ?>"></span>
<span class="label"><?php echo $section->label; // Allow HTML ?></span>
</a>
</li>
<?php endforeach;
// Return current buffer
return ob_get_clean();
}
/**
* Get all section contents
*
* @since 3.0
*
* @return string
*/
protected function get_all_section_contents() {
ob_start();
// Maybe reduce sections down to single
$sections = empty( $this->use_js )
? wp_filter_object_list( $this->sections, array( 'id' => $this->current_section ) )
: $this->sections;
// Bail if no sections
if ( empty( $sections ) ) {
return;
}
// Loop through sections
foreach ( $sections as $section ) :
// Special selected section
$selected = ! $this->is_current_section( $section->id )
? 'style="display: none;"'
: ''; ?>
<div id="<?php echo esc_attr( $this->id . $section->id ); ?>" class="section-content" <?php echo $selected; ?>><?php
// Callback or action
if ( ! empty( $section->callback ) ) {
if ( is_callable( $section->callback ) ) {
call_user_func( $section->callback, $this->item );
} elseif ( is_array( $section->callback ) && is_callable( $section->callback[0] ) ) {
isset( $section->callback[1] )
? call_user_func_array( $section->callback[0], $section->callback[1] )
: call_user_func_array( $section->callback[0], array() );
} else {
_e( 'Invalid section', 'easy-digital-downloads' );
}
} else {
die;
do_action( 'edd_' . $section->id . 'section_contents', $this );
}
?></div>
<?php endforeach;
// Return current buffer
return ob_get_clean();
}
/**
* Output a section
*
* @since 3.0
*
* @param object $item
*/
protected function section_general() {
?>
<table class="form-table rowfat">
<tbody>
<tr>
<th>
<label><?php esc_html_e( 'Empty', 'easy-digital-downloads' ); ?></label>
</th>
<td>
&mdash;
</td>
</tr>
</tbody>
</table>
<?php
}
}

View File

@ -0,0 +1,453 @@
<?php
/**
* Customer Email Addresses Table Class
*
* @package EDD
* @subpackage Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 3.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
use EDD\Admin\List_Table;
/**
* EDD_Customer_Addresses_Table Class
*
* Renders the Customer Reports table
*
* @since 3.0
*/
class EDD_Customer_Addresses_Table extends List_Table {
/**
* Get things started
*
* @since 3.0
* @see WP_List_Table::__construct()
*/
public function __construct() {
parent::__construct( array(
'singular' => 'address',
'plural' => 'addresses',
'ajax' => false
) );
$this->process_bulk_action();
$this->get_counts();
}
/**
* Gets the name of the primary column.
*
* @since 2.5
* @access protected
*
* @return string Name of the primary column.
*/
protected function get_primary_column_name() {
return 'address';
}
/**
* This function renders most of the columns in the list table.
*
* @since 3.0
*
* @param array $item Contains all the data of the customers
* @param string $column_name The name of the column
*
* @return string Column Name
*/
public function column_default( $item, $column_name ) {
switch ( $column_name ) {
case 'type' :
$value = edd_get_address_type_label( $item['type'] );
if ( ! empty( $item['is_primary'] ) ) {
$value .= '&nbsp;&nbsp;<span class="edd-chip">' . esc_html__( 'Primary', 'easy-digital-downloads' ) . '</span>';
}
break;
case 'date_created' :
$value = '<time datetime="' . esc_attr( $item['date_created'] ) . '">' . edd_date_i18n( $item['date_created'], 'M. d, Y' ) . '<br>' . edd_date_i18n( $item['date_created'], 'H:i' ) . ' ' . edd_get_timezone_abbr() . '</time>';
break;
default:
$value = ! empty( $item[ $column_name ] )
? esc_html( $item[ $column_name ] )
: '&mdash;';
break;
}
// Filter & return
return apply_filters( 'edd_customers_column_' . $column_name, $value, $item['id'] );
}
/**
* Return the contents of the "Name" column
*
* @since 3.0
*
* @param array $item
* @return string
*/
public function column_address( $item ) {
$state = $extra = '';
$status = $this->get_status();
$address = ! empty( $item['address'] ) ? $item['address'] : '&mdash;';
$address2 = ! empty( $item['address2'] ) ? $item['address2'] : '';
$city = ! empty( $item['city'] ) ? $item['city'] : '';
$code = ! empty( $item['postal_code'] ) ? $item['postal_code'] : '';
// Address2
if ( ! empty( $address2 ) ) {
$extra .= '<br>' . $address2;
}
// City & Zip
if ( ! empty( $city ) || ! empty( $code ) ) {
$extra .= '<br>' . implode( ' ', array( $city, $code ) );
}
// Get the item status
$item_status = ! empty( $item['status'] )
? $item['status']
: 'verified';
// Get the customer ID
$customer_id = ! empty( $item['customer_id'] )
? absint( $item['customer_id'] )
: 0;
// Link to customer
$customer_url = edd_get_admin_url( array(
'page' => 'edd-customers',
'view' => 'overview',
'id' => absint( $customer_id ),
) );
// State
if ( ( ! empty( $status ) && ( $status !== $item_status ) ) || ( $item_status !== 'active' ) ) {
switch ( $status ) {
case 'pending' :
$value = __( 'Pending', 'easy-digital-downloads' );
break;
case 'verified' :
case '' :
default :
$value = __( 'Active', 'easy-digital-downloads' );
break;
}
$state = ' &mdash; ' . $value;
}
// Concatenate and return
return '<strong><a class="row-title" href="' . esc_url( $customer_url ) . '#edd_general_addresses">' . esc_html( $address ) . '</a>' . esc_html( $state ) . '</strong>' . $extra . $this->row_actions( $this->get_row_actions( $item ) );
}
/**
* Gets the row actions for the customer address.
*
* @since 3.0
* @param array $item
* @return array
*/
private function get_row_actions( $item ) {
// Link to customer
$customer_url = edd_get_admin_url(
array(
'page' => 'edd-customers',
'view' => 'overview',
'id' => urlencode( $item['customer_id'] ),
)
);
// Actions
$actions = array(
'view' => '<a href="' . esc_url( $customer_url ) . '#edd_general_addresses">' . esc_html__( 'View', 'easy-digital-downloads' ) . '</a>'
);
if ( empty( $item['is_primary'] ) ) {
$delete_url = wp_nonce_url( edd_get_admin_url( array(
'page' => 'edd-customers',
'view' => 'overview',
'id' => urlencode( $item['id'] ),
'edd_action' => 'customer-remove-address'
) ), 'edd-remove-customer-address' );
$actions['delete'] = '<a href="' . esc_url( $delete_url ) . '">' . esc_html__( 'Delete', 'easy-digital-downloads' ) . '</a>';
}
/**
* Filter the customer address row actions.
*
* @since 3.0
* @param array $actions The array of row actions.
* @param array $item The specific item (customer address).
*/
return apply_filters( 'edd_customer_address_row_actions', $actions, $item );
}
/**
* Return the contents of the "Name" column
*
* @since 3.0
*
* @param array $item
* @return string
*/
public function column_customer( $item ) {
// Get the customer ID
$customer_id = ! empty( $item['customer_id'] )
? absint( $item['customer_id'] )
: 0;
// Bail if no customer ID
if ( empty( $customer_id ) ) {
return '&mdash;';
}
// Try to get the customer
$customer = edd_get_customer( $customer_id );
// Bail if customer no longer exists
if ( empty( $customer ) ) {
return '&mdash;';
}
// Link to customer
$customer_url = edd_get_admin_url( array(
'page' => 'edd-customers',
'page_type' => 'physical',
's' => 'c:' . absint( $customer_id )
) );
// Concatenate and return
return '<a href="' . esc_url( $customer_url ) . '">' . esc_html( $customer->name ) . '</a>';
}
/**
* Render the checkbox column
*
* @access public
* @since 3.0
*
* @param array $item Address object.
*
* @return string Displays a checkbox
*/
public function column_cb( $item ) {
$customer = edd_get_customer_by( 'id', $item['customer_id'] );
$name = sprintf(
/* translators: customer address id */
__( 'Address ID: %s', 'easy-digital-downloads' ),
$item['id']
);
if ( ! empty( $customer->name ) ) {
$name = $customer->name;
}
return sprintf(
'<input type="checkbox" name="%1$s[]" id="%1$s-%2$s" value="%2$s" /><label for="%1$s-%2$s" class="screen-reader-text">%3$s</label>',
/*$1%s*/ 'customer',
/*$2%s*/ esc_attr( $item['id'] ),
/* translators: customer name or address id */
esc_html( sprintf( __( 'Select %s', 'easy-digital-downloads' ), $name ) )
);
}
/**
* Retrieve the customer counts
*
* @access public
* @since 3.0
* @return void
*/
public function get_counts() {
$this->counts = edd_get_customer_address_counts();
}
/**
* Retrieve the table columns
*
* @since 3.0
* @return array $columns Array of all the list table columns
*/
public function get_columns() {
return apply_filters( 'edd_report_customer_columns', array(
'cb' => '<input type="checkbox" />',
'address' => __( 'Address', 'easy-digital-downloads' ),
'region' => __( 'Region', 'easy-digital-downloads' ),
'country' => __( 'Country', 'easy-digital-downloads' ),
'customer' => __( 'Customer', 'easy-digital-downloads' ),
'type' => __( 'Type', 'easy-digital-downloads' ),
'date_created' => __( 'Date', 'easy-digital-downloads' )
) );
}
/**
* Get the sortable columns
*
* @since 2.1
* @return array Array of all the sortable columns
*/
public function get_sortable_columns() {
return array(
'date_created' => array( 'date_created', true ),
'address' => array( 'address', false ),
'region' => array( 'region', true ),
'country' => array( 'country', true ),
'customer' => array( 'customer_id', false ),
'type' => array( 'type', false )
);
}
/**
* Retrieve the bulk actions
*
* @access public
* @since 3.0
* @return array Array of the bulk actions
*/
public function get_bulk_actions() {
return array(
'delete' => __( 'Delete', 'easy-digital-downloads' )
);
}
/**
* Process the bulk actions
*
* @access public
* @since 3.0
*/
public function process_bulk_action() {
if ( empty( $_REQUEST['_wpnonce'] ) ) {
return;
}
if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'bulk-addresses' ) ) {
return;
}
$ids = isset( $_GET['customer'] )
? $_GET['customer']
: false;
if ( ! is_array( $ids ) ) {
$ids = array( $ids );
}
foreach ( $ids as $id ) {
switch ( $this->current_action() ) {
case 'delete' :
edd_delete_customer_address( $id );
break;
}
}
}
/**
* Get all of the items to display, given the current filters
*
* @since 3.0
*
* @return array $data All the row data
*/
public function get_data() {
$data = array();
$search = $this->get_search();
$args = array( 'status' => $this->get_status() );
// Customer ID
if ( strpos( $search, 'c:' ) !== false ) {
$args['customer_id'] = trim( str_replace( 'c:', '', $search ) );
// Country
} elseif ( strpos( $search, 'country:' ) !== false ) {
$search = substr( $search, strlen( 'country:' ) );
$args['search'] = $search;
$args['search_columns'] = array( 'country' );
// Zip
} elseif ( strpos( $search, 'zip:' ) !== false ) {
$search = substr( $search, strlen( 'zip:' ) );
$args['search'] = $search;
$args['search_columns'] = array( 'zip' );
// Region
} elseif ( strpos( $search, 'region:' ) !== false ) {
$search = substr( $search, strlen( 'region:' ) );
$args['search'] = $search;
$args['search_columns'] = array( 'region' );
// City
} elseif ( strpos( $search, 'city:' ) !== false ) {
$search = substr( $search, strlen( 'city:' ) );
$args['search'] = $search;
$args['search_columns'] = array( 'city' );
// Any...
} else {
$args['search'] = $search;
$args['search_columns'] = array( 'address', 'address2', 'city', 'region', 'country', 'postal_code' );
}
// Parse pagination
$this->args = $this->parse_pagination_args( $args );
// Get the data
$addresses = edd_get_customer_addresses( $this->args );
if ( ! empty( $addresses ) ) {
foreach ( $addresses as $address ) {
$data[] = array(
'id' => $address->id,
'customer_id' => $address->customer_id,
'status' => $address->status,
'type' => $address->type,
'address' => $address->address,
'address2' => $address->address2,
'city' => $address->city,
'region' => $address->region,
'postal_code' => $address->postal_code,
'country' => $address->country,
'date_created' => $address->date_created,
'date_modified' => $address->date_modified,
'is_primary' => $address->is_primary,
);
}
}
return $data;
}
/**
* Setup the final data for the table
*
* @since 3.0
* @return void
*/
public function prepare_items() {
$this->_column_headers = array(
$this->get_columns(),
array(),
$this->get_sortable_columns()
);
$this->items = $this->get_data();
$status = $this->get_status( 'total' );
// Setup pagination
$this->set_pagination_args( array(
'total_pages' => ceil( $this->counts[ $status ] / $this->per_page ),
'total_items' => $this->counts[ $status ],
'per_page' => $this->per_page
) );
}
}

View File

@ -0,0 +1,440 @@
<?php
/**
* Customer Email Addresses Table Class
*
* @package EDD
* @subpackage Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 3.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
use EDD\Admin\List_Table;
/**
* EDD_Customer_Email_Addresses_Table Class
*
* Renders the Customer Reports table
*
* @since 3.0
*/
class EDD_Customer_Email_Addresses_Table extends List_Table {
/**
* Get things started
*
* @since 3.0
* @see WP_List_Table::__construct()
*/
public function __construct() {
parent::__construct( array(
'singular' => 'email',
'plural' => 'emails',
'ajax' => false
) );
$this->process_bulk_action();
$this->get_counts();
}
/**
* Gets the name of the primary column.
*
* @since 2.5
* @access protected
*
* @return string Name of the primary column.
*/
protected function get_primary_column_name() {
return 'email';
}
/**
* This function renders most of the columns in the list table.
*
* @since 3.0
*
* @param array $item Contains all the data of the customers
* @param string $column_name The name of the column
*
* @return string Column Name
*/
public function column_default( $item, $column_name ) {
switch ( $column_name ) {
case 'id' :
$value = $item['id'];
break;
case 'email' :
$value = '<a href="mailto:' . rawurlencode( $item['email'] ) . '">' . esc_html( $item['email'] ) . '</a>';
break;
case 'type' :
$value = ( 'primary' === $item['type'] )
? esc_html__( 'Primary', 'easy-digital-downloads' )
: esc_html__( 'Secondary', 'easy-digital-downloads' );
break;
case 'date_created' :
$value = '<time datetime="' . esc_attr( $item['date_created'] ) . '">' . edd_date_i18n( $item['date_created'], 'M. d, Y' ) . '<br>' . edd_date_i18n( $item['date_created'], 'H:i' ) . ' ' . edd_get_timezone_abbr() . '</time>';
break;
default:
$value = isset( $item[ $column_name ] )
? $item[ $column_name ]
: null;
break;
}
// Filter & return
return apply_filters( 'edd_customers_column_' . $column_name, $value, $item['id'] );
}
/**
* Return the contents of the "Name" column
*
* @since 3.0
*
* @param array $item
* @return string
*/
public function column_email( $item ) {
$state = '';
$status = $this->get_status();
$email = ! empty( $item['email'] ) ? $item['email'] : '&mdash;';
// Get the item status
$item_status = ! empty( $item['status'] )
? $item['status']
: 'verified';
// Get the customer ID
$customer_id = ! empty( $item['customer_id'] )
? absint( $item['customer_id'] )
: 0;
// Link to customer
$customer_url = edd_get_admin_url( array(
'page' => 'edd-customers',
'view' => 'overview',
'id' => absint( $customer_id ),
) );
// State
if ( ( ! empty( $status ) && ( $status !== $item_status ) ) || ( $item_status !== 'active' ) ) {
switch ( $status ) {
case 'pending' :
$value = __( 'Pending', 'easy-digital-downloads' );
break;
case 'verified' :
case '' :
default :
$value = __( 'Active', 'easy-digital-downloads' );
break;
}
$state = ' &mdash; ' . $value;
}
// Concatenate and return
return '<strong><a class="row-title" href="' . esc_url( $customer_url ) . '#edd_general_emails">' . esc_html( $email ) . '</a>' . esc_html( $state ) . '</strong>' . $this->row_actions( $this->get_row_actions( $item ) );
}
/**
* Gets the row actions for the customer email address.
*
* @since 3.0
* @param array $item
* @return array
*/
private function get_row_actions( $item ) {
// Link to customer
$customer_url = edd_get_admin_url(
array(
'page' => 'edd-customers',
'view' => 'overview',
'id' => urlencode( $item['customer_id'] ),
)
);
// Actions
$actions = array(
'view' => '<a href="' . esc_url( $customer_url ) . '#edd_general_emails">' . __( 'View', 'easy-digital-downloads' ) . '</a>',
);
// Non-primary email actions
if ( ! empty( $item['email'] ) && ( empty( $item['type'] ) || 'primary' !== $item['type'] ) ) {
$delete_url = wp_nonce_url( edd_get_admin_url( array(
'page' => 'edd-customers',
'view' => 'overview',
'id' => urlencode( $item['customer_id'] ),
'email' => rawurlencode( $item['email'] ),
'edd_action' => 'customer-remove-email',
) ), 'edd-remove-customer-email' );
$actions['delete'] = '<a href="' . esc_url( $delete_url ) . '">' . esc_html__( 'Delete', 'easy-digital-downloads' ) . '</a>';
}
/**
* Filter the customer email address row actions.
*
* @since 3.0
* @param array $actions The array of row actions.
* @param array $item The specific item (customer email address).
*/
return apply_filters( 'edd_customer_email_address_row_actions', $actions, $item );
}
/**
* Return the contents of the "Name" column
*
* @since 3.0
*
* @param array $item
* @return string
*/
public function column_customer( $item ) {
// Get the customer ID
$customer_id = ! empty( $item['customer_id'] )
? absint( $item['customer_id'] )
: 0;
// Bail if no customer ID
if ( empty( $customer_id ) ) {
return '&mdash;';
}
// Try to get the customer
$customer = edd_get_customer( $customer_id );
// Bail if customer no longer exists
if ( empty( $customer ) ) {
return '&mdash;';
}
// Link to customer
$customer_url = edd_get_admin_url( array(
'page' => 'edd-customers',
'page_type' => 'emails',
's' => 'c:' . absint( $customer_id )
) );
// Concatenate and return
return '<a href="' . esc_url( $customer_url ) . '">' . esc_html( $customer->name ) . '</a>';
}
/**
* Render the checkbox column
*
* @access public
* @since 3.0
*
* @param array $item
*
* @return string Displays a checkbox
*/
public function column_cb( $item ) {
$is_primary = ! empty( $item['type'] ) && 'primary' === $item['type'];
$primary_attributes = $is_primary ? ' disabled' : '';
$title = $is_primary ? __( 'Primary email addresses cannot be deleted.', 'easy-digital-downloads' ) : '';
return sprintf(
'<input type="checkbox" name="%1$s[]" id="%1$s-%2$s" value="%2$s" title="%4$s"%5$s /><label for="%1$s-%2$s" class="screen-reader-text">%3$s</label>',
/*$1%s*/ esc_attr( 'customer' ),
/*$2%s*/ esc_attr( $item['id'] ),
/* translators: customer email */
esc_html( sprintf( __( 'Select %s', 'easy-digital-downloads' ), $item['email'] ) ),
/*$4%s*/ esc_attr( $title ),
/*$5%s*/ $primary_attributes
);
}
/**
* Retrieve the customer counts
*
* @access public
* @since 3.0
* @return void
*/
public function get_counts() {
$this->counts = edd_get_customer_email_address_counts();
}
/**
* Retrieve the table columns
*
* @since 3.0
* @return array $columns Array of all the list table columns
*/
public function get_columns() {
return apply_filters( 'edd_report_customer_columns', array(
'cb' => '<input type="checkbox" />',
'email' => __( 'Email', 'easy-digital-downloads' ),
'customer' => __( 'Customer', 'easy-digital-downloads' ),
'type' => __( 'Type', 'easy-digital-downloads' ),
'date_created' => __( 'Date', 'easy-digital-downloads' )
) );
}
/**
* Get the sortable columns
*
* @since 2.1
* @return array Array of all the sortable columns
*/
public function get_sortable_columns() {
return array(
'date_created' => array( 'date_created', true ),
'email' => array( 'email', true ),
'customer' => array( 'customer_id', false ),
'type' => array( 'type', false )
);
}
/**
* Retrieve the bulk actions
*
* @access public
* @since 3.0
* @return array Array of the bulk actions
*/
public function get_bulk_actions() {
return array(
'delete' => __( 'Delete', 'easy-digital-downloads' )
);
}
/**
* Process the bulk actions
*
* @access public
* @since 3.0
*/
public function process_bulk_action() {
if ( empty( $_REQUEST['_wpnonce'] ) ) {
return;
}
if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'bulk-emails' ) ) {
return;
}
$ids = isset( $_GET['customer'] )
? $_GET['customer']
: false;
if ( ! is_array( $ids ) ) {
$ids = array( $ids );
}
/*
* Only non-primary email addresses can be deleted, so we're building up a safelist using the provided
* IDs. Each ID will be matched against this prior to deletion.
*/
$non_primary_address_ids = edd_get_customer_email_addresses( array(
'id__in' => $ids,
'type__not_in' => array( 'primary' ),
'fields' => 'id'
) );
foreach ( $ids as $id ) {
switch ( $this->current_action() ) {
case 'delete' :
if ( in_array( $id, $non_primary_address_ids ) ) {
edd_delete_customer_email_address( $id );
}
break;
}
}
}
/**
* Get all of the items to display, given the current filters
*
* @since 3.0
*
* @return array $data All the row data
*/
public function get_data() {
$data = array();
$search = $this->get_search();
$args = array( 'status' => $this->get_status() );
// Account for search stripping the "+" from emails.
if ( strpos( $search, ' ' ) ) {
$original_query = $search;
$search = str_replace( ' ', '+', $search );
if ( ! is_email( $search ) ) {
$search = $original_query;
}
}
// Email.
if ( is_email( $search ) ) {
$args['email'] = $search;
// Address ID.
} elseif ( is_numeric( $search ) ) {
$args['id'] = $search;
// Customer ID.
} elseif ( strpos( $search, 'c:' ) !== false ) {
$args['customer_id'] = trim( str_replace( 'c:', '', $search ) );
// Any...
} else {
$args['search'] = $search;
$args['search_columns'] = array( 'email' );
}
// Parse pagination.
$this->args = $this->parse_pagination_args( $args );
// Get the data.
$emails = edd_get_customer_email_addresses( $this->args );
if ( ! empty( $emails ) ) {
foreach ( $emails as $customer ) {
$data[] = array(
'id' => $customer->id,
'email' => $customer->email,
'customer_id' => $customer->customer_id,
'status' => $customer->status,
'type' => $customer->type,
'date_created' => $customer->date_created,
);
}
}
return $data;
}
/**
* Setup the final data for the table
*
* @since 3.0
* @return void
*/
public function prepare_items() {
$this->_column_headers = array(
$this->get_columns(),
array(),
$this->get_sortable_columns()
);
$this->items = $this->get_data();
$status = $this->get_status( 'total' );
// Setup pagination
$this->set_pagination_args( array(
'total_pages' => ceil( $this->counts[ $status ] / $this->per_page ),
'total_items' => $this->counts[ $status ],
'per_page' => $this->per_page
) );
}
}

View File

@ -0,0 +1,433 @@
<?php
/**
* Customer Reports Table Class
*
* @package EDD
* @subpackage Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.5
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
use EDD\Admin\List_Table;
/**
* EDD_Customer_Reports_Table Class
*
* Renders the Customer Reports table
*
* @since 1.5
*/
class EDD_Customer_Reports_Table extends List_Table {
/**
* Get things started
*
* @since 1.5
* @see WP_List_Table::__construct()
*/
public function __construct() {
parent::__construct( array(
'singular' => 'customer',
'plural' => 'customers',
'ajax' => false
) );
$this->process_bulk_action();
$this->get_counts();
}
/**
* Gets the name of the primary column.
*
* @since 2.5
* @access protected
*
* @return string Name of the primary column.
*/
protected function get_primary_column_name() {
return 'name';
}
/**
* This function renders most of the columns in the list table.
*
* @since 1.5
*
* @param array $item Contains all the data of the customers
* @param string $column_name The name of the column
*
* @return string Column Name
*/
public function column_default( $item, $column_name ) {
$timezone_abbreviation = edd_get_timezone_abbr();
switch ( $column_name ) {
case 'id' :
$value = esc_html( $item['id'] );
break;
case 'email' :
$value = '<a href="mailto:' . rawurlencode( $item['email'] ) . '">' . esc_html( $item['email'] ) . '</a>';
break;
case 'order_count' :
$url = edd_get_admin_url( array(
'page' => 'edd-payment-history',
'customer' => rawurlencode( $item['id'] ),
) );
$value = '<a href="' . esc_url( $url ) . '">' . esc_html( number_format_i18n( $item['order_count'] ) ) . '</a>';
break;
case 'spent' :
$value = edd_currency_filter( edd_format_amount( $item[ $column_name ] ) );
break;
case 'date_created' :
$value = '<time datetime="' . esc_attr( $item['date_created'] ) . '">' . edd_date_i18n( $item['date_created'], 'M. d, Y' ) . '<br>' . edd_date_i18n( $item['date_created'], 'H:i' ) . ' ' . $timezone_abbreviation . '</time>';
break;
default:
$value = isset( $item[ $column_name ] )
? esc_html( $item[ $column_name ] )
: null;
break;
}
// Filter & return
return apply_filters( 'edd_customers_column_' . esc_attr( $column_name ), $value, $item['id'] );
}
/**
* Return the contents of the "Name" column
*
* @since 3.0
*
* @param array $item
* @return string
*/
public function column_name( $item ) {
$state = '';
$status = $this->get_status();
$name = ! empty( $item['name'] ) ? $item['name'] : '&mdash;';
$item_status = ! empty( $item['status'] )
? $item['status']
: 'active';
// State
if ( ( ! empty( $status ) && ( $status !== $item_status ) ) || ( $item_status !== 'active' ) ) {
switch ( $status ) {
case 'pending' :
$value = __( 'Pending', 'easy-digital-downloads' );
break;
case 'active' :
case '' :
default :
$value = __( 'Active', 'easy-digital-downloads' );
break;
}
$state = ' &mdash; ' . $value;
}
// Get the customer's avatar
$avatar = get_avatar( $item['email'], 32 );
// View URL
$view_url = edd_get_admin_url(
array(
'page' => 'edd-customers',
'view' => 'overview',
'id' => urlencode( $item['id'] ),
)
);
// Concatenate and return
return $avatar . '<strong><a class="row-title" href="' . esc_url( $view_url ) . '">' . esc_html( $name ) . '</a>' . esc_html( $state ) . '</strong>' . $this->row_actions( $this->get_row_actions( $item ) );
}
/**
* Gets the row actions for the customer.
*
* @since 3.0
* @param array $item
* @return array
*/
private function get_row_actions( $item ) {
$view_url = edd_get_admin_url(
array(
'page' => 'edd-customers',
'view' => 'overview',
'id' => urlencode( $item['id'] ),
)
);
$logs_url = edd_get_admin_url(
array(
'page' => 'edd-tools',
'tab' => 'logs',
'customer' => urlencode( $item['id'] ),
)
);
$delete_url = edd_get_admin_url(
array(
'page' => 'edd-customers',
'view' => 'delete',
'id' => urlencode( $item['id'] ),
)
);
$actions = array(
'view' => '<a href="' . esc_url( $view_url ) . '">' . __( 'Edit', 'easy-digital-downloads' ) . '</a>',
'logs' => '<a href="' . esc_url( $logs_url ) . '">' . __( 'Logs', 'easy-digital-downloads' ) . '</a>',
'delete' => '<a href="' . esc_url( $delete_url ) . '#edd_general_delete">' . __( 'Delete', 'easy-digital-downloads' ) . '</a>',
);
/**
* Filter the customer row actions.
*
* @since 3.0
* @param array $actions The array of row actions.
* @param array $item The specific item (customer).
*/
return apply_filters( 'edd_customer_row_actions', $actions, $item );
}
/**
* Render the checkbox column
*
* @since 3.0
*
* @param array $item Customer data.
*
* @return string Displays a checkbox
*/
public function column_cb( $item ) {
$name = empty( $item['name'] ) ? $item['email'] : $item['name'];
return sprintf(
'<input type="checkbox" name="%1$s[]" id="%1$s-%2$s" value="%2$s" /><label for="%1$s-%2$s" class="screen-reader-text">%3$s</label>',
/*$1%s*/ 'customer',
/*$2%s*/ esc_attr( $item['id'] ),
/* translators: customer name or email */
esc_html( sprintf( __( 'Select %s', 'easy-digital-downloads' ), $name ) )
);
}
/**
* Retrieve the customer counts
*
* @since 3.0
* @return void
*/
public function get_counts() {
$this->counts = edd_get_customer_counts();
}
/**
* Retrieve the table columns
*
* @since 1.5
* @return array $columns Array of all the list table columns
*/
public function get_columns() {
return apply_filters( 'edd_report_customer_columns', array(
'cb' => '<input type="checkbox" />',
'name' => __( 'Name', 'easy-digital-downloads' ),
'email' => __( 'Email', 'easy-digital-downloads' ),
'order_count' => __( 'Orders', 'easy-digital-downloads' ),
'spent' => __( 'Spent', 'easy-digital-downloads' ),
'date_created' => __( 'Date', 'easy-digital-downloads' )
) );
}
/**
* Get the sortable columns
*
* @since 2.1
* @return array Array of all the sortable columns
*/
public function get_sortable_columns() {
return array(
'date_created' => array( 'date_created', true ),
'name' => array( 'name', true ),
'email' => array( 'email', true ),
'order_count' => array( 'purchase_count', false ),
'spent' => array( 'purchase_value', false )
);
}
/**
* Retrieve the bulk actions
*
* @since 3.0
* @return array Array of the bulk actions
*/
public function get_bulk_actions() {
return array(
'delete' => __( 'Delete', 'easy-digital-downloads' )
);
}
/**
* Process the bulk actions
*
* @since 3.0
*/
public function process_bulk_action() {
if ( empty( $_REQUEST['_wpnonce'] ) ) {
return;
}
if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'bulk-customers' ) ) {
return;
}
check_admin_referer( 'bulk-customers' );
$ids = isset( $_GET['customer'] )
? $_GET['customer']
: false;
if ( ! is_array( $ids ) ) {
$ids = array( $ids );
}
foreach ( $ids as $id ) {
switch ( $this->current_action() ) {
case 'delete' :
edd_delete_customer( $id );
break;
}
}
}
/**
* Builds and retrieves all the reports data.
*
* @since 1.5
* @deprecated 3.0 Use get_data()
*
* @return array All the data for customer reports.
*/
public function reports_data() {
_edd_deprecated_function( __METHOD__, '3.0', 'EDD_Customer_Reports_Table::get_data()' );
return $this->get_data();
}
/**
* Retrieves all of the items to display, given the current filters.
*
* @since 3.0
*
* @return array $data All the row data.
*/
public function get_data() {
$data = array();
$search = $this->get_search();
$args = array( 'status' => $this->get_status() );
// Account for search stripping the "+" from emails.
if ( strpos( $search, ' ' ) ) {
$original_query = $search;
$search = str_replace( ' ', '+', $search );
if ( ! is_email( $search ) ) {
$search = $original_query;
}
}
// Email search.
if ( is_email( $search ) ) {
$args['email'] = $search;
// Customer ID.
} elseif ( is_numeric( $search ) ) {
$args['id'] = $search;
} elseif ( strpos( $search, 'c:' ) !== false ) {
$args['id'] = trim( str_replace( 'c:', '', $search ) );
// User ID.
} elseif ( strpos( $search, 'user:' ) !== false ) {
$args['user_id'] = trim( str_replace( 'u:', '', $search ) );
} elseif ( strpos( $search, 'u:' ) !== false ) {
$args['user_id'] = trim( str_replace( 'u:', '', $search ) );
// Other...
} else {
$args['search'] = $search;
$args['search_columns'] = array( 'name', 'email' );
}
// Parse pagination
$this->args = $this->parse_pagination_args( $args );
if ( is_email( $search ) ) {
$customer_emails = new EDD\Database\Queries\Customer_Email_Address();
$customer_ids = $customer_emails->query(
array(
'fields' => 'customer_id',
'email' => $search,
)
);
$customers = edd_get_customers(
array(
'id__in' => $customer_ids,
)
);
} else {
$customers = edd_get_customers( $this->args );
}
// Get the data
if ( ! empty( $customers ) ) {
foreach ( $customers as $customer ) {
$data[] = array(
'id' => $customer->id,
'user_id' => $customer->user_id,
'name' => $customer->name,
'email' => $customer->email,
'order_count' => $customer->purchase_count,
'spent' => $customer->purchase_value,
'date_created' => $customer->date_created,
);
}
}
return $data;
}
/**
* Setup the final data for the table
*
* @since 1.5
* @return void
*/
public function prepare_items() {
$this->_column_headers = array(
$this->get_columns(),
array(),
$this->get_sortable_columns()
);
$this->items = $this->get_data();
$status = $this->get_status( 'total' );
// Add condition to be sure we don't divide by zero.
// If $this->per_page is 0, then set total pages to 1.
$total_pages = $this->per_page ? ceil( (int) $this->counts[ $status ] / (int) $this->per_page ) : 1;
// Setup pagination
$this->set_pagination_args( array(
'total_pages' => $total_pages,
'total_items' => $this->counts[ $status ],
'per_page' => $this->per_page,
) );
}
}

View File

@ -0,0 +1,635 @@
<?php
/**
* Customers - Admin Actions.
*
* @package EDD
* @subpackage Admin/Customers
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.3
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Update customer.
*
* @since 2.3
* @since 3.0 Updated to use new query methods and custom tables.
*
* @param array $args Form data being passed.
* @return false|array $output Response message.
*/
function edd_edit_customer( $args = array() ) {
// Bail if nothing new to edit.
if ( empty( $args ) || empty( $args['customerinfo'] ) || empty( $args['_wpnonce'] ) ) {
return false;
}
$customer_edit_role = edd_get_edit_customers_role();
// Bail if user cannot edit customers.
if ( ! is_admin() || ! current_user_can( $customer_edit_role ) ) {
wp_die( esc_html__( 'You do not have permission to edit this customer.', 'easy-digital-downloads' ) );
}
$customer_info = $args['customerinfo'];
$customer_id = (int) $customer_info['id'];
$nonce = $args['_wpnonce'];
// Bail if nonce check fails
if ( ! wp_verify_nonce( $nonce, 'edit-customer' ) ) {
wp_die( esc_html__( 'Cheatin\' eh?!', 'easy-digital-downloads' ) );
}
// Bail if customer does not exist.
$customer = edd_get_customer( $customer_id );
if ( ! $customer ) {
return false;
}
// Parse customer info with defaults.
$customer_info = wp_parse_args( $customer_info, array(
'name' => '',
'email' => '',
'date_created' => '',
'user_id' => 0
) );
if ( ! is_email( $customer_info['email'] ) ) {
edd_set_error( 'edd-invalid-email', __( 'Please enter a valid email address.', 'easy-digital-downloads' ) );
}
if ( (int) $customer_info['user_id'] !== (int) $customer->user_id ) {
// Make sure we don't already have this user attached to a customer
if ( ! empty( $customer_info['user_id'] ) && false !== edd_get_customer_by( 'user_id', $customer_info['user_id'] ) ) {
edd_set_error( 'edd-invalid-customer-user_id', sprintf( __( 'The User ID %d is already associated with a different customer.', 'easy-digital-downloads' ), $customer_info['user_id'] ) );
}
// Make sure it's actually a user
$user = get_user_by( 'id', $customer_info['user_id'] );
if ( ! empty( $customer_info['user_id'] ) && false === $user ) {
edd_set_error( 'edd-invalid-user_id', sprintf( __( 'The User ID %d does not exist. Please assign an existing user.', 'easy-digital-downloads' ), $customer_info['user_id'] ) );
}
}
// Record this for later
$previous_user_id = $customer->user_id;
// Bail if errors exist.
if ( edd_get_errors() ) {
return false;
}
$user_id = absint( $customer_info['user_id'] );
if ( empty( $user_id ) && ! empty( $customer_info['user_login'] ) ) {
// See if they gave an email, otherwise we'll assume login
$user_by_field = is_email( $customer_info['user_login'] )
? 'email'
: 'login';
$user = get_user_by( $user_by_field, $customer_info['user_login'] );
if ( $user ) {
$user_id = $user->ID;
} else {
edd_set_error( 'edd-invalid-user-string', sprintf( __( 'Failed to attach user. The login or email address %s was not found.', 'easy-digital-downloads' ), $customer_info['user_login'] ) );
}
}
// Setup the customer address, if present.
$address = array();
$address['address'] = isset( $customer_info['address'] )
? $customer_info['address']
: '';
$address['address2'] = isset( $customer_info['address2'] )
? $customer_info['address2']
: '';
$address['city'] = isset( $customer_info['city'] )
? $customer_info['city']
: '';
$address['country'] = isset( $customer_info['country'] )
? $customer_info['country']
: '';
$address['postal_code'] = isset( $customer_info['postal_code'] )
? $customer_info['postal_code']
: '';
$address['region'] = isset( $customer_info['region'] )
? $customer_info['region']
: '';
// Sanitize the inputs
$customer_data = array();
$customer_data['name'] = strip_tags( stripslashes( $customer_info['name'] ) );
$customer_data['email'] = $customer_info['email'];
$customer_data['user_id'] = $user_id;
$customer_data['date_created'] = gmdate( 'Y-m-d H:i:s', strtotime( $customer_info['date_created'] ) );
$customer_data = apply_filters( 'edd_edit_customer_info', $customer_data, $customer_id );
$address = apply_filters( 'edd_edit_customer_address', $address, $customer_id );
$customer_data = array_map( 'sanitize_text_field', $customer_data );
$address = array_map( 'sanitize_text_field', $address );
do_action( 'edd_pre_edit_customer', $customer_id, $customer_data, $address );
$output = array();
$previous_email = $customer->email;
// Add new address before update to skip exists checks
if ( $previous_email !== $customer_data['email'] ) {
$customer->add_email( $customer_data['email'], true );
}
// Update customer
if ( $customer->update( $customer_data ) ) {
$current_address = $customer->get_address();
$address['customer_id'] = $customer->id;
if ( $current_address ) {
edd_update_customer_address( $current_address->id, $address );
} else {
$address['is_primary'] = true;
edd_add_customer_address( $address );
}
$output['success'] = true;
$customer_data = array_merge( $customer_data, $address );
$output['customer_info'] = $customer_data;
} else {
$output['success'] = false;
}
do_action( 'edd_post_edit_customer', $customer_id, $customer_data );
if ( edd_doing_ajax() ) {
wp_send_json( $output );
}
return $output;
}
add_action( 'edd_edit-customer', 'edd_edit_customer', 10, 1 );
/**
* Add an email address to the customer from within the admin and log a customer note
*
* @since 2.6
* @param array $args Array of arguments: nonce, customer id, and email address
* @return mixed Echos JSON if doing AJAX. Returns array of success (bool) and message (string) if not AJAX.
*/
function edd_add_customer_email( $args = array() ) {
$customer_edit_role = edd_get_edit_customers_role();
if ( ! is_admin() || ! current_user_can( $customer_edit_role ) ) {
wp_die( __( 'You do not have permission to edit this customer.', 'easy-digital-downloads' ) );
}
$output = array();
if ( empty( $args ) || empty( $args['email'] ) || empty( $args['customer_id'] ) ) {
$output['success'] = false;
if ( empty( $args['email'] ) ) {
$output['message'] = __( 'Email address is missing.', 'easy-digital-downloads' );
} else if ( empty( $args['customer_id'] ) ) {
$output['message'] = __( 'Customer ID is required.', 'easy-digital-downloads' );
} else {
$output['message'] = __( 'An error has occured. Please try again.', 'easy-digital-downloads' );
}
} else if ( ! wp_verify_nonce( $args['_wpnonce'], 'edd-add-customer-email' ) ) {
$output = array(
'success' => false,
'message' => __( 'Nonce verification failed.', 'easy-digital-downloads' ),
);
} else if ( ! is_email( $args['email'] ) ) {
$output = array(
'success' => false,
'message' => __( 'Invalid email address.', 'easy-digital-downloads' ),
);
} else {
$email = sanitize_email( $args['email'] );
$customer_id = (int) $args['customer_id'];
$primary = 'true' === $args['primary'] ? true : false;
$customer = new EDD_Customer( $customer_id );
$customer_email_id = $customer->add_email( $email, $primary );
if ( false === $customer_email_id ) {
if ( in_array( $email, $customer->emails, true ) ) {
$output = array(
'success' => false,
'message' => __( 'Email already associated with this customer.', 'easy-digital-downloads' ),
);
} else {
$output = array(
'success' => false,
'message' => __( 'Email address is already associated with another customer.', 'easy-digital-downloads' ),
);
}
} else {
$redirect = edd_get_admin_url(
array(
'page' => 'edd-customers',
'view' => 'overview',
'id' => absint( $customer_id ),
'edd-message' => 'email-added',
'edd-email-id' => absint( $customer_email_id ),
)
);
$output = array(
'success' => true,
'message' => __( 'Email successfully added to customer.', 'easy-digital-downloads' ),
'redirect' => $redirect . '#edd_general_emails',
);
$user = wp_get_current_user();
$user_login = ! empty( $user->user_login ) ? $user->user_login : edd_get_bot_name();
$customer_note = sprintf( __( 'Email address %s added by %s', 'easy-digital-downloads' ), $email, $user_login );
$customer->add_note( $customer_note );
if ( $primary ) {
$customer_note = sprintf( __( 'Email address %s set as primary by %s', 'easy-digital-downloads' ), $email, $user_login );
$customer->add_note( $customer_note );
}
}
}
if ( ! isset( $customer_id ) ) {
$customer_id = isset( $args['customer_id'] ) ? $args['customer_id'] : false;
}
do_action( 'edd_post_add_customer_email', $customer_id, $args );
if ( edd_doing_ajax() ) {
wp_send_json( $output );
}
return $output;
}
add_action( 'edd_customer-add-email', 'edd_add_customer_email', 10, 1 );
/**
* Remove an email address to the customer from within the admin and log a customer note
* and redirect back to the customer interface for feedback
*
* @since 2.6
* @return void
*/
function edd_remove_customer_email() {
if ( empty( $_GET['id'] ) || ! is_numeric( $_GET['id'] ) ) {
return false;
}
if ( empty( $_GET['email'] ) || ! is_email( $_GET['email'] ) ) {
return false;
}
if ( empty( $_GET['_wpnonce'] ) ) {
return false;
}
$nonce = $_GET['_wpnonce'];
if ( ! wp_verify_nonce( $nonce, 'edd-remove-customer-email' ) ) {
wp_die( __( 'Nonce verification failed', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
$customer = new EDD_Customer( $_GET['id'] );
if ( $customer->remove_email( $_GET['email'] ) ) {
$url = edd_get_admin_url(
array(
'page' => 'edd-customers',
'view' => 'overview',
'id' => urlencode( $customer->id ),
'edd-message' => 'email-removed',
)
);
$user = wp_get_current_user();
$user_login = ! empty( $user->user_login ) ? $user->user_login : edd_get_bot_name();
$customer_note = sprintf( __( 'Email address %s removed by %s', 'easy-digital-downloads' ), sanitize_email( $_GET['email'] ), $user_login );
$customer->add_note( $customer_note );
} else {
$url = edd_get_admin_url(
array(
'page' => 'edd-customers',
'view' => 'overview',
'id' => urlencode( $customer->id ),
'edd-message' => 'email-remove-failed',
)
);
}
edd_redirect( $url . '#edd_general_emails' );
}
add_action( 'edd_customer-remove-email', 'edd_remove_customer_email', 10 );
/**
* Set an email address as the primary for a customer from within the admin and log a customer note
* and redirect back to the customer interface for feedback
*
* @since 2.6
* @return void
*/
function edd_set_customer_primary_email() {
if ( empty( $_GET['id'] ) || ! is_numeric( $_GET['id'] ) ) {
return false;
}
if ( empty( $_GET['email'] ) || ! is_email( $_GET['email'] ) ) {
return false;
}
if ( empty( $_GET['_wpnonce'] ) ) {
return false;
}
$nonce = $_GET['_wpnonce'];
if ( ! wp_verify_nonce( $nonce, 'edd-set-customer-primary-email' ) ) {
wp_die( __( 'Nonce verification failed', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
$customer = new EDD_Customer( $_GET['id'] );
if ( $customer->set_primary_email( $_GET['email'] ) ) {
$url = edd_get_admin_url(
array(
'page' => 'edd-customers',
'view' => 'overview',
'id' => urlencode( $customer->id ),
'edd-message' => 'primary-email-updated',
)
);
$user = wp_get_current_user();
$user_login = ! empty( $user->user_login ) ? $user->user_login : edd_get_bot_name();
$customer_note = sprintf( __( 'Email address %s set as primary by %s', 'easy-digital-downloads' ), sanitize_email( $_GET['email'] ), $user_login );
$customer->add_note( $customer_note );
} else {
$url = edd_get_admin_url(
array(
'page' => 'edd-customers',
'view' => 'overview',
'id' => urlencode( $customer->id ),
'edd-message' => 'primary-email-failed',
)
);
}
edd_redirect( $url . '#edd_general_emails' );
}
add_action( 'edd_customer-primary-email', 'edd_set_customer_primary_email', 10 );
/**
* Delete a customer
*
* @since 2.3
* @param array $args The $_POST array being passed
* @return int Whether it was a successful deletion
*/
function edd_customer_delete( $args = array() ) {
$customer_edit_role = edd_get_edit_customers_role();
if ( ! is_admin() || ! current_user_can( $customer_edit_role ) ) {
wp_die( __( 'You do not have permission to delete this customer.', 'easy-digital-downloads' ) );
}
if ( empty( $args ) ) {
return;
}
$customer_id = (int)$args['customer_id'];
$confirm = ! empty( $args['edd-customer-delete-confirm'] );
$remove_data = ! empty( $args['edd-customer-delete-records'] );
$nonce = $args['_wpnonce'];
if ( ! wp_verify_nonce( $nonce, 'delete-customer' ) ) {
wp_die( __( 'Cheatin\' eh?!', 'easy-digital-downloads' ) );
}
if ( ! $confirm ) {
edd_set_error( 'customer-delete-no-confirm', __( 'Please confirm you want to delete this customer', 'easy-digital-downloads' ) );
}
if ( edd_get_errors() ) {
edd_redirect(
edd_get_admin_url(
array(
'page' => 'edd-customers',
'view' => 'overview',
'id' => absint( $customer_id ),
)
)
);
}
$customer = new EDD_Customer( $customer_id );
do_action( 'edd_pre_delete_customer', $customer_id, $confirm, $remove_data );
$success = false;
if ( $customer->id > 0 ) {
$payments_array = explode( ',', $customer->payment_ids );
$success = edd_destroy_customer( $customer->id );
if ( $success ) {
if ( $remove_data ) {
// Remove all payments, logs, etc
foreach ( $payments_array as $payment_id ) {
edd_destroy_order( $payment_id );
}
} else {
// Just set the payments to customer_id of 0
foreach ( $payments_array as $payment_id ) {
edd_update_payment_meta( $payment_id, '_edd_payment_customer_id', 0 );
}
}
$redirect = edd_get_admin_url( array( 'page' => 'edd-customers', 'edd-message' => 'customer-deleted' ) );
} else {
edd_set_error( 'edd-customer-delete-failed', __( 'Error deleting customer', 'easy-digital-downloads' ) );
$redirect = edd_get_admin_url( array( 'page' => 'edd-customers', 'view' => 'delete', 'id' => absint( $customer_id ) ) );
}
} else {
edd_set_error( 'edd-customer-delete-invalid-id', __( 'Invalid Customer ID', 'easy-digital-downloads' ) );
$redirect = edd_get_admin_url( array( 'page' => 'edd-customers' ) );
}
edd_redirect( $redirect );
}
add_action( 'edd_delete-customer', 'edd_customer_delete', 10, 1 );
/**
* Disconnect a user ID from a customer
*
* @since 2.3
* @param array $args Array of arguments
* @return bool If the disconnect was successful
*/
function edd_disconnect_customer_user_id( $args = array() ) {
$customer_edit_role = edd_get_edit_customers_role();
if ( ! is_admin() || ! current_user_can( $customer_edit_role ) ) {
wp_die( __( 'You do not have permission to edit this customer.', 'easy-digital-downloads' ) );
}
if ( empty( $args ) ) {
return;
}
$customer_id = (int)$args['customer_id'];
$nonce = $args['_wpnonce'];
if ( ! wp_verify_nonce( $nonce, 'edit-customer' ) ) {
wp_die( __( 'Cheatin\' eh?!', 'easy-digital-downloads' ) );
}
$customer = new EDD_Customer( $customer_id );
if ( empty( $customer->id ) ) {
return false;
}
do_action( 'edd_pre_customer_disconnect_user_id', $customer_id, $customer->user_id );
$customer_args = array( 'user_id' => 0 );
if ( $customer->update( $customer_args ) ) {
$output['success'] = true;
} else {
$output['success'] = false;
edd_set_error( 'edd-disconnect-user-fail', __( 'Failed to disconnect user from customer', 'easy-digital-downloads' ) );
}
do_action( 'edd_post_customer_disconnect_user_id', $customer_id );
if ( edd_doing_ajax() ) {
wp_send_json( $output );
}
return $output;
}
add_action( 'edd_disconnect-userid', 'edd_disconnect_customer_user_id', 10, 1 );
/**
* Process manual verification of customer account by admin
*
* @since 2.4.8
* @return void
*/
function edd_process_admin_user_verification() {
if ( empty( $_GET['id'] ) || ! is_numeric( $_GET['id'] ) ) {
return false;
}
if ( empty( $_GET['_wpnonce'] ) ) {
return false;
}
$nonce = $_GET['_wpnonce'];
if ( ! wp_verify_nonce( $nonce, 'edd-verify-user' ) ) {
wp_die( __( 'Nonce verification failed', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
$customer = new EDD_Customer( $_GET['id'] );
edd_set_user_to_verified( $customer->user_id );
$url = edd_get_admin_url(
array(
'page' => 'edd-customers',
'view' => 'overview',
'id' => absint( $customer->id ),
'edd-message' => 'user-verified',
)
);
edd_redirect( $url );
}
add_action( 'edd_verify_user_admin', 'edd_process_admin_user_verification' );
/**
* Register the reset single customer stats batch processor
* @since 2.5
*/
function edd_register_batch_single_customer_recount_tool() {
add_action( 'edd_batch_export_class_include', 'edd_include_single_customer_recount_tool_batch_processer', 10, 1 );
}
add_action( 'edd_register_batch_exporter', 'edd_register_batch_single_customer_recount_tool', 10 );
/**
* Loads the tools batch processing class for recounting stats for a single customer
*
* @since 2.5
* @param string $class The class being requested to run for the batch export
* @return void
*/
function edd_include_single_customer_recount_tool_batch_processer( $class ) {
if ( 'EDD_Tools_Recount_Single_Customer_Stats' === $class ) {
require_once EDD_PLUGIN_DIR . 'includes/admin/tools/class-edd-tools-recount-single-customer-stats.php';
}
}
/**
* Removes a customer address
*
* @since 3.0
* @return void
*/
function edd_remove_customer_address() {
if ( ! is_admin() || ! current_user_can( edd_get_edit_customers_role() ) ) {
wp_die( __( 'You do not have permission to perform this action.', 'easy-digital-downloads' ) );
}
if ( empty( $_GET['id'] ) || ! is_numeric( $_GET['id'] ) || empty( $_GET['_wpnonce'] ) ) {
return;
}
if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'edd-remove-customer-address' ) ) {
wp_die( __( 'Nonce verification failed', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
$address = edd_fetch_customer_address( absint( $_GET['id'] ) );
$removed = $address instanceof EDD\Customers\Customer_Address ? edd_delete_customer_address( absint( $_GET['id'] ) ) : false;
$url = edd_get_admin_url( array(
'page' => 'edd-customers',
'view' => 'overview',
'id' => urlencode( $address->customer_id ),
'edd-message' => 'address-removed'
) );
if ( $removed ) {
$url = add_query_arg( 'edd-message', 'address-removed', $url );
} else {
$url = add_query_arg( 'edd-message', 'address-remove-failed', $url );
}
edd_redirect( $url . '#edd_general_addresses' );
}
add_action( 'edd_customer-remove-address', 'edd_remove_customer_address', 10 );

View File

@ -0,0 +1,185 @@
<?php
/**
* Customers - Admin Functions.
*
* @package EDD
* @subpackage Admin/Customers
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.3
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Register a view for the single customer view
*
* @since 2.3
* @param array $views An array of existing views
* @return array The altered list of views
*/
function edd_register_default_customer_views( $views ) {
return array_merge( $views, array(
'overview' => 'edd_customers_view',
'emails' => 'edd_customers_emails_view',
'addresses' => 'edd_customers_addresses_view',
'delete' => 'edd_customers_delete_view',
'notes' => 'edd_customer_notes_view',
'tools' => 'edd_customer_tools_view',
) );
}
add_filter( 'edd_customer_views', 'edd_register_default_customer_views', 1, 1 );
/**
* Register a tab for the single customer view
*
* @since 2.3
* @param array $tabs An array of existing tabs
* @return array The altered list of tabs
*/
function edd_register_default_customer_tabs( $tabs ) {
return array_merge( $tabs, array(
'overview' => array( 'dashicon' => 'dashicons-admin-users', 'title' => _x( 'Profile', 'Customer Details tab title', 'easy-digital-downloads' ) ),
'emails' => array( 'dashicon' => 'dashicons-email', 'title' => _x( 'Emails', 'Customer Emails tab title', 'easy-digital-downloads' ) ),
'addresses' => array( 'dashicon' => 'dashicons-admin-home', 'title' => _x( 'Addresses', 'Customer Addresses tab title', 'easy-digital-downloads' ) ),
'notes' => array( 'dashicon' => 'dashicons-admin-comments', 'title' => _x( 'Notes', 'Customer Notes tab title', 'easy-digital-downloads' ) ),
'tools' => array( 'dashicon' => 'dashicons-admin-tools', 'title' => _x( 'Tools', 'Customer Tools tab title', 'easy-digital-downloads' ) )
) );
}
add_filter( 'edd_customer_tabs', 'edd_register_default_customer_tabs', 1, 1 );
/**
* Register the Delete icon as late as possible so it's at the bottom
*
* @since 2.3.1
* @param array $tabs An array of existing tabs
* @return array The altered list of tabs, with 'delete' at the bottom
*/
function edd_register_delete_customer_tab( $tabs ) {
$tabs['delete'] = array(
'dashicon' => 'dashicons-trash',
'title' => _x( 'Delete', 'Delete Customer tab title', 'easy-digital-downloads' )
);
return $tabs;
}
add_filter( 'edd_customer_tabs', 'edd_register_delete_customer_tab', PHP_INT_MAX, 1 );
/**
* Remove the admin bar edit profile link when the user is not verified
*
* @since 2.4.4
* @return void
*/
function edd_maybe_remove_adminbar_profile_link() {
if ( current_user_can( 'manage_shop_settings' ) ) {
return;
}
if ( edd_user_pending_verification() ) {
global $wp_admin_bar;
$wp_admin_bar->remove_menu( 'edit-profile', 'user-actions' );
}
}
add_action( 'wp_before_admin_bar_render', 'edd_maybe_remove_adminbar_profile_link' );
/**
* Remove the admin menus and disable profile access for non-verified users
*
* @since 2.4.4
* @return void
*/
function edd_maybe_remove_menu_profile_links() {
if ( edd_doing_ajax() ) {
return;
}
if ( current_user_can( 'manage_shop_settings' ) ) {
return;
}
if ( edd_user_pending_verification() ) {
if( defined( 'IS_PROFILE_PAGE' ) && true === IS_PROFILE_PAGE ) {
$url = esc_url( edd_get_user_verification_request_url() );
$message = sprintf( __( 'Your account is pending verification. Please click the link in your email to activate your account. No email? <a href="%s">Click here</a> to send a new activation code.', 'easy-digital-downloads' ), esc_url( $url ) );
$title = __( 'Account Pending Verification', 'easy-digital-downloads' );
$args = array(
'response' => 403,
);
wp_die( $message, $title, $args );
}
remove_menu_page( 'profile.php' );
remove_submenu_page( 'users.php', 'profile.php' );
}
}
add_action( 'admin_init', 'edd_maybe_remove_menu_profile_links' );
/**
* Add Customer column to Users list table.
*
* @since 3.0
*
* @param array $columns Existing columns.
*
* @return array $columns Columns with `Customer` added.
*/
function edd_add_customer_column_to_users_table( $columns ) {
$columns['edd_customer'] = __( 'Customer', 'easy-digital-downloads' );
return $columns;
}
add_filter( 'manage_users_columns', 'edd_add_customer_column_to_users_table' );
/**
* Display customer details on Users list table.
*
* @since 3.0
*
* @param string $value Existing value of the custom column.
* @param string $column_name Column name.
* @param int $user_id User ID.
*
* @return string URL to Customer page, existing value otherwise.
*/
function edd_render_customer_column( $value, $column_name, $user_id ) {
if ( 'edd_customer' === $column_name ) {
$customer = new EDD_Customer( $user_id, true );
if ( $customer->id > 0 ) {
$name = '#' . $customer->id . ' ';
$name .= ! empty( $customer->name ) ? $customer->name : '<em>' . __( 'Unnamed Customer', 'easy-digital-downloads' ) . '</em>';
$view_url = edd_get_admin_url(
array(
'page' => 'edd-customers',
'view' => 'overview',
'id' => absint( $customer->id ),
)
);
return '<a href="' . esc_url( $view_url ) . '">' . $name . '</a>';
}
}
return $value;
}
add_action( 'manage_users_custom_column', 'edd_render_customer_column', 10, 3 );
/**
* Renders the customer details header (gravatar/name).
*
* @since 3.0
* @param \EDD_Customer $customer
* @return void
*/
function edd_render_customer_details_header( \EDD_Customer $customer ) {
?>
<div class="edd-item-header-small">
<?php echo get_avatar( $customer->email, 30 ); ?> <span><?php echo esc_html( $customer->name ); ?></span>
</div>
<?php
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,315 @@
<?php
/**
* Dashboard Widgets
*
* @package EDD
* @subpackage Admin/Dashboard
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Registers the dashboard widgets
*
* @author Sunny Ratilal
* @since 1.2.2
* @return void
*/
function edd_register_dashboard_widgets() {
if ( current_user_can( apply_filters( 'edd_dashboard_stats_cap', 'view_shop_reports' ) ) ) {
wp_add_dashboard_widget( 'edd_dashboard_sales', __('Easy Digital Downloads Sales Summary','easy-digital-downloads' ), 'edd_dashboard_sales_widget' );
}
}
add_action('wp_dashboard_setup', 'edd_register_dashboard_widgets', 10 );
/**
* Sales Summary Dashboard Widget
*
* Builds and renders the Sales Summary dashboard widget. This widget displays
* the current month's sales and earnings, total sales and earnings best selling
* downloads as well as recent purchases made on your EDD Store.
*
* @author Sunny Ratilal
* @since 1.2.2
* @return void
*/
function edd_dashboard_sales_widget() {
if ( ! edd_has_upgrade_completed( 'migrate_orders' ) ) {
if ( get_option( 'edd_v30_cli_migration_running' ) ) {
printf(
'<p>%1$s %2$s</p>',
esc_html__( 'Easy Digital Downloads is performing a database migration via WP-CLI.', 'easy-digital-downloads' ),
esc_html__( 'This summary will be available when that has completed.', 'easy-digital-downloads' )
);
return;
}
global $wpdb;
$orders = $wpdb->get_var( "SELECT ID FROM {$wpdb->posts} WHERE post_type = 'edd_payment' LIMIT 1" );
if ( ! empty( $orders ) ) {
$url = add_query_arg(
array(
'page' => 'edd-upgrades',
'edd-upgrade' => 'v30_migration',
),
admin_url( 'index.php' )
);
printf(
'<p>%1$s %2$s<a href="%3$s">%4$s</a></p>',
esc_html__( 'Easy Digital Downloads needs to upgrade the database.', 'easy-digital-downloads' ),
esc_html__( 'This summary will be available when that has completed.', 'easy-digital-downloads' ),
esc_url( $url ),
esc_html__( 'Begin the upgrade.', 'easy-digital-downloads' )
);
return;
}
}
wp_enqueue_script( 'edd-admin-dashboard' );
/**
* Action hook to add content to the dashboard widget.
* This content will not be replaced by the AJAX function:
* only the "edd-loading" content will.
*
* @since 2.11.4
*/
do_action( 'edd_dashboard_sales_widget' );
?>
<p class="edd-loading"><img src="<?php echo esc_url( EDD_PLUGIN_URL . 'assets/images/loading.gif' ); ?>"></p>
<?php
}
/**
* Gets the sales earnings/count data for the dashboard widget.
*
* @since 3.0.0
* @return array
*/
function edd_get_dashboard_sales_widget_data() {
$data = array();
$ranges = array( 'this_month', 'last_month', 'today', 'total' );
foreach ( $ranges as $range ) {
$args = array(
'range' => $range,
'output' => 'formatted',
'revenue_type' => 'net',
);
if ( 'total' === $range ) {
unset( $args['range'] );
}
// Remove filters so that deprecation notices are not unnecessarily logged outside of reports.
remove_all_filters( 'edd_report_views' );
$stats = new EDD\Stats( $args );
$data[ $range ] = array(
'earnings' => $stats->get_order_earnings(),
'count' => $stats->get_order_count(),
);
}
return $data;
}
/**
* Loads the dashboard sales widget via ajax
*
* @since 2.1
* @return void
*/
function edd_load_dashboard_sales_widget( ) {
if ( ! current_user_can( apply_filters( 'edd_dashboard_stats_cap', 'view_shop_reports' ) ) ) {
die();
}
$stats = new EDD_Payment_Stats();
$data = edd_get_dashboard_sales_widget_data(); ?>
<div class="edd_dashboard_widget">
<div class="table table_left table_current_month">
<table>
<thead>
<tr>
<td colspan="2"><?php esc_html_e( 'Current Month', 'easy-digital-downloads' ); ?> &mdash; <?php esc_html_e( 'Net', 'easy-digital-downloads' ); ?></td>
</tr>
</thead>
<tbody>
<tr>
<td class="first t monthly_earnings"><?php esc_html_e( 'Earnings', 'easy-digital-downloads' ); ?></td>
<td class="b b-earnings"><?php echo esc_html( $data['this_month']['earnings'] ); ?></td>
</tr>
<td class="first t monthly_sales"><?php echo esc_html( _n( 'Sale', 'Sales', $data['this_month']['count'], 'easy-digital-downloads' ) ); ?></td>
<td class="b b-sales"><?php echo esc_html( $data['this_month']['count'] ); ?></td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<td colspan="2"><?php esc_html_e( 'Last Month', 'easy-digital-downloads' ); ?> &mdash; <?php esc_html_e( 'Net', 'easy-digital-downloads' ); ?></td>
</tr>
</thead>
<tbody>
<tr>
<td class="first t earnings"><?php esc_html_e( 'Earnings', 'easy-digital-downloads' ); ?></td>
<td class="b b-last-month-earnings"><?php echo esc_html( $data['last_month']['earnings'] ); ?></td>
</tr>
<tr>
<td class="first t sales"><?php echo esc_html( _n( 'Sale', 'Sales', $data['last_month']['count'], 'easy-digital-downloads' ) ); ?></td>
<td class="b b-last-month-sales"><?php echo esc_html( $data['last_month']['count'] ); ?></td>
</tr>
</tbody>
</table>
</div>
<div class="table table_right table_today">
<table>
<thead>
<tr>
<td colspan="2">
<?php esc_html_e( 'Today', 'easy-digital-downloads' ); ?> &mdash; <?php esc_html_e( 'Net', 'easy-digital-downloads' ); ?>
</td>
</tr>
</thead>
<tbody>
<tr>
<td class="t sales"><?php esc_html_e( 'Earnings', 'easy-digital-downloads' ); ?></td>
<td class="last b b-earnings">
<?php echo esc_html( $data['today']['earnings'] ); ?>
</td>
</tr>
<tr class="t sales">
<td class="t sales"><?php echo esc_html( _n( 'Sale', 'Sales', $data['today']['count'], 'easy-digital-downloads' ) ); ?></td>
<td class="last b b-sales"><?php echo esc_html( $data['today']['count'] ); ?></td>
</tr>
</tbody>
</table>
</div>
<div class="table table_right table_totals">
<table>
<thead>
<tr>
<td colspan="2">
<?php esc_html_e( 'All Time', 'easy-digital-downloads' ); ?> &mdash; <?php esc_html_e( 'Net', 'easy-digital-downloads' ); ?>
</td>
</tr>
</thead>
<tbody>
<tr>
<td class="t earnings"><?php esc_html_e( 'Total Earnings', 'easy-digital-downloads' ); ?></td>
<td class="last b b-earnings"><?php echo esc_html( $data['total']['earnings'] ); ?></td>
</tr>
<tr>
<td class="t sales"><?php echo esc_html( _n( 'Sale', 'Sales', $data['total']['count'], 'easy-digital-downloads' ) ); ?></td>
<td class="last b b-sales"><?php echo esc_html( $data['total']['count'] ); ?></td>
</tr>
</tbody>
</table>
</div>
<div style="clear: both"></div>
<?php do_action( 'edd_sales_summary_widget_after_stats', $stats ); ?>
<?php
$orders = edd_get_orders(
array(
'number' => 5,
'status' => edd_get_net_order_statuses(),
'type' => 'sale',
)
);
if ( $orders ) { ?>
<div class="table recent_orders">
<h3><?php esc_html_e( 'Recent Orders', 'easy-digital-downloads' ); ?></h3>
<ul>
<?php
foreach ( $orders as $order ) {
$link = edd_get_admin_url(
array(
'page' => 'edd-payment-history',
'view' => 'view-order-details',
'id' => urlencode( $order->id ),
),
admin_url( 'edit.php' )
);
?>
<li class="edd_order_label">
<a href="<?php echo esc_url( $link ); ?>">
<?php
$customer = edd_get_customer( $order->customer_id );
$customer_name = ! empty( $customer->name ) ? $customer->name : __( 'No Name', 'easy-digital-downloads' );
$item_count = edd_count_order_items( array( 'order_id' => $order->id ) );
echo wp_kses_post(
sprintf(
/* translators: 1. customer name; 2. number of items purchased; 3. order total */
_n(
'%1$s purchased %2$s item for <strong>%3$s</strong>',
'%1$s purchased %2$s items for <strong>%3$s</strong>',
$item_count,
'easy-digital-downloads'
),
$customer_name,
$item_count,
edd_currency_filter( edd_format_amount( edd_get_order_total( $order->id ) ), $order->currency )
)
);
?>
</a>
<br /><?php echo esc_html( edd_date_i18n( $order->date_created ) ); ?> &mdash; <?php echo edd_get_status_label( $order->status ); ?>
</li>
<?php } // End foreach ?>
</ul>
<?php
$all_orders_link = edd_get_admin_url(
array(
'page' => 'edd-payment-history',
)
);
?>
<a href="<?php echo esc_url( $all_orders_link ); ?>" class="button-secondary"><?php esc_html_e( 'View All Orders', 'easy-digital-downloads' ); ?></a>
</div>
<?php } // End if ?>
<?php do_action( 'edd_sales_summary_widget_after_orders', $orders ); ?>
<?php
if ( has_action( 'edd_sales_summary_widget_after_purchases' ) ) {
_edd_deprecated_hook(
'edd_sales_summary_widget_after_purchases',
'3.1.0.1',
'edd_sales_summary_widget_after_orders',
'Note: The replacement hook uses the EDD 3.0 order objects, instead of payment objects. Developers will need to make adjustments accordingly.'
);
do_action( 'edd_sales_summary_widget_after_purchases', edd_get_payments( array( 'number' => 5, 'status' => 'complete' ) ) );
}
?>
</div>
<?php
die();
}
add_action( 'wp_ajax_edd_load_dashboard_widget', 'edd_load_dashboard_sales_widget' );
/**
* Add download count to At a glance widget
*
* @author Daniel J Griffiths
* @since 2.1
* @return void
*/
function edd_dashboard_at_a_glance_widget( $items ) {
$num_posts = wp_count_posts( 'download' );
if ( $num_posts && $num_posts->publish ) {
$text = _n( '%s ' . edd_get_label_singular(), '%s ' . edd_get_label_plural(), $num_posts->publish, 'easy-digital-downloads' );
$text = sprintf( $text, number_format_i18n( $num_posts->publish ) );
if ( current_user_can( 'edit_products' ) ) {
$text = sprintf( '<a class="download-count" href="edit.php?post_type=download">%1$s</a>', $text );
} else {
$text = sprintf( '<span class="download-count">%1$s</span>', $text );
}
$items[] = $text;
}
return $items;
}
add_filter( 'dashboard_glance_items', 'edd_dashboard_at_a_glance_widget', 1 );

View File

@ -0,0 +1,236 @@
<?php
/**
* Add Discount Page
*
* @package EDD
* @subpackage Admin/Discounts
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
?>
<div class="wrap">
<h1><?php esc_html_e( 'Add New Discount', 'easy-digital-downloads' ); ?></h1>
<hr class="wp-header-end">
<form id="edd-add-discount" action="" method="post">
<?php do_action( 'edd_add_discount_form_top' ); ?>
<table class="form-table">
<tbody>
<?php do_action( 'edd_add_discount_form_before_name' ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd-name"><?php esc_html_e( 'Name', 'easy-digital-downloads' ); ?></label>
</th>
<td>
<input name="name" required="required" id="edd-name" type="text" value="" placeholder="<?php esc_html_e( 'Summer Sale', 'easy-digital-downloads' ); ?>" />
<p class="description"><?php esc_html_e( 'The name of this discount. Customers will see this on checkout.', 'easy-digital-downloads' ); ?></p>
</td>
</tr>
<?php do_action( 'edd_add_discount_form_before_code' ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd-code"><?php esc_html_e( 'Code', 'easy-digital-downloads' ); ?></label>
</th>
<td>
<input type="text" required="required" id="edd-code" name="code" class="code" value="" pattern="[a-zA-Z0-9-_]+" placeholder="<?php esc_html_e( '10PERCENT', 'easy-digital-downloads' ); ?>" />
<p class="description"><?php esc_html_e( 'The code customers will enter to apply this discount. Only alphanumeric characters are allowed.', 'easy-digital-downloads' ); ?></p>
</td>
</tr>
<?php do_action( 'edd_add_discount_form_before_type' ); ?>
<?php do_action( 'edd_add_discount_form_before_amount' ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd-amount"><?php esc_html_e( 'Amount', 'easy-digital-downloads' ); ?></label>
</th>
<td>
<span class="edd-amount-type-wrapper">
<input type="text" required="required" class="edd-price-field" id="edd-amount" name="amount" value="" placeholder="<?php esc_html_e( '10.00', 'easy-digital-downloads' ); ?>"/>
<label for="edd-amount-type" class="screen-reader-text"><?php esc_html_e( 'Amount Type', 'easy-digital-downloads' ); ?></label>
<select name="amount_type" id="edd-amount-type">
<option value="percent">%</option>
<option value="flat"><?php echo esc_html( edd_currency_symbol() ); ?></option>
</select>
</span>
<p class="description"><?php esc_html_e( 'The amount as a percentage or flat rate. Cannot be left blank.', 'easy-digital-downloads' ); ?></p>
</td>
</tr>
<?php do_action( 'edd_add_discount_form_before_products' ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd_products"><?php printf( esc_html__( '%s Requirements', 'easy-digital-downloads' ), edd_get_label_singular() ); ?></label>
</th>
<td>
<?php echo EDD()->html->product_dropdown( array(
'name' => 'product_reqs[]',
'id' => 'edd_products',
'selected' => array(),
'multiple' => true,
'chosen' => true,
'placeholder' => sprintf( esc_html__( 'Select %s', 'easy-digital-downloads' ), esc_html( edd_get_label_plural() ) ),
) ); // WPCS: XSS ok. ?>
<div id="edd-discount-product-conditions" style="display:none;">
<p>
<select id="edd-product-condition" name="product_condition">
<option value="all"><?php printf( esc_html__( 'Cart must contain all selected %s', 'easy-digital-downloads' ), esc_html( edd_get_label_plural() ) ); ?></option>
<option value="any"><?php printf( esc_html__( 'Cart needs one or more of the selected %s', 'easy-digital-downloads' ), esc_html( edd_get_label_plural() ) ); ?></option>
</select>
</p>
<p>
<label>
<input type="radio" class="tog" name="scope" value="global" checked="checked"/>
<?php esc_html_e( 'Apply discount to entire purchase.', 'easy-digital-downloads' ); ?>
</label><br/>
<label>
<input type="radio" class="tog" name="scope" value="not_global"/>
<?php printf( esc_html__( 'Apply discount only to selected %s.', 'easy-digital-downloads' ), esc_html( edd_get_label_plural() ) ); ?>
</label>
</p>
</div>
<p class="description"><?php printf( esc_html__( '%s this discount can only be applied to. Leave blank for any.', 'easy-digital-downloads' ), esc_html( edd_get_label_plural() ) ); ?></p>
</td>
</tr>
<?php do_action( 'edd_add_discount_form_before_excluded_products' ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd-excluded-products"><?php printf( esc_html__( 'Excluded %s', 'easy-digital-downloads' ), esc_html( edd_get_label_plural() ) ); ?></label>
</th>
<td>
<?php echo EDD()->html->product_dropdown( array(
'name' => 'excluded_products[]',
'id' => 'excluded_products',
'selected' => array(),
'multiple' => true,
'chosen' => true,
'placeholder' => sprintf( esc_html__( 'Select %s', 'easy-digital-downloads' ), esc_html( edd_get_label_plural() ) ),
) ); // WPCS: XSS ok. ?>
<p class="description"><?php printf( esc_html__( '%s this discount cannot be applied to. Leave blank for none.', 'easy-digital-downloads' ), esc_html( edd_get_label_plural() ) ); ?></p>
</td>
</tr>
<?php do_action( 'edd_add_discount_form_before_start' ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd-start"><?php esc_html_e( 'Start date', 'easy-digital-downloads' ); ?></label>
</th>
<td class="edd-discount-datetime">
<input name="start_date" id="edd-start" type="text" value="" class="edd_datepicker" data-format="<?php echo esc_attr( edd_get_date_picker_format() ); ?>" placeholder="<?php echo esc_attr( edd_get_date_picker_format() ); ?>" />
<label class="screen-reader-text" for="start-date-hour">
<?php esc_html_e( 'Start Date Hour', 'easy-digital-downloads' ); ?>
</label>
<input type="number" min="0" max="24" step="1" name="start_date_hour" id="start-date-hour" placeholder="00" />
:
<label class="screen-reader-text" for="start-date-minute">
<?php esc_html_e( 'Start Date Minute', 'easy-digital-downloads' ); ?>
</label>
<input type="number" min="0" max="59" step="1" name="start_date_minute" id="start-date-minute" placeholder="00" />
<?php echo esc_html( ' (' . edd_get_timezone_abbr() . ')' ); ?>
<p class="description"><?php esc_html_e( 'Pick the date and time this discount will start on. Leave blank for no start date.', 'easy-digital-downloads' ); ?></p>
</td>
</tr>
<?php do_action( 'edd_add_discount_form_before_expiration' ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd-expiration"><?php esc_html_e( 'Expiration date', 'easy-digital-downloads' ); ?></label>
</th>
<td class="edd-discount-datetime">
<input name="end_date" id="edd-expiration" type="text" class="edd_datepicker" data-format="<?php echo esc_attr( edd_get_date_picker_format() ); ?>" placeholder="<?php echo esc_attr( edd_get_date_picker_format() ); ?>" />
<label class="screen-reader-text" for="end-date-hour">
<?php esc_html_e( 'Expiration Date Hour', 'easy-digital-downloads' ); ?>
</label>
<input type="number" min="0" max="24" step="1" name="end_date_hour" id="end-date-hour" placeholder="23" />
:
<label class="screen-reader-text" for="end-date-minute">
<?php esc_html_e( 'Expiration Date Minute', 'easy-digital-downloads' ); ?>
</label>
<input type="number" min="0" max="59" step="1" name="end_date_minute" id="end-date-minute" placeholder="59" />
<?php echo esc_html( ' (' . edd_get_timezone_abbr() . ')' ); ?>
<p class="description"><?php esc_html_e( 'Pick the date and time this discount will expire on. Leave blank to never expire.', 'easy-digital-downloads' ); ?></p>
</td>
</tr>
<?php do_action( 'edd_add_discount_form_before_min_cart_amount' ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd-min-cart-amount"><?php esc_html_e( 'Minimum Amount', 'easy-digital-downloads' ); ?></label>
</th>
<td>
<input type="text" id="edd-min-cart-amount" name="min_charge_amount" value="" placeholder="<?php esc_html_e( 'No minimum', 'easy-digital-downloads' ); ?>" />
<p class="description"><?php esc_html_e( 'The minimum subtotal of item prices in a cart before this discount may be applied.', 'easy-digital-downloads' ); ?></p>
</td>
</tr>
<?php do_action( 'edd_add_discount_form_before_max_uses' ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd-max-uses"><?php esc_html_e( 'Max Uses', 'easy-digital-downloads' ); ?></label>
</th>
<td>
<input type="text" id="edd-max-uses" name="max_uses" value="" placeholder="<?php esc_html_e( 'Unlimited', 'easy-digital-downloads' ); ?>" />
<p class="description"><?php esc_html_e( 'The maximum number of times this discount can be used.', 'easy-digital-downloads' ); ?></p>
</td>
</tr>
<?php do_action( 'edd_add_discount_form_before_use_once' ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd-use-once"><?php esc_html_e( 'Use Once Per Customer', 'easy-digital-downloads' ); ?></label>
</th>
<td>
<input type="checkbox" id="edd-use-once" name="once_per_customer" value="1"/>
<span class="description"><?php esc_html_e( 'Prevent customers from using this discount more than once.', 'easy-digital-downloads' ); ?></span>
</td>
</tr>
<?php
/**
* Action after "Use Once Per Customer" checkbox.
*
* @since 3.0
*/
?>
<?php do_action( 'edd_add_discount_form_after_use_once' ); ?>
</tbody>
</table>
<?php do_action( 'edd_add_discount_form_bottom' ); ?>
<p class="submit">
<input type="hidden" name="type" value="discount" />
<input type="hidden" name="edd-action" value="add_discount"/>
<input type="hidden" name="edd-redirect" value="<?php echo esc_url( edd_get_admin_url( array( 'page' => 'edd-discounts' ) ) ); ?>"/>
<input type="hidden" name="edd-discount-nonce" value="<?php echo esc_attr( wp_create_nonce( 'edd_discount_nonce' ) ); ?>"/>
<input type="submit" value="<?php esc_html_e( 'Add Discount Code', 'easy-digital-downloads' ); ?>" class="button-primary"/>
</p>
</form>
</div>

View File

@ -0,0 +1,430 @@
<?php
/**
* Discount Codes Table Class
*
* @package EDD
* @subpackage Admin/Discounts
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.4
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
use EDD\Admin\List_Table;
/**
* EDD_Discount_Codes_Table Class
*
* Renders the Discount Codes table on the Discount Codes page
*
* @since 1.4
* @since 3.0 Updated to work with the discount code migration to custom tables.
*/
class EDD_Discount_Codes_Table extends List_Table {
/**
* Get things started
*
* @since 1.4
* @see WP_List_Table::__construct()
*/
public function __construct() {
parent::__construct( array(
'singular' => 'discount',
'plural' => 'discounts',
'ajax' => false,
) );
$this->process_bulk_action();
$this->get_counts();
}
/**
* Get the base URL for the discount list table
*
* @since 3.0
*
* @return string
*/
public function get_base_url() {
// Remove some query arguments
$base = remove_query_arg( edd_admin_removable_query_args(), edd_get_admin_base_url() );
// Add base query args
return add_query_arg( array(
'page' => 'edd-discounts',
), $base );
}
/**
* Retrieve the table columns
*
* @since 1.4
*
* @return array $columns Array of all the list table columns
*/
public function get_columns() {
return apply_filters( 'edd_discounts_table_columns', array(
'cb' => '<input type="checkbox" />',
'name' => __( 'Name', 'easy-digital-downloads' ),
'code' => __( 'Code', 'easy-digital-downloads' ),
'amount' => __( 'Amount', 'easy-digital-downloads' ),
'use_count' => __( 'Uses', 'easy-digital-downloads' ),
'start_date' => __( 'Start Date', 'easy-digital-downloads' ),
'end_date' => __( 'End Date', 'easy-digital-downloads' )
) );
}
/**
* Retrieve the sortable columns
*
* @since 1.4
*
* @return array Array of all the sortable columns
*/
public function get_sortable_columns() {
return apply_filters( 'edd_discounts_table_sortable_columns', array(
'name' => array( 'name', false ),
'code' => array( 'code', false ),
'use_count' => array( 'use_count', false ),
'start_date' => array( 'start_date', false ),
'end_date' => array( 'end_date', false )
) );
}
/**
* Gets the name of the primary column.
*
* @since 2.5
* @access protected
*
* @return string Name of the primary column.
*/
protected function get_primary_column_name() {
return 'name';
}
/**
* This function renders most of the columns in the list table.
*
* @since 1.4
*
* @param EDD_Discount $discount Discount object.
* @param string $column_name The name of the column
*
* @return string Column Name
*/
public function column_default( $discount, $column_name ) {
$value = property_exists( $discount, $column_name ) ? $discount->$column_name : '';
return apply_filters( 'edd_discounts_table_column', $value, $discount, $column_name );
}
/**
* This function renders the amount column.
*
* @since 3.0
*
* @param EDD_Discount $discount Data for the discount code.
* @return string Formatted amount.
*/
public function column_amount( $discount ) {
return edd_format_discount_rate( $discount->type, $discount->amount );
}
/**
* This function renders the start column.
*
* @since 3.0
*
* @param EDD_Discount $discount Discount object.
* @return string Start date
*/
public function column_start_date( $discount ) {
$start_date = $discount->start_date;
$timezone_abbreviation = edd_get_timezone_abbr();
if ( $start_date ) {
$display = edd_date_i18n( $start_date, 'M. d, Y' ) . '<br>' . edd_date_i18n( $start_date, 'H:i' ) . ' ' . $timezone_abbreviation;
} else {
$display = '&mdash;';
}
return $display;
}
/**
* Render the Expiration column.
*
* @since 3.0
*
* @param EDD_Discount $discount Discount object.
* @return string Expiration date.
*/
public function column_end_date( $discount ) {
$expiration = $discount->end_date;
$timezone_abbreviation = edd_get_timezone_abbr();
if ( $expiration ) {
$display = edd_date_i18n( $expiration, 'M. d, Y' ) . '<br>' . edd_date_i18n( $expiration, 'H:i' ) . ' ' . $timezone_abbreviation;
} else {
$display = '&mdash;';
}
return $display;
}
/**
* Render the Name column.
*
* @since 1.4
*
* @param EDD_Discount $discount Discount object.
* @return string Data shown in the Name column
*/
public function column_name( $discount ) {
$base = $this->get_base_url();
$state = '';
$row_actions = array();
$status = $this->get_status();
// Bail if current user cannot manage discounts
if ( ! current_user_can( 'manage_shop_discounts' ) ) {
return;
}
// State
if ( ( ! empty( $status ) && ( $status !== $discount->status ) ) || ( 'active' !== $discount->status ) ) {
$state = ' &mdash; ' . edd_get_discount_status_label( $discount->id );
}
// Edit
$row_actions['edit'] = '<a href="' . esc_url( add_query_arg( array(
'edd-action' => 'edit_discount',
'discount' => absint( $discount->id ),
), $base ) ) . '">' . __( 'Edit', 'easy-digital-downloads' ) . '</a>';
// Active, so add "deactivate" action
if ( 'active' === strtolower( $discount->status ) ) {
$row_actions['cancel'] = '<a href="' . esc_url( wp_nonce_url( add_query_arg( array(
'edd-action' => 'deactivate_discount',
'discount' => absint( $discount->id ),
), $base ), 'edd_discount_nonce' ) ) . '">' . __( 'Deactivate', 'easy-digital-downloads' ) . '</a>';
// Inactive, so add "activate" action
} elseif ( 'inactive' === strtolower( $discount->status ) ) {
$row_actions['activate'] = '<a href="' . esc_url( wp_nonce_url( add_query_arg( array(
'edd-action' => 'activate_discount',
'discount' => absint( $discount->id ),
), $base ), 'edd_discount_nonce' ) ) . '">' . __( 'Activate', 'easy-digital-downloads' ) . '</a>';
}
// Delete
if ( 0 === (int) $discount->use_count ) {
$row_actions['delete'] = '<a href="' . esc_url( wp_nonce_url( add_query_arg( array(
'edd-action' => 'delete_discount',
'discount' => absint( $discount->id ),
), $base ), 'edd_discount_nonce' ) ) . '">' . __( 'Delete', 'easy-digital-downloads' ) . '</a>';
} else {
$row_actions['orders'] = '<a href="' . esc_url(
edd_get_admin_url(
array(
'page' => 'edd-payment-history',
'discount_id' => absint( $discount->id ),
)
)
) . '">' . __( 'View Orders', 'easy-digital-downloads' ) . '</a>';
}
// Filter all discount row actions
$row_actions = apply_filters( 'edd_discount_row_actions', $row_actions, $discount );
// Wrap discount title in strong anchor
$discount_title = '<strong><a class="row-title" href="' . esc_url( add_query_arg( array(
'edd-action' => 'edit_discount',
'discount' => absint( $discount->id ),
), $base ) ) . '">' . stripslashes( $discount->name ) . '</a>' . esc_html( $state ) . '</strong>';
/**
* Filter to allow additional content to be appended to the discount title.
*
* @since 3.0
*
* @param EDD_Discount $discount Discount object.
* @param string $base The base URL for the discount list table.
* @param string $status The queried discount status.
* @return string Additional data shown in the Name column
*/
$additional_content = apply_filters( 'edd_discount_row_after_title', '', $discount, $base, $status );
// Return discount title & row actions
return $discount_title . $additional_content . $this->row_actions( $row_actions );
}
/**
* Render the checkbox column.
*
* @since 1.4
*
* @param EDD_Discount $discount Discount object.
* @return string Checkbox HTML.
*/
public function column_cb( $discount ) {
return sprintf(
'<input type="checkbox" name="%1$s[]" id="%1$s-%2$s" value="%2$s" /><label for="%1$s-%2$s" class="screen-reader-text">%3$s</label>',
/*$1%s*/ 'discount',
/*$2%s*/ absint( $discount->id ),
/* translators: discount name */
esc_html( sprintf( __( 'Select %s', 'easy-digital-downloads' ), $discount->name ) )
);
}
/**
* Return discount code wrapped in a `<code>` tag.
*
* @since 3.0
*
* @param EDD_Discount $discount Discount object.
* @return string Discount code HTML.
*/
public function column_code( $discount ) {
return '<code class="edd-discount-code">' . $discount->code . '</code>';
}
/**
* Message to be displayed when there are no items.
*
* @since 1.7.2
*/
public function no_items() {
esc_html_e( 'No discounts found.', 'easy-digital-downloads' );
}
/**
* Retrieve the bulk actions
*
* @since 1.4
* @return array $actions Array of the bulk actions
*/
public function get_bulk_actions() {
return array(
'activate' => __( 'Activate', 'easy-digital-downloads' ),
'deactivate' => __( 'Deactivate', 'easy-digital-downloads' ),
'delete' => __( 'Delete', 'easy-digital-downloads' )
);
}
/**
* Process bulk actions.
*
* @since 1.4
*/
public function process_bulk_action() {
// Bail if a nonce was not supplied.
if ( ! isset( $_REQUEST['_wpnonce'] ) ) {
return;
}
if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'bulk-discounts' ) ) {
return;
}
check_admin_referer( 'bulk-discounts' );
$ids = wp_parse_id_list( (array) $this->get_request_var( 'discount', false ) );
// Bail if no IDs
if ( empty( $ids ) ) {
return;
}
foreach ( $ids as $id ) {
switch ( $this->current_action() ) {
case 'delete':
edd_delete_discount( $id );
break;
case 'cancel':
edd_update_discount_status( $id, 'cancelled' );
break;
case 'activate':
edd_update_discount_status( $id, 'active' );
break;
case 'deactivate':
edd_update_discount_status( $id, 'inactive' );
break;
}
}
}
/**
* Retrieve the discount code counts.
*
* @since 1.4
*/
public function get_counts() {
$this->counts = edd_get_discount_counts();
}
/**
* Retrieves all the data for all the discount codes.
*
* @since 1.4
* @deprecated 3.0 Use get_data()
*
* @return array Discount codes.
*/
public function discount_codes_data() {
_edd_deprecated_function( __METHOD__, '3.0', 'EDD_Discount_Codes_Table::get_data()' );
return $this->get_data();
}
/**
* Retrieves all of the table data for the discount codes.
*
* @since 3.0
*
* @return array Discount codes table data.
*/
public function get_data() {
// Parse pagination
$this->args = $this->parse_pagination_args( array(
'status' => $this->get_status(),
'search' => $this->get_search(),
) );
// Return data
return edd_get_discounts( $this->args );
}
/**
* Setup the final data for the table
*
* @since 1.4
*/
public function prepare_items() {
$columns = $this->get_columns();
$hidden = array();
$sortable = $this->get_sortable_columns();
$this->_column_headers = array( $columns, $hidden, $sortable );
$this->items = $this->get_data();
$status = $this->get_status( 'total' );
// Setup pagination
$this->set_pagination_args( array(
'total_pages' => ceil( $this->counts[ $status ] / $this->per_page ),
'total_items' => $this->counts[ $status ],
'per_page' => $this->per_page,
) );
}
}

View File

@ -0,0 +1,86 @@
<?php
/**
* Contextual Help
*
* @package EDD
* @subpackage Admin/Discounts
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.2.3
*/
use EDD\Admin\Pass_Manager;
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Adds the Contextual Help for the Discount Codes Page
*
* @since 1.3
* @return void
*/
function edd_discounts_contextual_help() {
$screen = get_current_screen();
$pass_manager = new Pass_Manager();
if ( $pass_manager->isFree() ) {
$docs_url = edd_link_helper(
'https://easydigitaldownloads.com/docs/',
array(
'utm_medium' => 'discounts-contextual-help',
'utm_content' => 'documentation',
)
);
$upgrade_url = edd_link_helper(
'https://easydigitaldownloads.com/lite-upgrade/',
array(
'utm_medium' => 'discounts-contextual-help',
'utm_content' => 'lite-upgrade',
)
);
$screen->set_help_sidebar(
'<p><strong>' . __( 'For more information:', 'easy-digital-downloads' ) . '</strong></p>' .
'<p>' . sprintf( __( 'Visit the <a href="%s">documentation</a> on the Easy Digital Downloads website.', 'easy-digital-downloads' ), $docs_url ) . '</p>' .
'<p>' . sprintf(
__( 'Need more from your Easy Digital Downloads store? <a href="%s">Upgrade Now</a>!', 'easy-digital-downloads' ),
$upgrade_url
) . '</p>'
);
}
$screen->add_help_tab( array(
'id' => 'edd-discount-general',
'title' => __( 'General', 'easy-digital-downloads' ),
'content' =>
'<p>' . __( 'Discount codes allow you to offer buyers special discounts by having them enter predefined codes during checkout.', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( 'Discount codes that are set to "inactive" cannot be redeemed.', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( 'Discount codes can be setup to only be used only one time by each customer. If a customer attempts to use a code a second time, they will be given an error.', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( 'Discount codes that have already been used cannot be deleted for data integrity and reporting purposes.', 'easy-digital-downloads' ) . '</p>'
) );
$screen->add_help_tab( array(
'id' => 'edd-discount-add',
'title' => __( 'Adding Discounts', 'easy-digital-downloads' ),
'content' =>
'<p>' . __( 'You can create any number of discount codes easily from this page.', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( 'Discount codes have several options:', 'easy-digital-downloads' ) . '</p>' .
'<ul>'.
'<li>' . __( '<strong>Name</strong> - this is the name given to the discount. Used primarily for administrative purposes.', 'easy-digital-downloads' ) . '</li>' .
'<li>' . __( '<strong>Code</strong> - this is the unique code that customers will enter during checkout to redeem the code.', 'easy-digital-downloads' ) . '</li>' .
'<li>' . __( '<strong>Type</strong> - this is the type of discount this code awards.', 'easy-digital-downloads' ) . '</li>' .
'<li>' . __( '<strong>Amount</strong> - this is the discount amount provided by this code. For percentage based discounts, enter a number such as 70 for 70%. Do not enter a percent sign.', 'easy-digital-downloads' ) . '</li>' .
'<li>' . __( '<strong>Requirements</strong> - this allows you to select the product(s) that are required to be purchased in order for a discount to be applied.', 'easy-digital-downloads' ) . '</li>' .
'<li>' . __( '<strong>Condition</strong> - this lets you set whether all selected products must be in the cart, or just a minimum of one.', 'easy-digital-downloads' ) . '</li>' .
'<li>' . __( '<strong>Apply discount only to selected Downloads?</strong> - If this box is checked, only the prices of the required products will be discounted. If left unchecked, the discount will apply to all products in the cart.', 'easy-digital-downloads' ) . '</li>' .
'<li>' . __( '<strong>Start Date</strong> - this is the date that this code becomes available. If a customer attempts to redeem the code prior to this date, they will be given an error. This is optional.', 'easy-digital-downloads' ) . '</li>' .
'<li>' . __( '<strong>Expiration Date</strong> - this is the end date for the discount. After this date, the code will no longer be able to be used. This is optional.', 'easy-digital-downloads' ) . '</li>' .
'<li>' . __( '<strong>Minimum Amount</strong> - this is the minimum purchase amount required to use this code. If a customer has less than this amount in their cart, they will be given an error. This is optional.', 'easy-digital-downloads' ) . '</li>' .
'<li>' . __( '<strong>Max Uses</strong> - this is the maximum number of times this discount can be redeemed. Once this number is reached, no more customers will be allowed to use it.', 'easy-digital-downloads' ) . '</li>' .
'</ul>'
) );
do_action( 'edd_discounts_contextual_help', $screen );
}
add_action( 'load-download_page_edd-discounts', 'edd_discounts_contextual_help' );

View File

@ -0,0 +1,394 @@
<?php
/**
* Discount Actions
*
* @package EDD
* @subpackage Admin/Discounts
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.0.8.1
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Sets up and stores a new discount code.
*
* @since 1.0
* @since 3.0 Added backwards compatibility for pre-3.0 discount data. Added discount start/end time.
*
* @param array $data Discount code data.
*/
function edd_admin_add_discount( $data = array() ) {
// Bail if no nonce or nonce fails.
if ( ! isset( $data['edd-discount-nonce'] ) || ! wp_verify_nonce( $data['edd-discount-nonce'], 'edd_discount_nonce' ) ) {
return;
}
// Bail if current user cannot manage shop discounts.
if ( ! current_user_can( 'manage_shop_discounts' ) ) {
wp_die( __( 'You do not have permission to create discount codes', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
// Bail if discount does not exist.
if ( edd_get_discount_by( 'code', $data['code'] ) ) {
edd_redirect( add_query_arg( 'edd-message', 'discount_exists', $data['edd-redirect'] ) );
}
// Bail if missing important data.
if ( empty( $data['name'] ) || empty( $data['code'] ) || empty( $data['amount_type'] ) || ( empty( $data['amount'] ) && 0 !== absint( $data['amount'] ) ) ) {
edd_redirect( add_query_arg( 'edd-message', 'discount_validation_failed' ) );
}
// Verify only accepted characters.
$sanitized = preg_replace( '/[^a-zA-Z0-9-_]+/', '', $data['code'] );
if ( strtoupper( $data['code'] ) !== strtoupper( $sanitized ) ) {
edd_redirect( add_query_arg( 'edd-message', 'discount_invalid_code' ) );
}
if ( ! is_numeric( $data['amount'] ) ) {
edd_redirect( add_query_arg( 'edd-message', 'discount_invalid_amount' ) );
}
// Setup default discount values.
$to_add = array();
$to_add['status'] = 'active';
$current_timestamp = current_time( 'timestamp' );
$data = array_filter( $data );
foreach ( $data as $column => $value ) {
switch ( $column ) {
// We skip these here as they are handled below.
case 'start_date':
case 'start':
case 'end_date':
case 'expiration':
break;
case 'product_reqs':
$to_add[ $column ] = $value;
break;
default:
$to_add[ $column ] = is_array( $value )
? array_map( 'sanitize_text_field', $value )
: sanitize_text_field( $value );
break;
}
}
// Start date.
if ( ! empty( $data['start_date'] ) ) {
$start_date = sanitize_text_field( $data['start_date'] );
$start_date_hour = isset( $data['start_date_hour'] ) && (int) $data['start_date_hour'] >= 0 && (int) $data['start_date_hour'] <= 23
? intval( $data['start_date_hour'] )
: '00';
$start_date_minute = isset( $data['start_date_minute'] ) && (int) $data['start_date_minute'] >= 0 && (int) $data['start_date_minute'] <= 59
? intval( $data['start_date_minute'] )
: '00';
// The start date is entered in the user's WP timezone. We need to convert it to UTC prior to saving now.
$date = edd_get_utc_equivalent_date( EDD()->utils->date( $start_date . ' ' . $start_date_hour . ':' . $start_date_minute . ':00', edd_get_timezone_id(), false ) );
$to_add['start_date'] = $date->format( 'Y-m-d H:i:s' );
}
// End date.
if ( ! empty( $data['end_date'] ) ) {
$end_date = sanitize_text_field( $data['end_date'] );
$end_date_hour = isset( $data['end_date_hour'] ) && (int) $data['end_date_hour'] >= 0 && (int) $data['end_date_hour'] <= 23
? intval( $data['end_date_hour'] )
: '23';
$end_date_minute = isset( $data['end_date_minute'] ) && (int) $data['end_date_minute'] >= 0 && (int) $data['end_date_minute'] <= 59
? intval( $data['end_date_minute'] )
: '59';
// The end date is entered in the user's WP timezone. We need to convert it to UTC prior to saving now.
$date = edd_get_utc_equivalent_date( EDD()->utils->date( $end_date . ' ' . $end_date_hour . ':' . $end_date_minute . ':00', edd_get_timezone_id(), false ) );
$to_add['end_date'] = $date->format( 'Y-m-d H:i:s' );
}
// Meta values.
$to_add['product_reqs'] = isset( $data['product_reqs'] ) ? wp_parse_id_list( $data['product_reqs'] ) : '';
$to_add['excluded_products'] = isset( $data['excluded_products'] ) ? wp_parse_id_list( $data['excluded_products'] ) : '';
$to_add = array_filter( $to_add );
// Strip out data that should not be sent to the query methods.
$to_strip = array(
'discount-id',
'edd-redirect',
'edd-action',
'edd-discount-nonce',
'start_date_minute',
'start_date_hour',
'end_date_minute',
'end_date_hour',
);
// Loop through fields to update, and unset known bad keys.
foreach ( $to_add as $key => $value ) {
if ( in_array( $key, $to_strip, true ) ) {
unset( $to_add[ $key ] );
}
}
// Attempt to add.
$created = edd_add_discount( $to_add );
$arg = ! empty( $created )
? 'discount_added'
: 'discount_add_failed';
// Redirect.
edd_redirect( add_query_arg( 'edd-message', sanitize_key( $arg ), $data['edd-redirect'] ) );
}
add_action( 'edd_add_discount', 'edd_admin_add_discount' );
/**
* Saves an edited discount
*
* @since 3.0
* @param array $data Discount code data
* @return void
*/
function edd_admin_edit_discount( $data = array() ) {
// Bail if no nonce or nonce fails
if ( ! isset( $data['edd-discount-nonce'] ) || ! wp_verify_nonce( $data['edd-discount-nonce'], 'edd_discount_nonce' ) ) {
return;
}
// Bail if current user cannot manage shop discounts
if ( ! current_user_can( 'manage_shop_discounts' ) ) {
wp_die( __( 'You do not have permission to edit discount codes', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
// Bail if discount does not exist
if ( empty( $data['discount-id'] ) ) {
wp_die( __( 'No discount ID supplied', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
// Setup default discount values
$discount_id = absint( $data['discount-id'] );
$discount = edd_get_discount( $discount_id );
// Bail if no discount
if ( empty( $discount ) || ( $discount->id <= 0 ) ) {
wp_die( __( 'Invalid discount', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
if ( empty( $data['amount'] ) || ! is_numeric( $data['amount'] ) ) {
edd_redirect( add_query_arg( 'edd-message', 'discount_invalid_amount' ) );
}
// Prepare update
$to_update = array();
$current_time = current_time( 'timestamp' );
$data = array_filter( $data );
foreach ( $data as $column => $value ) {
switch ( $column ) {
// We skip these here as they are handled below.
case 'start_date':
case 'start':
case 'end_date':
case 'expiration':
break;
case 'discount-id':
$to_update['id'] = $value;
break;
default :
$to_update[ $column ] = sanitize_text_field( $value );
break;
}
}
// Start date.
if ( ! empty( $data['start_date'] ) ) {
$start_date = sanitize_text_field( $data['start_date'] );
$start_date_hour = isset( $data['start_date_hour'] ) && (int) $data['start_date_hour'] >= 0 && (int) $data['start_date_hour'] <= 23
? intval( $data['start_date_hour'] )
: '00';
$start_date_minute = isset( $data['start_date_minute'] ) && (int) $data['start_date_minute'] >= 0 && (int) $data['start_date_minute'] <= 59
? intval( $data['start_date_minute'] )
: '00';
// The start date is entered in the user's WP timezone. We need to convert it to UTC prior to saving now.
$date = edd_get_utc_equivalent_date( EDD()->utils->date( $start_date . ' ' . $start_date_hour . ':' . $start_date_minute . ':00', edd_get_timezone_id(), false ) );
$to_update['start_date'] = $date->format( 'Y-m-d H:i:s' );
} else {
$to_update['start_date'] = null;
}
// End date.
if ( ! empty( $data['end_date'] ) ) {
$end_date = sanitize_text_field( $data['end_date'] );
$end_date_hour = isset( $data['end_date_hour'] ) && (int) $data['end_date_hour'] >= 0 && (int) $data['end_date_hour'] <= 23
? intval( $data['end_date_hour'] )
: '23';
$end_date_minute = isset( $data['end_date_minute'] ) && (int) $data['end_date_minute'] >= 0 && (int) $data['end_date_minute'] <= 59
? intval( $data['end_date_minute'] )
: '59';
// The end date is entered in the user's WP timezone. We need to convert it to UTC prior to saving now.
$date = edd_get_utc_equivalent_date( EDD()->utils->date( $end_date . ' ' . $end_date_hour . ':' . $end_date_minute . ':00', edd_get_timezone_id(), false ) );
$to_update['end_date'] = $date->format( 'Y-m-d H:i:s' );
} else {
$to_update['end_date'] = null;
}
// Known & accepted core discount meta
$to_update['product_reqs'] = isset( $data['product_reqs'] ) ? wp_parse_id_list( $data['product_reqs'] ) : '';
$to_update['excluded_products'] = isset( $data['excluded_products'] ) ? wp_parse_id_list( $data['excluded_products'] ) : '';
// "Once per customer" checkbox.
$to_update['once_per_customer'] = isset( $data['once_per_customer'] )
? 1
: 0;
// Strip out known non-columns
$to_strip = array(
// Legacy
'discount-id',
// Redirect
'edd-redirect',
'edd-action',
'edd-discount-nonce',
'_wp_http_referer',
// Time
'start_date_minute',
'start_date_hour',
'end_date_minute',
'end_date_hour'
);
// Loop through fields to update, and unset known bad keys
foreach ( $to_update as $key => $value ) {
if ( in_array( $key, $to_strip, true ) ) {
unset( $to_update[ $key ] );
}
}
// Attempt to update
$updated = edd_update_discount( $discount_id, $to_update );
$arg = ! empty( $updated )
? 'discount_updated'
: 'discount_not_changed';
// Redirect
edd_redirect( add_query_arg( 'edd-message', sanitize_key( $arg ), $data['edd-redirect'] ) );
}
add_action( 'edd_edit_discount', 'edd_admin_edit_discount' );
/**
* Listens for when a discount delete button is clicked and deletes the
* discount code
*
* @since 3.0
* @param array $data Discount code data
* @uses edd_delete_discount()
* @return void
*/
function edd_admin_delete_discount( $data = array() ) {
// Bail if no nonce or nonce fails
if ( ! isset( $data['_wpnonce'] ) || ! wp_verify_nonce( $data['_wpnonce'], 'edd_discount_nonce' ) ) {
wp_die( __( 'Trying to cheat or something?', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
// Bail if current user cannot manage shop
if ( ! current_user_can( 'manage_shop_discounts' ) ) {
wp_die( __( 'You do not have permission to delete discount codes', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
// Bail if discount does not exist
if ( empty( $data['discount'] ) ) {
wp_die( __( 'No discount ID supplied', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
// Setup default discount values
$discount_id = absint( $data['discount'] );
$deleted = edd_delete_discount( $discount_id );
$arg = ! empty( $deleted )
? 'discount_deleted'
: 'discount_deleted_failed';
// Redirect
edd_redirect( remove_query_arg( 'edd-action', add_query_arg( 'edd-message', sanitize_key( $arg ), $_SERVER['REQUEST_URI'] ) ) );
}
add_action( 'edd_delete_discount', 'edd_admin_delete_discount' );
/**
* Activates Discount Code
*
* Sets a discount status to active
*
* @since 1.0
* @param array $data Discount code data
* @uses edd_update_discount_status()
* @return void
*/
function edd_activate_discount( $data = array() ) {
// Bail if no nonce or nonce fails
if ( ! isset( $data['_wpnonce'] ) || ! wp_verify_nonce( $data['_wpnonce'], 'edd_discount_nonce' ) ) {
wp_die( __( 'Trying to cheat or something?', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
// Bail if current user cannot manage shop
if( ! current_user_can( 'manage_shop_discounts' ) ) {
wp_die( __( 'You do not have permission to edit discount codes', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
$discount_id = absint( $data['discount'] );
$activated = edd_update_discount_status( $discount_id, 'active' );
$arg = ! empty( $activated )
? 'discount_activated'
: 'discount_activation_failed';
// Redirect
edd_redirect( remove_query_arg( 'edd-action', add_query_arg( 'edd-message', sanitize_key( $arg ), $_SERVER['REQUEST_URI'] ) ) );
}
add_action( 'edd_activate_discount', 'edd_activate_discount' );
/**
* Deactivate Discount
*
* Sets a discount status to deactivate
*
* @since 1.0
* @param array $data Discount code data
* @uses edd_update_discount_status()
* @return void
*/
function edd_deactivate_discount( $data = array() ) {
// Bail if no nonce or nonce fails
if ( ! isset( $data['_wpnonce'] ) || ! wp_verify_nonce( $data['_wpnonce'], 'edd_discount_nonce' ) ) {
wp_die( __( 'Trying to cheat or something?', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
// Bail if current user cannot manage shop
if ( ! current_user_can( 'manage_shop_discounts' ) ) {
wp_die( __( 'You do not have permission to create discount codes', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
$discount_id = absint( $data['discount'] );
$activated = edd_update_discount_status( $discount_id, 'inactive' );
$arg = ! empty( $activated )
? 'discount_deactivated'
: 'discount_deactivation_failed';
// Redirect
edd_redirect( remove_query_arg( 'edd-action', add_query_arg( 'edd-message', sanitize_key( $arg ), $_SERVER['REQUEST_URI'] ) ) );
}
add_action( 'edd_deactivate_discount', 'edd_deactivate_discount' );

View File

@ -0,0 +1,69 @@
<?php
/**
* Discount Codes
*
* @package EDD
* @subpackage Admin/Discounts
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Renders the Discounts admin page.
*
* Here only for backwards compatibility
*
* @since 1.4
* @since 3.0 Nomenclature updated for consistency.
*/
function edd_discounts_page() {
// Enqueue scripts.
wp_enqueue_script( 'edd-admin-discounts' );
// Edit
if ( ! empty( $_GET['edd-action'] ) && ( 'edit_discount' === $_GET['edd-action'] ) ) {
wp_enqueue_script( 'edd-admin-notes' );
require_once EDD_PLUGIN_DIR . 'includes/admin/discounts/edit-discount.php';
// Add
} elseif ( ! empty( $_GET['edd-action'] ) && ( 'add_discount' === $_GET['edd-action'] ) ) {
require_once EDD_PLUGIN_DIR . 'includes/admin/discounts/add-discount.php';
// List tables
} else {
edd_adjustments_page();
}
}
/**
* Output the discounts page content, in the adjustments page action.
*
* @since 3.0
*/
function edd_discounts_page_content() {
require_once EDD_PLUGIN_DIR . 'includes/admin/discounts/class-discount-codes-table.php';
$discount_codes_table = new EDD_Discount_Codes_Table();
$discount_codes_table->prepare_items();
do_action( 'edd_discounts_page_top' ); ?>
<form id="edd-discounts-filter" method="get" action="<?php echo esc_url( edd_get_admin_url( array( 'page' => 'edd-discounts' ) ) ); ?>">
<?php $discount_codes_table->search_box( __( 'Search Discounts', 'easy-digital-downloads' ), 'edd-discounts' ); ?>
<input type="hidden" name="post_type" value="download" />
<input type="hidden" name="page" value="edd-discounts" />
<?php
$discount_codes_table->views();
$discount_codes_table->display();
?>
</form>
<?php do_action( 'edd_discounts_page_bottom' );
}
add_action( 'edd_adjustments_page_discount', 'edd_discounts_page_content' );

View File

@ -0,0 +1,297 @@
<?php
/**
* Edit Discount Page
*
* @package EDD
* @subpackage Admin/Discounts
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
// Bail if no discount passed
if ( ! isset( $_GET['discount'] ) || ! is_numeric( $_GET['discount'] ) ) {
wp_die( __( 'Something went wrong.', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 400 ) );
}
// Load discount
$discount_id = absint( $_GET['discount'] );
/** @var EDD_Discount */
$discount = edd_get_discount( $discount_id );
// Bail if discount does not exist
if ( empty( $discount ) ) {
wp_die( __( 'Something went wrong.', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 400 ) );
}
// Setup discount vars
$product_requirements = $discount->get_product_reqs();
$excluded_products = $discount->get_excluded_products();
$condition = $discount->get_product_condition();
$single_use = $discount->get_once_per_customer();
$type = $discount->get_type();
$notes = edd_get_discount_notes( $discount->id );
// Show/Hide
$flat_display = ( 'flat' === $type ) ? '' : ' style="display:none;"';
$percent_display = ( 'percent' === $type ) ? '' : ' style="display:none;"';
$no_notes_display = empty( $notes ) ? '' : ' style="display:none;"';
$condition_display = ! empty( $product_requirements ) ? '' : ' style="display:none;"';
// Dates & times
$discount_start_date = edd_get_edd_timezone_equivalent_date_from_utc( EDD()->utils->date( $discount->start_date, 'utc' ) );
$discount_end_date = edd_get_edd_timezone_equivalent_date_from_utc( EDD()->utils->date( $discount->end_date, 'utc' ) );
$start_date = $discount_start_date->format( 'Y-m-d' );
$start_hour = $discount_start_date->format( 'H' );
$start_minute = $discount_start_date->format( 'i' );
$end_date = $discount_end_date->format( 'Y-m-d' );
$end_hour = $discount_end_date->format( 'H' );
$end_minute = $discount_end_date->format( 'i' );
$hours = edd_get_hour_values();
$minutes = edd_get_minute_values();
?>
<div class="wrap">
<h1><?php _e( 'Edit Discount', 'easy-digital-downloads' ); ?></h1>
<hr class="wp-header-end">
<form id="edd-edit-discount" action="" method="post">
<?php do_action( 'edd_edit_discount_form_top', $discount->id, $discount ); ?>
<table class="form-table">
<tbody>
<?php do_action( 'edd_edit_discount_form_before_name', $discount->id, $discount ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd-name"><?php _e( 'Name', 'easy-digital-downloads' ); ?></label>
</th>
<td>
<input name="name" required="required" id="edd-name" type="text" value="<?php echo esc_attr( stripslashes( $discount->name ) ); ?>" placeholder="<?php esc_html_e( 'Summer Sale', 'easy-digital-downloads' ); ?>" />
<p class="description"><?php _e( 'The name of this discount. Customers will see this on checkout.', 'easy-digital-downloads' ); ?></p>
</td>
</tr>
<?php do_action( 'edd_edit_discount_form_before_code', $discount->id, $discount ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd-code"><?php _e( 'Code', 'easy-digital-downloads' ); ?></label>
</th>
<td>
<input type="text" required="required" id="edd-code" name="code" value="<?php echo esc_attr( $discount->code ); ?>" pattern="[a-zA-Z0-9-_]+" class="code" placeholder="<?php esc_html_e( '10PERCENT', 'easy-digital-downloads' ); ?>" />
<p class="description"><?php _e( 'The code customers will enter to apply this discount. Only alphanumeric characters are allowed.', 'easy-digital-downloads' ); ?></p>
</td>
</tr>
<?php do_action( 'edd_edit_discount_form_before_type', $discount->id, $discount ); ?>
<?php do_action( 'edd_edit_discount_form_before_amount', $discount->id, $discount ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd-amount"><?php _e( 'Amount', 'easy-digital-downloads' ); ?></label>
</th>
<td>
<span class="edd-amount-type-wrapper">
<input type="text" required="required" class="edd-price-field" id="edd-amount" name="amount" value="<?php echo esc_attr( edd_format_amount( $discount->amount ) ); ?>" placeholder="<?php esc_html_e( '10.00', 'easy-digital-downloads' ); ?>" />
<label for="edd-amount-type" class="screen-reader-text"><?php esc_html_e( 'Amount Type', 'easy-digital-downloads' ); ?></label>
<select name="amount_type" id="edd-amount-type">
<option value="percent" <?php selected( $type, 'percent' ); ?>>%</option>
<option value="flat"<?php selected( $type, 'flat' ); ?>><?php echo esc_html( edd_currency_symbol() ); ?></option>
</select>
</span>
<p class="description"><?php _e( 'The amount as a percentage or flat rate. Cannot be left blank.', 'easy-digital-downloads' ); ?></p>
</td>
</tr>
<?php do_action( 'edd_edit_discount_form_before_products', $discount->id, $discount ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd_products"><?php printf( __( '%s Requirements', 'easy-digital-downloads' ), edd_get_label_singular() ); ?></label>
</th>
<td>
<?php echo EDD()->html->product_dropdown( array(
'name' => 'product_reqs[]',
'id' => 'edd_products',
'selected' => $product_requirements,
'multiple' => true,
'chosen' => true,
'placeholder' => sprintf( __( 'Select %s', 'easy-digital-downloads' ), edd_get_label_plural() )
) ); ?>
<div id="edd-discount-product-conditions"<?php echo $condition_display; ?>>
<p>
<select id="edd-product-condition" name="product_condition">
<option value="all"<?php selected( 'all', $condition ); ?>><?php printf( __( 'Cart must contain all selected %s', 'easy-digital-downloads' ), edd_get_label_plural() ); ?></option>
<option value="any"<?php selected( 'any', $condition ); ?>><?php printf( __( 'Cart needs one or more of the selected %s', 'easy-digital-downloads' ), edd_get_label_plural() ); ?></option>
</select>
</p>
<p>
<label>
<input type="radio" class="tog" name="scope" value="global"<?php checked( 'global', $discount->scope ); ?>/>
<?php _e( 'Apply discount to entire purchase.', 'easy-digital-downloads' ); ?>
</label><br/>
<label>
<input type="radio" class="tog" name="scope" value="not_global"<?php checked( 'not_global', $discount->scope ); ?>/>
<?php printf( __( 'Apply discount only to selected %s.', 'easy-digital-downloads' ), edd_get_label_plural() ); ?>
</label>
</p>
</div>
<p class="description"><?php printf( __( '%s this discount can only be applied to. Leave blank for any.', 'easy-digital-downloads' ), edd_get_label_plural() ); ?></p>
</td>
</tr>
<?php do_action( 'edd_edit_discount_form_before_excluded_products', $discount->id, $discount ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd-excluded-products"><?php printf( __( 'Excluded %s', 'easy-digital-downloads' ), edd_get_label_plural() ); ?></label>
</th>
<td>
<?php echo EDD()->html->product_dropdown( array(
'name' => 'excluded_products[]',
'id' => 'excluded_products',
'selected' => $excluded_products,
'multiple' => true,
'chosen' => true,
'placeholder' => sprintf( __( 'Select %s', 'easy-digital-downloads' ), edd_get_label_plural() )
) ); ?>
<p class="description"><?php printf( __( '%s this discount cannot be applied to. Leave blank for none.', 'easy-digital-downloads' ), edd_get_label_plural() ); ?></p>
</td>
</tr>
<?php do_action( 'edd_edit_discount_form_before_start', $discount->id, $discount ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd-start"><?php _e( 'Start date', 'easy-digital-downloads' ); ?></label>
</th>
<td class="edd-discount-datetime">
<input name="start_date" id="edd-start" type="text" value="<?php echo esc_attr( false !== $discount->start_date ? $start_date : '' ); ?>" class="edd_datepicker" data-format="<?php echo esc_attr( edd_get_date_picker_format() ); ?>" placeholder="<?php echo esc_attr( edd_get_date_picker_format() ); ?>" />
<label class="screen-reader-text" for="start-date-hour">
<?php esc_html_e( 'Start Date Hour', 'easy-digital-downloads' ); ?>
</label>
<input type="number" min="0" max="24" step="1" name="start_date_hour" id="start-date-hour" value="<?php echo esc_attr( false !== $discount->start_date ? $start_hour : '' ); ?>" placeholder="00" />
:
<label class="screen-reader-text" for="start-date-minute">
<?php esc_html_e( 'Start Date Minute', 'easy-digital-downloads' ); ?>
</label>
<input type="number" min="0" max="59" step="1" name="start_date_minute" id="start-date-minute" value="<?php echo esc_attr( false !== $discount->start_date ? $start_minute : '' ); ?>" placeholder="00" />
<?php echo esc_html( ' (' . edd_get_timezone_abbr() . ')' ); ?>
<p class="description"><?php _e( 'Pick the date and time this discount will start on. Leave blank for no start date.', 'easy-digital-downloads' ); ?></p>
</td>
</tr>
<?php do_action( 'edd_edit_discount_form_before_expiration', $discount->id, $discount ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd-expiration"><?php _e( 'Expiration date', 'easy-digital-downloads' ); ?></label>
</th>
<td class="edd-discount-datetime">
<input name="end_date" id="edd-expiration" type="text" value="<?php echo esc_attr( false !== $discount->end_date ? $end_date : '' ); ?>" class="edd_datepicker" data-format="<?php echo esc_attr( edd_get_date_picker_format() ); ?>" placeholder="<?php echo esc_attr( edd_get_date_picker_format() ); ?>" />
<label class="screen-reader-text" for="end-date-hour">
<?php esc_html_e( 'Expiration Date Hour', 'easy-digital-downloads' ); ?>
</label>
<input type="number" min="0" max="24" step="1" name="end_date_hour" id="end-date-hour" value="<?php echo esc_attr( false !== $discount->end_date ? $end_hour : '' ); ?>" placeholder="23" />
:
<label class="screen-reader-text" for="end-date-minute">
<?php esc_html_e( 'Expiration Date Minute', 'easy-digital-downloads' ); ?>
</label>
<input type="number" min="0" max="59" step="1" name="end_date_minute" id="end-date-minute" value="<?php echo esc_attr( false !== $discount->end_date ? $end_minute : '' ); ?>" placeholder="59" />
<?php echo esc_html( ' (' . edd_get_timezone_abbr() . ')' ); ?>
<p class="description"><?php _e( 'Pick the date and time this discount will expire on. Leave blank to never expire.', 'easy-digital-downloads' ); ?></p>
</td>
</tr>
<?php do_action( 'edd_edit_discount_form_before_min_cart_amount', $discount->id, $discount ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd-min-cart-amount"><?php _e( 'Minimum Amount', 'easy-digital-downloads' ); ?></label>
</th>
<td>
<input type="text" id="edd-min-cart-amount" name="min_charge_amount" value="<?php echo esc_attr( edd_format_amount( $discount->min_charge_amount ) ); ?>" placeholder="<?php esc_html_e( 'No minimum', 'easy-digital-downloads' ); ?>" />
<p class="description"><?php _e( 'The minimum subtotal of item prices in a cart before this discount may be applied.', 'easy-digital-downloads' ); ?></p>
</td>
</tr>
<?php do_action( 'edd_edit_discount_form_before_max_uses', $discount->id, $discount ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd-max-uses"><?php _e( 'Max Uses', 'easy-digital-downloads' ); ?></label>
</th>
<td>
<input type="text" id="edd-max-uses" name="max_uses" value="<?php echo esc_attr( $discount->max_uses ); ?>" placeholder="<?php esc_html_e( 'Unlimited', 'easy-digital-downloads' ); ?>" />
<p class="description"><?php _e( 'The maximum number of times this discount can be used.', 'easy-digital-downloads' ); ?></p>
</td>
</tr>
<?php do_action( 'edd_edit_discount_form_before_use_once', $discount->id, $discount ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd-use-once"><?php _e( 'Use Once Per Customer', 'easy-digital-downloads' ); ?></label>
</th>
<td>
<input type="checkbox" id="edd-use-once" name="once_per_customer" value="1"<?php checked( true, $single_use ); ?>/>
<span class="description"><?php _e( 'Prevent customers from using this discount more than once.', 'easy-digital-downloads' ); ?></span>
</td>
</tr>
<?php do_action( 'edd_edit_discount_form_before_status', $discount->id, $discount ); ?>
<tr>
<th scope="row" valign="top">
<label for="edd-status"><?php _e( 'Status', 'easy-digital-downloads' ); ?></label>
</th>
<td>
<select name="status" id="edd-status">
<option value="active" <?php selected( $discount->status, 'active' ); ?>><?php _e( 'Active', 'easy-digital-downloads' ); ?></option>
<option value="inactive"<?php selected( $discount->status, 'inactive' ); ?>><?php _e( 'Inactive', 'easy-digital-downloads' ); ?></option>
</select>
<p class="description"><?php _e( 'The status of this discount code.', 'easy-digital-downloads' ); ?></p>
</td>
</tr>
<?php do_action( 'edd_edit_discount_form_before_notes', $discount->id, $discount ); ?>
<tr>
<th scope="row" valign="top">
<label for="notes"><?php _e( 'Discount Notes', 'easy-digital-downloads' ); ?></label>
</th>
<td>
<div class="edd-notes-wrapper">
<?php echo edd_admin_get_notes_html( $notes ); ?>
<?php echo edd_admin_get_new_note_form( $discount->id, 'discount' ); ?>
</div>
</td>
</tr>
</tbody>
</table>
<?php do_action( 'edd_edit_discount_form_bottom', $discount->id, $discount ); ?>
<p class="submit">
<input type="hidden" name="type" value="discount" />
<input type="hidden" name="edd-action" value="edit_discount" />
<input type="hidden" name="discount-id" value="<?php echo esc_attr( $discount->id ); ?>" />
<input type="hidden" name="edd-redirect" value="<?php echo esc_url( edd_get_admin_url( array( 'page' => 'edd-discounts', 'edd-action' => 'edit_discount', 'discount' => absint( $discount->id ) ) ) ); ?>" />
<input type="hidden" name="edd-discount-nonce" value="<?php echo esc_attr( wp_create_nonce( 'edd_discount_nonce' ) ); ?>" />
<input type="submit" value="<?php esc_html_e( 'Update Discount Code', 'easy-digital-downloads' ); ?>" class="button-primary" />
</p>
</form>
</div>

View File

@ -0,0 +1,124 @@
<?php
/**
* Contextual Help
*
* @package EDD
* @subpackage Admin/Downloads
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.2.3
*/
use EDD\Admin\Pass_Manager;
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Adds the Contextual Help for the main Downloads page
*
* @since 1.2.3
* @return void
*/
function edd_downloads_contextual_help() {
$screen = get_current_screen();
if ( $screen->id != 'download' ) {
return;
}
$pass_manager = new Pass_Manager();
if ( $pass_manager->isFree() ) {
$docs_url = edd_link_helper(
'https://easydigitaldownloads.com/docs/',
array(
'utm_medium' => 'downloads-contextual-help',
'utm_content' => 'documentation',
)
);
$upgrade_url = edd_link_helper(
'https://easydigitaldownloads.com/lite-upgrade/',
array(
'utm_medium' => 'downloads-contextual-help',
'utm_content' => 'lite-upgrade',
)
);
$screen->set_help_sidebar(
'<p><strong>' . __( 'For more information:', 'easy-digital-downloads' ) . '</strong></p>' .
'<p>' . sprintf( __( 'Visit the <a href="%s">documentation</a> on the Easy Digital Downloads website.', 'easy-digital-downloads' ), $docs_url ) . '</p>' .
'<p>' . sprintf(
__( 'Need more from your Easy Digital Downloads store? <a href="%s">Upgrade Now</a>!', 'easy-digital-downloads' ),
$upgrade_url
) . '</p>'
);
}
$screen->add_help_tab( array(
'id' => 'edd-download-configuration',
'title' => sprintf( __( '%s Settings', 'easy-digital-downloads' ), edd_get_label_singular() ),
'content' =>
'<p>' . __( '<strong>File Download Limit</strong> - Define how many times customers are allowed to download their purchased files. Leave at 0 for unlimited. Resending the purchase receipt will permit the customer one additional download if their limit has already been reached.', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( '<strong>Accounting Options</strong> - If enabled, define an individual SKU or product number for this download.', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( '<strong>Button Options</strong> - Disable the automatic output of the purchase button. If disabled, no button will be added to the download page unless the <code>[purchase_link]</code> shortcode is used.', 'easy-digital-downloads' ) . '</p>'
) );
$screen->add_help_tab( array(
'id' => 'edd-download-prices',
'title' => sprintf( __( '%s Prices', 'easy-digital-downloads' ), edd_get_label_singular() ),
'content' =>
'<p>' . __( '<strong>Enable variable pricing</strong> - By enabling variable pricing, multiple download options and prices can be configured.', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( '<strong>Enable multi-option purchases</strong> - By enabling multi-option purchases customers can add multiple variable price items to their cart at once.', 'easy-digital-downloads' ) . '</p>'
) );
$screen->add_help_tab( array(
'id' => 'edd-download-files',
'title' => sprintf( __( '%s Files', 'easy-digital-downloads' ), edd_get_label_singular() ),
'content' =>
'<p>' . __( '<strong>Product Type Options</strong> - Choose a default product type or a bundle. Bundled products automatically include access to other download&#39;s files when purchased.', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( '<strong>File Downloads</strong> - Define download file names and their respective file URL. Multiple files can be assigned to a single price, or variable prices.', 'easy-digital-downloads' ) . '</p>'
) );
$screen->add_help_tab( array(
'id' => 'edd-product-notes',
'title' => sprintf( __( '%s Instructions', 'easy-digital-downloads' ), edd_get_label_singular() ),
'content' => '<p>' . sprintf( __( 'Special instructions for this %s. These will be added to the sales receipt, and may be used by some extensions or themes.', 'easy-digital-downloads' ), strtolower( edd_get_label_singular() ) ) . '</p>'
) );
$colors = array(
'gray', 'pink', 'blue', 'green', 'teal', 'black', 'dark gray', 'orange', 'purple', 'slate'
);
$screen->add_help_tab( array(
'id' => 'edd-purchase-shortcode',
'title' => __( 'Purchase Shortcode', 'easy-digital-downloads' ),
'content' =>
'<p>' . __( '<strong>Purchase Shortcode</strong> - If the automatic output of the purchase button has been disabled via the Download Configuration box, a shortcode can be used to output the button or link.', 'easy-digital-downloads' ) . '</p>' .
'<p><code>[purchase_link id="#" price="1" text="Add to Cart" color="blue"]</code></p>' .
'<ul>
<li><strong>id</strong> - ' . __( 'The ID of a specific download to purchase.', 'easy-digital-downloads' ) . '</li>
<li><strong>price</strong> - ' . __( 'Whether to show the price on the purchase button. 1 to show the price, 0 to disable it.', 'easy-digital-downloads' ) . '</li>
<li><strong>text</strong> - ' . __( 'The text to be displayed on the button or link.', 'easy-digital-downloads' ) . '</li>
<li><strong>style</strong> - ' . __( '<em>button</em> | <em>text</em> - The style of the purchase link.', 'easy-digital-downloads' ) . '</li>
<li><strong>color</strong> - <em>' . implode( '</em> | <em>', $colors ) . '</em></li>
<li><strong>class</strong> - ' . __( 'One or more custom CSS classes you want applied to the button.', 'easy-digital-downloads' ) . '</li>
</ul>' .
'<p>' . sprintf( __( 'For more information, see <a href="%s">using Shortcodes</a> on the WordPress.org Codex or <a href="%s">Easy Digital Downloads Documentation</a>', 'easy-digital-downloads' ), 'https://codex.wordpress.org/Shortcode', 'https://docs.easydigitaldownloads.com/article/229-purchaselink' ) . '</p>'
) );
/**
* Fires off in the EDD Downloads Contextual Help Screen
*
* @since 1.2.3
* @param object $screen The current admin screen
*/
do_action( 'edd_downloads_contextual_help', $screen );
}
add_action( 'load-post.php', 'edd_downloads_contextual_help' );
add_action( 'load-post-new.php', 'edd_downloads_contextual_help' );

View File

@ -0,0 +1,378 @@
<?php
/**
* Dashboard Columns
*
* @package EDD
* @subpackage Admin/Downloads
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Download Columns
*
* Defines the custom columns and their order
*
* @since 1.0
* @param array $download_columns Array of download columns
* @return array $download_columns Updated array of download columns for Downloads
* Post Type List Table
*/
function edd_download_columns( $download_columns ) {
$category_labels = edd_get_taxonomy_labels( 'download_category' );
$tag_labels = edd_get_taxonomy_labels( 'download_tag' );
return apply_filters( 'edd_download_columns', array(
'cb' => '<input type="checkbox"/>',
'title' => __( 'Name', 'easy-digital-downloads' ),
'download_category' => $category_labels['menu_name'],
'download_tag' => $tag_labels['menu_name'],
'price' => __( 'Price', 'easy-digital-downloads' ),
'sales' => __( 'Net Sales', 'easy-digital-downloads' ),
'earnings' => __( 'Net Revenue', 'easy-digital-downloads' ),
'date' => __( 'Date', 'easy-digital-downloads' )
) );
}
add_filter( 'manage_edit-download_columns', 'edd_download_columns' );
/**
* Render Download Columns
*
* @since 1.0
* @param string $column_name Column name
* @param int $post_id Download (Post) ID
* @return void
*/
function edd_render_download_columns( $column_name, $post_id ) {
// Bail if not a download
if ( get_post_type( $post_id ) !== 'download' ) {
return;
}
switch ( $column_name ) {
case 'download_category':
$terms = get_the_term_list( $post_id, 'download_category', '', ', ', '');
echo ! empty( $terms )
? $terms
: '&mdash;';
break;
case 'download_tag':
$terms = get_the_term_list( $post_id, 'download_tag', '', ', ', '');
echo ! empty( $terms )
? $terms
: '&mdash;';
break;
case 'price':
if ( edd_has_variable_prices( $post_id ) ) {
echo edd_price_range( $post_id );
} else {
echo edd_price( $post_id, false );
echo '<input type="hidden" class="downloadprice-' . absint( $post_id ) . '" value="' . esc_attr( edd_get_download_price( $post_id ) ) . '" />';
}
break;
case 'sales':
if ( current_user_can( 'view_product_stats', $post_id ) ) {
$sales_url = add_query_arg( array(
'page' => 'edd-payment-history',
'product-id' => urlencode( $post_id )
), edd_get_admin_base_url() );
echo '<a href="' . esc_url( $sales_url ) . '">';
echo edd_get_download_sales_stats( $post_id );
echo '</a>';
} else {
echo '-';
}
break;
case 'earnings':
if ( current_user_can( 'view_product_stats', $post_id ) ) {
$report_url = edd_get_admin_url( array(
'page' => 'edd-reports',
'view' => 'downloads',
'products' => absint( $post_id ),
) );
echo '<a href="' . esc_url( $report_url ) . '">';
echo edd_currency_filter( edd_format_amount( edd_get_download_earnings_stats( $post_id ) ) );
echo '</a>';
} else {
echo '-';
}
break;
}
}
add_action( 'manage_posts_custom_column', 'edd_render_download_columns', 10, 2 );
/**
* Registers the sortable columns in the list table
*
* @since 1.0
* @param array $columns Array of the columns
* @return array $columns Array of sortable columns
*/
function edd_sortable_download_columns( $columns ) {
$columns['price'] = 'price';
$columns['sales'] = 'sales';
$columns['earnings'] = 'earnings';
return $columns;
}
add_filter( 'manage_edit-download_sortable_columns', 'edd_sortable_download_columns' );
/**
* Sorts Columns in the Downloads List Table
*
* @since 1.0
* @param array $vars Array of all the sort variables
* @return array $vars Array of all the sort variables
*/
function edd_sort_downloads( $vars ) {
// Check if we're viewing the "download" post type
if ( isset( $vars['post_type'] ) && 'download' == $vars['post_type'] ) {
// Check if 'orderby' is set to "sales"
if ( isset( $vars['orderby'] ) && 'sales' == $vars['orderby'] ) {
$vars = array_merge(
$vars,
array(
'meta_key' => '_edd_download_sales',
'orderby' => 'meta_value_num'
)
);
}
// Check if "orderby" is set to "earnings"
if ( isset( $vars['orderby'] ) && 'earnings' == $vars['orderby'] ) {
$vars = array_merge(
$vars,
array(
'meta_key' => '_edd_download_earnings',
'orderby' => 'meta_value_num'
)
);
}
// Check if "orderby" is set to "earnings"
if ( isset( $vars['orderby'] ) && 'price' == $vars['orderby'] ) {
$vars = array_merge(
$vars,
array(
'meta_key' => 'edd_price',
'orderby' => 'meta_value_num'
)
);
}
}
return $vars;
}
/**
* Sets restrictions on author of Downloads List Table
*
* @since 2.2
* @param array $vars Array of all sort variables
* @return array Array of all sort variables
*/
function edd_filter_downloads( $vars ) {
if ( isset( $vars['post_type'] ) && 'download' == $vars['post_type'] ) {
// If an author ID was passed, use it
if ( isset( $_REQUEST['author'] ) && ! current_user_can( 'view_shop_reports' ) ) {
$author_id = $_REQUEST['author'];
if ( (int) $author_id !== get_current_user_id() ) {
// Tried to view the products of another person, sorry
wp_die( __( 'You do not have permission to view this data.', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
$vars = array_merge(
$vars,
array(
'author' => get_current_user_id()
)
);
}
}
return $vars;
}
/**
* Download Load
*
* Sorts the downloads.
*
* @since 1.0
* @return void
*/
function edd_download_load() {
add_filter( 'request', 'edd_sort_downloads' );
add_filter( 'request', 'edd_filter_downloads' );
}
add_action( 'load-edit.php', 'edd_download_load', 9999 );
/**
* Add Download Filters
*
* Adds taxonomy drop down filters for downloads.
*
* @since 1.0
* @return void
*/
function edd_add_download_filters() {
global $typenow;
// Checks if the current post type is 'download'
if ( $typenow !== 'download') {
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>";
}
$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>";
}
if ( isset( $_REQUEST['all_posts'] ) && '1' === $_REQUEST['all_posts'] ) {
echo '<input type="hidden" name="all_posts" value="1" />';
} else if ( ! current_user_can( 'view_shop_reports' ) ) {
$author_id = get_current_user_id();
echo '<input type="hidden" name="author" value="' . esc_attr( $author_id ) . '" />';
}
}
add_action( 'restrict_manage_posts', 'edd_add_download_filters', 100 );
/**
* Remove Download Month Filter
*
* Removes the drop down filter for downloads by date.
*
* @author Daniel J Griffiths
* @since 2.1
* @param array $dates The preset array of dates
* @global $typenow The post type we are viewing
* @return array Empty array disables the dropdown
*/
function edd_remove_month_filter( $dates ) {
global $typenow;
if ( 'download' === $typenow ) {
$dates = array();
}
return $dates;
}
add_filter( 'months_dropdown_results', 'edd_remove_month_filter', 99 );
/**
* Adds price field to Quick Edit options
*
* @since 1.1.3.4
* @param string $column_name Name of the column
* @param string $post_type Current Post Type (i.e. download)
* @return void
*/
function edd_price_field_quick_edit( $column_name, $post_type ) {
// Bail if not price or download
if ( $column_name !== 'price' || $post_type !== 'download' ) {
return;
} ?>
<fieldset class="inline-edit-col-left">
<div id="edd-download-data" class="inline-edit-col">
<h4><?php echo sprintf( __( '%s Configuration', 'easy-digital-downloads' ), edd_get_label_singular() ); ?></h4>
<label>
<span class="title"><?php _e( 'Price', 'easy-digital-downloads' ); ?></span>
<span class="input-text-wrap">
<input type="text" name="_edd_regprice" class="text regprice" />
</span>
</label>
<br class="clear" />
</div>
</fieldset>
<?php
}
add_action( 'quick_edit_custom_box', 'edd_price_field_quick_edit', 10, 2 );
add_action( 'bulk_edit_custom_box', 'edd_price_field_quick_edit', 10, 2 );
/**
* Updates price when saving post
*
* @since 1.1.3.4
* @param int $post_id Download (Post) ID
* @return void
*/
function edd_price_save_quick_edit( $post_id ) {
if ( ! isset( $_POST['post_type']) || 'download' !== $_POST['post_type'] ) {
return;
}
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return $post_id;
}
if ( edd_doing_autosave() ) {
return $post_id;
}
if ( isset( $_REQUEST['_edd_regprice'] ) ) {
update_post_meta( $post_id, 'edd_price', strip_tags( stripslashes( $_REQUEST['_edd_regprice'] ) ) );
}
}
add_action( 'save_post', 'edd_price_save_quick_edit' );
/**
* Process bulk edit actions via AJAX
*
* @since 1.4.4
* @return void
*/
function edd_save_bulk_edit() {
$post_ids = ! empty( $_POST['post_ids'] )
? wp_parse_id_list( $_POST['post_ids'] )
: array();
if ( ! empty( $post_ids ) && is_array( $post_ids ) ) {
$price = isset( $_POST['price'] )
? strip_tags( stripslashes( $_POST['price'] ) )
: 0;
foreach ( $post_ids as $post_id ) {
if ( ! current_user_can( 'edit_post', $post_id ) ) {
continue;
}
if ( ! empty( $price ) ) {
update_post_meta( $post_id, 'edd_price', edd_sanitize_amount( $price ) );
}
}
}
die();
}
add_action( 'wp_ajax_edd_save_bulk_edit', 'edd_save_bulk_edit' );

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,89 @@
<?php
/**
* Email Summary Admin Class.
*
* @package EDD
* @subpackage Emails
* @copyright Copyright (c) 2022, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 3.1
*/
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;
/**
* EDD_Email_Summary_Admin Class.
*
* Takes care of Admin actions for Email Summaries.
*
* @since 3.1
*/
class EDD_Email_Summary_Admin {
/**
* WordPress SMTP error.
*
* @since 3.1
*
* @var bool|\WP_Error
*/
public $mail_smtp_error = false;
/**
* Class constructor.
*
* @since 3.1
*/
public function __construct() {
add_action( 'wp_ajax_edd_send_test_email_summary', array( $this, 'send_test_email_summary' ) );
}
/**
* Send test Email Summary.
*
* @since 3.1
*
* @param array $data GET Request array.
*/
public function send_test_email_summary() {
add_action( 'wp_mail_failed', array( $this, 'mail_failed' ) );
$output = array(
'status' => 'success',
'message' => __( 'The test Email Summary was sent successfully!', 'easy-digital-downloads' ),
);
$email = new EDD_Email_Summary( true );
$email_status = $email->send_email();
if ( ! $email_status ) {
$output['status'] = 'error';
// Generic error.
$output['message'] = __( 'There was an unknown problem while sending test Email Summary!', 'easy-digital-downloads' );
// SMTP error.
if ( $this->mail_smtp_error ) {
$output['message'] = $this->mail_smtp_error;
}
}
echo wp_json_encode( $output );
edd_die();
}
/**
* Get error message from failed SMTP.
*
* @since 3.1
*
* @param \WP_Error $error The WP Error thrown in WP core: `wp_mail_failed` hook.
*/
public function mail_failed( $error ) {
if ( ! is_wp_error( $error ) ) {
return;
}
$this->mail_smtp_error = $error->get_error_message();
}
}

View File

@ -0,0 +1,346 @@
<?php
namespace EDD\Admin\Extensions;
use \EDD\Admin\Pass_Manager;
abstract class Extension {
/**
* The product ID. This only needs to be set if the extending class is
* for a single product.
*
* @since 2.11.4
* @var int
*/
protected $item_id;
/**
* The settings tab where this item will show.
*
* @since 2.11.4
* @var string
*/
protected $settings_tab = '';
/**
* The required AA pass level.
*/
const PASS_LEVEL = Pass_Manager::PERSONAL_PASS_ID;
/**
* The Extension Manager
*
* @var \EDD\Admin\Extensions\Extension_Manager
*/
protected $manager;
/**
* The settings section for this item.
*
* @since 2.11.5
* @var string
*/
protected $settings_section = 'general';
public function __construct() {
$this->manager = new \EDD\Admin\Extensions\Extension_Manager( static::PASS_LEVEL );
}
/**
* Whether the extension is activated.
*
* @since 2.11.4
* @return bool
*/
abstract protected function is_activated();
/**
* Output the settings field (installation helper).
*
* @return void
*/
public function settings_field() {
if ( $this->is_activated() ) {
return;
}
$this->do_single_extension_card();
}
/**
* Outputs a single extension card.
*
* @since 2.11.4
* @param false|int $item_id Optional: the individual extension product ID.
* @return void
*/
public function do_single_extension_card( $item_id = false ) {
if ( empty( $item_id ) && empty( $this->item_id ) ) {
return;
}
$product_data = $this->get_product_data( $item_id );
if ( ! $product_data || empty( $product_data->title ) ) {
return;
}
$this->manager->do_extension_card(
$product_data,
$this->get_button_parameters( $product_data, $item_id ),
$this->get_link_parameters( $product_data ),
$this->get_configuration( $product_data )
);
}
/**
* Gets the product data for a specific extension.
*
* @param false|int $item_id
* @return bool|ProductData|array False if there is no data; product data object if there is, or possibly an array of arrays.
*/
public function get_product_data( $item_id = false ) {
require_once EDD_PLUGIN_DIR . 'includes/admin/extensions/class-extensions-api.php';
$api = new ExtensionsAPI();
$body = $this->get_api_body();
$api_item_id = $item_id ?: $this->item_id;
$product_data = $api->get_product_data( $body, $api_item_id );
if ( ! $product_data ) {
return false;
}
if ( $api_item_id ) {
return $product_data;
}
if ( $item_id && ! empty( $product_data[ $item_id ] ) ) {
return $product_data[ $item_id ];
}
return $product_data;
}
/**
* Gets the custom configuration for the extension.
*
* @since 2.11.4
* @param ProductData $product_data Optionally allows the product data to be parsed in the configuration.
* @return array
*/
protected function get_configuration( ProductData $product_data ) {
return array();
}
/**
* Formats a custom description array by running wpautop and converting it to a string.
*
* @since 2.11.4
* @param array $description The custom product description.
* @return string
*/
protected function format_description( array $description ) {
return implode( '', array_map( 'wpautop', $description ) );
}
/**
* Whether the current screen is an EDD setings screen.
*
* @since 2.11.4
* @return bool
*/
protected function is_edd_settings_screen() {
return edd_is_admin_page( 'settings', $this->settings_tab );
}
/**
* Whether the current screen is a download new/edit screen.
*
* @since 2.11.4
* @return bool
*/
protected function is_download_edit_screen() {
return edd_is_admin_page( 'download', 'edit' ) || edd_is_admin_page( 'download', 'new' );
}
/**
* Whether the section for an individual product can be registered/shown.
*
* @since 2.11.4
* @return bool
*/
protected function can_show_product_section() {
if ( ! $this->is_edd_settings_screen() ) {
return false;
}
if ( $this->is_activated() ) {
return false;
}
if ( ! $this->get_product_data() ) {
return false;
}
return true;
}
/**
* Gets the array for the body of the API request.
* Classes may need to override this (for example, to query a specific tag).
* Note that the first array key/value pair are used to create the option name.
*
* @return array
*/
protected function get_api_body() {
return array();
}
/**
* Gets the type for the button data-type attribute.
* This is intended to sync with the Products API request.
* Default is product.
*
* Really a shim for array_key_first.
*
* @param array $array
* @return string
*/
private function get_type( array $array ) {
$type = 'product';
if ( empty( $array ) ) {
return $type;
}
if ( function_exists( 'array_key_first' ) ) {
return array_key_first( $array );
}
foreach ( $array as $key => $unused ) {
return $key;
}
return $type;
}
/**
* Gets the button parameters.
* Classes should not need to replace this method.
*
* @param ProductData $product_data The extension data returned from the Products API.
* @param int|false $item_id Optional: the item ID.
* @return array
*/
protected function get_button_parameters( ProductData $product_data, $item_id = false ) {
if ( empty( $item_id ) ) {
$item_id = $this->item_id;
}
$body = $this->get_api_body();
$type = $this->get_type( $body );
$id = ! empty( $body[ $type ] ) ? $body[ $type ] : $this->item_id;
$button = array(
'type' => $type,
'id' => $id,
'product' => $item_id,
);
// If the extension is not installed, the button will prompt to install and activate it.
if ( ! $this->manager->is_plugin_installed( $product_data->basename ) ) {
if ( $this->manager->pass_can_download() ) {
$button = array(
/* translators: The extension name. */
'button_text' => sprintf( __( 'Log In to Your Account to Download %s', 'easy-digital-downloads' ), $product_data->title ),
'href' => $this->get_upgrade_url( $product_data, $item_id, true ),
'new_tab' => true,
'type' => $type,
);
} else {
$button = array(
/* translators: The extension name. */
'button_text' => sprintf( __( 'Upgrade Today to Access %s!', 'easy-digital-downloads' ), $product_data->title ),
'href' => $this->get_upgrade_url( $product_data, $item_id ),
'new_tab' => true,
'type' => $type,
);
}
} elseif ( ! empty( $product_data->basename ) && ! $this->manager->is_plugin_active( $product_data->basename ) ) {
// If the extension is installed, but not activated, the button will prompt to activate it.
$button['plugin'] = $product_data->basename;
$button['action'] = 'activate';
/* translators: The extension name. */
$button['button_text'] = sprintf( __( 'Activate %s', 'easy-digital-downloads' ), $product_data->title );
}
return $button;
}
/**
* Gets the upgrade URL for the button.
*
* @since 2.11.4
* @param ProductData $product_data The product data object.
* @param int $item_id The item/product ID.
* @param bool $has_access Whether the user already has access to the extension (based on pass level).
* @return string
*/
private function get_upgrade_url( ProductData $product_data, $item_id, $has_access = false ) {
if ( $has_access ) {
$url = 'https://easydigitaldownloads.com/your-account/your-downloads/';
} else {
$url = 'https://easydigitaldownloads.com/lite-upgrade';
}
$utm_parameters = array(
'utm_medium' => $this->settings_section,
'utm_content' => $product_data->slug,
);
return edd_link_helper(
$url,
$utm_parameters
);
}
/**
* Gets the array of parameters for the link to configure the extension.
*
* @since 2.11.4
* @param ProductData $product_data The product data object.
* @return array
*/
protected function get_link_parameters( ProductData $product_data ) {
$configuration = $this->get_configuration( $product_data );
$tab = ! empty( $configuration['tab'] ) ? $configuration['tab'] : $product_data->tab;
$section = ! empty( $configuration['section'] ) ? $configuration['section'] : $product_data->section;
if ( empty( $tab ) && empty( $section ) ) {
return array(
/* translators: the plural Downloads label. */
'button_text' => sprintf( __( 'View %s', 'easy-digital-downloads' ), edd_get_label_plural() ),
'href' => add_query_arg(
array(
'post_type' => 'download',
),
admin_url( 'edit.php' )
),
);
}
return array(
/* translators: The extension name. */
'button_text' => sprintf( __( 'Configure %s', 'easy-digital-downloads' ), $product_data->title ),
'href' => edd_get_admin_url(
array(
'page' => 'edd-settings',
'tab' => urlencode( $tab ),
'section' => urlencode( $section ),
)
),
);
}
/**
* Optionally hides the submit button on screens where it's not needed.
*
* @since 2.11.4
* @return void
*/
public function hide_submit_button() {
if ( ! $this->can_show_product_section() ) {
return;
}
?>
<style>p.submit{display:none;}</style>
<?php
}
}

View File

@ -0,0 +1,483 @@
<?php
namespace EDD\Admin\Extensions;
use \EDD\Admin\Pass_Manager;
class Extension_Manager {
/**
* All of the installed plugins on the site.
*
* @since 2.11.4
* @var array
*/
public $all_plugins;
/**
* The minimum pass ID required to install the extension.
*
* @since 2.11.4
* @var int
*/
private $required_pass_id;
/**
* Pass Manager class
*
* @var Pass_Manager
*/
protected $pass_manager;
public function __construct( $required_pass_id = null ) {
if ( $required_pass_id ) {
$this->required_pass_id = $required_pass_id;
}
$this->pass_manager = new Pass_Manager();
add_action( 'wp_ajax_edd_activate_extension', array( $this, 'activate' ) );
add_action( 'wp_ajax_edd_install_extension', array( $this, 'install' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'register_assets' ) );
}
/**
* Registers the extension manager script and style.
*
* @since 2.11.4
* @return void
*/
public function register_assets() {
if ( wp_script_is( 'edd-extension-manager', 'registered' ) ) {
return;
}
$minify = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
wp_register_style( 'edd-extension-manager', EDD_PLUGIN_URL . "assets/css/edd-admin-extension-manager.min.css", array(), EDD_VERSION );
wp_register_script( 'edd-extension-manager', EDD_PLUGIN_URL . "assets/js/edd-admin-extension-manager.js", array( 'jquery' ), EDD_VERSION, true );
wp_localize_script(
'edd-extension-manager',
'EDDExtensionManager',
array(
'activating' => __( 'Activating', 'easy-digital-downloads' ),
'installing' => __( 'Installing', 'easy-digital-downloads' ),
'plugin_install_failed' => __( 'Could not install the plugin. Please download and install it manually via Plugins > Add New.', 'easy-digital-downloads' ),
'extension_install_failed' => sprintf(
/* translators: 1. opening anchor tag, do not translate; 2. closing anchor tag, do not translate */
__( 'Could not install the extension. Please %1$sdownload it from your account%2$s and install it manually.', 'easy-digital-downloads' ),
'<a href="https://easydigitaldownloads.com/your-account/" target="_blank" rel="noopener noreferrer">',
'</a>'
),
'extension_manager_nonce' => wp_create_nonce( 'edd_extensionmanager' ),
)
);
}
/**
* Enqueues the extension manager script/style.
*
* @since 2.11.4
* @return void
*/
public function enqueue() {
wp_enqueue_style( 'edd-extension-manager' );
wp_enqueue_script( 'edd-extension-manager' );
}
/**
* Outputs a standard extension card.
*
* @since 2.11.4
* @param ProductData $product The product data object.
* @param array $inactive_parameters The array of information to build the button for an inactive/not installed plugin.
* @param array $active_parameters The array of information needed to build the link to configure an active plugin.
* @param array $configuration The optional array of data to override the product data retrieved from the API.
* @return void
*/
public function do_extension_card( ProductData $product, $inactive_parameters, $active_parameters, $configuration = array() ) {
$this->enqueue();
if ( ! empty( $configuration ) ) {
$product = $product->mergeConfig( $configuration );
}
?>
<div class="<?php echo esc_attr( implode( ' ', array_map( 'sanitize_html_class', $this->get_card_classes( $product ) ) ) ); ?>">
<h3 class="edd-extension-manager__title"><?php echo esc_html( $product->title ); ?></h3>
<div class="edd-extension-manager__body">
<?php if ( ! empty( $product->image ) ) : ?>
<div class="edd-extension-manager__image">
<img alt="" src="<?php echo esc_url( $product->image ); ?>" />
</div>
<?php endif; ?>
<?php if ( ! empty( $product->description ) ) : ?>
<div class="edd-extension-manager__description"><?php echo wp_kses_post( wpautop( $product->description ) ); ?></div>
<?php endif; ?>
<?php if ( ! empty( $product->features ) && is_array( $product->features ) ) : ?>
<div class="edd-extension-manager__features">
<ul>
<?php foreach ( $product->features as $feature ) : ?>
<li><span class="dashicons dashicons-yes"></span><?php echo esc_html( $feature ); ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<div class="edd-extension-manager__group">
<?php
if ( ! empty( $product->basename ) && ! $this->is_plugin_active( $product->basename ) ) {
?>
<div class="edd-extension-manager__step">
<?php $this->button( $inactive_parameters ); ?>
</div>
<?php
}
?>
<div class="edd-extension-manager__step">
<?php $this->link( $active_parameters ); ?>
</div>
</div>
</div>
</div>
<?php
}
/**
* Gets the CSS classes for the single extension card.
*
* @since 2.11.4
* @param ProductData $product The product data object.
* @return array The array of CSS classes.
*/
private function get_card_classes( $product ) {
$base_class = 'edd-extension-manager__card';
$card_classes = array(
$base_class,
);
$variation = 'stacked';
if ( ! empty( $product->style ) ) {
$variation = $product->style;
}
if ( 'detailed-2col' === $variation && ( empty( $product->features ) || ! is_array( $product->features ) ) ) {
$variation = 'detailed';
}
$card_classes[] = "{$base_class}--{$variation}";
return $card_classes;
}
/**
* Outputs the button to activate/install a plugin/extension.
* If a link is passed in the args, create a button style link instead (@uses $this->link()).
*
* @since 2.11.4
* @param array $args The array of parameters for the button.
* @return void
*/
public function button( $args ) {
if ( ! empty( $args['href'] ) ) {
$this->link( $args );
return;
}
$defaults = array(
'button_class' => 'button-primary',
'plugin' => '',
'action' => '',
'button_text' => '',
'type' => 'plugin',
'id' => '',
'product' => '',
'pass' => $this->required_pass_id,
);
$args = wp_parse_args( $args, $defaults );
if ( empty( $args['button_text'] ) ) {
return;
}
?>
<button
class="button <?php echo esc_attr( $args['button_class'] ); ?> edd-extension-manager__action"
<?php
foreach ( $args as $key => $attribute ) {
if ( empty( $attribute ) || in_array( $key, array( 'button_class', 'button_text' ), true ) ) {
continue;
}
printf(
' data-%s="%s"',
esc_attr( $key ),
esc_attr( $attribute )
);
}
?>
>
<?php echo esc_html( $args['button_text'] ); ?>
</button>
<?php
}
/**
* Outputs the link, if it should be a link.
*
* @param array $args
* @return void
*/
public function link( $args ) {
$defaults = array(
'button_class' => 'button-primary',
'button_text' => '',
);
$args = wp_parse_args( $args, $defaults );
if ( empty( $args['button_text'] ) ) {
return;
}
?>
<a
class="button <?php echo esc_attr( $args['button_class'] ); ?>"
href="<?php echo esc_url( $args['href'] ); ?>"
<?php echo ! empty( $args['new_tab'] ) ? ' target="_blank" rel="noopener noreferrer"' : ''; ?>
>
<?php echo esc_html( $args['button_text'] ); ?>
</a>
<?php
}
/**
* Installs and maybe activates a plugin or extension.
*
* @since 2.11.4
*/
public function install() {
// Run a security check.
check_ajax_referer( 'edd_extensionmanager', 'nonce', true );
$generic_error = esc_html__( 'There was an error while performing your request.', 'easy-digital-downloads' );
$type = ! empty( $_POST['type'] ) ? sanitize_text_field( $_POST['type'] ) : '';
$required_pass = ! empty( $_POST['pass'] ) ? sanitize_text_field( $_POST['pass'] ) : '';
$result = array(
'message' => $generic_error,
'is_activated' => false,
);
if ( ! $type ) {
wp_send_json_error( $result );
}
// Check if new installations are allowed.
if ( ! $this->can_install( $type, $required_pass ) ) {
wp_send_json_error( $result );
}
$result['message'] = 'plugin' === $type
? __( 'Could not install the plugin. Please download and install it manually via Plugins > Add New.', 'easy-digital-downloads' )
: sprintf(
/* translators: 1. opening anchor tag, do not translate; 2. closing anchor tag, do not translate */
__( 'Could not install the extension. Please %1$sdownload it from your account%2$s and install it manually.', 'easy-digital-downloads' ),
'<a href="https://easydigitaldownloads.com/your-account/" target="_blank" rel="noopener noreferrer">',
'</a>'
);
$plugin = ! empty( $_POST['plugin'] ) ? sanitize_text_field( $_POST['plugin'] ) : '';
if ( empty( $plugin ) ) {
wp_send_json_error( $result );
}
// Set the current screen to avoid undefined notices.
set_current_screen( 'download_page_edd-settings' );
// Prepare variables.
$url = esc_url_raw(
edd_get_admin_url(
array(
'page' => 'edd-addons',
)
)
);
ob_start();
$creds = request_filesystem_credentials( $url, '', false, false, null );
// Hide the filesystem credentials form.
ob_end_clean();
// Check for file system permissions.
if ( ! $creds ) {
wp_send_json_error( $result );
}
if ( ! WP_Filesystem( $creds ) ) {
wp_send_json_error( $result );
}
/*
* We do not need any extra credentials if we have gotten this far, so let's install the plugin.
*/
require_once EDD_PLUGIN_DIR . 'includes/admin/installers/class-plugin-silent-upgrader.php';
require_once EDD_PLUGIN_DIR . 'includes/admin/installers/class-plugin-silent-upgrader-skin.php';
require_once EDD_PLUGIN_DIR . 'includes/admin/installers/class-install-skin.php';
// Do not allow WordPress to search/download translations, as this will break JS output.
remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 );
// Create the plugin upgrader with our custom skin.
$installer = new \EDD\Admin\Installers\PluginSilentUpgrader( new \EDD\Admin\Installers\Install_Skin() );
// Error check.
if ( ! method_exists( $installer, 'install' ) || empty( $plugin ) ) {
wp_send_json_error( $result );
}
$installer->install( $plugin ); // phpcs:ignore
// Flush the cache and return the newly installed plugin basename.
wp_cache_flush();
$plugin_basename = $installer->plugin_info();
// Check for permissions.
if ( ! current_user_can( 'activate_plugins' ) ) {
$result['message'] = 'plugin' === $type ? esc_html__( 'Plugin installed.', 'easy-digital-downloads' ) : esc_html__( 'Extension installed.', 'easy-digital-downloads' );
wp_send_json_error( $result );
}
$this->activate( $plugin_basename );
}
/**
* Activates an existing extension.
*
* @since 2.11.4
* @param string $plugin_basename Optional: the plugin basename.
*/
public function activate( $plugin_basename = '' ) {
$result = array(
'message' => __( 'There was an error while performing your request.', 'easy-digital-downloads' ),
'is_activated' => false,
);
// Check for permissions.
if ( ! check_ajax_referer( 'edd_extensionmanager', 'nonce', false ) || ! current_user_can( 'activate_plugins' ) ) {
$result['message'] = __( 'Plugin activation is not available for you on this site.', 'easy-digital-downloads' );
wp_send_json_error( $result );
}
$already_installed = false;
if ( empty( $plugin_basename ) ) {
$plugin_basename = ! empty( $_POST['plugin'] ) ? sanitize_text_field( $_POST['plugin'] ) : '';
$already_installed = true;
}
$plugin_basename = sanitize_text_field( wp_unslash( $plugin_basename ) );
$type = ! empty( $_POST['type'] ) ? sanitize_text_field( $_POST['type'] ) : '';
if ( 'plugin' !== $type ) {
$type = 'extension';
}
$result = array(
/* translators: "extension" or "plugin" as defined by $type */
'message' => sprintf( __( 'Could not activate the %s.', 'easy-digital-downloads' ), esc_html( $type ) ),
'is_activated' => false,
);
if ( empty( $plugin_basename ) || empty( $type ) ) {
wp_send_json_error( $result );
}
$result['basename'] = $plugin_basename;
// Activate the plugin silently.
$activated = activate_plugin( $plugin_basename );
if ( ! is_wp_error( $activated ) ) {
if ( $already_installed ) {
$message = 'plugin' === $type ? esc_html__( 'Plugin activated.', 'easy-digital-downloads' ) : esc_html__( 'Extension activated.', 'easy-digital-downloads' );
} else {
$message = 'plugin' === $type ? esc_html__( 'Plugin installed & activated.', 'easy-digital-downloads' ) : esc_html__( 'Extension installed & activated.', 'easy-digital-downloads' );
}
$result['is_activated'] = true;
$result['message'] = $message;
wp_send_json_success( $result );
}
// Fallback error just in case.
wp_send_json_error( $result );
}
/**
* Determine if the plugin/extension installations are allowed.
*
* @since 2.11.4
*
* @param string $type Should be `plugin` or `extension`.
*
* @return bool
*/
public function can_install( $type, $required_pass_id = false ) {
if ( ! current_user_can( 'install_plugins' ) ) {
return false;
}
// Determine whether file modifications are allowed.
if ( ! wp_is_file_mod_allowed( 'edd_can_install' ) ) {
return false;
}
// All plugin checks are done.
if ( 'plugin' === $type ) {
return true;
}
return $this->pass_can_download( $required_pass_id );
}
/**
* Checks if a user's pass can download an extension.
*
* @since 2.11.4
* @return bool Returns true if the current site has an active pass and it is greater than or equal to the extension's minimum pass.
*/
public function pass_can_download( $required_pass_id = false ) {
$highest_pass_id = $this->pass_manager->highest_pass_id;
if ( ! $required_pass_id ) {
$required_pass_id = $this->required_pass_id;
}
return ! empty( $highest_pass_id ) && ! empty( $required_pass_id ) && $this->pass_manager->pass_compare( $highest_pass_id, $required_pass_id, '>=' );
}
/**
* Get all installed plugins.
*
* @since 2.11.4
* @return array
*/
public function get_plugins() {
if ( $this->all_plugins ) {
return $this->all_plugins;
}
$this->all_plugins = get_plugins();
return $this->all_plugins;
}
/**
* Check if a plugin is installed.
*
* @since 2.11.4
* @param string $plugin The path to the main plugin file, eg 'my-plugin/my-plugin.php'.
* @return boolean
*/
public function is_plugin_installed( $plugin ) {
return array_key_exists( $plugin, $this->get_plugins() );
}
/**
* Whether a given plugin is active or not.
*
* @since 2.11.4
* @param string|ProductData $basename_or_data The path to the main plugin file, eg 'my-plugin/my-plugin.php', or the product data object.
* @return boolean
*/
public function is_plugin_active( $basename_or_data ) {
$basename = ! empty( $basename_or_data->basename ) ? $basename_or_data->basename : $basename_or_data;
return ! empty( $basename ) && is_plugin_active( $basename );
}
}

View File

@ -0,0 +1,111 @@
<?php
namespace EDD\Admin\Extensions;
class ProductData {
/**
* The product name.
*
* @since 2.11.4
* @var string
*/
public $title;
/**
* The product slug.
*
* @since 2.11.4
* @var string
*/
public $slug = '';
/**
* The URL for the product featured image.
*
* @since 2.11.4
* @var string
*/
public $image;
/**
* The product description.
*
* @since 2.11.4
* @var string
*/
public $description;
/**
* The extension basename.
*
* @since 2.11.4
* @var string
*/
public $basename;
/**
* The settings tab where the extension settings will show.
*
* @since 2.11.4
* @var string
*/
public $tab;
/**
* The settings section for the extension.
*
* @since 2.11.4
* @var string
*/
public $section;
/**
* The product features.
*
* @since 2.11.4
* @var array
*/
public $features = array();
/**
* Take array and return object.
*
* @since 2.11.4
* @param array $array
* @return ProductData
* @throws \InvalidArgumentException
*/
public function fromArray( $array ) {
$expected_keys = array( 'title', 'slug', 'description', 'basename' );
$array_to_check = array_intersect_key( $array, array_flip( $expected_keys ) );
if ( empty( $array_to_check ) ) {
throw new \InvalidArgumentException(
'Invalid ProductData object, must have the exact following keys: ' . implode( ', ', $expected_keys )
);
}
$product_data = new self();
foreach ( $array as $key => $value ) {
$product_data->$key = $value;
}
return $product_data;
}
/**
* Merge an array of data into an object.
*
* @since 2.11.4
* @param array $configuration The custom configuration data.
* @return ProductData
*/
public function mergeConfig( array $configuration ) {
foreach ( $configuration as $key => $value ) {
$this->{$key} = $value;
}
return $this;
}
}

View File

@ -0,0 +1,222 @@
<?php
namespace EDD\Admin\Extensions;
class ExtensionsAPI {
/**
* Gets the product data from the EDD Products API.
*
* @since 2.11.4
* @param array $body The body for the API request.
* @param int $item_id The product ID, if querying a single product.
* @return false|array|ProductData
*/
public function get_product_data( $body = array(), $item_id = false ) {
if ( empty( $body ) ) {
if ( empty( $item_id ) ) {
return false;
}
$body = $this->get_api_body( $item_id );
}
$key = $this->array_key_first( $body );
// The option name is created from the first key/value pair of the API "body".
$option_name = sanitize_key( "edd_extension_{$key}_{$body[ $key ]}_data" );
$option = get_option( $option_name );
$is_stale = $this->option_has_expired( $option );
// The ProductData class.
$product_data = new ProductData();
// If the data is "fresh" and what we want exists, return it.
if ( $option && ! $is_stale ) {
if ( $item_id && ! empty( $option[ $item_id ] ) ) {
return $product_data->fromArray( $option[ $item_id ] );
} elseif ( ! empty( $option['timeout'] ) ) {
unset( $option['timeout'] );
return $option;
}
}
// Get all of the product data.
$all_product_data = $this->get_all_product_data();
// If no product data was retrieved, let the option sit for an hour.
if ( empty( $all_product_data ) ) {
$data = array(
'timeout' => strtotime( '+1 hour', time() ),
);
if ( $option && $is_stale ) {
$data = array_merge( $option, $data );
}
update_option(
$option_name,
$data,
false
);
if ( $item_id && ! empty( $option[ $item_id ] ) ) {
return $product_data->fromArray( $option[ $item_id ] );
}
unset( $option['timeout'] );
return $option;
}
$value = array(
'timeout' => strtotime( '+1 week', time() ),
);
if ( $item_id && ! empty( $all_product_data->$item_id ) ) {
$item = $all_product_data->$item_id;
$value[ $item_id ] = $this->get_item_data( $item );
} elseif ( in_array( $key, array( 'category', 'tag' ), true ) ) {
$term_id = $body[ $key ];
foreach ( $all_product_data as $item_id => $item ) {
if ( 'category' === $key && ( empty( $item->categories ) || ! in_array( $term_id, $item->categories, true ) ) ) {
continue;
} elseif ( 'tag' === $key && ( empty( $item->tags ) || ! in_array( $term_id, $item->tags, true ) ) ) {
continue;
}
$value[ $item_id ] = $this->get_item_data( $item );
}
}
update_option( $option_name, $value, false );
unset( $value['timeout'] );
return $item_id && ! empty( $value[ $item_id ] ) ? $product_data->fromArray( $value[ $item_id ] ) : $value;
}
/**
* Gets all of the product data, either from an option or an API request.
* If the option exists and has data, it will be an object.
*
* @since 2.11.4
* @return object|false
*/
private function get_all_product_data() {
// Possibly all product data is in an option. If it is, return it.
$all_product_data = get_option( 'edd_all_extension_data' );
if ( $all_product_data && ! $this->option_has_expired( $all_product_data ) ) {
return ! empty( $all_product_data['products'] ) ? $all_product_data['products'] : false;
}
// Otherwise, query the API.
$url = add_query_arg(
array(
'edd_action' => 'extension_data',
),
$this->get_products_url()
);
$request = wp_remote_get(
esc_url_raw( $url ),
array(
'timeout' => 15,
'sslverify' => true,
)
);
// If there was an API error, set option and return false.
if ( is_wp_error( $request ) || ( 200 !== wp_remote_retrieve_response_code( $request ) ) ) {
update_option(
'edd_all_extension_data',
array(
'timeout' => strtotime( '+1 hour', time() ),
),
false
);
return false;
}
// Fresh data has been retrieved, so update the option with a four hour timeout.
$all_product_data = json_decode( wp_remote_retrieve_body( $request ) );
$data = array(
'timeout' => strtotime( '+4 hours', time() ),
'products' => $all_product_data,
);
update_option( 'edd_all_extension_data', $data, false );
return $all_product_data;
}
/**
* Gets the product data as needed for the extension manager.
*
* @since 2.11.4
* @param object $item
* @return array
*/
private function get_item_data( $item ) {
return array(
'title' => ! empty( $item->title ) ? $item->title : '',
'slug' => ! empty( $item->slug ) ? $item->slug : '',
'image' => ! empty( $item->image ) ? $item->image : '',
'description' => ! empty( $item->excerpt ) ? $item->excerpt : '',
'basename' => ! empty( $item->custom_meta->basename ) ? $item->custom_meta->basename : '',
'tab' => ! empty( $item->custom_meta->settings_tab ) ? $item->custom_meta->settings_tab : '',
'section' => ! empty( $item->custom_meta->settings_section ) ? $item->custom_meta->settings_section : '',
);
}
/**
* Gets the base url for the products remote request.
*
* @since 2.11.4
* @return string
*/
private function get_products_url() {
if ( defined( 'EDD_PRODUCTS_URL' ) ) {
return EDD_PRODUCTS_URL;
}
return 'https://easydigitaldownloads.com/';
}
/**
* Gets the default array for the body of the API request.
* A class may override this by setting an array to query a tag or category.
* Note that the first array key/value pair are used to create the option name.
*
* @since 2.11.4
* @param int $item_id The product ID.
* @return array
*/
private function get_api_body( $item_id ) {
return array(
'product' => $item_id,
);
}
/**
* Gets the first key of an array.
* (Shims array_key_first for PHP < 7.3)
*
* @since 2.11.4
* @param array $array
* @return string|null
*/
private function array_key_first( array $array ) {
if ( function_exists( 'array_key_first' ) ) {
return array_key_first( $array );
}
foreach ( $array as $key => $unused ) {
return $key;
}
return null;
}
/**
* Checks whether a given option has "expired".
*
* @since 2.11.4
* @param array|false $option
* @return bool
*/
private function option_has_expired( $option ) {
return empty( $option['timeout'] ) || time() > $option['timeout'];
}
}

View File

@ -0,0 +1,132 @@
<?php
/**
* Email Marketing
*
* Manages automatic installation/activation for email marketing extensions.
*
* @package EDD
* @subpackage EmailMarketing
* @copyright Copyright (c) 2021, Easy Digital Downloads
* @license https://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.11.4
*/
namespace EDD\Admin\Settings;
use \EDD\Admin\Extensions\Extension;
class EmailMarketing extends Extension {
/**
* The EDD settings tab where this extension should show.
*
* @since 2.11.4
* @var string
*/
protected $settings_tab = 'marketing';
/**
* The settings section for this item.
*
* @since 2.11.5
* @var string
*/
protected $settings_section = 'email_marketing';
public function __construct() {
add_filter( 'edd_settings_sections_marketing', array( $this, 'add_section' ) );
add_action( 'edd_settings_tab_top_marketing_email_marketing', array( $this, 'field' ) );
parent::__construct();
}
/**
* Adds an email marketing section to the Marketing tab.
*
* @since 2.11.4
* @param array $sections
* @return array
*/
public function add_section( $sections ) {
if ( ! $this->is_edd_settings_screen() ) {
return $sections;
}
$product_data = $this->get_product_data();
if ( ! $product_data || ! is_array( $product_data ) ) {
return $sections;
}
$sections[ $this->settings_section ] = __( 'Email Marketing', 'easy-digital-downloads' );
return $sections;
}
/**
* Gets the customized configuration for the extension card.
*
* @since 2.11.4
* @param \EDD\Admin\Extensions\ProductData $product_data The product data object.
* @return array
*/
protected function get_configuration( \EDD\Admin\Extensions\ProductData $product_data ) {
$configuration = array();
if ( ! empty( $product_data->title ) ) {
/* translators: the product name */
$configuration['title'] = sprintf( __( 'Get %s Today!', 'easy-digital-downloads' ), $product_data->title );
}
return $configuration;
}
/**
* Adds the email marketing extensions as cards.
*
* @since 2.11.4
* @return void
*/
public function field() {
$this->hide_submit_button();
if ( $this->is_activated() ) {
printf( '<p>%s</p>', esc_html__( 'Looks like you have an email marketing extension installed, but we support more providers!', 'easy-digital-downloads' ) );
}
?>
<div class="edd-extension-manager__card-group">
<?php
foreach ( $this->get_product_data() as $item_id => $extension ) {
$this->do_single_extension_card( $item_id );
}
?>
</div>
<?php
}
/**
* Overrides the body array sent to the Products API.
*
* @since 2.11.4
* @return array
*/
protected function get_api_body() {
return array(
'tag' => 1578,
);
}
/**
* Whether any email marketing extension is active.
*
* @since 2.11.4
*
* @return bool True if any email marketing extension is active.
*/
protected function is_activated() {
foreach ( $this->get_product_data() as $extension ) {
// The data is stored in the database as an array--at this point it has not been converted to an object.
if ( ! empty( $extension['basename'] ) && $this->manager->is_plugin_active( $extension['basename'] ) ) {
return true;
}
}
return false;
}
}
new EmailMarketing();

View File

@ -0,0 +1,124 @@
<?php
/**
* Invoices
*
* Manages automatic installation/activation for Invoices.
*
* @package EDD
* @subpackage Invoices
* @copyright Copyright (c) 2021, Easy Digital Downloads
* @license https://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.11.4
*/
namespace EDD\Admin\Settings;
use \EDD\Admin\Extensions\Extension;
class Invoices extends Extension {
/**
* The product ID on EDD.
*
* @var integer
*/
protected $item_id = 375153;
/**
* The EDD settings tab where this extension should show.
*
* @since 2.11.4
* @var string
*/
protected $settings_tab = 'gateways';
/**
* The settings section for this item.
*
* @since 2.11.5
* @var string
*/
protected $settings_section = 'invoices';
/**
* The pass level required to access this extension.
*/
const PASS_LEVEL = \EDD\Admin\Pass_Manager::EXTENDED_PASS_ID;
public function __construct() {
add_filter( 'edd_settings_sections_gateways', array( $this, 'add_section' ) );
add_action( 'edd_settings_tab_top_gateways_invoices', array( $this, 'settings_field' ) );
add_action( 'edd_settings_tab_top_gateways_invoices', array( $this, 'hide_submit_button' ) );
parent::__construct();
}
/**
* Gets the custom configuration for Invoices.
*
* @since 2.11.4
* @param \EDD\Admin\Extensions\ProductData $product_data The product data object.
* @return array
*/
protected function get_configuration( \EDD\Admin\Extensions\ProductData $product_data ) {
return array(
'style' => 'detailed-2col',
'title' => 'Attractive Invoices For Your Customers',
'description' => $this->get_custom_description(),
'features' => array(
'Generate Attractive Invoices',
'Build Customer Confidence',
'PDF Download Support',
'Include in Purchase Emails',
'Customizable Templates',
),
);
}
/**
* Gets a custom description for the Invoices extension card.
*
* @since 2.11.4
* @return string
*/
private function get_custom_description() {
$description = array(
'Impress customers and build customer loyalty with attractive invoices. Making it easy to locate, save, and print purchase history builds trust with customers.',
'Provide a professional experience with customizable templates and one-click PDF downloads. ',
);
return $this->format_description( $description );
}
/**
* Adds the Invoices Payments section to the settings.
*
* @param array $sections
* @return array
*/
public function add_section( $sections ) {
if ( ! $this->can_show_product_section() ) {
return $sections;
}
$sections[ $this->settings_section ] = __( 'Invoices', 'easy-digital-downloads' );
return $sections;
}
/**
* Whether EDD Invoices active or not.
*
* @since 2.11.4
*
* @return bool True if Invoices is active.
*/
protected function is_activated() {
if ( $this->manager->is_plugin_active( $this->get_product_data() ) ) {
return true;
}
return class_exists( 'EDDInvoices' );
}
}
new Invoices();

View File

@ -0,0 +1,124 @@
<?php
/**
* Recurring Payments
*
* Manages automatic activation for Recurring Payments.
*
* @package EDD
* @subpackage Recurring
* @copyright Copyright (c) 2021, Easy Digital Downloads
* @license https://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.11.4
*/
namespace EDD\Admin\Settings;
use \EDD\Admin\Extensions\Extension;
class Recurring extends Extension {
/**
* The product ID on EDD.
*
* @var integer
*/
protected $item_id = 28530;
/**
* The EDD settings tab where this extension should show.
*
* @since 2.11.4
* @var string
*/
protected $settings_tab = 'gateways';
/**
* The pass level required to access this extension.
*/
const PASS_LEVEL = \EDD\Admin\Pass_Manager::EXTENDED_PASS_ID;
/**
* The settings section for this item.
*
* @since 2.11.5
* @var string
*/
protected $settings_section = 'recurring';
public function __construct() {
add_filter( 'edd_settings_sections_gateways', array( $this, 'add_section' ) );
add_action( 'edd_settings_tab_top_gateways_recurring', array( $this, 'settings_field' ) );
add_action( 'edd_settings_tab_top_gateways_recurring', array( $this, 'hide_submit_button' ) );
parent::__construct();
}
/**
* Gets the custom configuration for Recurring.
*
* @since 2.11.4
* @param \EDD\Admin\Extensions\ProductData $product_data The product data object.
* @return array
*/
protected function get_configuration( \EDD\Admin\Extensions\ProductData $product_data ) {
return array(
'style' => 'detailed-2col',
'title' => 'Increase Revenue By Selling Subscriptions!',
'description' => $this->get_custom_description(),
'features' => array(
'Flexible Recurring Payments',
'Custom Reminder Emails',
'Free Trial Support',
'Signup Fees',
'Recurring Revenue Reports',
),
);
}
/**
* Gets a custom description for the Recurring extension card.
*
* @since 2.11.4
* @return string
*/
private function get_custom_description() {
$description = array(
'Grow stable income by selling subscriptions and make renewals hassle free for your customers.',
'When your customers are automatically billed, you reduce the risk of missed payments and retain more customers.',
);
return $this->format_description( $description );
}
/**
* Adds the Recurring Payments section to the settings.
*
* @param array $sections
* @return array
*/
public function add_section( $sections ) {
if ( ! $this->can_show_product_section() ) {
return $sections;
}
$sections[ $this->settings_section ] = __( 'Recurring Payments', 'easy-digital-downloads' );
return $sections;
}
/**
* Whether EDD Recurring active or not.
*
* @since 2.11.4
*
* @return bool True if Recurring is active.
*/
protected function is_activated() {
if ( $this->manager->is_plugin_active( $this->get_product_data() ) ) {
return true;
}
return class_exists( 'EDD_Recurring' );
}
}
new Recurring();

View File

@ -0,0 +1,152 @@
<?php
/**
* Reviews
*
* Manages automatic activation for Reviews.
*
* @package EDD
* @subpackage Reviews
* @copyright Copyright (c) 2021, Easy Digital Downloads
* @license https://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.11.4
*/
namespace EDD\Admin\Settings;
use \EDD\Admin\Extensions\Extension;
class Reviews extends Extension {
/**
* The product ID on EDD.
*
* @var integer
*/
protected $item_id = 37976;
/**
* The EDD settings tab where this extension should show.
*
* @since 2.11.4
* @var string
*/
protected $settings_tab = 'marketing';
/**
* The settings section for this item.
*
* @since 2.11.5
* @var string
*/
protected $settings_section = 'reviews';
/**
* The pass level required to access this extension.
*/
const PASS_LEVEL = \EDD\Admin\Pass_Manager::EXTENDED_PASS_ID;
public function __construct() {
add_filter( 'edd_settings_sections_marketing', array( $this, 'add_section' ) );
add_action( 'edd_settings_tab_top_marketing_reviews', array( $this, 'settings_field' ) );
add_action( 'edd_settings_tab_top_marketing_reviews', array( $this, 'hide_submit_button' ) );
add_action( 'add_meta_boxes', array( $this, 'maybe_do_metabox' ) );
parent::__construct();
}
/**
* Gets the custom configuration for Reviews.
*
* @since 2.11.4
* @param \EDD\Admin\Extensions\ProductData $product_data The product data object.
* @return array
*/
protected function get_configuration( \EDD\Admin\Extensions\ProductData $product_data ) {
$configuration = array(
'title' => 'Build Trust With Real Customer Reviews',
);
$settings_configuration = array(
'style' => 'detailed-2col',
'description' => $this->get_custom_description(),
'features' => array(
'Request Reviews',
'Incentivize Reviewers',
'Full Schema.org Support',
'Embed Reviews Via Blocks',
'Limit Reviews to Customers',
'Vendor Reviews (with Frontend Submissions)',
),
);
return $this->is_edd_settings_screen() ? array_merge( $configuration, $settings_configuration ) : $configuration;
}
/**
* Gets a custom description for the Reviews extension card.
*
* @since 2.11.4
* @return string
*/
private function get_custom_description() {
$description = array(
'Increase sales on your site with social proof. 70% of online shoppers don\'t purchase before reading reviews.',
'Easily collect, manage, and beautifully display reviews all from your WordPress dashboard.',
);
return $this->format_description( $description );
}
/**
* Adds the Reviews section to the settings.
*
* @param array $sections
* @return array
*/
public function add_section( $sections ) {
if ( ! $this->can_show_product_section() ) {
return $sections;
}
$sections[ $this->settings_section ] = __( 'Reviews', 'easy-digital-downloads' );
return $sections;
}
/**
* If Reviews is not active, registers a metabox on individual download edit screen.
*
* @since 2.11.4
* @return void
*/
public function maybe_do_metabox() {
if ( ! $this->is_download_edit_screen() ) {
return;
}
if ( $this->is_activated() ) {
return;
}
add_meta_box(
'edd-reviews-status',
__( 'Product Reviews', 'easy-digital-downloads' ),
array( $this, 'settings_field' ),
'download',
'side',
'low'
);
}
/**
* Whether EDD Reviews active or not.
*
* @since 2.11.4
*
* @return bool True if Reviews is active.
*/
protected function is_activated() {
if ( $this->manager->is_plugin_active( $this->get_product_data() ) ) {
return true;
}
return function_exists( 'edd_reviews' );
}
}
new Reviews();

View File

@ -0,0 +1,232 @@
<?php
/**
* WP Mail SMTP
*
* Manages automatic installation/activation for WP Mail SMTP.
*
* @package EDD
* @subpackage WP_SMTP
* @copyright Copyright (c) 2021, Easy Digital Downloads
* @license https://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.11.4
*/
namespace EDD\Admin\Settings;
class WP_SMTP {
/**
* Array of configuration data for WP Mail SMTP.
*
* @var array
*/
private $config = array(
'lite_plugin' => 'wp-mail-smtp/wp_mail_smtp.php',
'lite_wporg_url' => 'https://wordpress.org/plugins/wp-mail-smtp/',
'lite_download_url' => 'https://downloads.wordpress.org/plugin/wp-mail-smtp.zip',
'pro_plugin' => 'wp-mail-smtp-pro/wp_mail_smtp.php',
'smtp_settings' => 'admin.php?page=wp-mail-smtp',
'smtp_wizard' => 'admin.php?page=wp-mail-smtp-setup-wizard',
);
/**
* The Extension Manager
*
* @var \EDD\Admin\Extensions\Extension_Manager
*/
private $manager;
public function __construct() {
add_filter( 'edd_settings_emails', array( $this, 'register_setting' ) );
add_action( 'edd_wpsmtp', array( $this, 'settings_field' ) );
$this->manager = new \EDD\Admin\Extensions\Extension_Manager();
}
/**
* Register the setting to show the WP SMTP installer if it isn't active.
*
* @param array $settings
* @return array
*/
public function register_setting( $settings ) {
if ( $this->is_smtp_configured() ) {
return $settings;
}
$settings['main']['wpsmtp'] = array(
'id' => 'wpsmtp',
'name' => __( 'Improve Email Deliverability', 'easy-digital-downloads' ),
'desc' => '',
'type' => 'hook',
);
return $settings;
}
/**
* Output the settings field (installation helper).
*
* @param array $args
* @return void
*/
public function settings_field( $args ) {
$this->manager->enqueue();
?>
<div class="edd-extension-manager__body">
<p class="edd-extension-manager__description">
<?php esc_html_e( 'WP Mail SMTP allows you to easily set up WordPress to use a trusted provider to reliably send emails, including sales notifications.', 'easy-digital-downloads' ); ?>
</p>
<div class="edd-extension-manager__group">
<div class="edd-extension-manager__step">
<?php $this->manager->button( $this->get_button_parameters() ); ?>
</div>
<?php
if ( $this->is_smtp_activated() ) {
return;
}
?>
<div class="edd-extension-manager__step" style="display:none;">
<?php $this->manager->link( $this->get_link_parameters() ); ?>
</div>
</div>
</div>
<?php
}
/**
* Gets the button parameters.
*
* @return array
*/
private function get_button_parameters() {
$button = array();
// If neither the lite nor pro plugin is installed, the button will prompt to install and activate the lite plugin.
if ( ! $this->manager->is_plugin_installed( $this->config['lite_plugin'] ) && ! $this->manager->is_plugin_installed( $this->config['pro_plugin'] ) ) {
$button['plugin'] = $this->config['lite_download_url'];
$button['action'] = 'install';
$button['button_text'] = __( 'Install & Activate WP Mail SMTP', 'easy-digital-downloads' );
} elseif ( ! $this->is_smtp_activated() ) {
// If one of the SMTP plugins is installed, but not activated, the button will prompt to activate it.
$button['plugin'] = $this->config['lite_plugin'];
$button['action'] = 'activate';
$button['button_text'] = __( 'Activate WP Mail SMTP', 'easy-digital-downloads' );
} elseif ( ! $this->is_smtp_configured() ) {
// If the plugin is active, but not configured, the button will send them to the setup wizard.
$button = $this->get_link_parameters();
}
return $button;
}
/**
* Gets the array of parameters for the link to configure WP Mail SMTP.
*
* @since 2.11.4
* @return array
*/
private function get_link_parameters() {
return $this->is_smtp_configured() ?
array(
'button_text' => __( 'Configure WP Mail SMTP', 'easy-digital-downloads' ),
'href' => admin_url( $this->config['smtp_settings'] ),
) :
array(
'button_text' => __( 'Run the WP Mail SMTP Setup Wizard', 'easy-digital-downloads' ),
'href' => admin_url( $this->config['smtp_wizard'] ),
);
}
/**
* Whether WP Mail SMTP plugin configured or not.
*
* @since 2.11.4
*
* @return bool True if some mailer is selected and configured properly.
*/
protected function is_smtp_configured() {
if ( ! $this->is_smtp_activated() || ! class_exists( '\\WPMailSMTP\\Options' ) ) {
return false;
}
$phpmailer = $this->get_phpmailer();
$mailer = \WPMailSMTP\Options::init()->get( 'mail', 'mailer' );
$is_mailer_complete = ! empty( $mailer ) && wp_mail_smtp()->get_providers()->get_mailer( $mailer, $phpmailer )->is_mailer_complete();
return 'mail' !== $mailer && $is_mailer_complete;
}
/**
* Whether WP Mail SMTP plugin active or not.
*
* @since 2.11.4
*
* @return bool True if SMTP plugin is active.
*/
protected function is_smtp_activated() {
return function_exists( 'wp_mail_smtp' ) && ( is_plugin_active( $this->config['lite_plugin'] ) || is_plugin_active( $this->config['pro_plugin'] ) );
}
/**
* Get $phpmailer instance.
*
* @since 2.11.4
*
* @return \PHPMailer|\PHPMailer\PHPMailer\PHPMailer Instance of PHPMailer.
*/
protected function get_phpmailer() {
if ( version_compare( get_bloginfo( 'version' ), '5.5-alpha', '<' ) ) {
$phpmailer = $this->get_phpmailer_v5();
} else {
$phpmailer = $this->get_phpmailer_v6();
}
return $phpmailer;
}
/**
* Get $phpmailer v5 instance.
*
* @since 2.11.4
*
* @return \PHPMailer Instance of PHPMailer.
*/
private function get_phpmailer_v5() {
global $phpmailer;
if ( ! ( $phpmailer instanceof \PHPMailer ) ) {
require_once ABSPATH . WPINC . '/class-phpmailer.php';
require_once ABSPATH . WPINC . '/class-smtp.php';
$phpmailer = new \PHPMailer( true ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
}
return $phpmailer;
}
/**
* Get $phpmailer v6 instance.
*
* @since 2.11.4
*
* @return \PHPMailer\PHPMailer\PHPMailer Instance of PHPMailer.
*/
private function get_phpmailer_v6() {
global $phpmailer;
if ( ! ( $phpmailer instanceof \PHPMailer\PHPMailer\PHPMailer ) ) {
require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php';
require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php';
require_once ABSPATH . WPINC . '/PHPMailer/Exception.php';
$phpmailer = new \PHPMailer\PHPMailer\PHPMailer( true ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
}
return $phpmailer;
}
}
new WP_SMTP();

View File

@ -0,0 +1,566 @@
<?php
/**
* Download import class
*
* This class handles importing downloads with the batch processing API
*
* @package EDD
* @subpackage Admin/Import
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.6
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Batch_Downloads_Import Class
*
* @since 2.6
*/
class EDD_Batch_Downloads_Import extends EDD_Batch_Import {
/**
* Set up our import config.
*
* @since 2.6
* @return void
*/
public function init() {
// Set up default field map values
$this->field_mapping = array(
'post_title' => '',
'post_name' => '',
'post_status' => 'draft',
'post_author' => '',
'post_date' => '',
'post_content' => '',
'post_excerpt' => '',
'price' => '',
'files' => '',
'categories' => '',
'tags' => '',
'sku' => '',
'earnings' => '',
'sales' => '',
'featured_image' => '',
'download_limit' => '',
'notes' => ''
);
}
/**
* Process a step
*
* @since 2.6
* @return bool
*/
public function process_step() {
$more = false;
if ( ! $this->can_import() ) {
wp_die( __( 'You do not have permission to import data.', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
$i = 1;
$offset = $this->step > 1 ? ( $this->per_step * ( $this->step - 1 ) ) : 0;
if( $offset > $this->total ) {
$this->done = true;
// Delete the uploaded CSV file.
unlink( $this->file );
}
if( ! $this->done && $this->csv ) {
$more = true;
foreach( $this->csv as $key => $row ) {
// Skip all rows until we pass our offset
if( $key + 1 <= $offset ) {
continue;
}
// Done with this batch
if( $i > $this->per_step ) {
break;
}
// Import Download
$args = array(
'post_type' => 'download',
'post_title' => '',
'post_name' => '',
'post_status' => '',
'post_author' => '',
'post_date' => '',
'post_content' => '',
'post_excerpt' => ''
);
foreach ( $args as $key => $field ) {
if ( ! empty( $this->field_mapping[ $key ] ) && ! empty( $row[ $this->field_mapping[ $key ] ] ) ) {
$args[ $key ] = $row[ $this->field_mapping[ $key ] ];
}
}
if ( empty( $args['post_author'] ) ) {
$user = wp_get_current_user();
$args['post_author'] = $user->ID;
} else {
// Check all forms of possible user inputs, email, ID, login.
if ( is_email( $args['post_author'] ) ) {
$user = get_user_by( 'email', $args['post_author'] );
} elseif ( is_numeric( $args['post_author'] ) ) {
$user = get_user_by( 'ID', $args['post_author'] );
} else {
$user = get_user_by( 'login', $args['post_author'] );
}
// If we don't find one, resort to the logged in user.
if ( false === $user ) {
$user = wp_get_current_user();
}
$args['post_author'] = $user->ID;
}
// Format the date properly
if ( ! empty( $args['post_date'] ) ) {
$timestamp = strtotime( $args['post_date'], current_time( 'timestamp' ) );
$date = date( 'Y-m-d H:i:s', $timestamp );
// If the date provided results in a date string, use it, or just default to today so it imports
if ( ! empty( $date ) ) {
$args['post_date'] = $date;
} else {
$date = '';
}
}
// Detect any status that could map to `publish`
if ( ! empty( $args['post_status'] ) ) {
$published_statuses = array(
'live',
'published',
);
$current_status = strtolower( $args['post_status'] );
if ( in_array( $current_status, $published_statuses ) ) {
$args['post_status'] = 'publish';
}
}
$download_id = wp_insert_post( $args );
// setup categories
if( ! empty( $this->field_mapping['categories'] ) && ! empty( $row[ $this->field_mapping['categories'] ] ) ) {
$categories = $this->str_to_array( $row[ $this->field_mapping['categories'] ] );
$this->set_taxonomy_terms( $download_id, $categories, 'download_category' );
}
// setup tags
if( ! empty( $this->field_mapping['tags'] ) && ! empty( $row[ $this->field_mapping['tags'] ] ) ) {
$tags = $this->str_to_array( $row[ $this->field_mapping['tags'] ] );
$this->set_taxonomy_terms( $download_id, $tags, 'download_tag' );
}
// setup price(s)
if( ! empty( $this->field_mapping['price'] ) && ! empty( $row[ $this->field_mapping['price'] ] ) ) {
$price = $row[ $this->field_mapping['price'] ];
$this->set_price( $download_id, $price );
}
// setup files
if( ! empty( $this->field_mapping['files'] ) && ! empty( $row[ $this->field_mapping['files'] ] ) ) {
$files = $this->convert_file_string_to_array( $row[ $this->field_mapping['files'] ] );
$this->set_files( $download_id, $files );
}
// Product Image
if( ! empty( $this->field_mapping['featured_image'] ) && ! empty( $row[ $this->field_mapping['featured_image'] ] ) ) {
$image = sanitize_text_field( $row[ $this->field_mapping['featured_image'] ] );
$this->set_image( $download_id, $image, $args['post_author'] );
}
// File download limit
if( ! empty( $this->field_mapping['download_limit'] ) && ! empty( $row[ $this->field_mapping['download_limit'] ] ) ) {
update_post_meta( $download_id, '_edd_download_limit', absint( $row[ $this->field_mapping['download_limit'] ] ) );
}
// Sale count
if( ! empty( $this->field_mapping['sales'] ) && ! empty( $row[ $this->field_mapping['sales'] ] ) ) {
update_post_meta( $download_id, '_edd_download_sales', absint( $row[ $this->field_mapping['sales'] ] ) );
}
// Earnings
if( ! empty( $this->field_mapping['earnings'] ) && ! empty( $row[ $this->field_mapping['earnings'] ] ) ) {
update_post_meta( $download_id, '_edd_download_earnings', edd_sanitize_amount( $row[ $this->field_mapping['earnings'] ] ) );
}
// Notes
if( ! empty( $this->field_mapping['notes'] ) && ! empty( $row[ $this->field_mapping['notes'] ] ) ) {
update_post_meta( $download_id, 'edd_product_notes', sanitize_text_field( $row[ $this->field_mapping['notes'] ] ) );
}
// SKU
if( ! empty( $this->field_mapping[ 'sku' ] ) && ! empty( $row[ $this->field_mapping[ 'sku' ] ] ) ) {
update_post_meta( $download_id, 'edd_sku', sanitize_text_field( $row[ $this->field_mapping['sku'] ] ) );
}
// Custom fields
$i++;
}
}
return $more;
}
/**
* Return the calculated completion percentage
*
* @since 2.6
* @return int
*/
public function get_percentage_complete() {
if( $this->total > 0 ) {
$percentage = ( $this->step * $this->per_step / $this->total ) * 100;
}
if( $percentage > 100 ) {
$percentage = 100;
}
return $percentage;
}
/**
* Set up and store the price for the download
*
* @since 2.6
* @return void
*/
private function set_price( $download_id = 0, $price = '' ) {
if( is_numeric( $price ) ) {
update_post_meta( $download_id, 'edd_price', edd_sanitize_amount( $price ) );
} else {
$prices = $this->str_to_array( $price );
if( ! empty( $prices ) ) {
$variable_prices = array();
$price_id = 1;
foreach( $prices as $price ) {
// See if this matches the EDD Download export for variable prices
if( false !== strpos( $price, ':' ) ) {
$price = array_map( 'trim', explode( ':', $price ) );
$variable_prices[ $price_id ] = array( 'name' => $price[ 0 ], 'amount' => $price[ 1 ] );
$price_id++;
}
}
update_post_meta( $download_id, '_variable_pricing', 1 );
update_post_meta( $download_id, 'edd_variable_prices', $variable_prices );
}
}
}
/**
* Set up and store the file downloads
*
* @since 2.6
* @return void
*/
private function set_files( $download_id = 0, $files = array() ) {
if( ! empty( $files ) ) {
$download_files = array();
$file_id = 1;
foreach( $files as $file ) {
$condition = '';
if ( false !== strpos( $file, ';' ) ) {
$split_on = strpos( $file, ';' );
$file_url = substr( $file, 0, $split_on );
$condition = substr( $file, $split_on + 1 );
} else {
$file_url = $file;
}
$download_file_args = array(
'index' => $file_id,
'file' => $file_url,
'name' => basename( $file_url ),
'condition' => empty( $condition ) ? 'all' : $condition
);
$download_files[ $file_id ] = $download_file_args;
$file_id++;
}
update_post_meta( $download_id, 'edd_download_files', $download_files );
}
}
/**
* Set up and store the Featured Image
*
* @since 2.6
* @return void
*/
private function set_image( $download_id = 0, $image = '', $post_author = 0 ) {
$is_url = false !== filter_var( $image, FILTER_VALIDATE_URL );
$is_local = $is_url && false !== strpos( site_url(), $image );
$ext = edd_get_file_extension( $image );
if( $is_url && $is_local ) {
// Image given by URL, see if we have an attachment already
$attachment_id = attachment_url_to_postid( $image );
} elseif( $is_url ) {
if( ! function_exists( 'media_sideload_image' ) ) {
require_once( ABSPATH . 'wp-admin/includes/file.php' );
}
// Image given by external URL
$url = media_sideload_image( $image, $download_id, '', 'src' );
if( ! is_wp_error( $url ) ) {
$attachment_id = attachment_url_to_postid( $url );
}
} elseif( false === strpos( $image, '/' ) && edd_get_file_extension( $image ) ) {
// Image given by name only
$upload_dir = wp_upload_dir();
if( file_exists( trailingslashit( $upload_dir['path'] ) . $image ) ) {
// Look in current upload directory first
$file = trailingslashit( $upload_dir['path'] ) . $image;
} else {
// Now look through year/month sub folders of upload directory for files with our image's same extension
$files = glob( $upload_dir['basedir'] . '/*/*/*' . $ext );
foreach( $files as $file ) {
if( basename( $file ) == $image ) {
// Found our file
break;
}
// Make sure $file is unset so our empty check below does not return a false positive
unset( $file );
}
}
if( ! empty( $file ) ) {
// We found the file, let's see if it already exists in the media library
$guid = str_replace( $upload_dir['basedir'], $upload_dir['baseurl'], $file );
$attachment_id = attachment_url_to_postid( $guid );
if( empty( $attachment_id ) ) {
// Doesn't exist in the media library, let's add it
$filetype = wp_check_filetype( basename( $file ), null );
// Prepare an array of post data for the attachment.
$attachment = array(
'guid' => $guid,
'post_mime_type' => $filetype['type'],
'post_title' => preg_replace( '/\.[^.]+$/', '', $image ),
'post_content' => '',
'post_status' => 'inherit',
'post_author' => $post_author
);
// Insert the attachment.
$attachment_id = wp_insert_attachment( $attachment, $file, $download_id );
// Make sure that this file is included, as wp_generate_attachment_metadata() depends on it.
require_once( ABSPATH . 'wp-admin/includes/image.php' );
// Generate the metadata for the attachment, and update the database record.
$attach_data = wp_generate_attachment_metadata( $attachment_id, $file );
wp_update_attachment_metadata( $attachment_id, $attach_data );
}
}
}
if( ! empty( $attachment_id ) ) {
return set_post_thumbnail( $download_id, $attachment_id );
}
return false;
}
/**
* Set up and taxonomy terms
*
* @since 2.6
* @return void
*/
private function set_taxonomy_terms( $download_id = 0, $terms = array(), $taxonomy = 'download_category' ) {
$terms = $this->maybe_create_terms( $terms, $taxonomy );
if( ! empty( $terms ) ) {
wp_set_object_terms( $download_id, $terms, $taxonomy );
}
}
/**
* Locate term IDs or create terms if none are found
*
* @since 2.6
* @return array
*/
private function maybe_create_terms( $terms = array(), $taxonomy = 'download_category' ) {
// Return of term IDs
$term_ids = array();
foreach( $terms as $term ) {
if( is_numeric( $term ) && 0 === (int) $term ) {
$t = get_term( $term, $taxonomy );
} else {
$t = get_term_by( 'name', $term, $taxonomy );
if( ! $t ) {
$t = get_term_by( 'slug', $term, $taxonomy );
}
}
if( ! empty( $t ) ) {
$term_ids[] = $t->term_id;
} else {
$term_data = wp_insert_term( $term, $taxonomy, array( 'slug' => sanitize_title( $term ) ) );
if( ! is_wp_error( $term_data ) ) {
$term_ids[] = $term_data['term_id'];
}
}
}
return array_map( 'absint', $term_ids );
}
/**
* Retrieve URL to Downloads list table
*
* @since 2.6
* @return string
*/
public function get_list_table_url() {
return edd_get_admin_base_url();
}
/**
* Retrieve Download label
*
* @since 2.6
* @return void
*/
public function get_import_type_label() {
return edd_get_label_plural( true );
}
}

View File

@ -0,0 +1,636 @@
<?php
/**
* Payment Import Class
*
* This class handles importing payments with the batch processing API
*
* @package EDD
* @subpackage Admin/Import
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.6
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Batch_Import Class
*
* @since 2.6
*/
class EDD_Batch_Payments_Import extends EDD_Batch_Import {
/**
* Set up our import config.
*
* @since 2.6
* @return void
*/
public function init() {
$this->per_step = 5;
// Set up default field map values
$this->field_mapping = array(
'total' => '',
'subtotal' => '',
'tax' => 'draft',
'number' => '',
'mode' => '',
'gateway' => '',
'date' => '',
'status' => '',
'email' => '',
'name' => '',
'first_name' => '',
'last_name' => '',
'edd_customer_id' => '',
'user_id' => '',
'discounts' => '',
'key' => '',
'transaction_id' => '',
'ip' => '',
'currency' => '',
'parent_payment_id' => '',
'downloads' => '',
'line1' => '',
'line2' => '',
'city' => '',
'state' => '',
'zip' => '',
'country' => '',
);
}
/**
* Process a step
*
* @since 2.6
* @return bool
*/
public function process_step() {
$more = false;
if ( ! $this->can_import() ) {
wp_die( __( 'You do not have permission to import data.', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
// Remove certain actions to ensure they don't fire when creating the payments
remove_action( 'edd_complete_purchase', 'edd_trigger_purchase_receipt', 999 );
remove_action( 'edd_admin_sale_notice', 'edd_admin_email_notice', 10 );
$i = 1;
$offset = $this->step > 1 ? ( $this->per_step * ( $this->step - 1 ) ) : 0;
if( $offset > $this->total ) {
$this->done = true;
// Clean up the temporary records in the payment import process
global $wpdb;
$sql = "DELETE FROM {$wpdb->prefix}edd_customermeta WHERE meta_key = '_canonical_import_id'";
$wpdb->query( $sql );
// Delete the uploaded CSV file.
unlink( $this->file );
}
if( ! $this->done && $this->csv ) {
$more = true;
foreach( $this->csv as $key => $row ) {
// Skip all rows until we pass our offset
if( $key + 1 <= $offset ) {
continue;
}
// Done with this batch
if( $i > $this->per_step ) {
break;
}
// Import payment
$this->create_payment( $row );
$i++;
}
}
return $more;
}
/**
* Set up and store a payment record from a CSV row
*
* @since 2.6
* @return void
*/
public function create_payment( $row = array() ) {
$payment = new EDD_Payment;
$payment->status = 'pending';
if( ! empty( $this->field_mapping['number'] ) && ! empty( $row[ $this->field_mapping['number'] ] ) ) {
$payment->number = sanitize_text_field( $row[ $this->field_mapping['number'] ] );
}
if( ! empty( $this->field_mapping['mode'] ) && ! empty( $row[ $this->field_mapping['mode'] ] ) ) {
$mode = strtolower( sanitize_text_field( $row[ $this->field_mapping['mode'] ] ) );
$mode = 'test' != $mode && 'live' != $mode ? false : $mode;
if( ! $mode ) {
$mode = edd_is_test_mode() ? 'test' : 'live';
}
$payment->mode = $mode;
}
if( ! empty( $this->field_mapping['date'] ) && ! empty( $row[ $this->field_mapping['date'] ] ) ) {
$date = sanitize_text_field( $row[ $this->field_mapping['date'] ] );
if( ! strtotime( $date ) ) {
$date = date( 'Y-m-d H:i:s', current_time( 'timestamp' ) );
} else {
$date = date( 'Y-m-d H:i:s', strtotime( $date ) );
}
$payment->date = $date;
}
$payment->customer_id = $this->set_customer( $row );
if( ! empty( $this->field_mapping['email'] ) && ! empty( $row[ $this->field_mapping['email'] ] ) ) {
$payment->email = sanitize_text_field( $row[ $this->field_mapping['email'] ] );
}
if ( ! empty( $this->field_mapping['name'] ) && ! empty( $row[ $this->field_mapping['name'] ] ) ) {
$payment->name = sanitize_text_field( $row[ $this->field_mapping['name'] ] );
} else {
if ( ! empty( $this->field_mapping['first_name'] ) && ! empty( $row[ $this->field_mapping['first_name'] ] ) ) {
$payment->first_name = sanitize_text_field( $row[ $this->field_mapping['first_name'] ] );
}
if ( ! empty( $this->field_mapping['last_name'] ) && ! empty( $row[ $this->field_mapping['last_name'] ] ) ) {
$payment->last_name = sanitize_text_field( $row[ $this->field_mapping['last_name'] ] );
}
}
if( ! empty( $this->field_mapping['user_id'] ) && ! empty( $row[ $this->field_mapping['user_id'] ] ) ) {
$user_id = sanitize_text_field( $row[ $this->field_mapping['user_id'] ] );
if( is_numeric( $user_id ) ) {
$user_id = absint( $row[ $this->field_mapping['user_id'] ] );
$user = get_userdata( $user_id );
} elseif( is_email( $user_id ) ) {
$user = get_user_by( 'email', $user_id );
} else {
$user = get_user_by( 'login', $user_id );
}
if( $user ) {
$payment->user_id = $user->ID;
$customer = new EDD_Customer( $payment->customer_id );
if( empty( $customer->user_id ) ) {
$customer->update( array( 'user_id' => $user->ID ) );
}
}
}
if( ! empty( $this->field_mapping['discounts'] ) && ! empty( $row[ $this->field_mapping['discounts'] ] ) ) {
$payment->discounts = sanitize_text_field( $row[ $this->field_mapping['discounts'] ] );
}
if( ! empty( $this->field_mapping['transaction_id'] ) && ! empty( $row[ $this->field_mapping['transaction_id'] ] ) ) {
$payment->transaction_id = sanitize_text_field( $row[ $this->field_mapping['transaction_id'] ] );
}
if( ! empty( $this->field_mapping['ip'] ) && ! empty( $row[ $this->field_mapping['ip'] ] ) ) {
$payment->ip = sanitize_text_field( $row[ $this->field_mapping['ip'] ] );
}
if( ! empty( $this->field_mapping['gateway'] ) && ! empty( $row[ $this->field_mapping['gateway'] ] ) ) {
$gateways = edd_get_payment_gateways();
$gateway = strtolower( sanitize_text_field( $row[ $this->field_mapping['gateway'] ] ) );
if( ! array_key_exists( $gateway, $gateways ) ) {
foreach( $gateways as $key => $enabled_gateway ) {
if( $enabled_gateway['checkout_label'] == $gateway ) {
$gateway = $key;
break;
}
}
}
$payment->gateway = $gateway;
}
if( ! empty( $this->field_mapping['currency'] ) && ! empty( $row[ $this->field_mapping['currency'] ] ) ) {
$payment->currency = strtoupper( sanitize_text_field( $row[ $this->field_mapping['currency'] ] ) );
}
if( ! empty( $this->field_mapping['key'] ) && ! empty( $row[ $this->field_mapping['key'] ] ) ) {
$payment->key = sanitize_text_field( $row[ $this->field_mapping['key'] ] );
}
if( ! empty( $this->field_mapping['parent_payment_id'] ) && ! empty( $row[ $this->field_mapping['parent_payment_id'] ] ) ) {
$payment->parent_payment_id = absint( $row[ $this->field_mapping['parent_payment_id'] ] );
}
if( ! empty( $this->field_mapping['downloads'] ) && ! empty( $row[ $this->field_mapping['downloads'] ] ) ) {
if( __( 'Products (Raw)', 'easy-digital-downloads' ) == $this->field_mapping['downloads'] ) {
// This is an EDD export so we can extract prices
$downloads = $this->get_downloads_from_edd( $row[ $this->field_mapping['downloads'] ] );
} else {
$downloads = $this->str_to_array( $row[ $this->field_mapping['downloads'] ] );
}
if( is_array( $downloads ) ) {
$download_count = count( $downloads );
foreach( $downloads as $download ) {
if( is_array( $download ) ) {
$download_name = $download['download'];
$price = $download['price'];
$tax = $download['tax'];
$price_id = $download['price_id'];
} else {
$download_name = $download;
}
$download_id = $this->maybe_create_download( $download_name );
if( ! $download_id ) {
continue;
}
$item_price = ! isset( $price ) ? edd_get_download_price( $download_id ) : $price;
$item_tax = ! isset( $tax ) ? ( $download_count > 1 ? 0.00 : $payment->tax ) : $tax;
$price_id = ! isset( $price_id ) ? false : $price_id;
$args = array(
'item_price' => $item_price,
'tax' => $item_tax,
'price_id' => $price_id,
);
$payment->add_download( $download_id, $args );
}
}
}
if( ! empty( $this->field_mapping['total'] ) && ! empty( $row[ $this->field_mapping['total'] ] ) ) {
$payment->total = edd_sanitize_amount( $row[ $this->field_mapping['total'] ] );
}
if( ! empty( $this->field_mapping['tax'] ) && ! empty( $row[ $this->field_mapping['tax'] ] ) ) {
$payment->tax = edd_sanitize_amount( $row[ $this->field_mapping['tax'] ] );
}
if( ! empty( $this->field_mapping['subtotal'] ) && ! empty( $row[ $this->field_mapping['subtotal'] ] ) ) {
$payment->subtotal = edd_sanitize_amount( $row[ $this->field_mapping['subtotal'] ] );
} else {
$payment->subtotal = $payment->total - $payment->tax;
}
$address = array( 'line1' => '', 'line2' => '', 'city' => '', 'state' => '', 'zip' => '', 'country' => '' );
foreach( $address as $key => $address_field ) {
if( ! empty( $this->field_mapping[ $key ] ) && ! empty( $row[ $this->field_mapping[ $key ] ] ) ) {
$address[ $key ] = sanitize_text_field( $row[ $this->field_mapping[ $key ] ] );
}
}
$payment->address = $address;
$payment->save();
// The status has to be set after payment is created to ensure status update properly
if( ! empty( $this->field_mapping['status'] ) && ! empty( $row[ $this->field_mapping['status'] ] ) ) {
$payment->status = strtolower( sanitize_text_field( $row[ $this->field_mapping['status'] ] ) );
} else {
$payment->status = 'complete';
}
// Save a second time to update stats
$payment->save();
}
private function set_customer( $row ) {
global $wpdb;
$customer = false;
$customer = false;
$email = '';
if( ! empty( $this->field_mapping['email'] ) && ! empty( $row[ $this->field_mapping['email'] ] ) ) {
$email = sanitize_text_field( $row[ $this->field_mapping['email'] ] );
}
// Look for a customer from the canonical source, if any
if( ! empty( $this->field_mapping['edd_customer_id'] ) && ! empty( $row[ $this->field_mapping['edd_customer_id'] ] ) ) {
$canonical_id = absint( $row[ $this->field_mapping['edd_customer_id'] ] );
$mapped_id = $wpdb->get_var( $wpdb->prepare( "SELECT edd_customer_id FROM $wpdb->edd_customermeta WHERE meta_key = '_canonical_import_id' AND meta_value = %d LIMIT 1", $canonical_id ) );
}
if( ! empty( $mapped_id ) ) {
$customer = new EDD_Customer( $mapped_id );
}
if( empty( $mapped_id ) || ! $customer->id > 0 ) {
// Look for a customer based on provided ID, if any
if ( ! empty( $this->field_mapping['edd_customer_id'] ) && ! empty( $row[ $this->field_mapping['edd_customer_id'] ] ) ) {
$customer_id = absint( $row[ $this->field_mapping['edd_customer_id'] ] );
$customer_by_id = new EDD_Customer( $customer_id );
}
// Now look for a customer based on provided email
if( ! empty( $email ) ) {
$customer_by_email = new EDD_Customer( $email );
}
// Now compare customer records. If they don't match, customer_id will be stored in meta and we will use the customer that matches the email
if ( ! empty( $customer_by_email ) && ( empty( $customer_by_id ) || $customer_by_id->id !== $customer_by_email->id ) ) {
$customer = $customer_by_email;
} elseif ( ! empty( $customer_by_id ) ) {
$customer = $customer_by_id;
if( ! empty( $email ) ) {
$customer->add_email( $email );
}
}
// Make sure we found a customer. Create one if not.
if ( empty( $customer->id ) ) {
if ( ! $customer instanceof EDD_Customer ) {
$customer = new EDD_Customer();
}
}
if ( ! empty( $this->field_mapping['name'] ) && ! empty( $row[ $this->field_mapping['name'] ] ) ) {
$name = $row[ $this->field_mapping['name'] ];
} else {
$first_name = '';
$last_name = '';
if ( ! empty( $this->field_mapping['first_name'] ) && ! empty( $row[ $this->field_mapping['first_name'] ] ) ) {
$first_name = $row[ $this->field_mapping['first_name'] ];
}
if ( ! empty( $this->field_mapping['last_name'] ) && ! empty( $row[ $this->field_mapping['last_name'] ] ) ) {
$last_name = $row[ $this->field_mapping['last_name'] ];
}
$name = $first_name . ' ' . $last_name;
}
$customer->create(
array(
'name' => sanitize_text_field( $name ),
'email' => empty( $email ) ? '' : $email,
)
);
if( ! empty( $canonical_id ) && (int) $canonical_id !== (int) $customer->id ) {
$customer->update_meta( '_canonical_import_id', $canonical_id );
}
}
if ( ! empty( $email ) && $email !== $customer->email ) {
$customer->add_email( $email );
}
return $customer->id;
}
/**
* Look up Download by title and create one if none is found
*
* @since 2.6
* @return int Download ID
*/
private function maybe_create_download( $title = '' ) {
if( ! is_string( $title ) ) {
return false;
}
$download = get_page_by_title( $title, OBJECT, 'download' );
if( $download ) {
$download_id = $download->ID;
} else {
$args = array(
'post_type' => 'download',
'post_title' => $title,
'post_author' => get_current_user_id()
);
$download_id = wp_insert_post( $args );
}
return $download_id;
}
/**
* Return the calculated completion percentage
*
* @since 2.6
* @return int
*/
public function get_downloads_from_edd( $data_str ) {
// Break string into separate products
$d_array = array();
$downloads = (array) explode( '/', $data_str );
if( $downloads ) {
foreach( $downloads as $key => $download ) {
$d = (array) explode( '|', $download );
if ( ! array_key_exists( 1, $d ) ) {
continue;
}
preg_match_all( '/\{(\d|(\d+(\.\d+|\d+)))\}/', $d[1], $matches );
if( false !== strpos( $d[1], '{' ) ) {
$price = trim( substr( $d[1], 0, strpos( $d[1], '{' ) ) );
} else {
$price = trim( $d[1] );
}
$price = floatval( $price );
$tax = isset( $matches[1][0] ) ? floatval( trim( $matches[1][0] ) ) : 0;
$price_id = isset( $matches[1][1] ) ? trim( $matches[1][1] ) : false;
$d_array[] = array(
'download' => trim( $d[0] ),
'price' => $price - $tax,
'tax' => $tax,
'price_id' => $price_id,
);
}
}
return $d_array;
}
/**
* Return the calculated completion percentage
*
* @since 2.6
* @return int
*/
public function get_percentage_complete() {
$total = count( $this->csv );
if( $total > 0 ) {
$percentage = ( $this->step * $this->per_step / $total ) * 100;
}
if( $percentage > 100 ) {
$percentage = 100;
}
return $percentage;
}
/**
* Retrieve the URL to the payments list table
*
* @since 2.6
* @return string
*/
public function get_list_table_url() {
return admin_url( 'edit.php?post_type=download&page=edd-payment-history' );
}
/**
* Retrieve the payments labels
*
* @since 2.6
* @return string
*/
public function get_import_type_label() {
return __( 'payments', 'easy-digital-downloads' );
}
}

View File

@ -0,0 +1,344 @@
<?php
/**
* Batch Import Class
*
* This is the base class for all batch import methods. Each data import type (customers, payments, etc) extend this class
*
* @package EDD
* @subpackage Admin/Import
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.6
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Batch_Import Class
*
* @since 2.6
*/
class EDD_Batch_Import {
/**
* The file being imported
*
* @since 2.6
*/
public $file;
/**
* The parsed CSV file being imported
*
* @since 2.6
*/
public $csv;
/**
* Total rows in the CSV file
*
* @since 2.6
*/
public $total;
/**
* The current step being processed
*
* @since 2.6
*/
public $step;
/**
* The number of items to process per step
*
* @since 2.6
*/
public $per_step = 20;
/**
* The capability required to import data
*
* @since 2.6
*/
public $capability_type = 'manage_shop_settings';
/**
* Is the import file empty
*
* @since 2.6
*/
public $is_empty = false;
/**
* Map of CSV columns > database fields
*
* @since 2.6
*/
public $field_mapping = array();
/**
* Get things started
*
* @param $_step int The step to process
* @since 2.6
*/
public function __construct( $_file = '', $_step = 1 ) {
$this->step = $_step;
$this->file = $_file;
$this->done = false;
$this->csv = $this->get_csv_file( $this->file );
$this->total = count( $this->csv );
$this->init();
}
/**
* Initialize the updater. Runs after import file is loaded but before any processing is done.
*
* @since 2.6
* @return void
*/
public function init() {}
/**
* Can we import?
*
* @since 2.6
* @return bool Whether we can iport or not
*/
public function can_import() {
return (bool) apply_filters( 'edd_import_capability', current_user_can( $this->capability_type ) );
}
/**
* Parses the CSV from the file and returns the data as an array.
*
* @since 2.11.5
* @param string $file
*
* @return array
*/
public function get_csv_file( $file ) {
$csv = array_map( 'str_getcsv', file( $this->file ) );
array_walk(
$csv,
function ( &$a ) use ( $csv ) {
/*
* Make sure the two arrays have the same lengths.
* If not, we trim the larger array to match the smaller one.
*/
$min = min( count( $csv[0] ), count( $a ) );
$headers = array_slice( $csv[0], 0, $min );
$values = array_slice( $a, 0, $min );
$a = array_combine( $headers, $values );
}
);
array_shift( $csv );
return $csv;
}
/**
* Get the CSV columns
*
* @since 2.6
* @return array The columns in the CSV
*/
public function get_columns() {
$columns = array();
if ( isset( $this->csv[0] ) && is_array( $this->csv[0] ) ) {
$columns = array_keys( $this->csv[0] );
}
return $columns;
}
/**
* Get the first row of the CSV
*
* This is used for showing an example of what the import will look like
*
* @since 2.6
* @return array The first row after the header of the CSV
*/
public function get_first_row() {
if ( ! is_array( $this->csv ) ) {
return array();
}
return array_map( array( $this, 'trim_preview' ), current( $this->csv ) );
}
/**
* Process a step
*
* @since 2.6
* @return bool
*/
public function process_step() {
$more = false;
if ( ! $this->can_import() ) {
wp_die( __( 'You do not have permission to import data.', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
return $more;
}
/**
* Return the calculated completion percentage
*
* @since 2.6
* @return int
*/
public function get_percentage_complete() {
return 100;
}
/**
* Map CSV columns to import fields
*
* @since 2.6
* @return void
*/
public function map_fields( $import_fields = array() ) {
$this->field_mapping = array_map( 'sanitize_text_field', $import_fields );
}
/**
* Retrieve the URL to the list table for the import data type
*
* @since 2.6
* @return string
*/
public function get_list_table_url() {}
/**
* Retrieve the label for the import type. Example: Payments
*
* @since 2.6
* @return string
*/
public function get_import_type_label() {}
/**
* Convert a string containing delimiters to an array
*
* @since 2.6
* @param $str Input string to convert to an array
* @return array
*/
public function str_to_array( $str = '' ) {
$array = array();
if( is_array( $str ) ) {
return array_map( 'trim', $str );
}
// Look for standard delimiters
if( false !== strpos( $str, '|' ) ) {
$delimiter = '|';
} elseif( false !== strpos( $str, ',' ) ) {
$delimiter = ',';
} elseif( false !== strpos( $str, ';' ) ) {
$delimiter = ';';
} elseif( false !== strpos( $str, '/' ) && ! filter_var( str_replace( ' ', '%20', $str ), FILTER_VALIDATE_URL ) && '/' !== substr( $str, 0, 1 ) ) {
$delimiter = '/';
}
if( ! empty( $delimiter ) ) {
$array = (array) explode( $delimiter, $str );
} else {
$array[] = $str;
}
return array_map( 'trim', $array );
}
/**
* Convert a files string containing delimiters to an array.
*
* This is identical to str_to_array() except it ignores all / characters.
*
* @since 2.9.20
* @param $str Input string to convert to an array
* @return array
*/
public function convert_file_string_to_array( $str = '' ) {
$array = array();
if( is_array( $str ) ) {
return array_map( 'trim', $str );
}
// Look for standard delimiters
if( false !== strpos( $str, '|' ) ) {
$delimiter = '|';
} elseif( false !== strpos( $str, ',' ) ) {
$delimiter = ',';
} elseif( false !== strpos( $str, ';' ) ) {
$delimiter = ';';
}
if( ! empty( $delimiter ) ) {
$array = (array) explode( $delimiter, $str );
} else {
$array[] = $str;
}
return array_map( 'trim', $array );
}
/**
* Trims a column value for preview
*
* @since 2.6
* @param $str Input string to trim down
* @return string
*/
public function trim_preview( $str = '' ) {
if( ! is_numeric( $str ) ) {
$long = strlen( $str ) >= 30;
$str = substr( $str, 0, 30 );
$str = $long ? $str . '...' : $str;
}
return $str;
}
}

View File

@ -0,0 +1,75 @@
<?php
/**
* Import Actions
*
* These are actions related to import data from Easy Digital Downloads.
*
* @package EDD
* @subpackage Admin/Import
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
*/
if ( ! defined( 'ABSPATH' ) ) { exit; }
/**
* Add a hook allowing extensions to register a hook on the batch export process
*
* @since 2.6
* @return void
*/
function edd_register_batch_importers() {
if ( is_admin() ) {
do_action( 'edd_register_batch_importer' );
}
}
add_action( 'plugins_loaded', 'edd_register_batch_importers' );
/**
* Register the payments batch importer
*
* @since 2.6
*/
function edd_register_payments_batch_import() {
add_action( 'edd_batch_import_class_include', 'edd_include_payments_batch_import_processer', 10 );
}
add_action( 'edd_register_batch_importer', 'edd_register_payments_batch_import', 10 );
/**
* Loads the payments batch process if needed
*
* @since 2.6
* @param string $class The class being requested to run for the batch import
* @return void
*/
function edd_include_payments_batch_import_processer( $class ) {
if ( 'EDD_Batch_Payments_Import' === $class ) {
require_once EDD_PLUGIN_DIR . 'includes/admin/import/class-batch-import-payments.php';
}
}
/**
* Register the downloads batch importer
*
* @since 2.6
*/
function edd_register_downloads_batch_import() {
add_action( 'edd_batch_import_class_include', 'edd_include_downloads_batch_import_processer', 10 );
}
add_action( 'edd_register_batch_importer', 'edd_register_downloads_batch_import', 10 );
/**
* Loads the downloads batch process if needed
*
* @since 2.6
* @param string $class The class being requested to run for the batch import
* @return void
*/
function edd_include_downloads_batch_import_processer( $class ) {
if ( 'EDD_Batch_Downloads_Import' === $class ) {
require_once EDD_PLUGIN_DIR . 'includes/admin/import/class-batch-import-downloads.php';
}
}

View File

@ -0,0 +1,243 @@
<?php
/**
* Import Functions
*
* These are functions are used for import data into Easy Digital Downloads.
*
* @package EDD
* @subpackage Admin/Import
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Upload an import file with ajax
*
* @since 2.6
* @return void
*/
function edd_do_ajax_import_file_upload() {
if ( ! function_exists( 'wp_handle_upload' ) ) {
require_once( ABSPATH . 'wp-admin/includes/file.php' );
}
require_once EDD_PLUGIN_DIR . 'includes/admin/import/class-batch-import.php';
if( ! wp_verify_nonce( $_REQUEST['edd_ajax_import'], 'edd_ajax_import' ) ) {
wp_send_json_error( array( 'error' => __( 'Nonce verification failed', 'easy-digital-downloads' ) ) );
}
if( empty( $_POST['edd-import-class'] ) ) {
wp_send_json_error( array( 'error' => __( 'Missing import parameters. Import class must be specified.', 'easy-digital-downloads' ), 'request' => $_REQUEST ) );
}
if( empty( $_FILES['edd-import-file'] ) ) {
wp_send_json_error( array( 'error' => __( 'Missing import file. Please provide an import file.', 'easy-digital-downloads' ), 'request' => $_REQUEST ) );
}
if ( empty( $_FILES['edd-import-file']['type'] ) || ! in_array( strtolower( $_FILES['edd-import-file']['type'] ), edd_importer_accepted_mime_types(), true ) ) {
wp_send_json_error( array( 'error' => __( 'The file you uploaded does not appear to be a CSV file.', 'easy-digital-downloads' ), 'request' => $_REQUEST ) );
}
if( ! file_exists( $_FILES['edd-import-file']['tmp_name'] ) ) {
wp_send_json_error( array( 'error' => __( 'Something went wrong during the upload process, please try again.', 'easy-digital-downloads' ), 'request' => $_REQUEST ) );
}
// Let WordPress import the file. We will remove it after import is complete
$import_file = wp_handle_upload( $_FILES['edd-import-file'], array( 'test_form' => false ) );
if ( $import_file && empty( $import_file['error'] ) ) {
$importer_class = sanitize_text_field( $_POST['edd-import-class'] );
$is_class_allowed = edd_importer_is_class_allowed( $importer_class );
if ( false === $is_class_allowed ) {
wp_send_json_error( array( 'error' => __( 'Invalid importer class supplied', 'easy-digital-downloads' ) ) );
}
do_action( 'edd_batch_import_class_include', $importer_class );
$import = new $importer_class( $import_file['file'] );
if( ! $import->can_import() ) {
wp_send_json_error( array( 'error' => __( 'You do not have permission to import data', 'easy-digital-downloads' ) ) );
}
wp_send_json_success( array(
'form' => $_POST,
'class' => $importer_class,
'upload' => $import_file,
'first_row' => $import->get_first_row(),
'columns' => $import->get_columns(),
'nonce' => wp_create_nonce( 'edd_ajax_import', 'edd_ajax_import' )
) );
} else {
/**
* Error generated by _wp_handle_upload()
* @see _wp_handle_upload() in wp-admin/includes/file.php
*/
wp_send_json_error( array( 'error' => $import_file['error'] ) );
}
exit;
}
add_action( 'edd_upload_import_file', 'edd_do_ajax_import_file_upload' );
/**
* Process batch imports via ajax
*
* @since 2.6
* @return void
*/
function edd_do_ajax_import() {
require_once EDD_PLUGIN_DIR . 'includes/admin/import/class-batch-import.php';
if( ! wp_verify_nonce( $_REQUEST['nonce'], 'edd_ajax_import' ) ) {
wp_send_json_error( array( 'error' => __( 'Nonce verification failed', 'easy-digital-downloads' ), 'request' => $_REQUEST ) );
}
if( empty( $_REQUEST['class'] ) ) {
wp_send_json_error( array( 'error' => __( 'Missing import parameters. Import class must be specified.', 'easy-digital-downloads' ), 'request' => $_REQUEST ) );
}
if( ! file_exists( $_REQUEST['upload']['file'] ) ) {
wp_send_json_error( array( 'error' => __( 'Something went wrong during the upload process, please try again.', 'easy-digital-downloads' ), 'request' => $_REQUEST ) );
}
$file = sanitize_text_field( $_REQUEST['upload']['file'] );
$mime_type_allowed = false;
if ( is_callable( 'mime_content_type' ) ) {
if ( in_array( mime_content_type( $file ), edd_importer_accepted_mime_types(), true ) ) {
$mime_type_allowed = true;
}
} else {
if ( wp_check_filetype( $file, edd_importer_accepted_mime_types() ) ) {
$mime_type_allowed = true;
}
}
if ( false === $mime_type_allowed ) {
wp_send_json_error( array( 'error' => __( 'The file you uploaded does not appear to be a CSV file.', 'easy-digital-downloads' ), 'request' => $_REQUEST ) );
}
$importer_class = sanitize_text_field( $_REQUEST['class'] );
$is_class_allowed = edd_importer_is_class_allowed( $importer_class );
if ( ! $is_class_allowed ) {
wp_send_json_error( array( 'error' => __( 'Invalid importer class supplied', 'easy-digital-downloads' ) ) );
}
do_action( 'edd_batch_import_class_include', $importer_class );
$step = absint( $_REQUEST['step'] );
$class = $importer_class;
$import = new $class( $file, $step );
if( ! $import->can_import() ) {
wp_send_json_error( array( 'error' => __( 'You do not have permission to import data', 'easy-digital-downloads' ) ) );
}
parse_str( $_REQUEST['mapping'], $map );
$import->map_fields( $map['edd-import-field'] );
$ret = $import->process_step( $step );
$percentage = $import->get_percentage_complete();
if( $ret ) {
$step += 1;
wp_send_json_success( array(
'step' => $step,
'percentage' => $percentage,
'columns' => $import->get_columns(),
'mapping' => $import->field_mapping,
'total' => $import->total
) );
} elseif ( true === $import->is_empty ) {
wp_send_json_error( array(
'error' => __( 'No data found for import parameters', 'easy-digital-downloads' )
) );
} else {
wp_send_json_success( array(
'step' => 'done',
'message' => sprintf(
__( 'Import complete! <a href="%s">View imported %s</a>.', 'easy-digital-downloads' ),
esc_url( $import->get_list_table_url() ),
esc_html( $import->get_import_type_label() )
)
) );
}
}
add_action( 'wp_ajax_edd_do_ajax_import', 'edd_do_ajax_import' );
/**
* Returns the array of accepted mime types for the importer.
*
* @since 3.0
* @return array
*/
function edd_importer_accepted_mime_types() {
return array(
'text/csv',
'text/comma-separated-values',
'text/plain',
'text/anytext',
'text/*',
'text/plain',
'text/anytext',
'text/*',
'application/csv',
'application/excel',
'application/vnd.ms-excel',
'application/vnd.msexcel',
);
}
/**
* Given an importer class name, is it allowed to process as an importer.
*
* @since 3.0.2
*
* @param string $class The class name to check.
*
* @return bool If the class is allowed to be used as an importer.
*/
function edd_importer_is_class_allowed( $class = '' ) {
$allowed_importer_classes = edd_get_importer_accepted_classes();
return in_array( $class, $allowed_importer_classes, true );
}
/**
* Returns a list of allowed importer classes.
*
* @since 3.0.2
*
* @return array An array of class names to allow during imports.
*/
function edd_get_importer_accepted_classes() {
return apply_filters(
'edd_accepted_importer_classes',
array(
'EDD_Batch_Downloads_Import',
'EDD_Batch_Payments_Import',
)
);
}

View File

@ -0,0 +1,29 @@
<?php
namespace EDD\Admin\Installers;
use EDD\Admin\Installers\PluginSilentUpgraderSkin;
/**
* Skin for on-the-fly addon installations.
*
* @since 1.0.0
* @since 1.5.6.1 Extend PluginSilentUpgraderSkin and clean up the class.
*/
class Install_Skin extends PluginSilentUpgraderSkin {
/**
* Instead of outputting HTML for errors, json_encode the errors and send them
* back to the Ajax script for processing.
*
* @since 1.0.0
*
* @param array $errors Array of errors with the install process.
*/
public function error( $errors ) {
if ( ! empty( $errors ) ) {
wp_send_json_error( $errors );
}
}
}

View File

@ -0,0 +1,57 @@
<?php
namespace EDD\Admin\Installers;
/** \WP_Upgrader_Skin class */
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skin.php';
/**
* Class PluginSilentUpgraderSkin.
*
* @internal Please do not use this class outside of core WPForms development. May be removed at any time.
*
* @since 2.11.4
*/
class PluginSilentUpgraderSkin extends \WP_Upgrader_Skin {
/**
* Empty out the header of its HTML content and only check to see if it has
* been performed or not.
*
* @since 2.11.4
*/
public function header() {
}
/**
* Empty out the footer of its HTML contents.
*
* @since 2.11.4
*/
public function footer() {
}
/**
* Instead of outputting HTML for errors, just return them.
* Ajax request will just ignore it.
*
* @since 2.11.4
*
* @param array $errors Array of errors with the install process.
*
* @return array
*/
public function error( $errors ) {
return $errors;
}
/**
* Empty out JavaScript output that calls function to decrement the update counts.
*
* @since 2.11.4
*
* @param string $type Type of update count to decrement.
*/
public function decrement_update_count( $type ) {
}
}

View File

@ -0,0 +1,608 @@
<?php
namespace EDD\Admin\Installers;
use WP_Error;
use WP_Upgrader;
use WP_Filesystem_Base;
/** \WP_Upgrader class */
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
/** \Plugin_Upgrader class */
require_once ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php';
/**
* In WP 5.3 a PHP 5.6 splat operator (...$args) was added to \WP_Upgrader_Skin::feedback().
* We need to remove all calls to *Skin::feedback() method, as we can't override it in own Skins
* without breaking support for PHP 5.3-5.5.
*
* @internal Please do not use this class outside of core EDD development. May be removed at any time.
*
* @since 1.5.6.1
*/
class PluginSilentUpgrader extends \Plugin_Upgrader {
/**
* Run an upgrade/installation.
*
* Attempt to download the package (if it is not a local file), unpack it, and
* install it in the destination folder.
*
* @since 1.5.6.1
*
* @param array $options {
* Array or string of arguments for upgrading/installing a package.
*
* @type string $package The full path or URI of the package to install.
* Default empty.
* @type string $destination The full path to the destination folder.
* Default empty.
* @type bool $clear_destination Whether to delete any files already in the
* destination folder. Default false.
* @type bool $clear_working Whether to delete the files form the working
* directory after copying to the destination.
* Default false.
* @type bool $abort_if_destination_exists Whether to abort the installation if the destination
* folder already exists. When true, `$clear_destination`
* should be false. Default true.
* @type bool $is_multi Whether this run is one of multiple upgrade/installation
* actions being performed in bulk. When true, the skin
* WP_Upgrader::header() and WP_Upgrader::footer()
* aren't called. Default false.
* @type array $hook_extra Extra arguments to pass to the filter hooks called by
* WP_Upgrader::run().
* }
* @return array|false|WP_error The result from self::install_package() on success, otherwise a WP_Error,
* or false if unable to connect to the filesystem.
*/
public function run( $options ) {
$defaults = array(
'package' => '', // Please always pass this.
'destination' => '', // And this
'clear_destination' => false,
'abort_if_destination_exists' => true, // Abort if the Destination directory exists, Pass clear_destination as false please
'clear_working' => true,
'is_multi' => false,
'hook_extra' => array(), // Pass any extra $hook_extra args here, this will be passed to any hooked filters.
);
$options = wp_parse_args( $options, $defaults );
/**
* Filter the package options before running an update.
*
* See also {@see 'upgrader_process_complete'}.
*
* @since 4.3.0
*
* @param array $options {
* Options used by the upgrader.
*
* @type string $package Package for update.
* @type string $destination Update location.
* @type bool $clear_destination Clear the destination resource.
* @type bool $clear_working Clear the working resource.
* @type bool $abort_if_destination_exists Abort if the Destination directory exists.
* @type bool $is_multi Whether the upgrader is running multiple times.
* @type array $hook_extra {
* Extra hook arguments.
*
* @type string $action Type of action. Default 'update'.
* @type string $type Type of update process. Accepts 'plugin', 'theme', or 'core'.
* @type bool $bulk Whether the update process is a bulk update. Default true.
* @type string $plugin Path to the plugin file relative to the plugins directory.
* @type string $theme The stylesheet or template name of the theme.
* @type string $language_update_type The language pack update type. Accepts 'plugin', 'theme',
* or 'core'.
* @type object $language_update The language pack update offer.
* }
* }
*/
$options = apply_filters( 'upgrader_package_options', $options );
if ( ! $options['is_multi'] ) { // call $this->header separately if running multiple times
$this->skin->header();
}
// Connect to the Filesystem first.
$res = $this->fs_connect( array( WP_CONTENT_DIR, $options['destination'] ) );
// Mainly for non-connected filesystem.
if ( ! $res ) {
if ( ! $options['is_multi'] ) {
$this->skin->footer();
}
return false;
}
$this->skin->before();
if ( is_wp_error( $res ) ) {
$this->skin->error( $res );
$this->skin->after();
if ( ! $options['is_multi'] ) {
$this->skin->footer();
}
return $res;
}
/*
* Download the package (Note, This just returns the filename
* of the file if the package is a local file)
*/
$download = $this->download_package( $options['package'], true );
// Allow for signature soft-fail.
// WARNING: This may be removed in the future.
if ( is_wp_error( $download ) && $download->get_error_data( 'softfail-filename' ) ) {
// Don't output the 'no signature could be found' failure message for now.
if ( (string) $download->get_error_code() !== 'signature_verification_no_signature' || WP_DEBUG ) {
// Outout the failure error as a normal feedback, and not as an error:
//$this->skin->feedback( $download->get_error_message() );
// Report this failure back to WordPress.org for debugging purposes.
wp_version_check(
array(
'signature_failure_code' => $download->get_error_code(),
'signature_failure_data' => $download->get_error_data(),
)
);
}
// Pretend this error didn't happen.
$download = $download->get_error_data( 'softfail-filename' );
}
if ( is_wp_error( $download ) ) {
$this->skin->error( $download );
$this->skin->after();
if ( ! $options['is_multi'] ) {
$this->skin->footer();
}
return $download;
}
$delete_package = ( (string) $download !== (string) $options['package'] ); // Do not delete a "local" file.
// Unzips the file into a temporary directory.
$working_dir = $this->unpack_package( $download, $delete_package );
if ( is_wp_error( $working_dir ) ) {
$this->skin->error( $working_dir );
$this->skin->after();
if ( ! $options['is_multi'] ) {
$this->skin->footer();
}
return $working_dir;
}
// With the given options, this installs it to the destination directory.
$result = $this->install_package(
array(
'source' => $working_dir,
'destination' => $options['destination'],
'clear_destination' => $options['clear_destination'],
'abort_if_destination_exists' => $options['abort_if_destination_exists'],
'clear_working' => $options['clear_working'],
'hook_extra' => $options['hook_extra'],
)
);
$this->skin->set_result( $result );
if ( is_wp_error( $result ) ) {
$this->skin->error( $result );
//$this->skin->feedback( 'process_failed' );
} else {
// Installation succeeded.
//$this->skin->feedback( 'process_success' );
}
$this->skin->after();
if ( ! $options['is_multi'] ) {
/**
* Fire when the upgrader process is complete.
*
* See also {@see 'upgrader_package_options'}.
*
* @since 3.6.0
* @since 3.7.0 Added to WP_Upgrader::run().
* @since 4.6.0 `$translations` was added as a possible argument to `$hook_extra`.
*
* @param WP_Upgrader $this WP_Upgrader instance. In other contexts, $this, might be a
* Theme_Upgrader, Plugin_Upgrader, Core_Upgrade, or Language_Pack_Upgrader instance.
* @param array $hook_extra {
* Array of bulk item update data.
*
* @type string $action Type of action. Default 'update'.
* @type string $type Type of update process. Accepts 'plugin', 'theme', 'translation', or 'core'.
* @type bool $bulk Whether the update process is a bulk update. Default true.
* @type array $plugins Array of the basename paths of the plugins' main files.
* @type array $themes The theme slugs.
* @type array $translations {
* Array of translations update data.
*
* @type string $language The locale the translation is for.
* @type string $type Type of translation. Accepts 'plugin', 'theme', or 'core'.
* @type string $slug Text domain the translation is for. The slug of a theme/plugin or
* 'default' for core translations.
* @type string $version The version of a theme, plugin, or core.
* }
* }
*/
do_action( 'upgrader_process_complete', $this, $options['hook_extra'] );
$this->skin->footer();
}
return $result;
}
/**
* Toggle maintenance mode for the site.
*
* Create/delete the maintenance file to enable/disable maintenance mode.
*
* @since 2.8.0
*
* @global WP_Filesystem_Base $wp_filesystem Subclass
*
* @param bool $enable True to enable maintenance mode, false to disable.
*/
public function maintenance_mode( $enable = false ) {
global $wp_filesystem;
$file = $wp_filesystem->abspath() . '.maintenance';
if ( $enable ) {
//$this->skin->feedback( 'maintenance_start' );
// Create maintenance file to signal that we are upgrading
$maintenance_string = '<?php $upgrading = ' . time() . '; ?>';
$wp_filesystem->delete( $file );
$wp_filesystem->put_contents( $file, $maintenance_string, FS_CHMOD_FILE );
} elseif ( ! $enable && $wp_filesystem->exists( $file ) ) {
//$this->skin->feedback( 'maintenance_end' );
$wp_filesystem->delete( $file );
}
}
/**
* Download a package.
*
* @since 2.8.0
* @since 5.5.0 Added the `$hook_extra` parameter.
*
* @param string $package The URI of the package. If this is the full path to an
* existing local file, it will be returned untouched.
* @param bool $check_signatures Whether to validate file signatures. Default false.
* @param array $hook_extra Extra arguments to pass to the filter hooks. Default empty array.
* @return string|WP_Error The full path to the downloaded package file, or a WP_Error object.
*/
public function download_package( $package, $check_signatures = false, $hook_extra = array() ) {
/**
* Filters whether to return the package.
*
* @since 3.7.0
* @since 5.5.0 Added the `$hook_extra` parameter.
*
* @param bool $reply Whether to bail without returning the package.
* Default false.
* @param string $package The package file name.
* @param WP_Upgrader $this The WP_Upgrader instance.
* @param array $hook_extra Extra arguments passed to hooked filters.
*/
$reply = apply_filters( 'upgrader_pre_download', false, $package, $this, $hook_extra );
if ( false !== $reply ) {
return $reply;
}
if ( ! preg_match( '!^(http|https|ftp)://!i', $package ) && file_exists( $package ) ) { // Local file or remote?
return $package; // Must be a local file.
}
if ( empty( $package ) ) {
return new WP_Error( 'no_package', $this->strings['no_package'] );
}
//$this->skin->feedback( 'downloading_package', $package );
$download_file = download_url( $package, 300, $check_signatures );
if ( is_wp_error( $download_file ) && ! $download_file->get_error_data( 'softfail-filename' ) ) {
return new WP_Error( 'download_failed', $this->strings['download_failed'], $download_file->get_error_message() );
}
return $download_file;
}
/**
* Unpack a compressed package file.
*
* @since 2.8.0
*
* @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
*
* @param string $package Full path to the package file.
* @param bool $delete_package Optional. Whether to delete the package file after attempting
* to unpack it. Default true.
* @return string|WP_Error The path to the unpacked contents, or a WP_Error on failure.
*/
public function unpack_package( $package, $delete_package = true ) {
global $wp_filesystem;
//$this->skin->feedback( 'unpack_package' );
$upgrade_folder = $wp_filesystem->wp_content_dir() . 'upgrade/';
//Clean up contents of upgrade directory beforehand.
$upgrade_files = $wp_filesystem->dirlist( $upgrade_folder );
if ( ! empty( $upgrade_files ) ) {
foreach ( $upgrade_files as $file ) {
$wp_filesystem->delete( $upgrade_folder . $file['name'], true );
}
}
// We need a working directory - Strip off any .tmp or .zip suffixes
$working_dir = $upgrade_folder . basename( basename( $package, '.tmp' ), '.zip' );
// Clean up working directory
if ( $wp_filesystem->is_dir( $working_dir ) ) {
$wp_filesystem->delete( $working_dir, true );
}
// Unzip package to working directory
$result = unzip_file( $package, $working_dir );
// Once extracted, delete the package if required.
if ( $delete_package ) {
unlink( $package );
}
if ( is_wp_error( $result ) ) {
$wp_filesystem->delete( $working_dir, true );
if ( $result->get_error_code() === 'incompatible_archive' ) {
return new WP_Error( 'incompatible_archive', $this->strings['incompatible_archive'], $result->get_error_data() );
}
return $result;
}
return $working_dir;
}
/**
* Install a package.
*
* Copies the contents of a package form a source directory, and installs them in
* a destination directory. Optionally removes the source. It can also optionally
* clear out the destination folder if it already exists.
*
* @since 2.8.0
*
* @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
* @global array $wp_theme_directories
*
* @param array|string $args {
* Optional. Array or string of arguments for installing a package. Default empty array.
*
* @type string $source Required path to the package source. Default empty.
* @type string $destination Required path to a folder to install the package in.
* Default empty.
* @type bool $clear_destination Whether to delete any files already in the destination
* folder. Default false.
* @type bool $clear_working Whether to delete the files form the working directory
* after copying to the destination. Default false.
* @type bool $abort_if_destination_exists Whether to abort the installation if
* the destination folder already exists. Default true.
* @type array $hook_extra Extra arguments to pass to the filter hooks called by
* WP_Upgrader::install_package(). Default empty array.
* }
*
* @return array|WP_Error The result (also stored in `WP_Upgrader::$result`), or a WP_Error on failure.
*/
public function install_package( $args = array() ) {
global $wp_filesystem, $wp_theme_directories;
$defaults = array(
'source' => '', // Please always pass this
'destination' => '', // and this
'clear_destination' => false,
'clear_working' => false,
'abort_if_destination_exists' => true,
'hook_extra' => array(),
);
$args = wp_parse_args( $args, $defaults );
// These were previously extract()'d.
$source = $args['source'];
$destination = $args['destination'];
$clear_destination = $args['clear_destination'];
// @todo does this need to be replaced?
// wpforms_set_time_limit( 300 );
if ( empty( $source ) || empty( $destination ) ) {
return new WP_Error( 'bad_request', $this->strings['bad_request'] );
}
//$this->skin->feedback( 'installing_package' );
/**
* Filter the install response before the installation has started.
*
* Returning a truthy value, or one that could be evaluated as a WP_Error
* will effectively short-circuit the installation, returning that value
* instead.
*
* @since 2.8.0
*
* @param bool|WP_Error $response Response.
* @param array $hook_extra Extra arguments passed to hooked filters.
*/
$res = apply_filters( 'upgrader_pre_install', true, $args['hook_extra'] );
if ( is_wp_error( $res ) ) {
return $res;
}
// Retain the Original source and destinations.
$remote_source = $args['source'];
$local_destination = $destination;
$source_files = array_keys( $wp_filesystem->dirlist( $remote_source ) );
$remote_destination = $wp_filesystem->find_folder( $local_destination );
$count_source_files = count( $source_files );
// Locate which directory to copy to the new folder, This is based on the actual folder holding the files.
if ( $count_source_files === 1 && $wp_filesystem->is_dir( trailingslashit( $args['source'] ) . $source_files[0] . '/' ) ) { // Only one folder? Then we want its contents.
$source = trailingslashit( $args['source'] ) . trailingslashit( $source_files[0] );
} elseif ( $count_source_files === 0 ) {
return new WP_Error( 'incompatible_archive_empty', $this->strings['incompatible_archive'], $this->strings['no_files'] ); // There are no files?
} else { // It's only a single file, the upgrader will use the folder name of this file as the destination folder. Folder name is based on zip filename.
$source = trailingslashit( $args['source'] );
}
/**
* Filter the source file location for the upgrade package.
*
* @since 2.8.0
* @since 4.4.0 The $hook_extra parameter became available.
*
* @param string $source File source location.
* @param string $remote_source Remote file source location.
* @param WP_Upgrader $this WP_Upgrader instance.
* @param array $hook_extra Extra arguments passed to hooked filters.
*/
$source = apply_filters( 'upgrader_source_selection', $source, $remote_source, $this, $args['hook_extra'] );
if ( is_wp_error( $source ) ) {
return $source;
}
// Has the source location changed? If so, we need a new source_files list.
if ( $source !== $remote_source ) {
$source_files = array_keys( $wp_filesystem->dirlist( $source ) );
}
/*
* Protection against deleting files in any important base directories.
* Theme_Upgrader & Plugin_Upgrader also trigger this, as they pass the
* destination directory (WP_PLUGIN_DIR / wp-content/themes) intending
* to copy the directory into the directory, whilst they pass the source
* as the actual files to copy.
*/
$protected_directories = array( ABSPATH, WP_CONTENT_DIR, WP_PLUGIN_DIR, WP_CONTENT_DIR . '/themes' );
if ( is_array( $wp_theme_directories ) ) {
$protected_directories = array_merge( $protected_directories, $wp_theme_directories );
}
if ( in_array( $destination, $protected_directories ) ) {
$remote_destination = trailingslashit( $remote_destination ) . trailingslashit( basename( $source ) );
$destination = trailingslashit( $destination ) . trailingslashit( basename( $source ) );
}
if ( $clear_destination ) {
// We're going to clear the destination if there's something there.
$removed = $this->clear_destination( $remote_destination );
/**
* Filter whether the upgrader cleared the destination.
*
* @since 2.8.0
*
* @param mixed $removed Whether the destination was cleared. true on success, WP_Error on failure
* @param string $local_destination The local package destination.
* @param string $remote_destination The remote package destination.
* @param array $hook_extra Extra arguments passed to hooked filters.
*/
$removed = apply_filters( 'upgrader_clear_destination', $removed, $local_destination, $remote_destination, $args['hook_extra'] );
if ( is_wp_error( $removed ) ) {
return $removed;
}
} elseif ( $args['abort_if_destination_exists'] && $wp_filesystem->exists( $remote_destination ) ) {
// If we're not clearing the destination folder and something exists there already, Bail.
// But first check to see if there are actually any files in the folder.
$_files = $wp_filesystem->dirlist( $remote_destination );
if ( ! empty( $_files ) ) {
$wp_filesystem->delete( $remote_source, true ); // Clear out the source files.
return new WP_Error( 'folder_exists', $this->strings['folder_exists'], $remote_destination );
}
}
// Create destination if needed.
if ( ! $wp_filesystem->exists( $remote_destination ) ) {
if ( ! $wp_filesystem->mkdir( $remote_destination, FS_CHMOD_DIR ) ) {
return new WP_Error( 'mkdir_failed_destination', $this->strings['mkdir_failed'], $remote_destination );
}
}
// Copy new version of item into place.
$result = copy_dir( $source, $remote_destination );
if ( is_wp_error( $result ) ) {
if ( $args['clear_working'] ) {
$wp_filesystem->delete( $remote_source, true );
}
return $result;
}
// Clear the Working folder?
if ( $args['clear_working'] ) {
$wp_filesystem->delete( $remote_source, true );
}
$destination_name = basename( str_replace( $local_destination, '', $destination ) );
if ( $destination_name === '.' ) {
$destination_name = '';
}
$this->result = compact( 'source', 'source_files', 'destination', 'destination_name', 'local_destination', 'remote_destination', 'clear_destination' );
/**
* Filter the installation response after the installation has finished.
*
* @since 2.8.0
*
* @param bool $response Installation response.
* @param array $hook_extra Extra arguments passed to hooked filters.
* @param array $result Installation result data.
*/
$res = apply_filters( 'upgrader_post_install', true, $args['hook_extra'], $this->result );
if ( is_wp_error( $res ) ) {
$this->result = $res;
return $res;
}
// Bombard the calling function will all the info which we've just used.
return $this->result;
}
/**
* Install a plugin package.
*
* @since 1.6.3
*
* @param string $package The full local path or URI of the package.
* @param array $args Optional. Other arguments for installing a plugin package. Default empty array.
*
* @return bool|\WP_Error True if the installation was successful, false or a WP_Error otherwise.
*/
public function install( $package, $args = array() ) {
$result = parent::install( $package, $args );
if ( true === $result ) {
do_action( 'edd_plugin_installed', $package );
}
return $result;
}
}

View File

@ -0,0 +1,132 @@
<?php
/**
* Notes Actions
*
* @package EDD
* @subpackage Admin/Discounts
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 3.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Add a note via AJAX.
*
* @since 3.0
*/
function edd_admin_ajax_add_note() {
// Check AJAX referrer
check_ajax_referer( 'edd_note', 'nonce' );
// Bail if user cannot delete notes
if ( ! current_user_can( 'edit_shop_payments' ) ) {
wp_die( -1 );
}
// Get object ID
$object_id = ! empty( $_POST['object_id'] )
? absint( $_POST['object_id'] )
: 0;
// Get object type
$object_type = ! empty( $_POST['object_type'] )
? sanitize_key( $_POST['object_type'] )
: '';
// Bail if no object
if ( empty( $object_id ) || empty( $object_type ) ) {
wp_die( -1 );
}
// Get note contents (maybe sanitize)
$note = ! empty( $_POST['note'] )
? trim( wp_kses( stripslashes_deep( $_POST['note'] ), edd_get_allowed_tags() ) )
: '';
// Bail if no note
if ( empty( $note ) ) {
wp_die( -1 );
}
// Add the note
$note_id = edd_add_note( array(
'object_id' => $object_id,
'object_type' => $object_type,
'content' => $note,
'user_id' => get_current_user_id()
) );
$x = new WP_Ajax_Response();
$x->add(
array(
'what' => 'edd_note_html',
'data' => edd_admin_get_note_html( $note_id, $object_id ),
)
);
$x->send();
}
add_action( 'wp_ajax_edd_add_note', 'edd_admin_ajax_add_note' );
/**
* Delete a note.
*
* @since 3.0
*
* @param array $data Data from $_GET.
*/
function edd_admin_delete_note( $data = array() ) {
// Bail if missing any data
if ( empty( $data['_wpnonce'] ) || empty( $data['note_id'] ) ) {
return;
}
// Bail if nonce fails
if ( ! wp_verify_nonce( $data['_wpnonce'], 'edd_delete_note_' . $data['note_id'] ) ) {
return;
}
// Try to delete
edd_delete_note( $data['note_id'] );
edd_redirect( edd_get_note_delete_redirect_url() );
}
add_action( 'edd_delete_note', 'edd_admin_delete_note' );
/**
* Delete a discount note via AJAX.
*
* @since 3.0
*/
function edd_admin_ajax_delete_note() {
// Check AJAX referrer
check_ajax_referer( 'edd_note', 'nonce' );
// Bail if user cannot delete notes
if ( ! current_user_can( 'manage_shop_settings' ) ) {
wp_die( -1 );
}
// Get note ID
$note_id = ! empty( $_POST['note_id'] )
? absint( $_POST['note_id'] )
: 0;
// Bail if no note
if ( empty( $note_id ) ) {
wp_die( -1 );
}
// Delete note
if ( edd_delete_note( $note_id ) ) {
wp_die( 1 );
}
wp_die( 0 );
}
add_action( 'wp_ajax_edd_delete_note', 'edd_admin_ajax_delete_note' );

View File

@ -0,0 +1,223 @@
<?php
/**
* Notes Functions
*
* @package EDD
* @subpackage Admin/Discounts
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 3.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Get the HTML used to output all of the notes for a single object
*
* @since 3.0
*
* @param array $notes
* @return string
*/
function edd_admin_get_notes_html( $notes = array() ) {
// Whether to show or hide the "No notes" default text
$no_notes_display = ! empty( $notes )
? ' style="display:none;"'
: '';
// Start a buffer
ob_start(); ?>
<div id="edd-notes" class="edd-notes">
<?php
// Output notes
foreach ( $notes as $note ) {
echo edd_admin_get_note_html( $note );
}
?>
<p class="edd-no-notes"<?php echo $no_notes_display; ?>>
<?php _e( 'No notes.', 'easy-digital-downloads' ); ?>
</p>
</div>
<?php
// Return the current buffer
return ob_get_clean();
}
/**
* Get the HTML used to output a single note, from an array of notes
*
* @since 3.0
* @param int $note_id
*
* @return string
*/
function edd_admin_get_note_html( $note_id = 0 ) {
/** @var $note EDD\Notes\Note For IDE type-hinting purposes. */
// Get the note
$note = is_numeric( $note_id )
? edd_get_note( $note_id )
: $note_id;
// No note, so bail
if ( empty( $note ) ) {
return;
}
// User
$user_id = $note->user_id;
$author = edd_get_bot_name();
if ( ! empty( $user_id ) ) {
/* translators: user ID */
$author = sprintf( __( 'User ID #%s', 'easy-digital-downloads' ), $user_id );
$user_object = get_userdata( $user_id );
if ( $user_object ) {
$author = ! empty( $user_object->display_name ) ? $user_object->display_name : $user_object->user_login;
}
}
// URL to delete note
$delete_note_url = wp_nonce_url( add_query_arg( array(
'edd-action' => 'delete_note',
'note_id' => absint( $note->id ),
) ), 'edd_delete_note_' . absint( $note->id ) );
// Start a buffer
ob_start();
?>
<div class="edd-note" id="edd-note-<?php echo esc_attr( $note->id ); ?>">
<div class="edd-note__header">
<strong class="edd-note-author"><?php echo esc_html( $author ); ?></strong>
<time datetime="<?php echo esc_attr( EDD()->utils->date( $note->date_created, null, true )->toDateTimeString() ); ?>"><?php echo edd_date_i18n( $note->date_created, 'datetime' ); ?></time>
<a href="<?php echo esc_url( $delete_note_url ); ?>#edd-notes" class="edd-delete-note" data-note-id="<?php echo esc_attr( $note->id ); ?>" data-object-id="<?php echo esc_attr( $note->object_id ); ?>" data-object-type="<?php echo esc_attr( $note->object_type ); ?>">
<?php echo esc_html_x( '&times;', 'Delete note', 'easy-digital-downloads' ); ?>
</a>
</div>
<?php echo wpautop( make_clickable( $note->content ) ); ?>
</div>
<?php
// Return the current buffer
return ob_get_clean();
}
/**
* Get the HTML used to add a note to an object ID and type
*
* @since 3.0
*
* @param int $object_id
* @param string $object_type
*
* @return string
*/
function edd_admin_get_new_note_form( $object_id = 0, $object_type = '' ) {
if ( ! current_user_can( 'edit_shop_payments' ) ) {
return '';
}
// Start a buffer
ob_start();?>
<div class="edd-add-note">
<div class="edd-form-group">
<label class="edd-form-group__label screen-reader-text" for="edd-note">
<?php esc_html_e( 'Note', 'easy-digital-downloads' ); ?>
</label>
<div id="edd-form-group__control">
<textarea name="edd-note" id="edd-note" class="edd-form-group__input"></textarea>
</div>
</div>
<div class="edd-form-group">
<button type="button" id="edd-add-note" class="edd-note-submit button button-secondary left" data-object-id="<?php echo esc_attr( $object_id ); ?>" data-object-type="<?php echo esc_attr( $object_type ); ?>">
<?php _e( 'Add Note', 'easy-digital-downloads' ); ?>
</button>
<span class="spinner"></span>
</div>
<?php wp_nonce_field( 'edd_note', 'edd_note_nonce' ); ?>
</div>
<?php
// Return the current buffer
return ob_get_clean();
}
/**
* Return the URL to redirect to after deleting a note
*
* For now, this is always the current URL, because we aren't ever sure where
* notes are being used. Maybe this will need a filter or something, someday.
*
* @since 3.0
*
* @return string
*/
function edd_get_note_delete_redirect_url() {
// HTTP or HTTPS
$scheme = is_ssl()
? 'https'
: 'http';
// Return the concatenated URL
return "{$scheme}://{$_SERVER[HTTP_HOST]}{$_SERVER[REQUEST_URI]}";
}
/**
* Return the HTML used to paginate through notes.
*
* @since 3.0
* @param array $args
*/
function edd_admin_get_notes_pagination( $args = array() ) {
// Parse args
$r = wp_parse_args( $args, array(
'total' => 0,
'pag_arg' => 'paged',
'base' => '%_%',
'show_all' => true,
'prev_text' => is_rtl() ? '&rarr;' : '&larr;',
'next_text' => is_rtl() ? '&larr;' : '&rarr;',
'add_fragment' => ''
) );
// Maximum notes per page
$per_page = apply_filters( 'edd_notes_per_page', 20 );
$r['total'] = ceil( $r['total'] / $per_page );
$r['format'] = "?{$r['pag_arg']}=%#%";
// Don't allow pagination beyond the boundaries
$r['current'] = ! empty( $_GET[ $r['pag_arg'] ] ) && is_numeric( $_GET[ $r['pag_arg'] ] )
? absint( $_GET[ $r['pag_arg'] ] )
: 1;
// Start a buffer
ob_start(); ?>
<div class="edd-note-pagination">
<?php echo paginate_links( $r ); ?>
</div>
<?php
// Return the current buffer
return ob_get_clean();
}

View File

@ -0,0 +1,670 @@
<?php
/**
* Admin Payment Actions
*
* @package EDD
* @subpackage Admin/Payments
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.9
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Handle order item changes
*
* @since 3.0
*
* @param array $request
*
* @return boolean
*/
function edd_handle_order_item_change( $request = array() ) {
// Bail if missing necessary properties
if (
empty( $request['_wpnonce'] ) ||
empty( $request['id'] ) ||
empty( $request['order_item'] )
) {
return false;
}
// Bail if nonce check fails
if ( ! wp_verify_nonce( $request['_wpnonce'], 'edd_order_item_nonce' ) ) {
return false;
}
// Default data
$data = array();
// Maybe add status to data to update
if ( ! empty( $request['status'] ) && ( 'inherit' === $request['status'] ) || in_array( $request['status'], array_keys( edd_get_payment_statuses() ), true )) {
$data['status'] = sanitize_key( $request['status'] );
}
// Update order item
if ( ! empty( $data ) ) {
edd_update_order_item( $request['order_item'], $data );
edd_redirect( edd_get_admin_url( array(
'page' => 'edd-payment-history',
'view' => 'view-order-details',
'id' => absint( $request['id'] )
) ) );
}
}
add_action( 'edd_handle_order_item_change', 'edd_handle_order_item_change' );
/**
* Process the changes from the `View Order Details` page.
*
* @since 1.9
* @since 3.0 Refactored to use new core objects and query methods.
*
* @param array $data Order data.
*/
function edd_update_payment_details( $data = array() ) {
// Bail if an empty array is passed.
if ( empty( $data ) ) {
wp_die( esc_html__( 'You do not have permission to edit this payment record', 'easy-digital-downloads' ), esc_html__( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
// Bail if the user does not have the correct permissions.
if ( ! current_user_can( 'edit_shop_payments', $data['edd_payment_id'] ) ) {
wp_die( esc_html__( 'You do not have permission to edit this payment record', 'easy-digital-downloads' ), esc_html__( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
check_admin_referer( 'edd_update_payment_details_nonce' );
// Retrieve the order ID and set up the order.
$order_id = absint( $data['edd_payment_id'] );
$order = edd_get_order( $order_id );
$order_update_args = array();
$unlimited = isset( $data['edd-unlimited-downloads'] ) ? '1' : null;
$new_status = sanitize_key( $data['edd-payment-status'] );
$date_string = EDD()->utils->get_date_string(
sanitize_text_field( $data['edd-payment-date'] ),
sanitize_text_field( $data['edd-payment-time-hour'] ),
sanitize_text_field( $data['edd-payment-time-min'] )
);
// The date is entered in the WP timezone. We need to convert it to UTC prior to saving now.
$date = edd_get_utc_equivalent_date( EDD()->utils->date( $date_string, edd_get_timezone_id(), false ) );
$date = $date->format( 'Y-m-d H:i:s' );
$order_update_args['date_created'] = $date;
// Customer
$curr_customer_id = sanitize_text_field( $data['current-customer-id'] );
$new_customer_id = sanitize_text_field( $data['customer-id'] );
// Create a new customer.
if ( isset( $data['edd-new-customer'] ) && 1 === (int) $data['edd-new-customer'] ) {
$email = isset( $data['edd-new-customer-email'] )
? sanitize_text_field( $data['edd-new-customer-email'] )
: '';
// Sanitize first name
$first_name = isset( $data['edd-new-customer-first-name'] )
? sanitize_text_field( $data['edd-new-customer-first-name'] )
: '';
// Sanitize last name
$last_name = isset( $data['edd-new-customer-last-name'] )
? sanitize_text_field( $data['edd-new-customer-last-name'] )
: '';
// Combine
$name = $first_name . ' ' . $last_name;
if ( empty( $email ) || empty( $name ) ) {
wp_die( esc_html__( 'New Customers require a name and email address', 'easy-digital-downloads' ) );
}
$customer = new EDD_Customer( $email );
if ( empty( $customer->id ) ) {
$customer_data = array(
'name' => $name,
'email' => $email,
);
$user_id = email_exists( $email );
if ( false !== $user_id ) {
$customer_data['user_id'] = $user_id;
}
if ( ! $customer->create( $customer_data ) ) {
$customer = new EDD_Customer( $curr_customer_id );
edd_set_error( 'edd-payment-new-customer-fail', __( 'Error creating new customer', 'easy-digital-downloads' ) );
}
} else {
wp_die( sprintf( __( 'A customer with the email address %s already exists. Please go back and assign this payment to them.', 'easy-digital-downloads' ), $email ) );
}
$previous_customer = new EDD_Customer( $curr_customer_id );
} elseif ( $curr_customer_id !== $new_customer_id ) {
$customer = new EDD_Customer( $new_customer_id );
$previous_customer = new EDD_Customer( $curr_customer_id );
} else {
$customer = new EDD_Customer( $curr_customer_id );
}
// Remove the stats and payment from the previous customer and attach it to the new customer
if ( isset( $previous_customer ) ) {
$previous_customer->remove_payment( $order_id, false );
$customer->attach_payment( $order_id, false );
// If purchase was completed and not ever refunded, adjust stats of customers
if ( 'revoked' === $new_status || 'complete' === $new_status ) {
$previous_customer->recalculate_stats();
if ( ! empty( $customer ) ) {
$customer->recalculate_stats();
}
}
$order_update_args['customer_id'] = $customer->id;
}
$order_update_args['user_id'] = $customer->user_id;
$order_update_args['email'] = $customer->email;
// Address.
$address = $data['edd_order_address'];
$order_address_id = absint( $address['address_id'] );
$order_address_details = array(
'name' => $customer->name,
'address' => $address['address'],
'address2' => $address['address2'],
'city' => $address['city'],
'region' => $address['region'],
'postal_code' => $address['postal_code'],
'country' => $address['country'],
);
if ( empty( $order_address_id ) ) {
// Unset the address_id which is 0.
unset( $address['address_id'] );
// Add the $order_id to the arguments to create this order address.
$order_address_details['order_id'] = $order_id;
edd_add_order_address( $order_address_details );
} else {
edd_update_order_address( $order_address_id, $order_address_details );
}
// Unlimited downloads.
if ( 1 === (int) $unlimited ) {
edd_update_order_meta( $order_id, 'unlimited_downloads', $unlimited );
} else {
edd_delete_order_meta( $order_id, 'unlimited_downloads' );
}
// Don't set the status in the update call (for back compat)
unset( $order_update_args['status'] );
// Attempt to update the order
$updated = edd_update_order( $order_id, $order_update_args );
// Bail if an error occurred
if ( false === $updated ) {
wp_die( __( 'Error updating order.', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 400 ) );
}
// Check if the status has changed, if so, we need to invoke the pertinent
// status processing method (for back compat)
if ( $new_status !== $order->status ) {
edd_update_order_status( $order_id, $new_status );
}
do_action( 'edd_updated_edited_purchase', $order_id );
edd_redirect( edd_get_admin_url( array(
'page' => 'edd-payment-history',
'view' => 'view-order-details',
'edd-message' => 'payment-updated',
'id' => absint( $order_id ),
) ) );
}
add_action( 'edd_update_payment_details', 'edd_update_payment_details' );
/**
* New in 3.0, permanently destroys an order, and all its data, and related data.
*
* @since 3.0
*
* @param array $data Arguments passed.
*/
function edd_trigger_destroy_order( $data ) {
if ( wp_verify_nonce( $data['_wpnonce'], 'edd_payment_nonce' ) ) {
$payment_id = absint( $data['purchase_id'] );
if ( ! current_user_can( 'delete_shop_payments', $payment_id ) ) {
wp_die( esc_html__( 'You do not have permission to edit this order.', 'easy-digital-downloads' ), esc_html__( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
edd_destroy_order( $payment_id );
$redirect_link = add_query_arg(
array(
'page' => 'edd-payment-history',
'edd-message' => 'payment_deleted',
'status' => 'trash',
),
edd_get_admin_base_url()
);
edd_redirect( $redirect_link );
}
}
add_action( 'edd_delete_order', 'edd_trigger_destroy_order' );
/**
* Trigger the action of moving an order to the 'trash' status
*
* @since 3.0
*
* @param $data
* @return void
*/
function edd_trigger_trash_order( $data ) {
if ( wp_verify_nonce( $data['_wpnonce'], 'edd_payment_nonce' ) ) {
$payment_id = absint( $data['purchase_id'] );
if ( ! current_user_can( 'delete_shop_payments', $payment_id ) ) {
wp_die( __( 'You do not have permission to edit this payment record', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
edd_trash_order( $payment_id );
$redirect = edd_get_admin_url( array(
'page' => 'edd-payment-history',
'edd-message' => 'order_trashed',
'order_type' => esc_attr( $data['order_type'] ),
) );
edd_redirect( $redirect );
}
}
add_action( 'edd_trash_order', 'edd_trigger_trash_order' );
/**
* Trigger the action of restoring an order from the 'trash' status
*
* @since 3.0
*
* @param $data
* @return void
*/
function edd_trigger_restore_order( $data ) {
if ( wp_verify_nonce( $data['_wpnonce'], 'edd_payment_nonce' ) ) {
$payment_id = absint( $data['purchase_id'] );
if ( ! current_user_can( 'delete_shop_payments', $payment_id ) ) {
wp_die( __( 'You do not have permission to edit this payment record', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
edd_restore_order( $payment_id );
$redirect = edd_get_admin_url( array(
'page' => 'edd-payment-history',
'edd-message' => 'order_restored',
'order_type' => esc_attr( $data['order_type'] ),
) );
edd_redirect( $redirect );
}
}
add_action( 'edd_restore_order', 'edd_trigger_restore_order' );
/**
* Retrieves a new download link for a purchased file
*
* @since 2.0
* @return string
*/
function edd_ajax_generate_file_download_link() {
$customer_view_role = apply_filters( 'edd_view_customers_role', 'view_shop_reports' );
if ( ! current_user_can( $customer_view_role ) ) {
die( '-1' );
}
$payment_id = absint( $_POST['payment_id'] );
$download_id = absint( $_POST['download_id'] );
$price_id = isset( $_POST['price_id'] ) && is_numeric( $_POST['price_id'] ) ? absint( $_POST['price_id'] ) : null;
if ( empty( $payment_id ) ) {
die( '-2' );
}
if ( empty( $download_id ) ) {
die( '-3' );
}
$payment_key = edd_get_payment_key( $payment_id );
$email = edd_get_payment_user_email( $payment_id );
$limit = edd_get_file_download_limit( $download_id );
if ( ! empty( $limit ) ) {
// Increase the file download limit when generating new links
edd_set_file_download_limit_override( $download_id, $payment_id );
}
$files = edd_get_download_files( $download_id, $price_id );
if ( ! $files ) {
die( '-4' );
}
$file_urls = '';
foreach( $files as $file_key => $file ) {
$file_urls .= edd_get_download_file_url( $payment_key, $email, $file_key, $download_id, $price_id );
$file_urls .= "\n\n";
}
die( $file_urls );
}
add_action( 'wp_ajax_edd_get_file_download_link', 'edd_ajax_generate_file_download_link' );
/**
* Renders the refund form that is used to process a refund.
*
* @since 3.0
*
* @return void
*/
function edd_ajax_generate_refund_form() {
// Verify we have a logged user.
if ( ! is_user_logged_in() ) {
$return = array(
'success' => false,
'message' => __( 'You must be logged in to perform this action.', 'easy-digital-downloads' ),
);
wp_send_json( $return, 401 );
}
// Verify the logged in user has permission to edit shop payments.
if ( ! current_user_can( 'edit_shop_payments' ) ) {
$return = array(
'success' => false,
'message' => __( 'Your account does not have permission to perform this action.', 'easy-digital-downloads' ),
);
wp_send_json( $return, 401 );
}
$order_id = isset( $_POST['order_id'] ) && is_numeric( $_POST['order_id'] ) ? absint( $_POST['order_id'] ) : false;
if ( empty( $order_id ) ) {
$return = array(
'success' => false,
'message' => __( 'Invalid order ID', 'easy-digital-downloads' ),
);
wp_send_json( $return, 400 );
}
$order = edd_get_order( $order_id );
if ( empty( $order ) ) {
$return = array(
'success' => false,
'message' => __( 'Invalid order', 'easy-digital-downloads' ),
);
wp_send_json( $return, 404 );
}
if ( 'refunded' === $order->status ) {
$return = array(
'success' => false,
'message' => __( 'Order is already refunded', 'easy-digital-downloads' ),
);
wp_send_json( $return, 404 );
}
if ( 'refund' === $order->type ) {
$return = array(
'success' => false,
'message' => __( 'Cannot refund an order that is already refunded.', 'easy-digital-downloads' ),
);
wp_send_json( $return, 404 );
}
// Output buffer the form before we include it in the JSON response.
ob_start();
?>
<div id="edd-submit-refund-status" style="display: none;">
<span class="edd-submit-refund-message"></span>
<a class="edd-submit-refund-url" href=""><?php _e( 'View Refund', 'easy-digital-downloads' ); ?></a>
</div>
<form id="edd-submit-refund-form" method="POST">
<?php
// Load list table if not already loaded
if ( ! class_exists( '\\EDD\\Admin\\Refund_Items_Table' ) ) {
require_once 'class-refund-items-table.php';
}
$refund_items = new EDD\Admin\Refund_Items_Table();
$refund_items->prepare_items();
$refund_items->display();
?>
</form>
<?php
$html = trim( ob_get_clean() );
$return = array(
'success' => true,
'html' => $html,
);
wp_send_json( $return, 200 );
}
add_action( 'wp_ajax_edd_generate_refund_form', 'edd_ajax_generate_refund_form' );
/**
* Processes the results from the Submit Refund form
*
* @since 3.0
* @return void
*/
function edd_ajax_process_refund_form() {
// Verify we have a logged user.
if ( ! is_user_logged_in() ) {
wp_send_json_error( __( 'You must be logged in to perform this action.', 'easy-digital-downloads' ), 401 );
}
// Verify the logged in user has permission to edit shop payments.
if ( ! current_user_can( 'edit_shop_payments' ) ) {
wp_send_json_error( __( 'Your account does not have permission to perform this action.', 'easy-digital-downloads' ), 401 );
}
if ( empty( $_POST['data'] ) || empty( $_POST['order_id'] ) ) {
wp_send_json_error( __( 'Missing form data or order ID.', 'easy-digital-downloads' ), 400 );
}
// Get our data out of the serialized string.
parse_str( $_POST['data'], $form_data );
// Verify the nonce.
$nonce = ! empty( $form_data['edd_process_refund'] ) ? sanitize_text_field( $form_data['edd_process_refund'] ) : false;
if ( empty( $nonce ) || ! wp_verify_nonce( $nonce, 'edd_process_refund' ) ) {
wp_send_json_error( __( 'Nonce validation failed when submitting refund.', 'easy-digital-downloads' ), 401 );
}
// Collect selected order items.
$order_items = array();
if ( ! empty( $form_data['refund_order_item'] ) && is_array( $form_data['refund_order_item'] ) ) {
foreach( $form_data['refund_order_item'] as $order_item_id => $order_item ) {
// If there's no quantity or subtotal - bail.
if ( empty( $order_item['quantity'] ) || empty( $order_item['subtotal'] ) ) {
continue;
}
$order_items[] = array(
'order_item_id' => absint( $order_item_id ),
'quantity' => intval( $order_item['quantity'] ),
'subtotal' => edd_sanitize_amount( $order_item['subtotal'] ),
'tax' => ! empty( $order_item['tax'] ) ? edd_sanitize_amount( $order_item['tax'] ) : 0.00
);
}
}
// Collect selected adjustments.
$adjustments = array();
if ( ! empty( $form_data['refund_order_adjustment'] ) && is_array( $form_data['refund_order_adjustment'] ) ) {
foreach( $form_data['refund_order_adjustment'] as $adjustment_id => $adjustment ) {
// If there's no quantity or subtotal - bail.
if ( empty( $adjustment['quantity'] ) || empty( $adjustment['subtotal'] ) ) {
continue;
}
$adjustments[] = array(
'adjustment_id' => absint( $adjustment_id ),
'quantity' => intval( $adjustment['quantity'] ),
'subtotal' => floatval( edd_sanitize_amount( $adjustment['subtotal'] ) ),
'tax' => ! empty( $adjustment['tax'] ) ? floatval( edd_sanitize_amount( $adjustment['tax'] ) ) : 0.00
);
}
}
$order_id = absint( $_POST['order_id'] );
$refund_id = edd_refund_order( $order_id, $order_items, $adjustments );
if ( is_wp_error( $refund_id ) ) {
wp_send_json_error( $refund_id->get_error_message() );
} elseif ( ! empty( $refund_id ) ) {
$return = array(
'refund_id' => $refund_id,
'message' => sprintf( __( 'Refund successfully processed.', 'easy-digital-downloads' ) ),
'refund_url' => edd_get_admin_url(
array(
'page' => 'edd-payment-history',
'view' => 'view-refund-details',
'id' => urlencode( $refund_id ),
)
)
);
wp_send_json_success( $return, 200 );
} else {
wp_send_json_error( __( 'Unable to process refund.', 'easy-digital-downloads' ), 401 );
}
}
add_action( 'wp_ajax_edd_process_refund_form', 'edd_ajax_process_refund_form' );
/**
* Process Orders list table bulk actions. This is necessary because we need to
* redirect to ensure filters do not get applied when bulk actions are being
* processed. This processing cannot happen within the `EDD_Payment_History_Table`
* class as at that point, it is too late to do a redirect.
*
* @since 3.0
*/
function edd_orders_list_table_process_bulk_actions() {
// Bail if this method was called directly.
if ( 'load-download_page_edd-payment-history' !== current_action() ) {
_doing_it_wrong( __FUNCTION__, 'This method is not meant to be called directly.', 'EDD 3.0' );
}
// Check the current user's capability.
if ( ! current_user_can( 'edit_shop_payments' ) ) {
return;
}
$action = isset( $_REQUEST['action'] ) // WPCS: CSRF ok.
? sanitize_text_field( $_REQUEST['action'] )
: '';
// Bail if we aren't processing bulk actions.
if ( '-1' === $action ) {
return;
}
$ids = isset( $_GET['order'] ) // WPCS: CSRF ok.
? $_GET['order']
: false;
if ( ! is_array( $ids ) ) {
$ids = array( $ids );
}
if ( empty( $action ) ) {
return;
}
check_admin_referer( 'bulk-orders' );
$ids = wp_parse_id_list( $ids );
foreach ( $ids as $id ) {
switch ( $action ) {
case 'trash':
edd_trash_order( $id );
break;
case 'restore':
edd_restore_order( $id );
break;
case 'set-status-complete':
edd_update_payment_status( $id, 'complete' );
break;
case 'set-status-pending':
edd_update_payment_status( $id, 'pending' );
break;
case 'set-status-processing':
edd_update_payment_status( $id, 'processing' );
break;
case 'set-status-revoked':
edd_update_payment_status( $id, 'revoked' );
break;
case 'set-status-failed':
edd_update_payment_status( $id, 'failed' );
break;
case 'set-status-abandoned':
edd_update_payment_status( $id, 'abandoned' );
break;
case 'set-status-preapproval':
edd_update_payment_status( $id, 'preapproval' );
break;
case 'set-status-cancelled':
edd_update_payment_status( $id, 'cancelled' );
break;
case 'resend-receipt':
edd_email_purchase_receipt( $id, false );
break;
}
do_action( 'edd_payments_table_do_bulk_action', $id, $action );
}
wp_safe_redirect( wp_get_referer() );
}
add_action( 'load-download_page_edd-payment-history', 'edd_orders_list_table_process_bulk_actions' );

View File

@ -0,0 +1,158 @@
<?php
/**
* Add Order Page.
*
* @package EDD
* @subpackage Admin/Orders
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 3.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
use EDD\Database\Rows\Order as Order;
/**
* Output the Add Order page
*
* @since 3.0
*/
function edd_add_order_page_content() {
wp_enqueue_script( 'edd-admin-orders' );
// Enqueued for backwards compatibility. Empty file.
wp_enqueue_script( 'edd-admin-payments' );
// Create empty order object to pass to callback functions.
$order = new Order( array(
'id' => 0,
'parent' => 0,
'order_number' => 0,
'status' => 'complete',
'date_created' => date( 'Y-m-d H:i:s' ),
'date_modified' => date( 'Y-m-d H:i:s' ),
'date_refundable' => null,
'user_id' => 0,
'customer_id' => 0,
'email' => '',
'ip' => edd_get_ip(),
'gateway' => '',
'mode' => '',
'currency' => edd_get_currency(),
'payment_key' => '',
'subtotal' => 0,
'discount' => 0,
'tax' => 0,
'total' => 0,
) );
?>
<form id="edd-add-order-form" method="post">
<?php edd_order_details_publish( $order ); ?>
<div class="wrap edd-wrap edd-clearfix">
<h1><?php esc_html_e( 'New Order', 'easy-digital-downloads' ); ?></h1>
<hr class="wp-header-end">
<div class="notice notice-error inline" id="edd-add-order-customer-error" style="display: none;">
<p><strong><?php esc_html_e( 'Error', 'easy-digital-downloads' ); ?>:</strong> <?php esc_html_e( 'Please select an existing customer or create a new customer.', 'easy-digital-downloads' ); ?></p>
</div>
<div class="notice notice-error inline" id="edd-add-order-no-items-error" style="display: none">
<p><strong><?php esc_html_e( 'Error', 'easy-digital-downloads' ); ?>:</strong> <?php esc_html_e( 'Please add an item to this order.', 'easy-digital-downloads' ); ?></p>
</div>
<?php do_action( 'edd_add_order_before' ); ?>
<?php do_action( 'edd_add_order_form_top' ); ?>
<div id="poststuff">
<div id="edd-dashboard-widgets-wrap">
<div id="post-body" class="metabox-holder columns-2">
<div id="postbox-container-2" class="postbox-container">
<div id="normal-sortables">
<?php
// Before body.
do_action( 'edd_add_order_details_main_before' );
// Items.
edd_order_details_overview( $order );
// Details sections.
edd_order_details_sections( $order );
// After body.
do_action( 'edd_add_order_details_main_after' );
?>
</div>
</div>
<div id="postbox-container-1" class="postbox-container">
<div id="side-sortables">
<?php
// Before sidebar.
do_action( 'edd_add_order_details_sidebar_before' );
// Attributes.
edd_order_details_attributes( $order );
// Extras.
edd_order_details_extras( $order );
// After sidebar.
do_action( 'edd_add_order_details_sidebar_after' );
?>
</div>
</div>
</div>
</div>
</div>
<?php
do_action( 'edd_add_order_form_bottom' );
wp_nonce_field( 'edd_add_order_nonce', 'edd_add_order_nonce' );
?>
<input type="hidden" name="edd_action" value="add_order" />
<?php do_action( 'edd_add_order_after' ); ?>
</div><!-- /.wrap -->
</form>
<div
id="edd-admin-order-add-item-dialog"
title="<?php esc_attr_e( 'Add Download', 'easy-digital-downloads' ); ?>"
style="display: none;"
>
<div id="edd-admin-order-add-item-dialog-content"></div>
</div>
<div
id="edd-admin-order-add-discount-dialog"
title="<?php esc_attr_e( 'Add Discount', 'easy-digital-downloads' ); ?>"
style="display: none;"
>
<div id="edd-admin-order-add-discount-dialog-content"></div>
</div>
<div
id="edd-admin-order-add-adjustment-dialog"
title="<?php esc_attr_e( 'Add Adjustment', 'easy-digital-downloads' ); ?>"
style="display: none;"
>
<div id="edd-admin-order-add-adjustment-dialog-content"></div>
</div>
<?php
}

View File

@ -0,0 +1,50 @@
<?php
/**
* Order Sections Class.
*
* @package EDD
* @subpackage Admin
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 3.0
*/
namespace EDD\Admin;
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Class for creating a vertically tabbed UI for reports.
*
* @since 3.0
*/
class Order_Sections extends Sections {
/**
* Output the contents
*
* @since 3.0
*/
public function display() {
$use_js = ! empty( $this->use_js )
? ' use-js'
: '';
$role = $this->use_js ? 'tablist' : 'menu';
?>
<div class="edd-sections-wrap edd-order-sections-wrapper">
<div class="edd-vertical-sections meta-box <?php echo $use_js; ?>">
<ul class="section-nav" role="<?php echo esc_attr( $role ); ?>">
<?php echo $this->get_all_section_links(); ?>
</ul>
<div class="section-wrap">
<?php echo $this->get_all_section_contents(); ?>
</div>
<br class="clear">
</div>
</div>
<?php
}
}

View File

@ -0,0 +1,755 @@
<?php
/**
* Refund Items Table Class.
*
* @package EDD
* @subpackage Admin/Orders
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 3.0
*/
namespace EDD\Admin;
// Exit if accessed directly
use EDD\Orders\Order;
use EDD\Orders\Order_Adjustment;
use EDD\Orders\Order_Item;
defined( 'ABSPATH' ) || exit;
/**
* Order_Items_Table Class.
*
* Renders the Refund Items table on the Refund modal.
*
* @since 3.0
*/
class Refund_Items_Table extends List_Table {
/**
* Constructor.
*
* @since 3.0
* @see WP_List_Table::__construct()
*/
public function __construct() {
global $hook_suffix;
parent::__construct( array(
'singular' => 'refund-item',
'plural' => 'refund-items',
'ajax' => false,
) );
$this->get_counts();
}
/**
* Get the base URL for the order item list table.
*
* @since 3.0
*
* @return string Base URL.
*/
public function get_base_url() {}
/**
* Retrieve the view types.
*
* @since 3.0
*
* @return array $views All the views available.
*/
public function get_views() {
return array();
}
/**
* Retrieve the table columns.
*
* @since 3.0
*
* @return array $columns Array of all the list table columns.
*/
public function get_columns() {
$columns = array(
'cb' => '<input type="checkbox" />',
'name' => __( 'Product', 'easy-digital-downloads' ),
'amount' => __( 'Unit Price', 'easy-digital-downloads' ),
'quantity' => __( 'Quantity', 'easy-digital-downloads' ),
'subtotal' => __( 'Subtotal', 'easy-digital-downloads' ),
);
// Maybe add tax column.
$order = $this->get_order();
if ( $order && $order->get_tax_rate() ) {
$columns['tax'] = __( 'Tax', 'easy-digital-downloads' );
}
// Total at the end.
$columns['total'] = __( 'Total', 'easy-digital-downloads' );
// Return columns.
return $columns;
}
/**
* Retrieve the sortable columns.
*
* @since 3.0
*
* @return array Array of all the sortable columns.
*/
public function get_sortable_columns() { return array(); }
/**
* Gets the name of the primary column.
*
* @since 2.5
* @access protected
*
* @return string Name of the primary column.
*/
protected function get_primary_column_name() {
return 'name';
}
/**
* Generates a unique ID for an item, to be used as HTML IDs.
* We cannot simply use `$item->id` because it's possible that an order item and order adjustment
* could have the same ID.
*
* @param Order_Item|Order_Adjustment $item
*
* @since 3.0
* @return string
*/
private function get_item_unique_id( $item ) {
return $item instanceof Order_Item ? 'order-item-' . $item->id : 'order-adjustment-' . $item->id;
}
/**
* Returns a string that designates the type of object. This is used in HTML `name` attributes.
*
* @param Order_Item|Order_Adjustment $item
*
* @since 3.0
* @return string
*/
private function get_object_type( $item ) {
return $item instanceof Order_Item ? 'order_item' : 'order_adjustment';
}
/**
* Returns the item display name.
*
* @param Order_Item|Order_Adjustment $item
*
* @since 3.0
* @return string
*/
private function get_item_display_name( $item ) {
$name = '';
if ( $item instanceof Order_Item ) {
return $item->get_order_item_name();
}
if ( $item instanceof Order_Adjustment ) {
$name = __( 'Order Fee', 'easy-digital-downloads' );
if ( 'credit' === $item->type ) {
$name = __( 'Order Credit', 'easy-digital-downloads' );
}
if ( ! empty( $item->description ) ) {
$name .= ': ' . $item->description;
}
}
return $name;
}
/**
* This function renders most of the columns in the list table.
*
* @since 3.0
*
* @param Order_Item|Order_Adjustment $item Order item or adjustment object.
* @param string $column_name The name of the column.
*
* @return string Column name.
*/
public function column_default( $item, $column_name ) {
$object_type = $this->get_object_type( $item );
$item_id = $this->get_item_unique_id( $item );
switch ( $column_name ) {
case 'amount':
return $this->format_currency( $item, $column_name );
case 'total':
return $this->format_currency( $item, $column_name, 0 );
case 'quantity':
return $this->get_quantity_column( $item, $column_name, $item_id, $object_type );
case 'subtotal':
case 'tax':
return $this->get_adjustable_column( $item, $column_name, $item_id, $object_type );
default:
return property_exists( $item, $column_name )
? $item->{$column_name}
: '';
}
}
/**
* This private function formats a column value for currency.
*
* @since 3.0
*
* @param Order_Item|Order_Adjustment $item Item object.
* @param string $column_name ID of the column being displayed.
* @param false|float $amount_override Amount override, in case it's not in the order item.
*
* @return string Formatted amount.
*/
private function format_currency( $item, $column_name, $amount_override = false ) {
$symbol = $this->get_currency_symbol( $item->order_id );
$currency_pos = edd_get_option( 'currency_position', 'before' );
$formatted_amount = '';
if ( 'before' === $currency_pos ) {
$formatted_amount .= $symbol;
}
// Order Adjustments do not have an `amount` column. We can use `subtotal` instead.
if ( 'amount' === $column_name && $item instanceof Order_Adjustment ) {
$column_name = 'subtotal';
}
$amount = false !== $amount_override ? $amount_override : $item->{$column_name};
$formatted_amount .= '<span data-' . $column_name . '="' . edd_sanitize_amount( $amount ) . '">' . edd_format_amount( $amount, true, $this->get_order_currency_decimals( $item->order_id ) ) . '</span>';
if ( 'after' === $currency_pos ) {
$formatted_amount .= $symbol;
}
return $formatted_amount;
}
/**
* This private function returns the form input for refundable items,
* or amounts for items which have already been refunded.
*
* @param Order_Item $item The item object.
* @param string $column_name ID of the column being displayed.
* @param string $item_id Unique ID of the order item for the refund modal.
* @param string $object_type The item type.
* @return string
*/
private function get_adjustable_column( $item, $column_name, $item_id, $object_type ) {
if ( 'refunded' === $item->status ) {
return $this->format_currency( $item, $column_name, 0 );
}
$currency_pos = edd_get_option( 'currency_position', 'before' );
// Maximum amounts that can be refunded.
$refundable_amounts = $item->get_refundable_amounts();
$amount_remaining = array_key_exists( $column_name, $refundable_amounts ) ? $refundable_amounts[ $column_name ] : $item->{$column_name};
/*
* Original amount.
* For subtotals, we actually do subtotal minus discounts for simplicity so that the end user
* doesn't have to juggle that.
*/
$original_amount = $item->{$column_name};
if ( 'subtotal' === $column_name && ! empty( $item->discount ) ) {
$original_amount -= $item->discount;
}
ob_start();
?>
<div class="edd-form-group">
<label for="edd-order-item-<?php echo esc_attr( $item_id ); ?>-refund-<?php echo esc_attr( $column_name ); ?>" class="screen-reader-text">
<?php
if ( 'subtotal' === $column_name ) {
esc_html_e( 'Amount to refund, excluding tax', 'easy-digital-downloads' );
} else {
esc_html_e( 'Amount of tax to refund', 'easy-digital-downloads' );
}
?>
</label>
<div class="edd-form-group__control">
<?php
if ( 'before' === $currency_pos ) {
echo '<span class="edd-amount-control__currency is-before">';
echo esc_html( $this->get_currency_symbol( $item->order_id ) );
echo '</span>';
}
?>
<span class="edd-amount-control__input">
<input
type="text"
id="edd-order-item-<?php echo esc_attr( $item_id ); ?>-refund-<?php echo esc_attr( $column_name ); ?>"
class="edd-order-item-refund-<?php echo esc_attr( $column_name ); ?> edd-order-item-refund-input"
name="refund_<?php echo esc_attr( $object_type ); ?>[<?php echo esc_attr( $item->id ); ?>][<?php echo esc_attr( $column_name ); ?>]"
value="<?php echo esc_attr( edd_format_amount( $amount_remaining, true, $this->get_order_currency_decimals( $item->order_id ) ) ); ?>"
placeholder="<?php echo esc_attr( edd_format_amount( 0, true, $this->get_order_currency_decimals( $item->order_id ) ) ); ?>"
data-original="<?php echo esc_attr( $original_amount ); ?>"
data-max="<?php echo esc_attr( $amount_remaining ); ?>"
disabled
/>
</span>
<?php
if ( 'after' === $currency_pos ) {
echo '<span class="edd-amount-control__currency is-after">';
echo esc_html( $this->get_currency_symbol( $item->order_id ) );
echo '</span>';
}
?>
</div>
<small class="edd-order-item-refund-max-amount">
<?php
echo _x( 'Max:', 'Maximum input amount', 'easy-digital-downloads' ) . '&nbsp;';
echo $this->format_currency( $item, $column_name, $amount_remaining );
?>
</small>
</div>
<?php
return ob_get_clean();
}
/**
* Gets the quantity column content.
*
* @since 3.0
*
* @param Order_Item|Order_Adjustment $item Order item or adjustment object.
* @param string $column_name The name of the column.
* @param string $item_id Unique ID of the order item for the refund modal.
* @param string $object_type The item type.
* @return string
*/
private function get_quantity_column( $item, $column_name, $item_id, $object_type ) {
$refundable_amounts = $item->get_refundable_amounts();
$item_quantity = 'order_item' === $object_type ? $refundable_amounts['quantity'] : 1;
ob_start();
?>
<div class="edd-form-group">
<label for="edd_order_item_quantity_<?php echo esc_attr( $item_id ); ?>" class="screen-reader-text">
<?php esc_html_e( 'Quantity to refund', 'easy-digital-downloads' ); ?>
</label>
<div class="edd-form-group__control">
<?php if ( 'order_item' !== $object_type ) : ?>
<input type="hidden" data-original="<?php echo esc_attr( $item_quantity ); ?>" id="edd_order_item_quantity_<?php echo esc_attr( $item_id ); ?>" class="edd-order-item-refund-quantity edd-order-item-refund-input readonly" name="refund_<?php echo esc_attr( $object_type ); ?>[<?php echo esc_attr( $item->id ); ?>][quantity]" value="<?php echo esc_attr( $item_quantity ); ?>" disabled />
<?php else : ?>
<?php
$options = range( 1, $item_quantity );
array_unshift( $options, '' );
unset( $options[0] );
$args = array(
'options' => $options,
'name' => 'refund_' . esc_attr( $object_type ) . '[' . esc_attr( $item->id ) . '][quantity]',
'id' => 'edd-order-item-quantity-' . esc_attr( $item_id ),
'class' => 'edd-order-item-refund-quantity edd-order-item-refund-input',
'disabled' => true,
'show_option_all' => false,
'show_option_none' => false,
'chosen' => false,
'selected' => $item_quantity,
'data' => array(
'max' => intval( $item_quantity ),
'original' => intval( $item->quantity ),
),
);
?>
<?php echo EDD()->html->select( $args ); ?>
<?php endif; ?>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* Retrieves the number of decimals for a given order.
*
* @param int $order_id
*
* @since 3.0
* @return int|null
*/
private function get_order_currency_decimals( $order_id ) {
static $currency_decimals = null;
if ( is_null( $currency_decimals ) ) {
$order = edd_get_order( $order_id );
if ( $order ) {
$currency_decimals = edd_currency_decimal_filter( 2, $order->currency );
} else {
$currency_decimals = 2;
}
}
return $currency_decimals;
}
/**
* Retrieves the currency symbol for a given order item.
*
* @param int $order_id
*
* @since 3.0
* @return string|null
*/
private function get_currency_symbol( $order_id ) {
static $symbol = null;
if ( is_null( $symbol ) ) {
$order = edd_get_order( $order_id );
if ( $order ) {
$symbol = edd_currency_symbol( $order->currency );
}
}
return $symbol;
}
/**
* Render the checkbox column
*
* @since 3.0
*
* @param Order_Item|Order_Adjustment $item Order Item or Order Adjustment object.
*
* @return string
*/
public function column_cb( $item ) {
$object_type = $this->get_object_type( $item );
$refundable_amounts = $item->get_refundable_amounts();
$total_remaining = array_key_exists( 'total', $refundable_amounts ) ? floatval( $refundable_amounts['total'] ) : 0.00;
if ( 'refunded' !== $item->status && 0.00 != $total_remaining ) {
return sprintf(
'<input type="checkbox" name="%1$s[]" id="%1$s-%2$s" class="edd-order-item-refund-checkbox" value="%2$s" /><label for="%1$s-%2$s" class="screen-reader-text">%3$s</label>',
/*$1%s*/
'refund_' . esc_attr( $object_type ),
/*$2%s*/
esc_attr( $item->id ),
/* translators: product name */
esc_html( sprintf( __( 'Select %s', 'easy-digital-downloads' ), $this->get_item_display_name( $item ) ) )
);
}
return '';
}
/**
* Render the Name Column
*
* @since 3.0
*
* @param Order_Item|Order_Adjustment $item Order Item object.
*
* @return string Data shown in the Name column
*/
public function column_name( $item ) {
$checkbox_id = 'refund_' . $this->get_object_type( $item ) . '-' . $item->id;
$display_name = esc_html( $this->get_item_display_name( $item ) );
$status_label = ! empty( $item->status ) && 'complete' !== $item->status ? ' &mdash; ' . edd_get_status_label( $item->status ) : '';
if ( 'refunded' === $item->status ) {
return '<span class="row-title">' . $display_name . '</span>' . esc_html( $status_label );
}
return '<label for="' . esc_attr( $checkbox_id ) . '" class="row-title">' . $display_name . '</label>' . $status_label;
}
/**
* Message to be displayed when there are no items
*
* @since 3.0
*/
public function no_items() {
esc_html_e( 'No items found.', 'easy-digital-downloads' );
}
/**
* Retrieve the bulk actions
*
* @since 3.0
* @return array $actions Array of the bulk actions
*/
public function get_bulk_actions() { return array(); }
/**
* Process the bulk actions
*
* @since 3.0
*/
public function process_bulk_action() {}
/**
* Retrieve the order_item code counts
*
* @todo Fees aren't included in this count, but where does this actually get used anyway?
*
* @since 3.0
*/
public function get_counts() {
// Maybe retrieve counts.
if ( ! edd_is_add_order_page() ) {
// Check for an order ID
$order_id = ! empty( $_POST['order_id'] )
? absint( $_POST['order_id'] ) // WPCS: CSRF ok.
: 0;
// Get counts
$this->counts = edd_get_order_item_counts( array(
'order_id' => $order_id,
) );
}
}
/**
* Retrieve all order data to be shown on the refund table.
* This includes order items and order adjustments.
*
* @since 3.0
* @return Order[]|Order_Adjustment[] All order items and order adjustments associated with the current order.
*/
public function get_data() {
$order = $this->get_order();
if ( empty( $order ) ) {
return array();
}
// Get order items.
$order_items = edd_get_order_items( array(
'order_id' => $order->id,
'number' => 999,
) );
// Get order fees
$order_fees = $order->get_fees();
// Get order credits.
$credits = edd_get_order_adjustments( array(
'object_id' => $order->id,
'object_type' => 'order',
'type' => 'credit',
) );
return array_merge( $order_items, $order_fees, $credits );
}
/**
* Setup the final data for the table
*
* @since 3.0
*/
public function prepare_items() {
$this->_column_headers = array(
$this->get_columns(),
array(),
$this->get_sortable_columns(),
);
$this->items = $this->get_data();
}
/**
* Generates content for a single row of the table
*
* @since 3.0
*
* @param Order_Item|Order_Adjustment $item Order item object.
*/
public function single_row( $item ) {
$is_adjustment = $item instanceof Order_Adjustment;
$item_class = $is_adjustment ? $item->object_id : $item->order_id;
// Status.
$classes = array_map( 'sanitize_html_class', array(
'order-' . $item_class,
$item->status,
'refunditem',
) );
// Turn into a string.
$class = implode( ' ', $classes );
$item_id = $this->get_item_unique_id( $item );
$is_credit = $is_adjustment && 'credit' === $item->type;
?>
<tr id="order-item-<?php echo esc_attr( $item_id ); ?>" <?php echo esc_attr( $is_adjustment ? 'data-order-item-adjustment' : 'data-order-item' ); ?>="<?php echo esc_attr( $item->id ); ?>" <?php echo $is_credit ? 'data-credit="1"' : ''; ?> class="<?php echo esc_html( $class ); ?>">
<?php $this->single_row_columns( $item ); ?>
</tr>
<?php
}
/**
* Displays the table.
*
* @since 3.0
*/
public function display() {
$singular = $this->_args['singular'];
wp_nonce_field( 'edd_process_refund', 'edd_process_refund' );
$this->screen->render_screen_reader_content( 'heading_list' );
?>
<table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>">
<thead>
<tr>
<?php $this->print_column_headers(); ?>
</tr>
</thead>
<tbody id="the-list"<?php
if ( $singular ) {
echo " data-wp-lists='list:$singular'";
} ?>>
<?php $this->display_rows_or_placeholder(); ?>
</tbody>
</table>
<div class="edd-submit-refund-actions">
<?php
/**
* Triggers after the table, but before the submit button.
*
* @param Order $order
*
* @since 3.0
*/
do_action( 'edd_after_submit_refund_table', $this->get_order() );
$this->display_tablenav( 'bottom' );
?>
</div>
<?php
}
/**
* Adds custom submit button below the refund items table.
*
* @param string $which
* @since 3.0
*/
protected function display_tablenav( $which ) {
if ( 'bottom' !== $which ) {
return;
}
?>
<div class="tablenav bottom">
<button id="edd-submit-refund-submit" class="button button-primary" disabled><?php esc_html_e( 'Submit Refund', 'easy-digital-downloads' ); ?></button>
</div>
<?php
}
/**
* Displays the rows.
*
* This is overridden in order to add columns for the totals.
*
* @since 3.0
*/
public function display_rows() {
static $currency_symbol = null;
$order_id = false;
$currency_position = edd_get_option( 'currency_position', 'before' );
foreach ( $this->items as $item ) {
if ( empty( $order_id ) ) {
$order_id = $item->order_id;
}
$this->single_row( $item );
}
$currency_symbol = $this->get_currency_symbol( $order_id );
// Now we need to add the columns for the totals.
?>
<tr id="edd-refund-submit-subtotal" class="edd-refund-submit-line-total">
<td colspan="<?php echo esc_attr( $this->get_column_count() ); ?>">
<span class="row-title edd-refund-submit-line-total-name"><?php esc_html_e( 'Refund Subtotal:', 'easy-digital-downloads' ); ?></span>
<?php
$currency_symbol_output = sprintf( '<span>%s</span>', $currency_symbol );
$before = 'before' === $currency_position ? $currency_symbol_output : '';
$after = 'after' === $currency_position ? $currency_symbol_output : '';
$amount = edd_format_amount( 0.00, true, $this->get_order_currency_decimals( $order_id ) );
printf(
'<span class="edd-refund-submit-line-total-amount">%1$s<span id="edd-refund-submit-subtotal-amount">%2$s</span>%3$s</span>',
$before, // phpcs:ignore
esc_attr( $amount ),
$after // phpcs:ignore
);
?>
</td>
</tr>
<?php
$order = $this->get_order();
if ( $order && $order->get_tax_rate() ) :
?>
<tr id="edd-refund-submit-tax" class="edd-refund-submit-line-total">
<td colspan="<?php echo esc_attr( $this->get_column_count() ); ?>">
<span class="row-title edd-refund-submit-line-total-name"><?php esc_html_e( 'Refund Tax Total:', 'easy-digital-downloads' ); ?></span>
<?php
printf(
'<span class="edd-refund-submit-line-total-amount">%1$s<span id="edd-refund-submit-tax-amount">%2$s</span>%3$s</span>',
$before, // phpcs:ignore
esc_attr( $amount ),
$after // phpcs:ignore
);
?>
</td>
</tr>
<?php endif; ?>
<tr id="edd-refund-submit-total" class="edd-refund-submit-line-total">
<td colspan="<?php echo esc_attr( $this->get_column_count() ); ?>">
<span class="row-title edd-refund-submit-line-total-name"><?php esc_html_e( 'Refund Total:', 'easy-digital-downloads' ); ?></span>
<?php
printf(
'<span class="edd-refund-submit-line-total-amount">%1$s<span id="edd-refund-submit-total-amount">%2$s</span>%3$s</span>',
$before, // phpcs:ignore
esc_attr( $amount ),
$after // phpcs:ignore
);
?>
</td>
</tr>
<?php
}
/**
* Gets the order object.
*
* @since 3.0
* @return Order|false
*/
private function get_order() {
$order_id = ! empty( $_POST['order_id'] )
? absint( $_POST['order_id'] ) // phpcs:ignore
: 0;
return ! empty( $order_id ) ? edd_get_order( $order_id ) : false;
}
}

View File

@ -0,0 +1,148 @@
<?php
/**
* Contextual Help
*
* @package EDD
* @subpackage Admin/Payments
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.4
*/
use EDD\Admin\Pass_Manager;
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Payments contextual help.
*
* @access private
* @since 1.4
* @return void
*/
function edd_payments_contextual_help() {
$screen = get_current_screen();
// Only show on main "Orders" screen.
if ( 'download_page_edd-payment-history' !== $screen->id ) {
return;
}
// Do not show on Add or View Order/Refund.
if ( isset( $_GET['view'] ) ) {
return;
}
$pass_manager = new Pass_Manager();
if ( $pass_manager->isFree() ) {
$docs_url = edd_link_helper(
'https://easydigitaldownloads.com/docs/',
array(
'utm_medium' => 'orders-contextual-help',
'utm_content' => 'documentation',
)
);
$upgrade_url = edd_link_helper(
'https://easydigitaldownloads.com/lite-upgrade/',
array(
'utm_medium' => 'orders-contextual-help',
'utm_content' => 'lite-upgrade',
)
);
$screen->set_help_sidebar(
'<p><strong>' . __( 'For more information:', 'easy-digital-downloads' ) . '</strong></p>' .
'<p>' . sprintf( __( 'Visit the <a href="%s">documentation</a> on the Easy Digital Downloads website.', 'easy-digital-downloads' ), $docs_url ) . '</p>' .
'<p>' . sprintf(
__( 'Need more from your Easy Digital Downloads store? <a href="%s">Upgrade Now</a>!', 'easy-digital-downloads' ),
$upgrade_url
) . '</p>'
);
}
$screen->add_help_tab( array(
'id' => 'edd-payments-overview',
'title' => __( 'Overview', 'easy-digital-downloads' ),
'content' =>
'<p>' . __( 'This screen provides access to all of the orders and refunds in your store.', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( 'Orders can be searched by email address, user name, or filtered by status, mode, date range, gateway, and more!', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( 'To maintain accurate reporting and accounting, we strongly advise against deleting any completed order data.', 'easy-digital-downloads' ) . '</p>'
) );
$screen->add_help_tab( array(
'id' => 'edd-orders',
'title' => __( '&mdash; Orders', 'easy-digital-downloads' ),
'content' =>
'<p>' . __( 'Orders are placed by customers when they buy things from your store.', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( 'Every order contains a snapshot of your store at the time the order was placed, and is made up of many different pieces of information.', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( 'Things like products, discounts, taxes, fees, and customer email address, are all examples of information that is saved with each order.', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( 'Both full and partial refunds are supported.', 'easy-digital-downloads' ) . '</p>'
) );
$screen->add_help_tab( array(
'id' => 'edd-refunds',
'title' => __( '&mdash; Refunds', 'easy-digital-downloads' ),
'content' =>
'<p>' . __( 'Refunds are created when a customer would like money back from a completed order.', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( 'Every refund refers back to the original order, and only contains the items and adjustments that were refunded.', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( 'Refunds could be entire orders, or single products.', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( 'Once an item is refunded, it cannot be undone; it can only be repurchased.', 'easy-digital-downloads' ) . '</p>'
) );
$screen->add_help_tab( array(
'id' => 'edd-payments-search',
'title' => __( 'Search', 'easy-digital-downloads' ),
'content' =>
'<p>' . __( 'The order history can be searched in several different ways.', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( 'You can enter:', 'easy-digital-downloads' ) . '</p>' .
'<ul>
<li>' . __( 'The order ID', 'easy-digital-downloads' ) . '</li>
<li>' . __( 'The 32-character order key', 'easy-digital-downloads' ) . '</li>
<li>' . __( 'The customer\'s email address', 'easy-digital-downloads' ) . '</li>
<li>' . sprintf(
__( 'The customer\'s name or ID prefixed by %s', 'easy-digital-downloads' ),
'<code>customer:</code>'
) . '</li>
<li>' . sprintf(
__( 'A user\'s ID prefixed by %s', 'easy-digital-downloads' ),
'<code>user:</code>'
) . '</li>
<li>' . sprintf(
__( 'The %s ID prefixed by %s', 'easy-digital-downloads' ),
edd_get_label_singular(),
'<code>#</code>'
) . '</li>
<li>' . sprintf(
__( 'The Discount Code prefixed by %s', 'easy-digital-downloads' ),
'<code>discount:</code>'
) . '</li>
<li>' . sprintf(
__( 'A transaction ID prefixed by %s', 'easy-digital-downloads' ),
'<code>txn:</code>'
) . '</li>
</ul>',
) );
$screen->add_help_tab( array(
'id' => 'edd-payments-details',
'title' => __( 'Details', 'easy-digital-downloads' ),
'content' =>
'<p>' . __( 'Each order can be further inspected by clicking the corresponding <em>View Order Details</em> link. This will provide more information including:', 'easy-digital-downloads' ) . '</p>' .
'<ul>
<li><strong>Purchased File</strong> - ' . __( 'The file associated with the purchase.', 'easy-digital-downloads' ) . '</li>
<li><strong>Purchase Date</strong> - ' . __( 'The exact date and time the order was completed.', 'easy-digital-downloads' ) . '</li>
<li><strong>Discount Used</strong> - ' . __( 'If a coupon or discount was used during the checkout process.', 'easy-digital-downloads' ) . '</li>
<li><strong>Name</strong> - ' . __( "The buyer's name.", 'easy-digital-downloads' ) . '</li>
<li><strong>Email</strong> - ' . __( "The buyer's email address.", 'easy-digital-downloads' ) . '</li>
<li><strong>Payment Notes</strong> - ' . __( 'Any customer-specific notes related to the order.', 'easy-digital-downloads' ) . '</li>
<li><strong>Payment Method</strong> - ' . __( 'The name of the order gateway used to complete the order.', 'easy-digital-downloads' ) . '</li>
<li><strong>Purchase Key</strong> - ' . __( 'A unique key used to identify the order.', 'easy-digital-downloads' ) . '</li>
</ul>'
) );
do_action( 'edd_payments_contextual_help', $screen );
}
add_action( 'load-download_page_edd-payment-history', 'edd_payments_contextual_help' );

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,237 @@
<?php
/**
* Functions to render Orders page.
*
* @package EDD
* @subpackage Admin/Payments
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/** Navigation ****************************************************************/
/**
* Output the primary orders page navigation
*
* @since 3.0
* @param string $active_tab
*/
function edd_orders_page_primary_nav( $active_tab = '' ) {
ob_start();?>
<nav class="nav-tab-wrapper edd-nav-tab-wrapper" aria-label="<?php esc_attr_e( 'Secondary menu', 'easy-digital-downloads' ); ?>">
<?php
// Get the order pages
$tabs = edd_get_order_pages();
// Loop through order pages and create tabs
foreach ( $tabs as $tab_id => $tab_name ) {
// Remove
$tab_url = add_query_arg( array(
'settings-updated' => false,
'order_type' => sanitize_key( $tab_id )
) );
// Remove the section from the tabs so we always end up at the main section
$tab_url = remove_query_arg( array( 'section', 'status' ), $tab_url );
$active = $active_tab === $tab_id
? ' nav-tab-active'
: '';
// Link
echo '<a href="' . esc_url( $tab_url ) . '" class="nav-tab' . $active . '">'; // WPCS: XSS ok.
echo esc_html( $tab_name );
echo '</a>';
}
?>
</nav>
<?php
echo ob_get_clean(); // WPCS: XSS ok.
}
/**
* Retrieve the order pages.
*
* Used only by the primary tab navigation for orders.
*
* @since 3.0
*
* @return array
*/
function edd_get_order_pages() {
// Get types and setup return value
$types = edd_get_order_types();
$retval = array();
// Loop through and get type IDs and labels
foreach ( $types as $type_id => $type ) {
// Skip if hidden
if ( empty( $type['show_ui'] ) ) {
continue;
}
// Add to return array
$retval[ $type_id ] = ! empty( $type['labels']['plural'] )
? $type['labels']['plural']
: ucwords( $type_id );
}
// Filter & return
return (array) apply_filters( 'edd_get_order_pages', $retval );
}
/**
* Get the payment view
*
* @since 3.0
*
* @return string
*/
function edd_get_payment_view() {
return ! empty( $_GET['view'] ) // WPCS: CSRF ok.
? sanitize_key( $_GET['view'] ) // WPCS: CSRF ok.
: 'list';
}
/**
* Render one of the Order pages.
*
* @since 1.0
* @since 3.0 Nomenclature updated for consistency.
* Add a link to manually add orders.
* Changed to switch statement.
*/
function edd_payment_history_page() {
// What are we viewing?
switch ( edd_get_payment_view() ) {
// View Order
case 'view-order-details' :
require_once EDD_PLUGIN_DIR . 'includes/admin/payments/view-order-details.php';
break;
// Add Order
case 'add-order' :
require_once EDD_PLUGIN_DIR . 'includes/admin/payments/add-order.php';
edd_add_order_page_content();
break;
// View Refund
case 'view-refund-details' :
require_once EDD_PLUGIN_DIR . 'includes/admin/payments/view-refund.php';
edd_view_refund_page_content();
break;
// List Table
case 'list' :
default :
edd_order_list_table_content();
break;
}
}
/**
* Output the list table used to list out all orders.
*
* @since 3.0
*/
function edd_order_list_table_content() {
require_once EDD_PLUGIN_DIR . 'includes/admin/payments/class-payments-table.php';
$orders_table = new EDD_Payment_History_Table();
$orders_table->prepare_items();
$active_tab = sanitize_key( $orders_table->get_request_var( 'order_type', 'sale' ) );
$admin_url = edd_get_admin_url( array( 'page' => 'edd-payment-history' ) ); ?>
<div class="wrap">
<h1 class="wp-heading-inline"><?php esc_html_e( 'Orders', 'easy-digital-downloads' ); ?></h1>
<?php
if ( 'sale' === $active_tab ) {
$add_new_url = add_query_arg( array( 'view' => 'add-order' ), $admin_url );
printf(
'<a href="%s" class="page-title-action">%s</a>',
esc_url( $add_new_url ),
esc_html__( 'Add New', 'easy-digital-downloads' )
);
}
?>
<hr class="wp-header-end">
<?php edd_orders_page_primary_nav( $active_tab ); ?>
<?php do_action( 'edd_payments_page_top' ); ?>
<form id="edd-payments-filter" method="get" action="<?php echo esc_url( $admin_url ); ?>">
<input type="hidden" name="post_type" value="download" />
<input type="hidden" name="page" value="edd-payment-history" />
<input type="hidden" name="order_type" value="<?php echo esc_attr( $active_tab ); ?>" />
<?php
$orders_table->views();
$orders_table->advanced_filters();
$orders_table->display();
?>
</form>
<?php do_action( 'edd_payments_page_bottom' ); ?>
</div>
<?php
}
/**
* Orders admin titles.
*
* @since 1.6
* @since 3.0 Updated to use new nomenclature.
*
* @param string $admin_title
* @param string $title
*
* @return string Updated admin title.
*/
function edd_view_order_details_title( $admin_title, $title ) {
// Bail if we aren't on the Orders page.
if ( 'download_page_edd-payment-history' !== get_current_screen()->base ) {
return $admin_title;
}
// Get the view
$view = edd_get_payment_view();
// Which view?
switch ( $view ) {
// Edit/View
case 'view-order-details':
case 'edit-payment':
$title = __( 'Edit Order', 'easy-digital-downloads' ) . ' &mdash; ' . $admin_title;
break;
// Add
case 'add-order':
$title = __( 'Add New Order', 'easy-digital-downloads' ) . ' &mdash; ' . $admin_title;
break;
// List
case 'list' :
default:
$title = $admin_title;
break;
}
return $title;
}
add_filter( 'admin_title', 'edd_view_order_details_title', 10, 2 );

View File

@ -0,0 +1,368 @@
<?php
/**
* Refund Details Sections
*
* @package EDD
* @subpackage Admin/Orders
* @copyright Copyright (c) 2021, Sandhills Development, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 3.0
*/
use EDD\Orders\Order;
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/** Header ******************************************************************/
/**
* Outputs Refund header region/notice of Refund vs. Order.
*
* @since 3.0
*
* @param Order $refund Current Refund.
*/
function edd_refund_details_notice( $refund ) {
$order_url = edd_get_admin_url(
array(
'id' => absint( $refund->parent ),
'page' => 'edd-payment-history',
'view' => 'view-order-details',
)
);
?>
<div class="edit-post-editor-regions__header">
<div class="edit-post-header notice-warning" style="border-left-width: 4px; border-left-style: solid;">
<div class="edit-post-header__settings">
<a href="<?php echo esc_url( $order_url ); ?>" class="button button-secondary" autofocus>
<?php esc_html_e( 'View Original Order', 'easy-digital-downloads' ); ?>
</a>
</div>
<div class="edit-post-header__toolbar">
<span alt="f223" class="edd-help-tip dashicons dashicons-editor-help" title="<?php _e( 'A refund is a read-only record to help balance your store&#39;s books.', 'easy-digital-downloads' ); ?>"></span>&nbsp;
<?php esc_html_e( 'You are viewing a refund record.', 'easy-digital-downloads' ); ?>
</div>
</div>
</div>
<?php
}
/** Main **********************************************************************/
/**
* Outputs the Refund Items box.
*
* @since 3.0
*
* @param Order $refund Current Refund.
*/
function edd_refund_details_items( $refund ) {
$_items = array();
$_adjustments = array();
$items = edd_get_order_items( array(
'order_id' => $refund->id,
'number' => 999,
) );
foreach ( $items as $item ) {
$item_adjustments = array();
$adjustments = edd_get_order_adjustments( array(
'object_id' => $item->id,
'number' => 999,
'object_type' => 'order_item',
'type' => array(
'discount',
'credit',
'fee',
),
) );
foreach ( $adjustments as $adjustment ) {
// @todo edd_get_order_adjustment_to_json()?
$adjustment_args = array(
'id' => esc_html( $adjustment->id ),
'objectId' => esc_html( $adjustment->object_id ),
'objectType' => esc_html( $adjustment->object_type ),
'typeId' => esc_html( $adjustment->type_id ),
'type' => esc_html( $adjustment->type ),
'description' => esc_html( $adjustment->description ),
'subtotal' => esc_html( $adjustment->subtotal ),
'tax' => esc_html( $adjustment->tax ),
'total' => esc_html( $adjustment->total ),
'dateCreated' => esc_html( $adjustment->date_created ),
'dateModified' => esc_html( $adjustment->date_modified ),
'uuid' => esc_html( $adjustment->uuid ),
);
$item_adjustments[] = $adjustment_args;
$_adjustments[] = $adjustment_args;
}
// @todo edd_get_order_item_to_json()?
$_items[] = array(
'id' => esc_html( $item->id ),
'orderId' => esc_html( $item->order_id ),
'productId' => esc_html( $item->product_id ),
'productName' => esc_html( $item->get_order_item_name() ),
'priceId' => esc_html( $item->price_id ),
'cartIndex' => esc_html( $item->cart_index ),
'type' => esc_html( $item->type ),
'status' => esc_html( $item->status ),
'quantity' => esc_html( $item->quantity ),
'amount' => esc_html( $item->amount ),
'subtotal' => esc_html( $item->subtotal ),
'discount' => esc_html( $item->discount ),
'tax' => esc_html( $item->tax ),
'total' => esc_html( $item->total ),
'dateCreated' => esc_html( $item->date_created ),
'dateModified' => esc_html( $item->date_modified ),
'uuid' => esc_html( $item->uuid ),
);
}
$adjustments = edd_get_order_adjustments( array(
'object_id' => $refund->id,
'number' => 999,
'object_type' => 'order',
'type' => array(
'discount',
'credit',
'fee',
),
) );
foreach ( $adjustments as $adjustment ) {
// @todo edd_get_order_adjustment_to_json()?
$_adjustments[] = array(
'id' => esc_html( $adjustment->id ),
'objectId' => esc_html( $adjustment->object_id ),
'objectType' => esc_html( $adjustment->object_type ),
'typeId' => esc_html( $adjustment->type_id ),
'type' => esc_html( $adjustment->type ),
'description' => esc_html( $adjustment->description ),
'subtotal' => esc_html( $adjustment->subtotal ),
'tax' => esc_html( $adjustment->tax ),
'total' => esc_html( $adjustment->total ),
'dateCreated' => esc_html( $adjustment->date_created ),
'dateModified' => esc_html( $adjustment->date_modified ),
'uuid' => esc_html( $adjustment->uuid ),
);
}
$has_tax = 'none';
$tax_rate = $refund->id ? $refund->get_tax_rate() : false;
$location = array(
'rate' => $tax_rate,
'country' => '',
'region' => '',
);
if ( $tax_rate ) {
$has_tax = $location;
$has_tax['rate'] = $tax_rate;
if ( $refund->tax_rate_id ) {
$tax_rate_object = $refund->get_tax_rate_object();
if ( $tax_rate_object ) {
$has_tax['country'] = $tax_rate_object->name;
$has_tax['region'] = $tax_rate_object->description;
}
}
}
wp_localize_script(
'edd-admin-orders',
'eddAdminOrderOverview',
array(
'items' => $_items,
'adjustments' => $_adjustments,
'refunds' => array(),
'isAdding' => false,
'isRefund' => true,
'hasQuantity' => true,
'hasTax' => $has_tax,
'order' => array(
'currency' => $refund->currency,
'currencySymbol' => html_entity_decode( edd_currency_symbol( $refund->currency ) ),
'subtotal' => $refund->subtotal,
'discount' => $refund->discount,
'tax' => $refund->tax,
'total' => $refund->total,
),
)
);
$templates = array(
'no-items',
'subtotal',
'tax',
'total',
'item',
'adjustment',
'adjustment-discount',
);
foreach ( $templates as $tmpl ) {
echo '<script type="text/html" id="tmpl-edd-admin-order-' . esc_attr( $tmpl ) . '">';
require_once EDD_PLUGIN_DIR . 'includes/admin/views/tmpl-order-' . $tmpl . '.php';
echo '</script>';
}
?>
<div id="edd-order-overview" class="postbox edd-edit-purchase-element edd-order-overview">
<table id="edd-order-overview-summary" class="widefat wp-list-table edd-order-overview-summary edd-order-overview-summary--refund">
<thead>
<tr>
<th class="column-name column-primary"><?php echo esc_html( edd_get_label_singular() ); ?></th>
<th class="column-amount"><?php esc_html_e( 'Unit Price', 'easy-digital-downloads' ); ?></th>
<th class="column-quantity"><?php esc_html_e( 'Quantity', 'easy-digital-downloads' ); ?></th>
<th class="column-subtotal column-right"><?php esc_html_e( 'Amount', 'easy-digital-downloads' ); ?></th>
</tr>
</thead>
</table>
</div>
<?php
}
/**
* Outputs Refund Notes box.
*
* @since 3.0
*
* @param Order $refund Current Refund.
*/
function edd_refund_details_notes( $refund ) {
?>
<div id="edd-order-items" class="postbox edd-edit-purchase-element">
<h2 class="hndle">
<?php esc_html_e( 'Refund Notes', 'easy-digital-downloads' ); ?>
</h2>
<div class="inside">
<?php edd_order_details_notes( $refund ); ?>
</div>
</div>
<?php
}
/**
* Outputs "Refund Details" box.
*
* @since 3.0
*
* @param Order $refund Current Refund.
*/
function edd_refund_details_attributes( $refund ) {
$refund_date = edd_get_edd_timezone_equivalent_date_from_utc( EDD()->utils->date( $refund->date_created, 'utc', true ) );
$trash_url = wp_nonce_url(
edd_get_admin_url( array(
'edd-action' => 'trash_order',
'purchase_id' => absint( $refund->id ),
'order_type' => 'refund',
'page' => 'edd-payment-history',
) ),
'edd_payment_nonce'
);
$order_url = edd_get_admin_url(
array(
'id' => absint( $refund->parent ),
'page' => 'edd-payment-history',
'view' => 'view-order-details',
)
);
$order = edd_get_order( $refund->parent );
?>
<div class="postbox">
<h2 class="hndle">
<?php esc_html_e( 'Refund Attributes', 'easy-digital-downloads' ); ?>
</h2>
<div class="edd-admin-box-inside">
<time datetime="<?php echo esc_attr( EDD()->utils->date( $refund->date_created, null, true )->toDateTimeString() ); ?>" style="line-height: normal">
<?php echo edd_date_i18n( $refund->date_created, 'M. d, Y' ) . '<br />' . edd_date_i18n( strtotime( $refund->date_created ), 'H:i' ); ?> <?php echo esc_html( edd_get_timezone_abbr() ); ?>
</time>
<br /><br />
<a href="<?php echo esc_url( $trash_url ); ?>" class="edd-delete-payment edd-delete">
<?php esc_html_e( 'Move to Trash', 'easy-digital-downloads' ); ?>
</a>
</div>
<div class="edd-admin-box-inside edd-admin-box-inside--row">
<div class="edd-form-group">
<span class="edd-form-group__label">
<?php esc_html_e( 'Original Order', 'easy-digital-downloads' ); ?>
</span>
<div class="edd-form-group__control">
<a href="<?php echo esc_url( $order_url ); ?>"><?php echo esc_html( $order->number ); ?></a>
</div>
</div>
</div>
</div>
<?php
}
/**
* Outputs related Refunds.
*
* @since 3.0
*
* @param Order $refund
*/
function edd_refund_details_related_refunds( $refund ) {
$refunds = array_filter(
edd_get_order_refunds( $refund->parent ),
function( $related_refund ) use ( $refund ) {
return $related_refund->id !== $refund->id;
}
);
if ( empty( $refunds ) ) {
return;
}
?>
<div class="postbox edd-order-data">
<h2 class="hndle">
<?php esc_html_e( 'Related Refunds', 'easy-digital-downloads' ); ?>
</h2>
<?php
foreach( $refunds as $refund ) :
$refund_url = edd_get_admin_url( array(
'page' => 'edd-payment-history',
'view' => 'view-refund-details',
'id' => absint( $refund->id ),
) );
?>
<div class="edd-admin-box-inside">
<div class="edd-form-group">
<a href="<?php echo esc_url( $refund_url ); ?>" class="edd-form-group__label">
<?php echo esc_html( $refund->number ); ?>
</a>
<div class="edd-form-group__control">
<time datetime="<?php echo esc_attr( EDD()->utils->date( $refund->date_created, null, true )->toDateTimeString() ); ?>" style="line-height: normal">
<?php echo edd_date_i18n( $refund->date_created, 'M. d, Y' ) . '<br />' . edd_date_i18n( strtotime( $refund->date_created ), 'H:i' ); ?> <?php echo esc_html( edd_get_timezone_abbr() ); ?>
</time>
</div>
</div>
<?php endforeach; ?>
</div>
<?php
}

View File

@ -0,0 +1,139 @@
<?php
/**
* View Order Details
*
* @package EDD
* @subpackage Admin/Payments
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.6
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* View Order Details Page
*
* @since 1.6
* @since 3.0 Updated to use the new EDD\Orders\Order object.
*/
if ( ! isset( $_GET['id'] ) || ! is_numeric( $_GET['id'] ) ) {
wp_die( __( 'Order ID not supplied. Please try again', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ) );
}
$order_id = absint( $_GET['id'] );
$order = edd_get_order( $order_id );
// Check that the order exists in the database.
if ( empty( $order ) ) {
wp_die( __( 'The specified ID does not belong to an order. Please try again', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ) );
}
if ( 'refund' === $order->type ) {
$refund_link = edd_get_admin_url(
array(
'page' => 'edd-payment-history',
'view' => 'view-refund-details',
'id' => urlencode( $order->id ),
)
);
wp_die( sprintf( __( 'The specified ID is for a refund, not an order. Please <a href="%s">access the refund directly</a>.', 'easy-digital-downloads' ), esc_url( $refund_link ) ), __( 'Error', 'easy-digital-downloads' ) );
}
wp_enqueue_script( 'edd-admin-orders' );
// Enqueued for backwards compatibility. Empty file.
wp_enqueue_script( 'edd-admin-payments' );
?>
<form id="edd-edit-order-form" method="post">
<?php edd_order_details_publish( $order ); ?>
<div class="wrap edd-wrap edd-clearfix">
<h1><?php printf( esc_html__( 'Order: %s', 'easy-digital-downloads' ), esc_html( $order->number ) ); ?></h1>
<hr class="wp-header-end">
<div class="notice notice-error inline" id="edd-add-order-customer-error" style="display: none;">
<p><strong><?php esc_html_e( 'Error', 'easy-digital-downloads' ); ?>:</strong> <?php esc_html_e( 'Please select an existing customer or create a new customer.', 'easy-digital-downloads' ); ?></p>
</div>
<?php do_action( 'edd_view_order_details_before', $order->id ); ?>
<?php do_action( 'edd_view_order_details_form_top', $order->id ); ?>
<div id="poststuff">
<div id="edd-dashboard-widgets-wrap">
<div id="post-body" class="metabox-holder columns-2">
<div id="postbox-container-2" class="postbox-container">
<div id="normal-sortables">
<?php
// Before body
do_action( 'edd_view_order_details_main_before', $order->id );
// Overview
edd_order_details_overview( $order );
// Details sections
edd_order_details_sections( $order );
// Legacy hook from pre version 3 of Easy Digital Downloads.
do_action( 'edd_view_order_details_billing_after', $order->id );
// After body
do_action( 'edd_view_order_details_main_after', $order->id );
?>
</div>
</div>
<div id="postbox-container-1" class="postbox-container">
<div id="side-sortables">
<?php
// Before sidebar
do_action( 'edd_view_order_details_sidebar_before', $order->id );
// Attributes
edd_order_details_attributes( $order );
// Extras
edd_order_details_extras( $order );
// After sidebar
do_action( 'edd_view_order_details_sidebar_after', $order->id );
?>
</div>
</div>
</div>
</div>
</div>
<?php
do_action( 'edd_view_order_details_form_bottom', $order->id );
wp_nonce_field( 'edd_update_payment_details_nonce' ); ?>
<input type="hidden" name="edd_payment_id" value="<?php echo esc_attr( $order->id ); ?>"/>
<input type="hidden" name="edd_action" value="update_payment_details"/>
<?php do_action( 'edd_view_order_details_after', $order->id ); ?>
</div><!-- /.wrap -->
</form>
<div id="edd-refund-order-dialog" title="<?php esc_html_e( 'Submit Refund', 'easy-digital-downloads' ); ?>"></div>
<div
id="edd-admin-order-copy-download-link-dialog"
title="<?php printf( esc_html__( 'Copy %s Links', 'easy-digital-downloads' ), esc_html( edd_get_label_singular() ) ); ?>"
style="display: none;"
>
<div id="edd-admin-order-copy-download-link-dialog-content"></div>
</div>

View File

@ -0,0 +1,128 @@
<?php
/**
* View Refund page.
*
* @package EDD
* @subpackage Admin/Payments
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 3.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Outputs the View Refund page.
*
* @since 3.0
*/
function edd_view_refund_page_content() {
// @todo Avoid killing page ouput.
if ( ! isset( $_GET['id'] ) || ! is_numeric( $_GET['id'] ) ) {
wp_die( __( 'Refund ID not supplied. Please try again.', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ) );
}
$refund_id = absint( $_GET['id'] );
$refund = edd_get_order( $refund_id );
// Check that the refund exists in the database.
// @todo Avoid killing page ouput.
if ( empty( $refund ) || 'refund' !== $refund->type ) {
wp_die( __( 'The specified ID does not belong to an refund. Please try again.', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ) );
}
wp_enqueue_script( 'edd-admin-orders' );
// Enqueued for backwards compatibility. Empty file.
wp_enqueue_script( 'edd-admin-payments' );
?>
<?php edd_refund_details_notice( $refund ); ?>
<div class="wrap edd-wrap">
<h1><?php printf( esc_html__( 'Refund: %s', 'easy-digital-downloads' ), $refund->order_number ); ?></h1>
<?php
/**
* Allows output before Refund page content.
*
* @since 3.0
*
* @param int $refund_id ID of the current Refund.
*/
do_action( 'edd_view_refund_details_before', $refund->id );
?>
<div id="poststuff">
<div id="edd-dashboard-widgets-wrap">
<div id="post-body" class="metabox-holder columns-2">
<div id="postbox-container-2" class="postbox-container">
<div id="normal-sortables">
<?php
/**
* Allows output before the Refund details.
*
* @since 3.0
*
* @param int $refund_id ID of the current Refund.
*/
do_action( 'edd_view_refund_details_main_before', $refund->id );
// Refund Items.
edd_refund_details_items( $refund );
// Notes.
edd_refund_details_notes( $refund );
/**
* Allows further output after the Refund details.
*
* @since 3.0
*
* @param int $refund_id ID of the current Refund.
*/
do_action( 'edd_view_refund_details_main_after', $refund->id );
?>
</div>
</div>
<div id="postbox-container-1" class="postbox-container">
<div id="side-sortables">
<?php
/**
* Allows output before Refund sidebar content.
*
* @since 3.0
*
* @param int $refund_id ID of the current Refund.
*/
do_action( 'edd_view_refund_details_sidebar_before', $refund->id );
// Attributes.
edd_refund_details_attributes( $refund );
// Related Refunds.
edd_refund_details_related_refunds( $refund );
/**
* Allows further output after Refund sidebar content.
*
* @since 3.0
*
* @param int $refund_id ID of the current Refund.
*/
do_action( 'edd_view_refund_details_sidebar_after', $refund->id );
?>
</div>
</div>
</div>
</div>
</div>
</div><!-- /.wrap -->
<?php
}

View File

@ -0,0 +1,61 @@
<?php
/**
* Admin Plugins
*
* @package EDD
* @subpackage Admin/Plugins
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.8
*/
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;
/**
* Plugins row action links
*
* @since 1.8
* @since 3.1 Targeted just our plugin.
*
* @param array $links Already defined action links.
*
* @return array $links
*/
function edd_plugin_action_links( $links = array() ) {
$edd_links = array();
$pass_manager = new EDD\Admin\Pass_Manager();
if ( ! $pass_manager->has_pass() ) {
$url = edd_link_helper(
'https://easydigitaldownloads.com/lite-upgrade/',
array(
'utm_medium' => 'all-plugins',
'utm_content' => 'upgrade-to-pro',
)
);
$edd_links['edd-pro-upgrade'] = sprintf( '<a href="%s" target="_blank">' . __( 'Upgrade to Pro', 'easy-digital-downloads' ) . '</a>', $url );
}
$settings_url = edd_get_admin_url(
array(
'page' => 'edd-settings',
)
);
$edd_links['settings'] = '<a href="' . esc_url( $settings_url ) . '">' . esc_html__( 'Settings', 'easy-digital-downloads' ) . '</a>';
// Return array of links.
return array_merge( $edd_links, $links );
}
add_filter( 'plugin_action_links_easy-digital-downloads/easy-digital-downloads.php', 'edd_plugin_action_links', 10, 2 );
/**
* Load any CSS we need for the plugins list table.
*/
function edd_plugin_list_styles() {
echo '<style>.edd-pro-upgrade a, .edd-pro-upgrade a:hover{color: #1da867;font-weight: 600;}</style>';
}
add_action( 'admin_print_styles-plugins.php', 'edd_plugin_list_styles' );

View File

@ -0,0 +1,172 @@
<?php
/**
* Promo Handler
*
* Handles logic for displaying and dismissing promotional notices.
*
* @package easy-digital-downloads
* @copyright Copyright (c) 2021, Sandhills Development, LLC
* @license GPL2+
* @since 2.10.6
*/
namespace EDD\Admin\Promos;
use EDD\Admin\Promos\Notices\Notice;
use Sandhills\Utils\Persistent_Dismissible;
class PromoHandler {
/**
* Registered notices.
*
* @var string[]
*/
private $notices = array(
'\\EDD\\Admin\\Promos\\Notices\\License_Upgrade_Notice',
'\\EDD\\Admin\\Promos\\Notices\\Five_Star_Review_Dashboard',
'\\EDD\\Admin\\Promos\\Notices\\Five_Star_Review_Settings',
);
/**
* Notices constructor.
*/
public function __construct() {
add_action( 'wp_ajax_edd_dismiss_promo_notice', array( $this, 'dismiss_notice' ) );
$this->load_notices();
}
/**
* Loads and displays all registered promotional notices.
*
* @since 2.10.6
*/
private function load_notices() {
require_once EDD_PLUGIN_DIR . 'includes/admin/promos/notices/abstract-notice.php';
foreach ( $this->notices as $notice_class_name ) {
if ( ! class_exists( $notice_class_name ) ) {
$file_name = strtolower( str_replace( '_', '-', basename( str_replace( '\\', '/', $notice_class_name ) ) ) );
$file_path = EDD_PLUGIN_DIR . 'includes/admin/promos/notices/class-' . $file_name . '.php';
if ( file_exists( $file_path ) ) {
require_once $file_path;
}
}
if ( ! class_exists( $notice_class_name ) ) {
continue;
}
add_action( $notice_class_name::DISPLAY_HOOK, function () use ( $notice_class_name ) {
/** @var Notice $notice */
$notice = new $notice_class_name();
if ( $notice->should_display() ) {
$notice->display();
}
}, $notice_class_name::DISPLAY_PRIORITY );
}
}
/**
* Determines whether or not a notice has been dismissed.
*
* @since 2.10.6
*
* @param string $id ID of the notice to check.
*
* @return bool
*/
public static function is_dismissed( $id ) {
$is_dismissed = (bool) Persistent_Dismissible::get( array(
'id' => 'edd-' . $id
) );
return true === $is_dismissed;
}
/**
* Dismisses a notice.
*
* @since 2.10.6
*
* @param string $id ID of the notice to dismiss.
* @param int $dismissal_length Number of seconds to dismiss the notice for, or `0` for forever.
*/
public static function dismiss( $id, $dismissal_length = 0 ) {
Persistent_Dismissible::set( array(
'id' => 'edd-' . $id,
'life' => $dismissal_length
) );
}
/**
* AJAX callback for dismissing a notice.
*
* @since 2.10.6
*/
public function dismiss_notice() {
$notice_id = ! empty( $_POST['notice_id'] ) ? sanitize_text_field( $_POST['notice_id'] ) : false;
if ( empty( $notice_id ) ) {
wp_send_json_error( __( 'Missing notice ID.', 'easy-digital-downloads' ), 400 );
}
if ( empty( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'edd-dismiss-notice-' . sanitize_key( $_POST['notice_id'] ) ) ) {
wp_send_json_error( __( 'You do not have permission to perform this action.', 'easy-digital-downloads' ), 403 );
}
$notice_class_name = $this->get_notice_class_name( $notice_id );
// No matching notice class was found.
if ( ! $notice_class_name ) {
wp_send_json_error( __( 'You do not have permission to perform this action.', 'easy-digital-downloads' ), 403 );
}
// Check whether the current user can dismiss the notice.
if ( ! defined( $notice_class_name . '::CAPABILITY' ) || ! current_user_can( $notice_class_name::CAPABILITY ) ) {
wp_send_json_error( __( 'You do not have permission to perform this action.', 'easy-digital-downloads' ), 403 );
}
$dismissal_length = ! empty( $_POST['lifespan'] ) ? absint( $_POST['lifespan'] ) : 0;
self::dismiss( sanitize_key( $_POST['notice_id'] ), $dismissal_length );
wp_send_json_success();
}
/**
* Gets the notice class name for a given notice ID.
*
* @since 2.11.4
* @param string $notice_id The notice ID to match.
* @return bool|string The class name or false if no matching class was found.
*/
private function get_notice_class_name( $notice_id ) {
$notice_class_name = false;
// Look through the registered notice classes for the one being dismissed.
foreach ( $this->notices as $notice_class_to_check ) {
if ( ! class_exists( $notice_class_to_check ) ) {
$file_name = strtolower( str_replace( '_', '-', basename( str_replace( '\\', '/', $notice_class_to_check ) ) ) );
$file_path = EDD_PLUGIN_DIR . 'includes/admin/promos/notices/class-' . $file_name . '.php';
if ( file_exists( $file_path ) ) {
require_once $file_path;
}
}
if ( ! class_exists( $notice_class_to_check ) ) {
continue;
}
$notice = new $notice_class_to_check();
if ( $notice->get_id() === $notice_id ) {
$notice_class_name = $notice_class_to_check;
break;
}
}
return $notice_class_name;
}
}
new PromoHandler();

View File

@ -0,0 +1,54 @@
{
"blurbs":[
{
"headline":"Are new features and products getting ignored?",
"content":"Smart marketers know that emails with high click-through rates that promote new products and features will lead to more sales. Connect your Easy Digital Downloads powered store to your favorite email marketing service to boost revenue.",
"button_text":"Read More",
"button_link":"https://easydigitaldownloads.com/downloads/category/extensions/email/?utm_source=plugin&utm_medium=email&utm_campaign=summaries",
"conditions":{
"current_pass":"free"
}
},
{
"headline":"Are you missing out on future revenue?",
"content":"On average, up to 60% of customers do not manually renew their license keys. That's lost revenue for your business. Enable subscriptions on your licensed products and keep the revenue flowing!",
"button_text":"Read More",
"button_link":"https://easydigitaldownloads.com/downloads/recurring-payments/?utm_source=plugin&utm_medium=email&utm_campaign=summaries",
"conditions":{
"active_plugins":[
"EDD-Software-Licensing/edd-software-licenses.php"
],
"inactive_plugins":[
"edd-recurring/edd-recurring.php"
],
"current_pass":"pass-any"
}
},
{
"headline":"Are you giving things away for free?",
"content":"Lead magnets are the easiest way to capture potential customers. Use our Free Downloads add-on, and start capturing those leads today.",
"button_text":"Read More",
"button_link":"https://easydigitaldownloads.com/blog/how-to-add-lead-magnets-in-wordpress-to-grow-your-email-list/?utm_source=plugin&utm_medium=email&utm_campaign=summaries",
"conditions":{
"has_downloads":[
"free"
],
"inactive_plugins":[
"edd-free-downloads/edd-free-downloads.php"
]
}
},
{
"headline":"Who are your biggest fans?",
"content":"Did you know that 72% of customers say that a product review is the key to making their purchasing decision? Learn how to add reviews to your store, and let your happy customers be your brand ambassadors!",
"button_text":"Read More",
"button_link":"https://easydigitaldownloads.com/blog/how-to-add-product-reviews-to-your-website/?utm_source=plugin&utm_medium=email&utm_campaign=summaries",
"conditions":{
"inactive_plugins":[
"edd-reviews/edd-reviews.php"
],
"current_pass":"pass-any"
}
}
]
}

View File

@ -0,0 +1,125 @@
<?php
/**
* Notice
*
* @package easy-digital-downloads
* @copyright Copyright (c) 2021, Sandhills Development, LLC
* @license GPL2+
* @since 2.10.6
*/
namespace EDD\Admin\Promos\Notices;
use EDD\Admin\Promos\PromoHandler;
abstract class Notice {
/**
* Action hook for displaying the notice.
*/
const DISPLAY_HOOK = 'admin_notices';
/**
* The priority for the display hook.
*/
const DISPLAY_PRIORITY = 10;
/**
* Type of promotional notice.
*/
const TYPE = 'top-of-page';
/**
* Whether or not the notice can be dismissed.
*/
const DISMISSIBLE = true;
/**
* The capability required to view/dismiss the notice.
*/
const CAPABILITY = 'manage_options';
/**
* Displays the notice content.
*
* @return void
*/
abstract protected function _display();
/**
* Generates a unique ID for this notice.
* It's the class name (without the namespace) and with underscores converted to hyphens.
*
* @since 2.10.6
*
* @return string
*/
public function get_id() {
return strtolower( str_replace( '_', '-', basename( str_replace( '\\', '/', get_class( $this ) ) ) ) );
}
/**
* Determines whether or not the notice should be displayed.
* Typically individual notices should not override this method, as it combines
* a dismissal check and custom display logic (`_should_display()`). Custom logic
* should go in `_should_display()`.
*
* @since 2.10.6
*
* @return bool
*/
public function should_display() {
return current_user_can( static::CAPABILITY ) && ! PromoHandler::is_dismissed( $this->get_id() ) && $this->_should_display();
}
/**
* Duration (in seconds) that the notice is dismissed for.
* `0` means it's dismissed permanently.
*
* @return int
*/
public static function dismiss_duration() {
return 0;
}
/**
* Individual notices can override this method to control display logic.
*
* @since 2.10.6
*
* @return bool
*/
protected function _should_display() {
return true;
}
/**
* Displays the notice.
* Individual notices typically should not override this method, as it contains
* all the notice wrapper logic. Instead, notices should override `_display()`
*
* @since 2.10.6
* @return void
*/
public function display() {
?>
<div
id="edd-admin-notice-<?php echo esc_attr( $this->get_id() ); ?>"
class="edd-admin-notice-<?php echo esc_attr( sanitize_html_class( static::TYPE ) ); ?> edd-promo-notice"
data-nonce="<?php echo esc_attr( wp_create_nonce( 'edd-dismiss-notice-' . $this->get_id() ) ); ?>"
data-id="<?php echo esc_attr( $this->get_id() ); ?>"
data-lifespan="<?php echo esc_attr( static::dismiss_duration() ); ?>"
>
<?php $this->_display(); ?>
<?php if ( static::DISMISSIBLE ) : ?>
<button class="button-link edd-promo-notice-dismiss">
&times;
<span class="screen-reader-text"><?php esc_html_e( 'Dismiss notice', 'easy-digital-downloads' ); ?></span>
</button>
<?php endif; ?>
</div>
<?php
}
}

View File

@ -0,0 +1,187 @@
<?php
/**
* Dashboard Review Notice
*
* @package easy-digital-downloads
* @copyright Copyright (c) 2021, Easy Digital Downloads
* @license GPL2+
* @since 2.11.4
*/
namespace EDD\Admin\Promos\Notices;
class Five_Star_Review_Dashboard extends Notice {
/**
* Action hook for displaying the notice.
*/
const DISPLAY_HOOK = 'edd_dashboard_sales_widget';
/**
* Type of promotional notice.
*/
const TYPE = 'dashboard';
/**
* Capability required to view or dismiss the notice.
*/
const CAPABILITY = 'manage_shop_settings';
/**
* The current screen.
*
* @var string
*/
protected $screen = 'dashboard';
/**
* The ID of the notice. Defined specifically here as we intend to use it twice.
*
* @since 2.11.4
* @return string
*/
public function get_id() {
return 'five-star-review';
}
/**
* Display the notice.
*
* @since 2.11.4
* @return void
*/
public function display() {
?>
<div
id="edd-admin-notice-<?php echo esc_attr( $this->get_id() ); ?>"
class="edd-admin-notice-<?php echo esc_attr( sanitize_html_class( static::TYPE ) ); ?> edd-promo-notice"
data-nonce="<?php echo esc_attr( wp_create_nonce( 'edd-dismiss-notice-' . $this->get_id() ) ); ?>"
data-id="<?php echo esc_attr( $this->get_id() ); ?>"
data-lifespan="<?php echo esc_attr( static::dismiss_duration() ); ?>"
>
<?php
$this->_display();
?>
</div>
<?php
}
/**
* The promo notice content.
*
* @since 2.11.4
* @return void
*/
public function _display() {
?>
<div class="edd-review-step edd-review-step-1">
<p><?php esc_html_e( 'Hey, I noticed you\'ve made quite a few sales with Easy Digital Downloads! Are you enjoying Easy Digital Downloads?', 'easy-digital-downloads' ); ?></p>
<div class="edd-review-actions">
<button class="button-primary edd-review-switch-step" data-step="3"><?php esc_html_e( 'Yes', 'easy-digital-downloads' ); ?></button><br />
<button class="button-link edd-review-switch-step" data-step="2"><?php esc_html_e( 'Not Really', 'easy-digital-downloads' ); ?></button>
</div>
</div>
<div class="edd-review-step edd-review-step-2" style="display:none;">
<p><?php esc_html_e( 'We\'re sorry to hear you aren\'t enjoying Easy Digital Downloads. We would love a chance to improve. Could you take a minute and let us know what we can do better?', 'easy-digital-downloads' ); ?></p>
<div class="edd-review-actions">
<a href="<?php echo esc_url( $this->url() ); ?>" class="button button-secondary edd-promo-notice-dismiss" target="_blank"><?php esc_html_e( 'Give Feedback', 'easy-digital-downloads' ); ?></a><br>
<button class="button-link edd-promo-notice-dismiss"><?php esc_html_e( 'No thanks', 'easy-digital-downloads' ); ?></button>
</div>
</div>
<div class="edd-review-step edd-review-step-3" style="display:none;">
<p><?php esc_html_e( 'That\'s awesome! Could you please do me a BIG favor and give it a 5-star rating on WordPress to help us spread the word and boost our motivation?', 'easy-digital-downloads' ); ?></p>
<p><strong><?php echo wp_kses( __( '~ Chris Klosowski<br>President of Easy Digital Downloads', 'easy-digital-downloads' ), array( 'br' => array() ) ); ?></strong></p>
<div class="edd-review-actions">
<a href="https://wordpress.org/support/plugin/easy-digital-downloads/reviews/?filter=5#new-post" class="button button-primary edd-promo-notice-dismiss" target="_blank" rel="noopener noreferrer"><?php esc_html_e( 'Ok, you deserve it!', 'easy-digital-downloads' ); ?></a><br>
<button class="button-link edd-promo-notice-dismiss"><?php esc_html_e( 'No thanks', 'easy-digital-downloads' ); ?></button>
</div>
</div>
<img alt="" class="edd-peeking" src="<?php echo esc_url( EDD_PLUGIN_URL . 'assets/images/edd-peeking.png' ); ?>" />
<script type="text/javascript">
document.addEventListener( 'DOMContentLoaded', function() {
var steps = document.querySelectorAll( '.edd-review-switch-step' );
steps.forEach( function(step) {
step.addEventListener( 'click', function ( e ) {
e.preventDefault();
var target = this.getAttribute( 'data-step' );
if ( target ) {
var notice = this.closest( '.edd-promo-notice' );
var review_step = notice.querySelector( '.edd-review-step-' + target );
if ( review_step ) {
var thisStep = this.closest( '.edd-review-step' );
eddFadeOut( thisStep );
eddFadeIn( review_step );
}
}
} )
} )
function eddFadeIn( element ) {
var op = 0;
element.style.opacity = op;
element.style.display = 'block';
var timer = setInterval( function () {
if ( op >= 1 ) {
clearInterval( timer );
}
element.style.opacity = op;
element.style.filter = 'alpha(opacity=' + op * 100 + ')';
op = op + 0.1;
}, 80 );
}
function eddFadeOut( element ) {
var op = 1;
var timer = setInterval( function () {
if ( op <= 0 ) {
element.style.display = 'none';
clearInterval( timer );
}
element.style.opacity = op;
element.style.filter = 'alpha(opacity=' + op * 100 + ')';
op = op - 0.1;
}, 80 );
}
} );
</script>
<?php
}
/**
* Whether the notice should display.
*
* @since 2.11.4
* @return bool
*/
protected function _should_display() {
$activated = edd_get_activation_date();
// Do not show if EDD was activated less than 30 days ago.
if ( ! is_numeric( $activated ) || ( $activated + ( DAY_IN_SECONDS * 30 ) ) > time() ) {
return false;
}
// @todo Change this to edd_count_orders in 3.0
$payments = edd_count_payments();
return isset( $payments->publish ) && $payments->publish >= 15;
}
/**
* Builds the UTM parameters for the URLs.
*
* @since 2.11.4
*
* @return string
*/
private function url() {
$url = edd_link_helper(
'https://easydigitaldownloads.com/plugin-feedback/',
array(
'utm_medium' => 'feedback-' . static::TYPE,
'utm_content' => 'give-feedback',
)
);
return $url;
}
}

View File

@ -0,0 +1,68 @@
<?php
/**
* Settings Review Notice
*
* @package easy-digital-downloads
* @copyright Copyright (c) 2021, Easy Digital Downloads
* @license GPL2+
* @since 2.11.4
*/
namespace EDD\Admin\Promos\Notices;
class Five_Star_Review_Settings extends Five_Star_Review_Dashboard {
/**
* Action hook for displaying the notice.
*/
const DISPLAY_HOOK = 'admin_notices';
/**
* Type of promotional notice.
*/
const TYPE = 'admin-notice';
/**
* The current screen.
*
* @var string
*/
protected $screen = 'plugin-settings-page';
/**
* Display the notice.
* This extends the parent method because the container classes are different.
*
* @since 2.11.4
* @return void
*/
public function display() {
?>
<div
id="edd-admin-notice-<?php echo esc_attr( $this->get_id() ); ?>"
class="notice notice-info edd-admin-notice-<?php echo esc_attr( sanitize_html_class( static::TYPE ) ); ?> edd-promo-notice"
data-nonce="<?php echo esc_attr( wp_create_nonce( 'edd-dismiss-notice-' . $this->get_id() ) ); ?>"
data-id="<?php echo esc_attr( $this->get_id() ); ?>"
data-lifespan="<?php echo esc_attr( static::dismiss_duration() ); ?>"
>
<?php
parent::_display();
?>
</div>
<?php
}
/**
* Whether the notice should display.
* This extends the general method as this notice should only display on EDD settings screens.
*
* @since 2.11.4
* @return bool
*/
protected function _should_display() {
if ( ! edd_is_admin_page( 'settings' ) ) {
return false;
}
return parent::_should_display();
}
}

View File

@ -0,0 +1,266 @@
<?php
/**
* License Upgrade Notice
*
* @package easy-digital-downloads
* @copyright Copyright (c) 2021, Sandhills Development, LLC
* @license GPL2+
* @since 2.10.6
*/
namespace EDD\Admin\Promos\Notices;
use EDD\Admin\Pass_Manager;
class License_Upgrade_Notice extends Notice {
const DISPLAY_HOOK = 'in_admin_header';
/**
* Number of EDD license keys that have been entered.
* Not validated to make sure they're actually active; this is
* just an indicator if any licenses exist at all.
*
* @var array
*/
private $number_license_keys;
/**
* @var Pass_Manager
*/
private $pass_manager;
/**
* License_Upgrade_Notice constructor.
*/
public function __construct() {
global $edd_licensed_products;
$this->number_license_keys = is_array( $edd_licensed_products ) ? count( $edd_licensed_products ) : 0;
$this->pass_manager = new Pass_Manager();
}
/**
* This notice lasts 90 days.
*
* @return int
*/
public static function dismiss_duration() {
return 3 * MONTH_IN_SECONDS;
}
/**
* Determines if the current page is an EDD admin page.
*
* @return bool
*/
private function is_edd_admin_page() {
if ( defined( 'EDD_DOING_TESTS' ) && EDD_DOING_TESTS ) {
return true;
}
$screen = get_current_screen();
if ( ! $screen instanceof \WP_Screen || 'dashboard' === $screen->id || ! edd_is_admin_page( '', '', false ) ) {
return false;
}
return true;
}
/**
* @inheritDoc
*
* @return bool
*/
protected function _should_display() {
if ( ! $this->is_edd_admin_page() ) {
return false;
}
// Someone with no license keys entered always sees a notice.
if ( 0 === $this->number_license_keys ) {
return true;
}
// If we have no pass data yet, don't show the notice because we don't yet know what it should say.
if ( ! $this->pass_manager->has_pass_data ) {
return false;
}
// If someone has an extended pass or higher, and has an active AffiliateWP license, don't show.
try {
if (
$this->pass_manager->has_pass() &&
Pass_Manager::pass_compare( $this->pass_manager->highest_pass_id, Pass_Manager::EXTENDED_PASS_ID, '>=' ) &&
$this->has_affiliate_wp_license() &&
$this->has_mi_license()
) {
return false;
}
} catch ( \Exception $e ) {
return true;
}
return true;
}
/**
* Determines whether or not AffiliateWP is installed and has a license key.
*
* @since 2.10.6
*
* @return bool
*/
private function has_affiliate_wp_license() {
if ( ! function_exists( 'affiliate_wp' ) ) {
return false;
}
return (bool) affiliate_wp()->settings->get( 'license_key' );
}
/**
* Determines whether or not MonsterInsights is installed and has a license key.
*
* @since 2.11.6
*
* @return bool
*/
private function has_mi_license() {
if ( ! class_exists( 'MonsterInsights' ) ) {
return false;
}
$mi_license = \MonsterInsights::$instance->license->get_license_key();
return ! empty( $mi_license );
}
/**
* @inheritDoc
*/
protected function _display() {
try {
if ( 0 === $this->number_license_keys ) {
$utm_parameters = $this->query_args( 'core' );
$link_url = $this->build_url(
'https://easydigitaldownloads.com/lite-upgrade/',
$utm_parameters
);
$help_url = edd_link_helper(
'https://easydigitaldownloads.com/what-is-an-edd-pass/',
array(
'utm_medium' => 'top-promo',
'utm_content' => 'what-is-a-pass',
)
);
// No license keys active at all.
printf(
/* Translators: %1$s opening anchor tag; %2$s closing anchor tag */
__( 'You are using the free version of Easy Digital Downloads. %1$sPurchase a pass%2$s to get email marketing tools and recurring payments. %3$sAlready have a Pass?%4$s', 'easy-digital-downloads' ),
'<a href="' . $link_url . '" target="_blank">',
'</a>',
'<a href="' . $help_url . '" target="_blank">',
'</a>'
);
} elseif ( ! $this->pass_manager->highest_pass_id ) {
$utm_parameters = $this->query_args( 'extension-license' );
$link_url = $this->build_url(
'https://easydigitaldownloads.com/your-account/',
$utm_parameters
);
// Individual product license active, but no pass.
printf(
/* Translators: %1$s opening anchor tag; %2$s closing anchor tag */
__( 'For access to additional Easy Digital Downloads extensions to grow your store, consider %1$spurchasing a pass%2$s.', 'easy-digital-downloads' ),
'<a href="' . $link_url . '" target="_blank">',
'</a>'
);
} elseif ( Pass_Manager::pass_compare( $this->pass_manager->highest_pass_id, Pass_Manager::PERSONAL_PASS_ID, '=' ) ) {
$utm_parameters = $this->query_args( 'personal-pass' );
$link_url = $this->build_url(
'https://easydigitaldownloads.com/your-account/',
$utm_parameters
);
// Personal pass active.
printf(
/* Translators: %1$s opening anchor tag; %2$s closing anchor tag */
__( 'You are using Easy Digital Downloads with a Personal Pass. Consider %1$supgrading%2$s to get recurring payments and more.', 'easy-digital-downloads' ),
'<a href="' . $link_url . '" target="_blank">',
'</a>'
);
} elseif ( Pass_Manager::pass_compare( $this->pass_manager->highest_pass_id, Pass_Manager::EXTENDED_PASS_ID, '>=' ) ) {
if ( ! $this->has_affiliate_wp_license() ) {
$link_url = edd_link_helper(
'https://affiliatewp.com',
array(
'utm_medium' => 'top-promo',
'utm_content' => 'affiliate-wp',
)
);
printf(
/* Translators: %1$s opening anchor tag; %2$s closing anchor tag */
__( 'Grow your business and make more money with affiliate marketing. %1$sGet AffiliateWP%2$s', 'easy-digital-downloads' ),
'<a href="' . $link_url . '" target="_blank">',
'</a>'
);
} elseif( ! $this->has_mi_license() ) {
printf(
/* Translators: %1$s opening anchor tag; %2$s closing anchor tag */
__( 'Gain access to powerful insights to grow your traffic and revenue. %1$sGet MonsterInsights%2$s', 'easy-digital-downloads' ),
'<a href="' . esc_url( 'https://monsterinsights.com?utm_campaign=xsell&utm_source=eddplugin&utm_content=top-promo' ) . '" target="_blank">',
'</a>'
);
}
}
} catch ( \Exception $e ) {
// If we're in here, that means we have an invalid pass ID... what should we do? :thinking:.
}
}
/**
* Builds the UTM parameters for the URLs.
*
* @since 2.10.6
*
* @param string $upgrade_from License type upgraded from.
* @param string $source Current page.
*
* @return string[]
*/
private function query_args( $upgrade_from, $source = '' ) {
return array(
'utm_medium' => 'top-promo',
'utm_content' => 'upgrade-from-' . urlencode( $upgrade_from ),
);
}
/**
* Build a link with UTM parameters
*
* @since 3.1
*
* @param string $url The Base URL.
* @param array $utm_parameters The UTM tags for the URL.
*
* @return string
*/
private function build_url( $url, $utm_parameters ) {
return esc_url(
edd_link_helper(
$url,
$utm_parameters
)
);
}
}

View File

@ -0,0 +1,164 @@
<?php
/**
* API Requests Log View Class
*
* @package EDD
* @subpackage Admin/Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.5
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_API_Request_Log_Table List Table Class
*
* @since 1.5
* @since 3.0 Updated to use the custom tables and new query classes.
*/
class EDD_API_Request_Log_Table extends EDD_Base_Log_List_Table {
/**
* Log type
*
* @var string
*/
protected $log_type = 'api_requests';
/**
* Get things started
*
* @since 1.5
* @see WP_List_Table::__construct()
*/
public function __construct() {
parent::__construct();
}
/**
* Retrieve the table columns
*
* @since 1.5
* @return array $columns Array of all the list table columns
*/
public function get_columns() {
return array(
'ID' => __( 'Log ID', 'easy-digital-downloads' ),
'details' => __( 'Request Details', 'easy-digital-downloads' ),
'version' => __( 'API Version', 'easy-digital-downloads' ),
'ip' => __( 'Request IP', 'easy-digital-downloads' ),
'speed' => __( 'Request Speed', 'easy-digital-downloads' ),
'date' => __( 'Date', 'easy-digital-downloads' )
);
}
/**
* Gets the name of the primary column.
*
* @since 2.5
* @access protected
*
* @return string Name of the primary column.
*/
protected function get_primary_column_name() {
return 'ID';
}
/**
* This function renders most of the columns in the list table.
*
* @since 1.5
*
* @param array $item Contains all the data of the api request
* @param string $column_name The name of the column
*
* @return string Column Name
*/
public function column_default( $item, $column_name ) {
switch ( $column_name ) {
case 'ip':
return '<a href="' . esc_url( 'https://ipinfo.io/' . esc_attr( $item['ip'] ) ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $item['ip'] ) . '</a>';
default:
return $item[ $column_name ];
}
}
/**
* Output Error Message column
*
* @since 1.5
* @param array $item Contains all the data of the log
* @return void
*/
public function column_details( $item ) {
?>
<a href="#TB_inline?width=640&amp;inlineId=log-details-<?php echo esc_attr( $item['ID'] ); ?>" class="thickbox"><?php esc_html_e( 'View Request', 'easy-digital-downloads' ); ?></a>
<div id="log-details-<?php echo absint( $item['ID'] ); ?>" style="display:none;">
<?php
$request = $item['request'];
$error = $item['error'];
echo '<p><strong>' . __( 'API Request:', 'easy-digital-downloads' ) . '</strong></p>';
echo '<div>' . $request . '</div>';
if ( ! empty( $error ) ) {
echo '<p><strong>' . __( 'Error', 'easy-digital-downloads' ) . '</strong></p>';
echo '<div>' . esc_html( $error ) . '</div>';
}
echo '<p><strong>' . __( 'API User:', 'easy-digital-downloads' ) . '</strong></p>';
echo '<div>' . $item['user_id'] . '</div>';
echo '<p><strong>' . __( 'API Key:', 'easy-digital-downloads' ) . '</strong></p>';
echo '<div>' . $item['api_key'] . '</div>';
echo '<p><strong>' . __( 'Request Date:', 'easy-digital-downloads' ) . '</strong></p>';
echo '<div>' . $item['date'] . '</div>';
?>
</div>
<?php
}
/**
* Gets the log entries for the current view
*
* @since 1.5
*
* @return array $logs_data Array of all the Log entries
*/
public function get_logs( $log_query = array() ) {
$logs_data = array();
$logs = edd_get_api_request_logs( $log_query );
if ( $logs ) {
foreach ( $logs as $log ) {
/** @var $log EDD\Logs\Api_Request_Log */
$logs_data[] = array(
'ID' => $log->id,
'version' => $log->version,
'speed' => $log->time,
'ip' => $log->ip,
'date' => $log->date_created,
'api_key' => $log->api_key,
'request' => $log->request,
'error' => $log->error,
'user_id' => $log->user_id,
);
}
}
return $logs_data;
}
/**
* Get the total number of items
*
* @since 3.0
*
* @param array $log_query
*
* @return int
*/
public function get_total( $log_query = array() ) {
return edd_count_api_request_logs( $log_query );
}
}

View File

@ -0,0 +1,560 @@
<?php
/**
* Base Log List Table.
*
* @package EDD
* @subpackage Admin/Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.4.4
* @since 3.0 Updated to use the custom tables.
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
use EDD\Admin\List_Table;
/**
* EDD_Base_Log_List_Table Class
*
* @since 3.0
*/
class EDD_Base_Log_List_Table extends List_Table {
/**
* Log type
*
* @var string
*/
protected $log_type = 'logs';
/**
* Get things started
*
* @since 3.0
* @see WP_List_Table::__construct()
*/
public function __construct() {
parent::__construct( array(
'singular' => 'log',
'plural' => 'logs',
'ajax' => false
) );
$this->filter_bar_hooks();
}
/**
* Generate the table navigation above or below the table
*
* Removes the referrer nonce from parent class.
*
* @since 3.0.0
* @param string $which
*/
protected function display_tablenav( $which ) {
?>
<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
}
/**
* Hook in filter bar actions
*
* @since 3.0
*/
private function filter_bar_hooks() {
add_action( 'edd_admin_filter_bar_logs', array( $this, 'filter_bar_items' ) );
add_action( 'edd_after_admin_filter_bar_logs', array( $this, 'filter_bar_searchbox' ) );
}
/**
* Gets the name of the primary column.
*
* @since 3.0
* @access protected
*
* @return string Name of the primary column.
*/
protected function get_primary_column_name() {
return 'id';
}
/**
* Return the current log view
*
* @since 3.0
* @return string
*/
public function get_filtered_view() {
return isset( $_GET['view'] ) && array_key_exists( $_GET['view'], edd_log_default_views() )
? sanitize_text_field( $_GET['view'] )
: 'file_downloads';
}
/**
* Return the user we are filtering logs by, if any
*
* @since 3.0
* @return mixed int If User ID, string If Email/Login
*/
public function get_filtered_user() {
return isset( $_GET['user'] ) ? absint( $_GET['user'] ) : false;
}
/**
* Return the customer we are filtering logs by, if any
*
* @since 3.0
* @return int|string|false int If customer ID, string If Email, false if not present
*/
public function get_filtered_customer() {
$ret = false;
if ( isset( $_GET['customer'] ) ) {
$customer = new EDD_Customer( sanitize_text_field( $_GET['customer'] ) );
if ( ! empty( $customer->id ) ) {
$ret = $customer->id;
}
}
return $ret;
}
/**
* Return the start-date of the filter
*
* @since 3.0
*
* @return string Start date to filter by
*/
public function get_filtered_start_date() {
return sanitize_text_field( $this->get_request_var( 'start-date', null ) );
}
/**
* Return the end-date of the filter
*
* @since 3.0
*
* @return string End date to filter by
*/
public function get_filtered_end_date() {
return sanitize_text_field( $this->get_request_var( 'end-date', null ) );
}
/**
* Return the ID of the download we're filtering logs by
*
* @since 3.0
*
* @return int Download ID.
*/
public function get_filtered_download() {
return absint( $this->get_request_var( 'download', false ) );
}
/**
* Return the ID of the payment we're filtering logs by
*
* @since 3.0
*
* @return int Payment ID.
*/
public function get_filtered_payment() {
return absint( $this->get_request_var( 'payment', false ) );
}
/**
* Gets the meta query for the log query.
*
* This is used to return log entries that match our search query, user query, or download query.
*
* @since 3.0
*
* @return array $meta_query
*/
public function get_meta_query() {
return array();
}
/**
* Outputs the log views.
*
* @since 3.0
*/
public function bulk_actions( $which = '' ) {
return;
}
/**
* Renders the Reports page views drop down
*
* @since 3.0
* @return void
*/
public function log_views() {
$views = edd_log_default_views();
$current_view = $this->get_filtered_view(); ?>
<select id="edd-logs-view" name="view">
<?php foreach ( $views as $view_id => $label ) : ?>
<option value="<?php echo esc_attr( $view_id ); ?>" <?php selected( $view_id, $current_view ); ?>><?php echo esc_html( $label ); ?></option>
<?php endforeach; ?>
</select>
<?php
/**
* Fires immediately after the logs view actions are rendered in the Logs screen.
*
* @since 3.0
*/
do_action( 'edd_log_view_actions' );
?>
<input type="hidden" name="customer" value="<?php echo $this->get_filtered_customer(); ?>" />
<input type="hidden" name="post_type" value="download" />
<input type="hidden" name="page" value="edd-tools" />
<input type="hidden" name="tab" value="logs" />
<?php
}
/**
* Sets up the downloads filter
*
* @since 3.0
* @since 3.1 Accepts a download ID to filter by for the selected value.
*
* @param int $download The filtered download ID, default: 0.
*
* @return void
*/
public function downloads_filter( $download = 0 ) {
$args = array(
'id' => 'edd-log-download-filter',
'name' => 'download',
'chosen' => true,
);
if ( ! empty( $download ) ) {
$args['selected'] = $download;
}
echo EDD()->html->product_dropdown( $args );
}
/**
* Gets the log entries for the current view
*
* @since 3.0
*
* @return array $logs_data Array of all the logs.
*/
function get_logs( $log_query = array() ) {
return array();
}
/**
* Get the total number of items
*
* @since 3.0
*
* @param array $log_query
*
* @return int
*/
public function get_total( $log_query = array() ) {
return count( array() );
}
/**
* Empty method to hide view links on all logs table
*
* @since 3.0
*/
public function get_views() {
// Intentionally empty
}
/**
* Retrieves the logs data.
*
* @since 3.0
*
* @return array Logs data.
*/
public function get_data() {
$log_query = $this->get_query_args();
return $this->get_logs( $log_query );
}
/**
* Setup the final data for the table.
*
* @since 3.0
*/
public function prepare_items() {
$this->_column_headers = array(
$this->get_columns(),
array(),
$this->get_sortable_columns()
);
$this->items = $this->get_data();
$log_query = $this->get_query_args( false );
$total_items = $this->get_total( $log_query );
$this->set_pagination_args( array(
'total_pages' => ceil( $total_items / $this->per_page ),
'total_items' => $total_items,
'per_page' => $this->per_page,
) );
}
/**
* Return array of query arguments
*
* @since 3.0
*
* @param bool $paginate Whether to add pagination arguments
*
* @return array
*/
protected function get_query_args( $paginate = true ) {
// Defaults
$retval = array(
'product_id' => $this->get_filtered_download(),
'customer_id' => $this->get_filtered_customer(),
'order_id' => $this->get_filtered_payment(),
'meta_query' => $this->get_meta_query(),
);
// Search
$search = $this->get_search();
if ( ! empty( $search ) ) {
if ( filter_var( $search, FILTER_VALIDATE_IP ) ) {
$retval['ip'] = $search;
} elseif ( is_email( $search ) ) {
if ( 'api_requests' === $this->log_type ) {
// API requests are linked to user accounts, so we're checking user data here.
$user = get_user_by( 'email', $search );
if ( ! empty( $user->ID ) ) {
$retval['user_id'] = $user->ID;
} else {
// This is a fallback to help ensure an invalid email will produce zero results.
$retval['search'] = $search;
}
} else {
// All other logs are linked to customers.
$customer = edd_get_customer_by( 'email', $search );
if ( ! empty( $customer->id ) ) {
$retval['customer_id'] = $customer->id;
} else {
// This is a fallback to help ensure an invalid email will produce zero results.
$retval['search'] = $search;
}
}
} elseif ( 'api_requests' === $this->log_type && 32 === strlen( $search ) ) {
// Look for an API key
$retval['api_key'] = $search;
} elseif ( 'api_requests' === $this->log_type && stristr( $search, 'token:' ) ) {
// Look for an API token
$retval['token'] = str_ireplace( 'token:', '', $search );
} elseif ( is_numeric( $search ) ) {
if ( 'api_requests' === $this->log_type ) {
// API requests are linked to user accounts, so we're checking user data here.
$user = get_user_by( 'email', $search );
if ( ! empty( $user->ID ) ) {
$retval['user_id'] = $user->ID;
} else {
$retval['search'] = $search;
}
} else {
// All other logs are linked to customers.
$customer = edd_get_customer( $search );
if ( ! empty( $customer->id ) ) {
$retval['customer_id'] = $customer->id;
} elseif ( 'file_downloads' === $this->log_type ) {
$retval['product_id'] = $search;
} else {
$retval['search'] = $search;
}
}
} else {
if ( 'file_downloads' === $this->log_type ) {
$this->file_search = true;
} else {
$retval['search'] = $search;
}
}
}
// Start date
$start_date = $this->get_filtered_start_date();
$end_date = $this->get_filtered_end_date();
// Setup original array
if ( ! empty( $start_date ) || ! empty( $end_date ) ) {
$retval['date_created_query']['column'] = 'date_created';
// Start date
if ( ! empty( $start_date ) ) {
$retval['date_created_query'][] = array(
'column' => 'date_created',
'after' => \Carbon\Carbon::parse( date( 'Y-m-d H:i:s', strtotime( "{$start_date} midnight" ) ), edd_get_timezone_id() )->setTimezone( 'UTC' )->toDateTimeString(),
);
}
// End date
if ( ! empty( $end_date ) ) {
$retval['date_created_query'][] = array(
'column' => 'date_created',
'before' => \Carbon\Carbon::parse( date( 'Y-m-d H:i:s', strtotime( "{$end_date} + 1 day" ) ), edd_get_timezone_id() )->setTimezone( 'UTC' )->toDateTimeString(),
);
}
}
$retval = array_filter( $retval );
// Return query arguments
return ( true === $paginate )
? $this->parse_pagination_args( $retval )
: $retval;
}
/**
* Output advanced filters for payments
*
* @since 3.0
*/
public function advanced_filters() {
edd_admin_filter_bar( 'logs' );
}
/**
* Output filter bar items
*
* @since 3.0
*/
public function filter_bar_items() {
// Get values
$start_date = $this->get_filtered_start_date();
$end_date = $this->get_filtered_end_date();
$download = $this->get_filtered_download();
$customer = $this->get_filtered_customer();
$view = $this->get_filtered_view();
$clear_url = edd_get_admin_url( array(
'page' => 'edd-tools',
'tab' => 'logs',
'view' => sanitize_key( $view ),
) ); ?>
<span id="edd-type-filter">
<?php $this->log_views(); ?>
</span>
<span id="edd-date-filters" class="edd-from-to-wrapper">
<?php
echo EDD()->html->date_field( array(
'id' => 'start-date',
'name' => 'start-date',
'placeholder' => _x( 'From', 'date filter', 'easy-digital-downloads' ),
'value' => $start_date
) );
echo EDD()->html->date_field( array(
'id' => 'end-date',
'name' => 'end-date',
'placeholder' => _x( 'To', 'date filter', 'easy-digital-downloads' ),
'value' => $end_date
) );
?></span>
<span id="edd-download-filter">
<?php $this->downloads_filter( $download ); ?>
</span>
<?php if ( ! empty( $customer ) ) : ?>
<span id="edd-customer-filter">
<?php printf( esc_html__( 'Customer ID: %d', 'easy-digital-downloads' ), $customer ); ?>
</span>
<?php endif; ?>
<input type="submit" class="button-secondary" value="<?php esc_attr_e( 'Filter', 'easy-digital-downloads' ); ?>"/>
<?php if ( ! empty( $start_date ) || ! empty( $end_date ) || ! empty( $download ) || ! empty( $customer ) ) : ?>
<a href="<?php echo esc_url( $clear_url ); ?>" class="button-secondary">
<?php esc_html_e( 'Clear', 'easy-digital-downloads' ); ?>
</a>
<?php endif; ?>
<?php
}
/**
* Output the filter bar searchbox
*
* @since 3.0
*/
public function filter_bar_searchbox() {
do_action( 'edd_logs_advanced_filters_row' );
$this->search_box( __( 'Search', 'easy-digital-downloads' ), 'edd-logs' );
}
/**
* Show the search field
*
* @since 3.0
*
* @param string $text Label for the search box
* @param string $input_id ID of the search box
*
* @return void
*/
public function search_box( $text, $input_id ) {
// Bail if no customers and no search
if ( empty( $_REQUEST['s'] ) && ! $this->has_items() ) {
return;
}
$input_id = $input_id . '-search-input';
if ( ! empty( $_REQUEST['orderby'] ) ) {
echo '<input type="hidden" name="orderby" value="' . esc_attr( $_REQUEST['orderby'] ) . '" />';
}
if ( ! empty( $_REQUEST['order'] ) ) {
echo '<input type="hidden" name="order" value="' . esc_attr( $_REQUEST['order'] ) . '" />';
}
?>
<p class="search-form">
<label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"><?php echo esc_html( $text ); ?>:</label>
<input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="s" value="<?php _admin_search_query(); ?>" placeholder="<?php esc_html_e( 'Search logs...', 'easy-digital-downloads' ); ?>" />
</p>
<?php
}
}

View File

@ -0,0 +1,380 @@
<?php
/**
* Earnings by Category Reports Table Class
*
* @package EDD
* @subpackage Admin/Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.4
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
use EDD\Admin\List_Table;
/**
* EDD_Categories_Reports_Table Class
*
* Renders the Download Reports table
*
* @since 2.4
*/
class EDD_Categories_Reports_Table extends List_Table {
/**
* Get things started
*
* @since 2.4
* @see WP_List_Table::__construct()
*/
public function __construct() {
// Set parent defaults
parent::__construct( array(
'singular' => 'report-earning',
'plural' => 'report-earnings',
'ajax' => false
) );
}
/**
* Gets the name of the primary column.
*
* @since 2.5
* @access protected
*
* @return string Name of the primary column.
*/
protected function get_primary_column_name() {
return 'label';
}
/**
* This function renders most of the columns in the list table.
*
* @since 2.4
*
* @param array $item Contains all the data of the downloads
* @param string $column_name The name of the column
*
* @return string Column Name
*/
public function column_default( $item, $column_name ) {
return $item[ $column_name ];
}
/**
* Retrieve the table columns
*
* @since 2.4
* @return array $columns Array of all the list table columns
*/
public function get_columns() {
return array(
'label' => __( 'Category', 'easy-digital-downloads' ),
'total_sales' => __( 'Total Sales', 'easy-digital-downloads' ),
'total_earnings' => __( 'Total Earnings', 'easy-digital-downloads' ),
'avg_sales' => __( 'Monthly Sales Avg', 'easy-digital-downloads' ),
'avg_earnings' => __( 'Monthly Earnings Avg', 'easy-digital-downloads' )
);
}
/**
* Outputs the reporting views
*
* @since 1.5
* @return void
*/
public function display_tablenav( $which = '' ) {
?>
<div class="tablenav <?php echo esc_attr( $which ); ?>">
<div class="alignleft actions bulkactions">
<?php
if ( 'top' === $which ) {
edd_report_views();
}
?>
</div>
</div>
<?php
}
/**
* Builds and retrieves of all the categories reports data.
*
* @since 2.4
* @edeprecated 3.0 Use get_data()
*
* @return array All the data for customer reports.
*/
public function reports_data() {
_edd_deprecated_function( __METHOD__, '3.0', 'EDD_Categories_Reports_Table::get_data()' );
return $this->get_data();
}
/**
* Builds and retrieves all of the categories reports data.
*
* @since 3.0
*
* @return array Categories reports table data.
*/
public function get_data() {
/*
* Date filtering
*/
$dates = edd_get_report_dates();
$include_taxes = empty( $_GET['exclude_taxes'] ) ? true : false;
if ( ! empty( $dates[ 'year' ] ) ) {
$date = new DateTime();
$date->setDate( $dates[ 'year' ], $dates[ 'm_start' ], $dates[ 'day' ] );
$start_date = $date->format( 'Y-m-d' );
$date->setDate( $dates[ 'year_end' ], $dates[ 'm_end' ], $dates[ 'day_end' ] );
$end_date = $date->format( 'Y-m-d' );
$cached_report_key = 'edd_earnings_by_category_data' . $start_date . '_' . $end_date;
} else {
$start_date = false;
$end_date = false;
$cached_report_key = 'edd_earnings_by_category_data';
}
$cached_reports = get_transient( $cached_report_key );
if ( false !== $cached_reports ) {
$reports_data = $cached_reports;
} else {
$reports_data = array();
$categories = get_terms( 'download_category', array(
'parent' => 0,
'hierarchical' => 0,
'hide_empty' => false
) );
foreach ( $categories as $category ) {
$category_slugs = array( $category->slug );
$child_terms = get_terms( 'download_category', array(
'parent' => $category->term_id,
'hierarchical' => 0
) );
if ( ! empty( $child_terms ) ) {
foreach ( $child_terms as $child_term ) {
$category_slugs[] = $child_term->slug;
}
}
$downloads = get_posts( array(
'post_type' => 'download',
'posts_per_page' => -1,
'fields' => 'ids',
'tax_query' => array(
array(
'taxonomy' => 'download_category',
'field' => 'slug',
'terms' => $category_slugs
)
)
) );
$sales = $avg_sales = 0;
$earnings = $avg_earnings = 0.00;
foreach ( $downloads as $download ) {
$current_sales = EDD()->payment_stats->get_sales( $download, $start_date, $end_date );
$current_earnings = EDD()->payment_stats->get_earnings( $download, $start_date, $end_date, $include_taxes );
$current_average_sales = edd_get_average_monthly_download_sales( $download );
$current_average_earnings = edd_get_average_monthly_download_earnings( $download );
$sales += $current_sales;
$earnings += $current_earnings;
$avg_sales += $current_average_sales;
$avg_earnings += $current_average_earnings;
}
$avg_earnings = round( $avg_earnings, edd_currency_decimal_filter() );
if ( ! empty( $avg_earnings ) && $avg_sales < 1 ) {
$avg_sales = __( 'Less than 1', 'easy-digital-downloads' );
} else {
$avg_sales = round( edd_format_amount( $avg_sales, false ) );
}
$reports_data[] = array(
'ID' => $category->term_id,
'label' => $category->name,
'total_sales' => edd_format_amount( $sales, false ),
'total_sales_raw' => $sales,
'total_earnings' => edd_currency_filter( edd_format_amount( $earnings ) ),
'total_earnings_raw' => $earnings,
'avg_sales' => $avg_sales,
'avg_earnings' => edd_currency_filter( edd_format_amount( $avg_earnings ) ),
'is_child' => false,
);
if ( ! empty( $child_terms ) ) {
foreach ( $child_terms as $child_term ) {
$child_downloads = get_posts( array(
'post_type' => 'download',
'posts_per_page' => -1,
'fields' => 'ids',
'tax_query' => array(
array(
'taxonomy' => 'download_category',
'field' => 'slug',
'terms' => $child_term->slug
)
)
) );
$child_sales = $child_avg_sales = 0;
$child_earnings = $child_avg_earnings = 0.00;
foreach ( $child_downloads as $child_download ) {
$current_average_sales = $current_sales = EDD()->payment_stats->get_sales( $child_download, $start_date, $end_date );
$current_average_earnings = $current_earnings = EDD()->payment_stats->get_earnings( $child_download, $start_date, $end_date );
$release_date = get_post_field( 'post_date', $child_download );
$diff = abs( current_time( 'timestamp' ) - strtotime( $release_date ) );
$months = floor( $diff / ( 30 * 60 * 60 * 24 ) ); // Number of months since publication
if ( $months > 0 ) {
$current_average_sales = ( $current_sales / $months );
$current_average_earnings = ( $current_earnings / $months );
}
$child_sales += $current_sales;
$child_earnings += $current_earnings;
$child_avg_sales += $current_average_sales;
$child_avg_earnings += $current_average_earnings;
}
$child_avg_sales = round( $child_avg_sales / count( $child_downloads ) );
$child_avg_earnings = round( $child_avg_earnings / count( $child_downloads ), edd_currency_decimal_filter() );
$reports_data[] = array(
'ID' => $child_term->term_id,
'label' => '&#8212; ' . $child_term->name,
'total_sales' => edd_format_amount( $child_sales, false ),
'total_sales_raw' => $child_sales,
'total_earnings' => edd_currency_filter( edd_format_amount( $child_earnings ) ),
'total_earnings_raw' => $child_earnings,
'avg_sales' => edd_format_amount( $child_avg_sales, false ),
'avg_earnings' => edd_currency_filter( edd_format_amount( $child_avg_earnings ) ),
'is_child' => true
);
}
}
}
}
return $reports_data;
}
/**
* Output the Category Sales Mix Pie Chart
*
* @since 2.4
* @return string The HTML for the outputted graph
*/
public function output_sales_graph() {
if ( empty( $this->items ) ) {
return;
}
$data = array();
$total_sales = 0;
foreach ( $this->items as $item ) {
$total_sales += $item['total_sales_raw'];
if ( ! empty( $item[ 'is_child' ] ) || empty( $item[ 'total_sales_raw' ] ) ) {
continue;
}
$data[ $item[ 'label' ] ] = $item[ 'total_sales_raw' ];
}
if ( empty( $total_sales ) ) {
echo '<p><em>' . __( 'No sales for dates provided.', 'easy-digital-downloads' ) . '</em></p>';
}
// Sort High to Low, prior to filter so people can reorder if they please
arsort( $data );
$data = apply_filters( 'edd_category_sales_graph_data', $data );
$options = apply_filters( 'edd_category_sales_graph_options', array(
'legend_formatter' => 'eddLegendFormatterSales',
), $data );
$pie_graph = new EDD_Pie_Graph( $data, $options );
$pie_graph->display();
}
/**
* Output the Category Earnings Mix Pie Chart
*
* @since 2.4
* @return string The HTML for the outputted graph
*/
public function output_earnings_graph() {
if ( empty( $this->items ) ) {
return;
}
$data = array();
$total_earnings = 0;
foreach ( $this->items as $item ) {
$total_earnings += $item['total_earnings_raw'];
if ( ! empty( $item[ 'is_child' ] ) || empty( $item[ 'total_earnings_raw' ] ) ) {
continue;
}
$data[ $item[ 'label' ] ] = $item[ 'total_earnings_raw' ];
}
if ( empty( $total_earnings ) ) {
echo '<p><em>' . __( 'No earnings for dates provided.', 'easy-digital-downloads' ) . '</em></p>';
}
// Sort High to Low, prior to filter so people can reorder if they please
arsort( $data );
$data = apply_filters( 'edd_category_earnings_graph_data', $data );
$options = apply_filters( 'edd_category_earnings_graph_options', array(
'legend_formatter' => 'eddLegendFormatterEarnings',
), $data );
$pie_graph = new EDD_Pie_Graph( $data, $options );
$pie_graph->display();
}
/**
* Setup the final data for the table
*
* @since 2.4
* @uses EDD_Categories_Reports_Table::get_columns()
* @uses EDD_Categories_Reports_Table::get_sortable_columns()
* @uses EDD_Categories_Reports_Table::reports_data()
* @return void
*/
public function prepare_items() {
$columns = $this->get_columns();
$hidden = array(); // No hidden columns
$sortable = $this->get_sortable_columns();
$this->_column_headers = array( $columns, $hidden, $sortable );
$this->items = $this->get_data();
}
}

View File

@ -0,0 +1,298 @@
<?php
/**
* Download Reports Table Class
*
* @package EDD
* @subpackage Admin/Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.5
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
use EDD\Admin\List_Table;
/**
* EDD_Download_Reports_Table Class
*
* Renders the Download Reports table
*
* @since 1.5
*/
class EDD_Download_Reports_Table extends List_Table {
/**
* @var object Query results
* @since 1.5.2
*/
private $products;
/**
* Get things started
*
* @since 1.5
* @see WP_List_Table::__construct()
*/
public function __construct() {
parent::__construct( array(
'singular' => 'report-download',
'plural' => 'report-downloads',
'ajax' => false
) );
add_action( 'edd_report_view_actions', array( $this, 'category_filter' ) );
$this->query();
}
/**
* Gets the name of the primary column.
*
* @since 2.5
* @access protected
*
* @return string Name of the primary column.
*/
protected function get_primary_column_name() {
return 'title';
}
/**
* This function renders most of the columns in the list table.
*
* @since 1.5
*
* @param array $item Contains all the data of the downloads
* @param string $column_name The name of the column
*
* @return string Column Name
*/
public function column_default( $item, $column_name ) {
switch( $column_name ){
case 'earnings' :
return edd_currency_filter( edd_format_amount( $item[ $column_name ] ) );
case 'average_sales' :
return round( $item[ $column_name ] );
case 'average_earnings' :
return edd_currency_filter( edd_format_amount( $item[ $column_name ] ) );
case 'details' :
$url = edd_get_admin_url(
array(
'page' => 'edd-reports',
'view' => 'downloads',
'download-id' => absint( $item['ID'] ),
)
);
return '<a href="' . esc_url( $url ) . '">' . __( 'View Detailed Report', 'easy-digital-downloads' ) . '</a>';
default:
return $item[ $column_name ];
}
}
/**
* Retrieve the table columns
*
* @since 1.5
* @return array $columns Array of all the list table columns
*/
public function get_columns() {
return array(
'title' => edd_get_label_singular(),
'sales' => __( 'Sales', 'easy-digital-downloads' ),
'earnings' => __( 'Earnings', 'easy-digital-downloads' ),
'average_sales' => __( 'Monthly Average Sales', 'easy-digital-downloads' ),
'average_earnings' => __( 'Monthly Average Earnings', 'easy-digital-downloads' ),
'details' => __( 'Detailed Report', 'easy-digital-downloads' )
);
}
/**
* Retrieve the sortable columns
*
* @since 1.4
* @return array Array of all the sortable columns
*/
public function get_sortable_columns() {
return array(
'title' => array( 'title', true ),
'sales' => array( 'sales', false ),
'earnings' => array( 'earnings', false )
);
}
/**
* Retrieve the category being viewed
*
* @since 1.5.2
* @return int Category ID
*/
public function get_category() {
return absint( $this->get_request_var( 'category', 0 ) );
}
/**
* Retrieve the total number of downloads
*
* @since 1.5
* @return int $total Total number of downloads
*/
public function get_total_downloads() {
$total = 0;
$counts = wp_count_posts( 'download', 'readable' );
foreach( $counts as $count ) {
$total += $count;
}
return $total;
}
/**
* Outputs the reporting views
*
* These aren't really bulk actions but this outputs the markup in the
* right place.
*
* @since 1.5
* @return void
*/
public function bulk_actions( $which = '' ) {
edd_report_views();
}
/**
* Attaches the category filter to the log views
*
* @since 1.5.2
* @return void
*/
public function category_filter() {
if ( get_terms( 'download_category' ) ) {
echo EDD()->html->category_dropdown( 'category', $this->get_category() );
}
}
/**
* Performs the products query
*
* @since 1.5.2
* @return void
*/
public function query() {
$orderby = sanitize_text_field( $this->get_request_var( 'orderby', 'title' ) );
$order = sanitize_text_field( $this->get_request_var( 'order', 'DESC' ) );
$category = $this->get_category();
$args = array(
'post_type' => 'download',
'post_status' => 'publish',
'order' => $order,
'fields' => 'ids',
'posts_per_page' => $this->per_page,
'paged' => $this->get_paged(),
'suppress_filters' => true
);
if ( ! empty( $category ) ) {
$args['tax_query'] = array(
array(
'taxonomy' => 'download_category',
'terms' => $category
)
);
}
switch ( $orderby ) {
case 'title' :
$args['orderby'] = 'title';
break;
case 'sales' :
$args['orderby'] = 'meta_value_num';
$args['meta_key'] = '_edd_download_sales';
break;
case 'earnings' :
$args['orderby'] = 'meta_value_num';
$args['meta_key'] = '_edd_download_earnings';
break;
}
$r = apply_filters( 'edd_download_reports_prepare_items_args', $args, $this );
$this->products = new WP_Query( $r );
}
/**
* Build and retrieves all of the download reports data.
*
* @since 1.5
* @deprecated 3.0 Use get_data()
*
* @return array All the data for customer reports.
*/
public function reports_data() {
_edd_deprecated_function( __METHOD__, '3.0', 'EDD_Download_Reports_Table::get_data()' );
return $this->get_data();
}
/**
* Retrieves all of the download reports data.
*
* @since 3.0
*
* @return array Download reports table data.
*/
public function get_data() {
$reports_data = array();
$downloads = $this->products->posts;
if ( $downloads ) {
foreach ( $downloads as $download ) {
$reports_data[] = array(
'ID' => $download,
'title' => get_the_title( $download ),
'sales' => edd_get_download_sales_stats( $download ),
'earnings' => edd_get_download_earnings_stats( $download ),
'average_sales' => edd_get_average_monthly_download_sales( $download ),
'average_earnings' => edd_get_average_monthly_download_earnings( $download ),
);
}
}
return $reports_data;
}
/**
* Setup the final data for the table
*
* @since 1.5
* @uses EDD_Download_Reports_Table::get_columns()
* @uses EDD_Download_Reports_Table::get_sortable_columns()
* @uses EDD_Download_Reports_Table::get_total_downloads()
* @uses EDD_Download_Reports_Table::get_data()
* @uses EDD_Download_Reports_Table::set_pagination_args()
* @return void
*/
public function prepare_items() {
$this->_column_headers = array(
$this->get_columns(),
array(),
$this->get_sortable_columns()
);
$total_items = $this->get_total_downloads();
$this->items = $this->get_data();
$this->set_pagination_args( array(
'total_pages' => ceil( $total_items / $this->per_page ),
'total_items' => $total_items,
'per_page' => $this->per_page,
) );
}
}

View File

@ -0,0 +1,291 @@
<?php
/**
* Graphs
*
* This class handles building pretty report graphs
*
* @package EDD
* @subpackage Admin/Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.9
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Graph Class
*
* @since 1.9
*/
class EDD_Graph {
/*
Simple example:
data format for each point: array( location on x, location on y )
$data = array(
'Label' => array(
array( 1, 5 ),
array( 3, 8 ),
array( 10, 2 )
),
'Second Label' => array(
array( 1, 7 ),
array( 4, 5 ),
array( 12, 8 )
)
);
$graph = new EDD_Graph( $data );
$graph->display();
*/
/**
* Data to graph
*
* @var array
* @since 1.9
*/
private $data;
/**
* Unique ID for the graph
*
* @var string
* @since 1.9
*/
private $id = '';
/**
* Graph options
*
* @var array
* @since 1.9
*/
private $options = array();
/**
* Get things started
*
* @since 1.9
*/
public function __construct( $_data ) {
$this->data = $_data;
// Generate unique ID
$this->id = 'a' . md5( rand() );
// Setup default options;
$this->options = array(
'y_mode' => null,
'x_mode' => null,
'y_decimals' => 0,
'x_decimals' => 0,
'y_position' => 'right',
'time_format' => '%d/%b',
'ticksize_unit' => 'day',
'ticksize_num' => 1,
'multiple_y_axes' => false,
'bgcolor' => '#f9f9f9',
'bordercolor' => '#ccc',
'color' => '#bbb',
'borderwidth' => 2,
'bars' => false,
'lines' => true,
'points' => true,
'additional_options' => '',
);
}
/**
* Set an option
*
* @param $key The option key to set
* @param $value The value to assign to the key
* @since 1.9
*/
public function set( $key, $value ) {
$this->options[ $key ] = $value;
}
/**
* Get an option
*
* @param $key The option key to get
* @since 1.9
*/
public function get( $key ) {
return isset( $this->options[ $key ] ) ? $this->options[ $key ] : false;
}
/**
* Get graph data
*
* @since 1.9
*/
public function get_data() {
return apply_filters( 'edd_get_graph_data', $this->data, $this );
}
/**
* Load the graphing library script
*
* @since 1.9
*/
public function load_scripts() {
wp_enqueue_script( 'edd-jquery-flot', EDD_PLUGIN_URL . 'assets/js/vendor/jquery.flot.min.js' );
wp_enqueue_script( 'edd-jquery-flot-time', EDD_PLUGIN_URL . 'assets/js/vendor/jquery.flot.time.min.js' );
/**
* Fires immediately after the legacy Flot JS graphing framework is enqueued.
*
* @since 1.9
*/
do_action( 'edd_graph_load_scripts' );
}
/**
* Build the graph and return it as a string
*
* @var array
* @since 1.9
* @return string
*/
public function build_graph() {
$yaxis_count = 1;
$this->load_scripts();
ob_start();
?>
<script type="text/javascript">
jQuery( document ).ready( function($) {
$.plot(
$("#edd-graph-<?php echo $this->id; ?>"),
[
<?php foreach( $this->get_data() as $label => $data ) : ?>
{
label: "<?php echo esc_attr( $label ); ?>",
id: "<?php echo sanitize_key( $label ); ?>",
// data format is: [ point on x, value on y ]
data: [<?php foreach( $data as $point ) { echo '[' . implode( ',', $point ) . '],'; } ?>],
points: {
show: <?php echo $this->options['points'] ? 'true' : 'false'; ?>,
},
bars: {
show: <?php echo $this->options['bars'] ? 'true' : 'false'; ?>,
barWidth: 12,
aling: 'center'
},
lines: {
show: <?php echo $this->options['lines'] ? 'true' : 'false'; ?>
},
<?php if( $this->options['multiple_y_axes'] ) : ?>
yaxis: <?php echo $yaxis_count; ?>
<?php endif; ?>
},
<?php $yaxis_count++; endforeach; ?>
],
{
// Options
grid: {
show: true,
aboveData: false,
color: "<?php echo $this->options['color']; ?>",
backgroundColor: "<?php echo $this->options['bgcolor']; ?>",
borderColor: "<?php echo $this->options['bordercolor']; ?>",
borderWidth: <?php echo absint( $this->options['borderwidth'] ); ?>,
clickable: false,
hoverable: true
},
xaxis: {
mode: "<?php echo $this->options['x_mode']; ?>",
timeFormat: "<?php echo $this->options['x_mode'] == 'time' ? $this->options['time_format'] : ''; ?>",
tickSize: "<?php echo $this->options['x_mode'] == 'time' ? '' : $this->options['ticksize_num']; ?>",
<?php if( $this->options['x_mode'] != 'time' ) : ?>
tickDecimals: <?php echo $this->options['x_decimals']; ?>
<?php endif; ?>
},
yaxis: {
position: 'right',
min: 0,
mode: "<?php echo $this->options['y_mode']; ?>",
timeFormat: "<?php echo $this->options['y_mode'] == 'time' ? $this->options['time_format'] : ''; ?>",
<?php if( $this->options['y_mode'] != 'time' ) : ?>
tickDecimals: <?php echo $this->options['y_decimals']; ?>
<?php endif; ?>
},
<?php echo $this->options['additional_options']; ?>
}
);
function edd_flot_tooltip(x, y, contents) {
$('<div id="edd-flot-tooltip">' + contents + '</div>').css( {
position: 'absolute',
display: 'none',
top: y + 5,
left: x + 5,
border: '1px solid #fdd',
padding: '2px',
'background-color': '#fee',
opacity: 0.80,
zIndex: 3,
}).appendTo("body").fadeIn(200);
}
var previousPoint = null;
$("#edd-graph-<?php echo $this->id; ?>").bind("plothover", function (event, pos, item) {
$("#x").text(pos.x.toFixed(2));
$("#y").text(pos.y.toFixed(2));
if (item) {
if (previousPoint != item.dataIndex) {
previousPoint = item.dataIndex;
$("#edd-flot-tooltip").remove();
var x = item.datapoint[0].toFixed(2),
y = item.datapoint[1].toFixed(2);
if( item.series.id.includes('earnings') ) {
if( edd_vars.currency_pos == 'before' ) {
edd_flot_tooltip( item.pageX, item.pageY, item.series.label + ' ' + edd_vars.currency_sign + y );
} else {
edd_flot_tooltip( item.pageX, item.pageY, item.series.label + ' ' + y + edd_vars.currency_sign );
}
} else {
edd_flot_tooltip( item.pageX, item.pageY, item.series.label + ' ' + y.replace( '.00', '' ) );
}
}
} else {
$("#edd-flot-tooltip").remove();
previousPoint = null;
}
});
});
</script>
<div id="edd-graph-<?php echo $this->id; ?>" class="edd-graph" style="height: 300px;"></div>
<?php
return ob_get_clean();
}
/**
* Output the final graph
*
* @since 1.9
*/
public function display() {
do_action( 'edd_before_graph', $this );
echo $this->build_graph();
do_action( 'edd_after_graph', $this );
}
}

View File

@ -0,0 +1,199 @@
<?php
/**
* Graphs
*
* This class handles building pretty report graphs
*
* @package EDD
* @subpackage Admin/Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.4
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Graph Class
*
* @since 2.4
*/
class EDD_Pie_Graph extends EDD_Graph {
/*
Simple example:
data format for each point: array( location on x, location on y )
$data = array(
array( 'Label' => 'value' ),
array( 'Label 2' => 'value 2' ),
);
$graph = new EDD_Pie_Graph( $data );
$graph->display();
*/
/**
* Data to graph
*
* @var array
* @since 2.4
*/
private $data;
/**
* Unique ID for the graph
*
* @var string
* @since 2.4
*/
private $id = '';
/**
* Graph options
*
* @var array
* @since 2.4
*/
private $options = array();
/**
* Get things started
*
* @since 2.4
*/
public function __construct( $_data, $options = array() ) {
$this->data = $_data;
// Set this so filters recieving $this can quickly know if it's a graph they want to modify
$this->type = 'pie';
// Generate unique ID, add 'a' since md5 can leave a numerical first character
$this->id = 'a' . md5( rand() );
// Setup default options;
$this->options = wp_parse_args( $options, array(
'radius' => 1,
'legend' => true,
'legend_formatter' => false,
'legend_columns' => 3,
'legend_position' => 's',
'show_labels' => false,
'label_threshold' => 0.01,
'label_formatter' => 'eddLabelFormatter',
'label_bg_opacity' => 0.75,
'label_radius' => 1,
'height' => '300',
'hoverable' => true,
'clickable' => false,
'threshold' => false,
) );
add_action( 'edd_graph_load_scripts', array( $this, 'load_additional_scripts' ) );
}
/**
* Load the graphing library script
*
* @since 2.4
*/
public function load_additional_scripts() {
wp_enqueue_script( 'edd-jquery-flot-pie', EDD_PLUGIN_URL . 'assets/js/vendor/jquery.flot.pie.min.js' );
}
/**
* Build the graph and return it as a string
*
* @var array
* @since 2.4
* @return string
*/
public function build_graph() {
if ( count( $this->data ) ) {
$this->load_scripts();
ob_start();
?>
<script type="text/javascript">
var <?php echo $this->id; ?>_data = [
<?php foreach ( $this->data as $label => $value ) : ?>
<?php echo '{ label: "' . esc_attr( $label ) . '", data: "' . $value . '" },' . "\n"; ?>
<?php endforeach; ?>
];
var <?php echo $this->id; ?>_options = {
series: {
pie: {
show: true,
radius: <?php echo $this->options['radius']; ?>,
label: [],
},
edd_vars: {
id: '<?php echo $this->id; ?>',
}
},
legend: {
show: <?php echo $this->options['legend']; ?>,
},
grid: {},
};
<?php if ( true === $this->options['show_labels'] ) : ?>
<?php echo $this->id; ?>_options.series.pie.label.show = true;
<?php echo $this->id; ?>_options.series.pie.label.formatter = <?php echo $this->options['label_formatter']; ?>;
<?php echo $this->id; ?>_options.series.pie.label.threshold = <?php echo $this->options['label_threshold']; ?>;
<?php echo $this->id; ?>_options.series.pie.label.radius = <?php echo $this->options['label_radius']; ?>;
<?php echo $this->id; ?>_options.series.pie.label.background = { opacity: <?php echo $this->options['label_bg_opacity']; ?> };
<?php endif; ?>
<?php if ( true === $this->options['legend'] && ! empty( $this->options['legend_formatter'] ) ) : ?>
<?php echo $this->id; ?>_options.legend.labelFormatter = <?php echo $this->options['legend_formatter']; ?>;
<?php echo $this->id; ?>_options.legend.noColumns = <?php echo $this->options['legend_columns']; ?>;
<?php echo $this->id; ?>_options.legend.position = "<?php echo $this->options['legend_position']; ?>";
<?php endif; ?>
<?php if ( true === $this->options['hoverable'] ) : ?>
<?php echo $this->id; ?>_options.grid.hoverable = true;
<?php endif; ?>
<?php if ( true === $this->options['clickable'] ) : ?>
<?php echo $this->id; ?>_options.grid.clickable = true;
<?php endif; ?>
jQuery( document ).ready( function($) {
var <?php echo $this->id; ?>Chart = $('#edd-pie-graph-<?php echo $this->id; ?>');
$.plot( <?php echo $this->id; ?>Chart, <?php echo $this->id; ?>_data, <?php echo $this->id; ?>_options );
<?php if ( ! wp_is_mobile() ) : ?>
$(<?php echo $this->id; ?>Chart).on('plothover', function (event, pos, item) {
$('.edd-legend-item-wrapper').css('background-color', 'inherit');
if ( item ) {
var label = item.series.label;
var id = item.series.edd_vars.id;
var slug = label.toLowerCase().replace(/\s/g, '-');
var legendTarget = '#' + id + slug;
$('.edd-legend-item-wrapper' + legendTarget).css('background-color', '#f0f0f0');
}
});
<?php endif; ?>
});
</script>
<div class="edd-pie-graph-wrap">
<div id="edd-pie-graph-<?php echo $this->id; ?>" class="edd-pie-graph" style="height: <?php echo $this->options['height']; ?>px;"></div>
<div id="edd-pie-legend-<?php echo $this->id; ?>" class="edd-pie-legend"></div>
</div>
<?php
}
return apply_filters( 'edd_pie_graph_output', ob_get_clean(), $this->id, $this->data, $this->options );
}
}

View File

@ -0,0 +1,165 @@
<?php
/**
* Customers Export Class
*
* This class handles customer export
*
* @package EDD
* @subpackage Admin/Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.4.4
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Customers_Export Class
*
* @since 1.4.4
*/
class EDD_Customers_Export extends EDD_Export {
/**
* Our export type. Used for export-type specific filters/actions
*
* @var string
* @since 1.4.4
*/
public $export_type = 'customers';
/**
* Set the export headers
*
* @since 1.4.4
* @return void
*/
public function headers() {
edd_set_time_limit();
$extra = '';
if ( ! empty( $_POST['edd_export_download'] ) ) {
$extra = sanitize_title( get_the_title( absint( $_POST['edd_export_download'] ) ) ) . '-';
}
nocache_headers();
header( 'Content-Type: text/csv; charset=utf-8' );
header( 'Content-Disposition: attachment; filename="' . apply_filters( 'edd_customers_export_filename', 'edd-export-' . $extra . $this->export_type . '-' . date( 'm-d-Y' ) ) . '.csv"' );
header( 'Expires: 0' );
}
/**
* Set the CSV columns
*
* @since 1.4.4
* @return array $cols All the columns
*/
public function csv_cols() {
if ( ! empty( $_POST['edd_export_download'] ) ) {
$cols = array(
'first_name' => __( 'First Name', 'easy-digital-downloads' ),
'last_name' => __( 'Last Name', 'easy-digital-downloads' ),
'email' => __( 'Email', 'easy-digital-downloads' ),
'date' => __( 'Date Purchased', 'easy-digital-downloads' )
);
} else {
$cols = array();
if( 'emails' != $_POST['edd_export_option'] ) {
$cols['name'] = __( 'Name', 'easy-digital-downloads' );
}
$cols['email'] = __( 'Email', 'easy-digital-downloads' );
if( 'full' == $_POST['edd_export_option'] ) {
$cols['purchases'] = __( 'Total Purchases', 'easy-digital-downloads' );
$cols['amount'] = __( 'Total Purchased', 'easy-digital-downloads' ) . ' (' . html_entity_decode( edd_currency_filter( '' ) ) . ')';
}
}
return $cols;
}
/**
* Get the Export Data
*
* @since 1.4.4
* @global object $wpdb Used to query the database using the WordPress
* Database API
* @global object $edd_logs EDD Logs Object
* @return array $data The data for the CSV file
*/
public function get_data() {
$data = array();
if ( ! empty( $_POST['edd_export_download'] ) ) {
$edd_logs = EDD()->debug_log;
$args = array(
'post_parent' => absint( $_POST['edd_export_download'] ),
'log_type' => 'sale',
'nopaging' => true
);
if( isset( $_POST['edd_price_option'] ) ) {
$args['meta_query'] = array(
array(
'key' => '_edd_log_price_id',
'value' => (int) $_POST['edd_price_option']
)
);
}
$logs = $edd_logs->get_connected_logs( $args );
if ( $logs ) {
foreach ( $logs as $log ) {
$payment_id = get_post_meta( $log->ID, '_edd_log_payment_id', true );
$user_info = edd_get_payment_meta_user_info( $payment_id );
$data[] = array(
'first_name' => $user_info['first_name'],
'last_name' => $user_info['last_name'],
'email' => $user_info['email'],
'date' => $log->post_date
);
}
}
} else {
// Export all customers
$customers = edd_get_customers( array(
'limit' => 9999999,
) );
$i = 0;
foreach ( $customers as $customer ) {
if( 'emails' != $_POST['edd_export_option'] ) {
$data[$i]['name'] = $customer->name;
}
$data[$i]['email'] = $customer->email;
if( 'full' == $_POST['edd_export_option'] ) {
$data[$i]['purchases'] = $customer->purchase_count;
$data[$i]['amount'] = edd_format_amount( $customer->purchase_value );
}
$i++;
}
}
$data = apply_filters( 'edd_export_get_data', $data );
$data = apply_filters( 'edd_export_get_data_' . $this->export_type, $data );
return $data;
}
}

View File

@ -0,0 +1,113 @@
<?php
/**
* Customers Export Class
*
* This class handles customer export
*
* @package EDD
* @subpackage Admin/Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.4.4
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Download_History_Export Class
*
* @since 1.4.4
*/
class EDD_Download_History_Export extends EDD_Export {
/**
* Our export type. Used for export-type specific filters/actions
*
* @var string
* @since 1.4.4
*/
public $export_type = 'download_history';
/**
* Set the export headers
*
* @since 1.4.4
* @return void
*/
public function headers() {
edd_set_time_limit();
$month = isset( $_POST['month'] ) ? absint( $_POST['month'] ) : date( 'n' );
$year = isset( $_POST['year'] ) ? absint( $_POST['year'] ) : date( 'Y' );
nocache_headers();
header( 'Content-Type: text/csv; charset=utf-8' );
header( 'Content-Disposition: attachment; filename="' . apply_filters( 'edd_download_history_export_filename', 'edd-export-' . $this->export_type . '-' . $month . '-' . $year ) . '.csv"' );
header( 'Expires: 0' );
}
/**
* Set the CSV columns
*
* @since 1.4.4
* @return array $cols All the columns
*/
public function csv_cols() {
$cols = array(
'date' => __( 'Date', 'easy-digital-downloads' ),
'user' => __( 'Downloaded by', 'easy-digital-downloads' ),
'ip' => __( 'IP Address', 'easy-digital-downloads' ),
'download' => __( 'Product', 'easy-digital-downloads' ),
'file' => __( 'File', 'easy-digital-downloads' )
);
return $cols;
}
/**
* Get the Export Data
*
* @since 1.4.4
* @global object $edd_logs EDD Logs Object
* @return array $data The data for the CSV file
*/
public function get_data() {
$edd_logs = EDD()->debug_log;
$data = array();
$args = array(
'nopaging' => true,
'log_type' => 'file_download',
'monthnum' => isset( $_POST['month'] ) ? absint( $_POST['month'] ) : date( 'n' ),
'year' => isset( $_POST['year'] ) ? absint( $_POST['year'] ) : date( 'Y' )
);
$logs = $edd_logs->get_connected_logs( $args );
if ( $logs ) {
foreach ( $logs as $log ) {
$user_info = get_post_meta( $log->ID, '_edd_log_user_info', true );
$files = edd_get_download_files( $log->post_parent );
$file_id = (int) get_post_meta( $log->ID, '_edd_log_file_id', true );
$file_name = isset( $files[ $file_id ]['name'] ) ? $files[ $file_id ]['name'] : null;
$user = get_userdata( $user_info['id'] );
$user = $user ? $user->user_login : $user_info['email'];
$data[] = array(
'date' => $log->post_date,
'user' => $user,
'ip' => get_post_meta( $log->ID, '_edd_log_ip', true ),
'download' => get_the_title( $log->post_parent ),
'file' => $file_name
);
}
}
$data = apply_filters( 'edd_export_get_data', $data );
$data = apply_filters( 'edd_export_get_data_' . $this->export_type, $data );
return $data;
}
}

View File

@ -0,0 +1,201 @@
<?php
/**
* Payments Export Class
*
* This class handles payment export
*
* @package EDD
* @subpackage Admin/Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.4.4
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Payments_Export Class
*
* @since 1.4.4
*/
class EDD_Payments_Export extends EDD_Export {
/**
* Our export type. Used for export-type specific filters/actions
* @var string
* @since 1.4.4
*/
public $export_type = 'payments';
/**
* Set the export headers
*
* @since 1.6
* @return void
*/
public function headers() {
edd_set_time_limit();
$month = isset( $_POST['month'] ) ? absint( $_POST['month'] ) : date( 'n' );
$year = isset( $_POST['year'] ) ? absint( $_POST['year'] ) : date( 'Y' );
nocache_headers();
header( 'Content-Type: text/csv; charset=utf-8' );
header( 'Content-Disposition: attachment; filename="' . apply_filters( 'edd_payments_export_filename', 'edd-export-' . $this->export_type . '-' . $month . '-' . $year ) . '.csv"' );
header( 'Expires: 0' );
}
/**
* Set the CSV columns
*
* @since 1.4.4
* @return array $cols All the columns
*/
public function csv_cols() {
$cols = array(
'id' => __( 'ID', 'easy-digital-downloads' ), // unaltered payment ID (use for querying)
'seq_id' => __( 'Payment Number', 'easy-digital-downloads' ), // sequential payment ID
'email' => __( 'Email', 'easy-digital-downloads' ),
'first' => __( 'First Name', 'easy-digital-downloads' ),
'last' => __( 'Last Name', 'easy-digital-downloads' ),
'address1' => __( 'Address', 'easy-digital-downloads' ),
'address2' => __( 'Address (Line 2)', 'easy-digital-downloads' ),
'city' => __( 'City', 'easy-digital-downloads' ),
'state' => __( 'State', 'easy-digital-downloads' ),
'country' => __( 'Country', 'easy-digital-downloads' ),
'zip' => __( 'Zip / Postal Code', 'easy-digital-downloads' ),
'products' => __( 'Products', 'easy-digital-downloads' ),
'skus' => __( 'SKUs', 'easy-digital-downloads' ),
'currency' => __( 'Currency', 'easy-digital-downloads' ),
'amount' => __( 'Amount', 'easy-digital-downloads' ),
'tax' => __( 'Tax', 'easy-digital-downloads' ),
'discount' => __( 'Discount Code', 'easy-digital-downloads' ),
'gateway' => __( 'Payment Method', 'easy-digital-downloads' ),
'trans_id' => __( 'Transaction ID', 'easy-digital-downloads' ),
'key' => __( 'Purchase Key', 'easy-digital-downloads' ),
'date' => __( 'Date', 'easy-digital-downloads' ),
'user' => __( 'User', 'easy-digital-downloads' ),
'status' => __( 'Status', 'easy-digital-downloads' )
);
if( ! edd_use_skus() ){
unset( $cols['skus'] );
}
if ( ! edd_get_option( 'enable_sequential' ) ) {
unset( $cols['seq_id'] );
}
return $cols;
}
/**
* Get the Export Data
*
* @since 1.4.4
* @global object $wpdb Used to query the database using the WordPress
* Database API
* @return array $data The data for the CSV file
*/
public function get_data() {
global $wpdb;
$data = array();
$payments = edd_get_payments( array(
'offset' => 0,
'number' => 9999999,
'mode' => edd_is_test_mode() ? 'test' : 'live',
'status' => isset( $_POST['edd_export_payment_status'] ) ? $_POST['edd_export_payment_status'] : 'any',
'month' => isset( $_POST['month'] ) ? absint( $_POST['month'] ) : date( 'n' ),
'year' => isset( $_POST['year'] ) ? absint( $_POST['year'] ) : date( 'Y' )
) );
foreach ( $payments as $payment ) {
$payment_meta = edd_get_payment_meta( $payment->ID );
$user_info = edd_get_payment_meta_user_info( $payment->ID );
$downloads = edd_get_payment_meta_cart_details( $payment->ID );
$total = edd_get_payment_amount( $payment->ID );
$user_id = isset( $user_info['id'] ) && $user_info['id'] != -1 ? $user_info['id'] : $user_info['email'];
$products = '';
$skus = '';
if ( $downloads ) {
foreach ( $downloads as $key => $download ) {
// Download ID
$id = isset( $payment_meta['cart_details'] ) ? $download['id'] : $download;
// If the download has variable prices, override the default price
$price_override = isset( $payment_meta['cart_details'] ) ? $download['price'] : null;
$price = edd_get_download_final_price( $id, $user_info, $price_override );
// Display the Downoad Name
$products .= get_the_title( $id ) . ' - ';
if ( edd_use_skus() ) {
$sku = edd_get_download_sku( $id );
if ( ! empty( $sku ) )
$skus .= $sku;
}
if ( isset( $downloads[ $key ]['item_number'] ) && isset( $downloads[ $key ]['item_number']['options'] ) ) {
$price_options = $downloads[ $key ]['item_number']['options'];
if ( isset( $price_options['price_id'] ) ) {
$products .= edd_get_price_option_name( $id, $price_options['price_id'], $payment->ID ) . ' - ';
}
}
$products .= html_entity_decode( edd_currency_filter( $price ) );
if ( $key != ( count( $downloads ) -1 ) ) {
$products .= ' / ';
if( edd_use_skus() )
$skus .= ' / ';
}
}
}
if ( is_numeric( $user_id ) ) {
$user = get_userdata( $user_id );
} else {
$user = false;
}
$currency_code = edd_get_payment_currency_code( $payment->ID );
$data[] = array(
'id' => $payment->ID,
'seq_id' => edd_get_payment_number( $payment->ID ),
'email' => $payment_meta['email'],
'first' => $user_info['first_name'],
'last' => $user_info['last_name'],
'address1' => isset( $user_info['address']['line1'] ) ? $user_info['address']['line1'] : '',
'address2' => isset( $user_info['address']['line2'] ) ? $user_info['address']['line2'] : '',
'city' => isset( $user_info['address']['city'] ) ? $user_info['address']['city'] : '',
'state' => isset( $user_info['address']['state'] ) ? $user_info['address']['state'] : '',
'country' => isset( $user_info['address']['country'] ) ? $user_info['address']['country'] : '',
'zip' => isset( $user_info['address']['zip'] ) ? $user_info['address']['zip'] : '',
'products' => $products,
'skus' => $skus,
'currency' => $currency_code,
'amount' => html_entity_decode( edd_format_amount( $total, $currency_code ) ),
'tax' => html_entity_decode( edd_format_amount( edd_get_payment_tax( $payment->ID, $payment_meta ), $currency_code ) ),
'discount' => isset( $user_info['discount'] ) && $user_info['discount'] != 'none' ? $user_info['discount'] : __( 'none', 'easy-digital-downloads' ),
'gateway' => edd_get_gateway_admin_label( edd_get_payment_meta( $payment->ID, '_edd_payment_gateway', true ) ),
'trans_id' => edd_get_payment_transaction_id( $payment->ID ),
'key' => $payment_meta['key'],
'date' => $payment->post_date,
'user' => $user ? $user->display_name : __( 'guest', 'easy-digital-downloads' ),
'status' => edd_get_payment_status( $payment, true )
);
}
$data = apply_filters( 'edd_export_get_data', $data );
$data = apply_filters( 'edd_export_get_data_' . $this->export_type, $data );
return $data;
}
}

View File

@ -0,0 +1,202 @@
<?php
/**
* Base export class.
*
* This is the base class for all export methods. Each data export type (customers, payments, etc) extend this class.
*
* @package EDD
* @subpackage Admin/Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.4.4
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Export Class
*
* @since 1.4.4
*/
class EDD_Export {
/**
* Our export type. Used for export-type specific filters/actions
* @var string
* @since 1.4.4
*/
public $export_type = 'default';
/**
* Can we export?
*
* @since 1.4.4
*
* @return bool True if exporting is allowed, false otherwise.
*/
public function can_export() {
return (bool) apply_filters( 'edd_export_capability', current_user_can( 'export_shop_reports' ) );
}
/**
* Set the export headers.
*
* @since 1.4.4
* @since 3.0 Add BOM to the CSV export.
*/
public function headers() {
edd_set_time_limit();
nocache_headers();
header( 'Content-Type: text/csv; charset=utf-8' );
header( 'Content-Disposition: attachment; filename="edd-export-' . $this->export_type . '-' . date( 'm-d-Y' ) . '.csv"' );
header( 'Expires: 0' );
/**
* We need to append a BOM to the export so that Microsoft Excel knows
* that the file is in Unicode.
*
* @see https://github.com/easydigitaldownloads/easy-digital-downloads/issues/4859
*/
echo "\xEF\xBB\xBF";
}
/**
* Set the CSV columns.
*
* @since 1.4.4
*
* @return array $cols CSV columns.
*/
public function csv_cols() {
$cols = array(
'id' => __( 'ID', 'easy-digital-downloads' ),
'date' => __( 'Date', 'easy-digital-downloads' ),
);
return $cols;
}
/**
* Retrieve the CSV columns.
*
* @since 1.4.4
*
* @return array $cols Array of the columns.
*/
public function get_csv_cols() {
$cols = $this->csv_cols();
return apply_filters( 'edd_export_csv_cols_' . $this->export_type, $cols );
}
/**
* Output the CSV columns.
*
* @since 1.4.4
*/
public function csv_cols_out() {
$cols = $this->get_csv_cols();
$i = 1;
// Output each column.
foreach ( $cols as $col_id => $column ) {
echo '"' . addslashes( $column ) . '"';
echo count( $cols ) === $i
? ''
: ',';
$i++;
}
echo "\r\n";
}
/**
* Get the data being exported.
*
* @since 1.4.4
*
* @return array $data Data for export.
*/
public function get_data() {
// Just a sample data array
$data = array(
0 => array(
'id' => '',
'data' => date( 'F j, Y' ),
),
1 => array(
'id' => '',
'data' => date( 'F j, Y' ),
),
);
$data = apply_filters( 'edd_export_get_data', $data );
$data = apply_filters( 'edd_export_get_data_' . $this->export_type, $data );
return $data;
}
/**
* Output the CSV rows.
*
* @since 1.4.4
*/
public function csv_rows_out() {
$data = $this->get_data();
$cols = $this->get_csv_cols();
// Output each row.
foreach ( $data as $row ) {
$i = 1;
foreach ( $row as $col_id => $column ) {
// Make sure the column is valid.
if ( array_key_exists( $col_id, $cols ) ) {
echo '"' . addslashes( $column ) . '"';
echo count( $cols ) === $i
? ''
: ',';
$i++;
}
}
echo "\r\n";
}
}
/**
* Perform the export.
*
* @since 1.4.4
*
*
* @uses EDD_Export::can_export()
* @uses EDD_Export::headers()
* @uses EDD_Export::csv_cols_out()
* @uses EDD_Export::csv_rows_out()
*/
public function export() {
// Bail if user if unauthorized.
if ( ! $this->can_export() ) {
wp_die( __( 'You do not have permission to export data.', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
// Set headers
$this->headers();
// Output CSV columns (headers)
$this->csv_cols_out();
// Output CSV rows
$this->csv_rows_out();
edd_die();
}
}

View File

@ -0,0 +1,257 @@
<?php
/**
* File Downloads Log List Table.
*
* @package EDD
* @subpackage Admin/Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.4.4
* @since 3.0 Updated to use the custom tables.
*/
use EDD\Logs\File_Download_Log;
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_File_Downloads_Log_Table Class
*
* @since 1.4
* @since 3.0 Updated to use the custom tables and new query classes.
*/
class EDD_File_Downloads_Log_Table extends EDD_Base_Log_List_Table {
/**
* Log type
*
* @var string
*/
protected $log_type = 'file_downloads';
/**
* Are we searching for files?
*
* @var bool
* @since 1.4
*/
public $file_search = false;
/**
* Store each unique product's files so they only need to be queried once
*
* @var array
* @since 1.9
*/
private $queried_files = array();
/**
* Get things started
*
* @since 1.4
* @see WP_List_Table::__construct()
*/
public function __construct() {
parent::__construct();
}
/**
* This function renders most of the columns in the list table.
*
* @since 1.4
*
* @param array $item Contains all the data of the log item.
* @param string $column_name The name of the column.
*
* @return string Column Name
*/
public function column_default( $item, $column_name ) {
$base_url = remove_query_arg( 'paged' );
switch ( $column_name ) {
case 'download' :
$download = new EDD_Download( $item[ $column_name ] );
$column_value = ! empty( $item['price_id'] )
? edd_get_download_name( $download->ID, $item['price_id'] )
: edd_get_download_name( $download->ID );
return '<a href="' . esc_url( add_query_arg( 'download', absint( $download->ID ), $base_url ) ) . '" >' . esc_html( $column_value ) . '</a>';
case 'customer' :
return ! empty( $item[ 'customer' ]->id )
? '<a href="' . esc_url( add_query_arg( 'customer', absint( $item['customer']->id ), $base_url ) ) . '">' . esc_html( $item['customer']->name ) . '</a>'
: '&mdash;';
case 'payment_id' :
$number = edd_get_payment_number( $item['payment_id'] );
return ! empty( $number )
? '<a href="' . esc_url( edd_get_admin_url( array( 'page' => 'edd-payment-history', 'view' => 'view-order-details', 'id' => absint( $item['payment_id'] ) ) ) ) . '">' . esc_html( $number ) . '</a>'
: '&mdash;';
case 'ip' :
return '<a href="' . esc_url( 'https://ipinfo.io/' . esc_attr( $item['ip'] ) ) . '" target="_blank" rel="noopener noreferrer">' . esc_html( $item['ip'] ) . '</a>';
case 'file':
return ! empty( $item['file'] )
? esc_html( $item['file'] )
: '&mdash;';
default:
return $item[ $column_name ];
}
}
/**
* Set the table columns.
*
* @since 1.4
*
* @return array $columns Array of all the list table columns
*/
public function get_columns() {
return array(
'ID' => __( 'Log ID', 'easy-digital-downloads' ),
'download' => edd_get_label_singular(),
'customer' => __( 'Customer', 'easy-digital-downloads' ),
'payment_id' => __( 'Order Number', 'easy-digital-downloads' ),
'file' => __( 'File', 'easy-digital-downloads' ),
'ip' => __( 'IP Address', 'easy-digital-downloads' ),
'user_agent' => __( 'User Agent', 'easy-digital-downloads' ),
'date' => __( 'Date', 'easy-digital-downloads' )
);
}
/**
* Gets the log entries for the current view
*
* @since 1.4
*
* @param $log_query array Arguments for getting logs.
*
* @return array $logs_data Array of all the logs.
*/
function get_logs( $log_query = array() ) {
$logs_data = array();
$logs = edd_get_file_download_logs( $log_query );
if ( $logs ) {
foreach ( $logs as $log ) {
/** @var $log File_Download_Log */
$customer_id = ! empty( $log->customer_id ) ? (int) $log->customer_id : edd_get_payment_customer_id( $log->order_id );
$files = $this->get_log_files( $log, $customer_id );
$file_id = $log->file_id;
/**
* Filters the ID of the file that was actually downloaded from this log.
*
* @param int $file_id
* @param File_Download_Log $log
*/
$file_id = apply_filters( 'edd_log_file_download_file_id', $file_id, $log );
$file_name = '';
if ( ! empty( $files ) && is_numeric( $file_id ) && isset( $files[ $file_id ] ) ) {
$file_name = ! empty( $files[ $file_id ]['name'] )
? $files[ $file_id ]['name']
: edd_get_file_name( $files[ $file_id ] );
}
if ( empty( $this->file_search ) || ( ! empty( $this->file_search ) && strpos( strtolower( $file_name ), strtolower( $this->get_search() ) ) !== false ) ) {
$logs_data[] = array(
'ID' => $log->id,
'download' => $log->product_id,
'customer' => new EDD_Customer( $customer_id ),
'payment_id' => $log->order_id,
'price_id' => $log->price_id,
'file' => $file_name,
'ip' => $log->ip,
'user_agent' => $log->user_agent,
'date' => $log->date_created,
);
}
}
}
return $logs_data;
}
/**
* Retrieves the array of files associated with the log.
*
* This method contains a lot of logic to ensure backwards compatibility with how
* the `edd_log_file_download_download_files` filter was formatted in EDD 2.x.
*
* @since 3.0
*
* @param File_Download_Log $log
* @param int $customer_id
*
* @return array
*/
protected function get_log_files( File_Download_Log $log, $customer_id ) {
$customer = ! empty( $customer_id ) ? edd_get_customer( $customer_id ) : false;
/*
* Get the files associated with this download and store them in a property to prevent
* multiple queries for the same download.
* This is needed for backwards compatibility in the `edd_log_file_download_download_files` filter.
*/
if ( ! array_key_exists( $log->product_id, $this->queried_files ) ) {
$files = get_post_meta( $log->product_id, 'edd_download_files', true );
$this->queried_files[ $log->product_id ] = $files;
} else {
$files = $this->queried_files[ $log->product_id ];
}
/*
* User info is needed for backwards compatibility in the `edd_log_file_download_download_files` filter.
*/
$user = ! empty( $customer->user_id ) ? get_userdata( $customer->user_id ) : false;
$user_info = ! empty( $user )
? array(
'id' => $user->ID,
'email' => $user->user_email,
'name' => $user->display_name,
)
: array();
/*
* Build up an array of meta.
*/
$meta = edd_get_file_download_log_meta( $log->id );
// These meta keys no longer exist, but we need to create them for use in the filter.
$backwards_compat_meta = array(
'_edd_log_user_info' => $user_info,
'_edd_log_user_id' => ! empty( $customer->user_id ) ? $customer->user_id : false,
'_edd_log_customer_id' => $customer_id,
'_edd_log_file_id' => $log->file_id,
'_edd_log_ip' => $log->ip,
'_edd_log_payment_id' => $log->order_id,
'_edd_log_price_id' => $log->price_id,
);
foreach( $backwards_compat_meta as $meta_key => $meta_value ) {
$meta[ $meta_key ] = array( $meta_value );
}
/**
* Filters the array of all files linked to the product.
*
* @param array $files Files linked to the product.
* @param File_Download_Log $log Log record from the database.
* @param array $meta What used to be the meta array in EDD 2.9 and lower.
*/
return apply_filters( 'edd_log_file_download_download_files', $files, $log, $meta );
}
/**
* Setup the final data for the table.
*
* @since 1.5
*/
public function get_total( $log_query = array() ) {
return edd_count_file_download_logs( $log_query );
}
}

View File

@ -0,0 +1,158 @@
<?php
/**
* Gateway Error Log View Class
*
* @package EDD
* @subpackage Admin/Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Gateway_Error_Log_Table Class
*
* @since 1.4
* @since 3.0 Updated to use the custom tables and new query classes.
*/
class EDD_Gateway_Error_Log_Table extends EDD_Base_Log_List_Table {
/**
* Get things started
*
* @since 1.4
* @see WP_List_Table::__construct()
*/
public function __construct() {
parent::__construct();
}
/**
* Gets the name of the primary column.
*
* @since 2.5
* @access protected
*
* @return string Name of the primary column.
*/
protected function get_primary_column_name() {
return 'ID';
}
/**
* This function renders most of the columns in the list table.
*
* @since 1.4
*
* @param array $item Contains all the data of the log item
* @param string $column_name The name of the column
*
* @return string Column Name
*/
public function column_default( $item, $column_name ) {
switch ( $column_name ) {
case 'error' :
return $item['error'];
case 'payment_id' :
return ! empty( $item['payment_id'] ) ? $item['payment_id'] : '&ndash;';
default:
return $item[ $column_name ];
}
}
/**
* Output Error Message Column
*
* @since 1.4.4
* @param array $item Contains all the data of the log
* @return void
*/
public function column_message( $item ) {
?>
<a href="#TB_inline?width=640&amp;inlineId=log-message-<?php echo esc_attr( $item['ID'] ); ?>" class="thickbox"><?php esc_html_e( 'View Log Message', 'easy-digital-downloads' ); ?></a>
<div id="log-message-<?php echo esc_attr( $item['ID'] ); ?>" style="display:none;">
<?php
$log_message = $item['content'];
$serialized = strpos( $log_message, '{"' );
// Check to see if the log message contains serialized information
if ( $serialized !== false ) {
$length = strlen( $log_message ) - $serialized;
$intro = substr( $log_message, 0, - $length );
$data = substr( $log_message, $serialized, strlen( $log_message ) - 1 );
echo wpautop( $intro );
echo '<strong>' . wpautop( __( 'Log data:', 'easy-digital-downloads' ) ) . '</strong>';
echo '<div style="word-wrap: break-word;">' . wpautop( $data ) . '</div>';
} else {
// No serialized data found
echo wpautop( $log_message );
}
?>
</div>
<?php
}
/**
* Retrieve the table columns
*
* @since 1.4
* @return array $columns Array of all the list table columns
*/
public function get_columns() {
return array(
'ID' => __( 'Log ID', 'easy-digital-downloads' ),
'payment_id' => __( 'Order Number', 'easy-digital-downloads' ),
'error' => __( 'Error', 'easy-digital-downloads' ),
'message' => __( 'Error Message', 'easy-digital-downloads' ),
'gateway' => __( 'Gateway', 'easy-digital-downloads' ),
'date' => __( 'Date', 'easy-digital-downloads' )
);
}
/**
* Gets the log entries for the current view
*
* @since 1.4
* @param array $log_query Query arguments
* @global object $edd_logs EDD Logs Object
* @return array $logs_data Array of all the Log entries
*/
public function get_logs( $log_query = array() ) {
$logs_data = array();
$log_query['type'] = 'gateway_error';
$logs = edd_get_logs( $log_query );
if ( $logs ) {
foreach ( $logs as $log ) {
/** @var $log EDD\Logs\Log */
$logs_data[] = array(
'ID' => $log->id,
'payment_id' => $log->object_id,
'error' => $log->title ? $log->title : __( 'Payment Error', 'easy-digital-downloads' ),
'gateway' => edd_get_payment_gateway( $log->object_id ),
'date' => $log->date_created,
'content' => $log->content,
);
}
}
return $logs_data;
}
/**
* Setup the final data for the table.
*
* @since 1.5
*/
public function get_total( $log_query = array() ) {
$log_query['type'] = 'gateway_error';
return edd_count_logs( $log_query );
}
}

View File

@ -0,0 +1,159 @@
<?php
/**
* Gateways Reports Table Class
*
* @package EDD
* @subpackage Admin/Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.5
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
use EDD\Admin\List_Table;
/**
* EDD_Gateway_Reports_Table Class
*
* Renders the Download Reports table
*
* @since 1.5
*/
class EDD_Gateway_Reports_Table extends List_Table {
/**
* Get things started
*
* @since 1.5
* @see WP_List_Table::__construct()
*/
public function __construct() {
parent::__construct( array(
'singular' => 'report-gateway',
'plural' => 'report-gateways',
'ajax' => false
) );
}
/**
* Gets the name of the primary column.
*
* @since 2.5
* @access protected
*
* @return string Name of the primary column.
*/
protected function get_primary_column_name() {
return 'label';
}
/**
* This function renders most of the columns in the list table.
*
* @since 1.5
*
* @param array $item Contains all the data of the downloads
* @param string $column_name The name of the column
*
* @return string Column Name
*/
public function column_default( $item, $column_name ) {
return $item[ $column_name ];
}
/**
* Retrieve the table columns
*
* @since 1.5
* @return array $columns Array of all the list table columns
*/
public function get_columns() {
return array(
'label' => __( 'Gateway', 'easy-digital-downloads' ),
'complete_sales' => __( 'Complete Sales', 'easy-digital-downloads' ),
'pending_sales' => __( 'Pending / Failed Sales', 'easy-digital-downloads' ),
'total_sales' => __( 'Total Sales', 'easy-digital-downloads' )
);
}
/**
* Outputs the reporting views
*
* @since 1.5
* @return void
*/
public function bulk_actions( $which = '' ) {
// These aren't really bulk actions but this outputs the markup in
// the right place.
edd_report_views();
}
/**
* Builds and retrieves all of the payment gateways reports data.
*
* @since 1.5
* @deprecated 3.0 Use get_data()
*
* @return array All the data for customer reports.
*/
public function reports_data() {
_edd_deprecated_function( __METHOD__, '3.0', 'EDD_Gateway_Reports_Table::get_data()' );
return $this->get_data();
}
/**
* Retrieves all of the payment gateways reports data.
*
* @since 3.0
*
* @return array Payment gateways reports table data.
*/
public function get_data() {
$reports_data = array();
$gateways = edd_get_payment_gateways();
foreach ( $gateways as $gateway_id => $gateway ) {
$complete_count = edd_count_sales_by_gateway( $gateway_id, edd_get_gross_order_statuses() );
$pending_count = edd_count_sales_by_gateway( $gateway_id, edd_get_incomplete_order_statuses() );
$reports_data[] = array(
'ID' => $gateway_id,
'label' => $gateway['admin_label'],
'complete_sales' => edd_format_amount( $complete_count, false ),
'pending_sales' => edd_format_amount( $pending_count, false ),
'total_sales' => edd_format_amount( $complete_count + $pending_count, false ),
);
}
return $reports_data;
}
/**
* Setup the final data for the table
*
* @since 1.5
* @uses EDD_Gateway_Reports_Table::get_columns()
* @uses EDD_Gateway_Reports_Table::get_sortable_columns()
* @uses EDD_Gateway_Reports_Table::reports_data()
* @return void
*/
public function prepare_items() {
$columns = $this->get_columns();
$hidden = array(); // No hidden columns
$sortable = $this->get_sortable_columns();
$this->_column_headers = array( $columns, $hidden, $sortable );
$this->items = $this->get_data();
}
}
/**
* Back-compat for typo
*
* @see https://github.com/easydigitaldownloads/easy-digital-downloads/issues/6549
*/
class_alias( 'EDD_Gateway_Reports_Table', 'EDD_Gateawy_Reports_Table' );

View File

@ -0,0 +1,51 @@
<?php
/**
* Reports Sections Class.
*
* @package EDD
* @subpackage Admin
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 3.0
*/
namespace EDD\Admin;
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Class for creating a vertically tabbed UI for reports.
*
* @since 3.0
*/
class Reports_Sections extends Sections {
/**
* Output the contents
*
* @since 3.0
*/
public function display() {
$use_js = ! empty( $this->use_js )
? ' use-js'
: '';
$role = $this->use_js ? 'tablist' : 'menu';
?>
<div class="edd-sections-wrap edd-reports-wrapper">
<div class="edd-vertical-sections<?php echo $use_js; ?>">
<ul class="section-nav" role="<?php echo esc_attr( $role ); ?>">
<?php echo $this->get_all_section_links(); ?>
</ul>
<div class="section-wrap">
<?php echo $this->get_all_section_contents(); ?>
</div>
<br class="clear">
</div>
</div>
<?php
}
}

View File

@ -0,0 +1,263 @@
<?php
/**
* Sales Log Table.
*
* @package EDD
* @subpackage Admin/Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Sales_Log_Table Class.
*
* @since 1.4
* @since 3.0 Updated to use the custom tables and new query classes.
* Updated table to display order items as sales logs have been deprecated.
*/
class EDD_Sales_Log_Table extends EDD_Base_Log_List_Table {
/**
* Gets the name of the primary column.
*
* @since 2.5
* @access protected
*
* @return string Name of the primary column.
*/
protected function get_primary_column_name() {
return 'ID';
}
/**
* This function renders most of the columns in the list table.
*
* @since 1.4
*
* @param array $item Contains all the data of the log item.
* @param string $column_name The name of the column.
*
* @return string Column data.
*/
public function column_default( $item, $column_name ) {
$return = '';
$currency = $item['currency'];
switch ( $column_name ) {
case 'download':
$download_id = $item[ $column_name ];
$download = edd_get_download( $download_id );
$price_id = isset( $item['price_id'] ) && is_numeric( $item['price_id'] )
? absint( $item['price_id'] )
: null;
$title = $download->get_name( $price_id );
$return = '<a href="' . esc_url( add_query_arg( 'download', urlencode( $item[ $column_name ] ) ) ) . '" >' . esc_html( $title ) . '</a>';
break;
case 'customer':
$name = ! empty( $item['customer']->name )
? $item['customer']->name
: '<em>' . __( 'Unnamed Customer', 'easy-digital-downloads' ) . '</em>';
$return = '<a href="' . esc_url( edd_get_admin_url( array( 'page' => 'edd-customers', 'view' => 'overview', 'id' => absint( $item['customer']->id ) ) ) ) . '">#' . esc_html( $item['customer']->id ) . ' ' . esc_html( $name ) . '</a>';
break;
case 'item_price':
$return = edd_currency_filter( edd_format_amount( $item['item_price'] ), $currency );
break;
case 'amount':
$return = edd_currency_filter( edd_format_amount( $item['amount'] / $item['quantity'] ), $currency );
break;
case 'ID':
$return = '<a href="' . esc_url( edd_get_admin_url( array( 'page' => 'edd-payment-history', 'view' => 'view-order-details', 'id' => absint( $item['order_id'] ) ) ) ) . '">' . absint( $item['ID'] ) . '</a>';
break;
default:
$return = $item[ $column_name ];
break;
}
return $return;
}
/**
* Retrieve the table columns
*
* @since 1.4
* @return array $columns Array of all the list table columns
*/
public function get_columns() {
return array(
'ID' => __( 'Order Number', 'easy-digital-downloads' ),
'customer' => __( 'Customer', 'easy-digital-downloads' ),
'download' => edd_get_label_singular(),
'amount' => __( 'Item Amount', 'easy-digital-downloads' ),
'date' => __( 'Date', 'easy-digital-downloads' ),
);
}
/**
* Return array of query arguments.
*
* @since 3.0
*
* @param boolean $paginate
*
* @return array
*/
protected function get_query_args( $paginate = true ) {
$retval = parent::get_query_args( $paginate );
$user = $this->get_filtered_user();
if ( $user ) {
// Show only logs from a specific user
$retval['user_id'] = $user;
}
$search = $this->get_search();
if ( $search ) {
if ( is_email( $search ) ) {
$field = 'email';
} else {
// Look for a user
$field = 'user_id';
if ( ! is_numeric( $search ) ) {
// Searching for user by username
$user = get_user_by( 'login', $search );
if ( $user ) {
// Found one, set meta value to user's ID
$search = $user->ID;
} else {
// No user found so let's do a real search query
$users = new WP_User_Query( array(
'search' => $search,
'search_columns' => array( 'user_url', 'user_nicename' ),
'number' => 1,
'fields' => 'ids',
) );
$found_user = $users->get_results();
if ( $found_user ) {
$search = $found_user[0];
}
}
}
}
if ( ! $this->file_search ) {
$retval[ $field ] = $search;
}
}
return $retval;
}
/**
* Gets the log entries for the current view.
*
* @since 1.4
* @since 3.0 Refactored to fetch from order items table.
*
* @param array $log_query Query vars.
* @return array $data Array of all the sales.
*/
public function get_logs( $log_query = array() ) {
$data = $order_args = array();
// Customer ID
if ( ! empty( $log_query['customer_id'] ) ) {
$order_args = array(
'customer_id' => $log_query['customer_id'],
'no_found_rows' => true
);
// Customer Email
} elseif ( ! empty( $log_query['email'] ) ) {
$order_args = array(
'email' => $log_query['email'],
'no_found_rows' => true
);
}
// Maybe query for orders first
if ( ! empty( $order_args ) ) {
$orders = edd_get_orders( $order_args );
$log_query['order_id__in'] = wp_list_pluck( $orders, 'id' );
}
// Query order items
$order_items = edd_get_order_items( $log_query );
// Bail if no order items
if ( empty( $order_items ) ) {
return $data;
}
// Maybe prime orders
if ( empty( $orders ) ) {
$order_ids = array_values( array_unique( wp_list_pluck( $order_items, 'order_id' ) ) );
if ( count( $order_ids ) > 2 ) {
$orders = edd_get_orders( array(
'id__in' => $order_ids,
'no_found_rows' => true
) );
}
}
// Maybe prime customers
if ( ! empty( $orders ) ) {
$customer_ids = array_values( array_unique( wp_list_pluck( $orders, 'customer_id' ) ) );
if ( count( $customer_ids ) > 2 ) {
edd_get_customers( array(
'id__in' => $customer_ids,
'no_found_rows' => true
) );
}
}
// Loop through order items
foreach ( $order_items as $order_item ) {
$order = edd_get_order( $order_item->order_id );
$data[] = array(
'ID' => $order->get_number(),
'order_id' => $order->id,
'customer' => edd_get_customer( $order->customer_id ),
'download' => $order_item->product_id,
'price_id' => $order_item->price_id,
'item_price' => $order_item->amount,
'amount' => $order_item->total,
'date' => EDD()->utils->date( $order_item->date_created, null, true )->toDateTimeString(),
'quantity' => $order_item->quantity,
'currency' => $order->currency,
);
}
return $data;
}
/**
* Get the total number of items
*
* @since 3.0
*
* @param array $log_query
*
* @return int
*/
public function get_total( $log_query = array() ) {
return edd_count_order_items( $log_query );
}
}

View File

@ -0,0 +1,92 @@
<?php
/**
* Contextual Help
*
* @package EDD
* @subpackage Admin/Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.4
*/
use EDD\Admin\Pass_Manager;
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Reports contextual help.
*
* @access private
* @since 1.4
* @return void
*/
function edd_reporting_contextual_help() {
$screen = get_current_screen();
if ( $screen->id != 'download_page_edd-reports' ) {
return;
}
$pass_manager = new Pass_Manager();
if ( $pass_manager->isFree() ) {
$docs_url = edd_link_helper(
'https://easydigitaldownloads.com/docs/',
array(
'utm_medium' => 'reports-contextual-help',
'utm_content' => 'documentation',
)
);
$upgrade_url = edd_link_helper(
'https://easydigitaldownloads.com/lite-upgrade/',
array(
'utm_medium' => 'reports-contextual-help',
'utm_content' => 'lite-upgrade',
)
);
$screen->set_help_sidebar(
'<p><strong>' . __( 'For more information:', 'easy-digital-downloads' ) . '</strong></p>' .
'<p>' . sprintf( __( 'Visit the <a href="%s">documentation</a> on the Easy Digital Downloads website.', 'easy-digital-downloads' ), $docs_url ) . '</p>' .
'<p>' . sprintf(
__( 'Need more from your Easy Digital Downloads store? <a href="%s">Upgrade Now</a>!', 'easy-digital-downloads' ),
$upgrade_url
) . '</p>'
);
}
$screen->add_help_tab( array(
'id' => 'edd-reports',
'title' => __( 'Reports', 'easy-digital-downloads' ),
'content' => '<p>' . __( 'This screen provides you with reports for your earnings, downloads, customers and taxes.', 'easy-digital-downloads' ) . '</p>'
) );
$screen->add_help_tab( array(
'id' => 'edd-reports-export',
'title' => __( 'Export', 'easy-digital-downloads' ),
'content' =>
'<p>' . __( 'This screen allows you to export your reports into a CSV format.', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( '<strong>Sales and Earnings</strong> - This report exports all of the sales and earnings that you have made in the current year. It includes your sales and earnings for each product as well a graphs of sales and earnings so you can compare them for each month.', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( '<strong>Payment History</strong> - This report exports all of the payments you have received on your EDD store in a CSV format. It includes the contact details of the customer, the products they have purchased as well as any discount codes they have used and the final price they have paid.', 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( "<strong>Customers</strong> - This report exports all of your customers in a CSV format. It exports the customer's name and email address and the amount of products they have purchased as well as the final price of their total purchases.", 'easy-digital-downloads' ) . '</p>' .
'<p>' . __( '<strong>Download History</strong> - This report exports all of the downloads you have received in the current month into a CSV. It exports the date the file was downloaded, the customer it was downloaded by, their IP address, the name of the product and the file they downloaded.', 'easy-digital-downloads' ) . '</p>'
) );
if( ! empty( $_GET['tab'] ) && 'logs' == $_GET['tab'] ) {
$screen->add_help_tab( array(
'id' => 'edd-reports-log-search',
'title' => __( 'Search File Downloads', 'easy-digital-downloads' ),
'content' =>
'<p>' . __( 'The file download log can be searched in several different ways:', 'easy-digital-downloads' ) . '</p>' .
'<ul>
<li>' . __( 'You can enter the customer\'s email address', 'easy-digital-downloads' ) . '</li>
<li>' . __( 'You can enter the customer\'s IP address', 'easy-digital-downloads' ) . '</li>
<li>' . __( 'You can enter the download file\'s name', 'easy-digital-downloads' ) . '</li>
</ul>'
) );
}
do_action( 'edd_reports_contextual_help', $screen );
}
add_action( 'load-download_page_edd-reports', 'edd_reporting_contextual_help' );

View File

@ -0,0 +1,132 @@
<?php
/**
* Batch API Request Logs Export Class
*
* This class handles API request logs export
*
* @package EDD
* @subpackage Admin/Reporting/Export
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.7
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Batch_API_Requests_Export Class
*
* @since 2.7
*/
class EDD_Batch_API_Requests_Export extends EDD_Batch_Export {
/**
* Our export type. Used for export-type specific filters/actions
*
* @var string
* @since 2.7
*/
public $export_type = 'api_requests';
/**
* Set the CSV columns.
*
* @since 2.7
* @return array $cols All the columns
*/
public function csv_cols() {
$cols = array(
'ID' => __( 'Log ID', 'easy-digital-downloads' ),
'request' => __( 'API Request', 'easy-digital-downloads' ),
'ip' => __( 'IP Address', 'easy-digital-downloads' ),
'user' => __( 'API User', 'easy-digital-downloads' ),
'key' => __( 'API Key', 'easy-digital-downloads' ),
'version' => __( 'API Version', 'easy-digital-downloads' ),
'speed' => __( 'Request Speed', 'easy-digital-downloads' ),
'date' => __( 'Date', 'easy-digital-downloads' )
);
return $cols;
}
/**
* Get the export data.
*
* @since 2.7
* @since 3.0 Updated to use new query methods.
*
* @return array $data The data for the CSV file.
*/
public function get_data() {
$data = array();
$args = array(
'number' => 30,
'offset' => ( $this->step * 30 ) - 30
);
if ( ! empty( $this->start ) || ! empty( $this->end ) ) {
$args['date_query'] = $this->get_date_query();
}
$logs = edd_get_api_request_logs( $args );
foreach ( $logs as $log ) {
/** @var EDD\Logs\Api_Request_Log $log */
$data[] = array(
'ID' => $log->id,
'request' => $log->request,
'ip' => $log->ip,
'user' => $log->user_id,
'key' => $log->api_key,
'version' => $log->version,
'speed' => $log->time,
'date' => $log->date_created
);
}
$data = apply_filters( 'edd_export_get_data', $data );
$data = apply_filters( 'edd_export_get_data_' . $this->export_type, $data );
return ! empty( $data )
? $data
: false;
}
/**
* Return the calculated completion percentage.
*
* @since 2.7
* @since 3.0 Updated to use new query methods.
*
* @return int Percentage complete.
*/
public function get_percentage_complete() {
$args = array(
'fields' => 'ids',
);
if ( ! empty( $this->start ) || ! empty( $this->end ) ) {
$args['date_query'] = $this->get_date_query();
}
$total = edd_count_api_request_logs( $args );
$percentage = 100;
if ( $total > 0 ) {
$percentage = ( ( 30 * $this->step ) / $total ) * 100;
}
if ( $percentage > 100 ) {
$percentage = 100;
}
return $percentage;
}
public function set_properties( $request ) {
$this->start = isset( $request['api-requests-export-start'] ) ? sanitize_text_field( $request['api-requests-export-start'] ) : '';
$this->end = isset( $request['api-requests-export-end'] ) ? sanitize_text_field( $request['api-requests-export-end'] ) : '';
}
}

View File

@ -0,0 +1,230 @@
<?php
/**
* Batch Customers Export Class
*
* This class handles customer export
*
* @package EDD
* @subpackage Admin/Reporting/Export
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.4
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Batch_Customers_Export Class
*
* @since 2.4
* @since 3.0 Allowed customers to be exported by taxonomy.
*/
class EDD_Batch_Customers_Export extends EDD_Batch_Export {
/**
* Our export type. Used for export-type specific filters/actions.
*
* @var string
* @since 2.4
*/
public $export_type = 'customers';
/**
* Taxonomy.
*
* @since 3.0
* @var int
*/
public $taxonomy = null;
/**
* Set the CSV columns.
*
* @since 2.4
*
* @return array $cols All the columns
*/
public function csv_cols() {
return array(
'id' => __( 'ID', 'easy-digital-downloads' ),
'user_id' => __( 'User ID', 'easy-digital-downloads' ),
'name' => __( 'Name', 'easy-digital-downloads' ),
'email' => __( 'Email', 'easy-digital-downloads' ),
'purchases' => __( 'Number of Purchases', 'easy-digital-downloads' ),
'amount' => __( 'Customer Value', 'easy-digital-downloads' ),
'payment_ids' => __( 'Payment IDs', 'easy-digital-downloads' ),
'date_created' => __( 'Date Created', 'easy-digital-downloads' ),
);
}
/**
* Get the export data.
*
* @since 2.4
* @since 3.0 Updated to use new query methods.
*
* @return array $data The data for the CSV file.
*/
public function get_data() {
global $wpdb;
$data = array();
// Taxonomy.
if ( ! empty( $this->taxonomy ) ) {
$taxonomy = $wpdb->prepare( 't.term_id = %d', $this->taxonomy );
$limit = $wpdb->prepare( '%d, %d', 30 * ( $this->step - 1 ), 30 );
$sql = "SELECT DISTINCT o.customer_id
FROM {$wpdb->terms} t
INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
INNER JOIN {$wpdb->term_relationships} tr ON tr.term_taxonomy_id = tt.term_taxonomy_id
INNER JOIN {$wpdb->edd_order_items} oi ON tr.object_id = oi.product_id
INNER JOIN {$wpdb->edd_orders} o ON oi.order_id = o.id
WHERE {$taxonomy}
LIMIT {$limit}";
$results = $wpdb->get_col( $sql ); // WPCS: unprepared SQL ok.
if ( $results ) {
foreach ( $results as $customer_id ) {
$customer = new EDD_Customer( $customer_id );
$name = ! empty( $customer->name ) ? $customer->name : '';
if ( preg_match( '~^[+\-=@]~m', $name ) ) {
$name = "'{$name}";
}
$data[] = array(
'id' => $customer->id,
'name' => $name,
'email' => $customer->email,
'purchases' => $customer->purchase_count,
'amount' => edd_format_amount( $customer->purchase_value ),
);
}
}
// Download.
} elseif ( ! empty( $this->download ) ) {
// Export customers of a specific product
$args = array(
'product_id' => absint( $this->download ),
'number' => 30,
'offset' => 30 * ( $this->step - 1 ),
);
if ( null !== $this->price_id ) {
$args['price_id'] = (int) $this->price_id;
}
$order_items = edd_get_order_items( $args );
if ( $order_items ) {
foreach ( $order_items as $item ) {
$order = edd_get_order( $item->order_id );
$customer = new EDD_Customer( $order->customer_id );
$name = ! empty( $customer->name ) ? $customer->name : '';
if ( preg_match( '~^[+\-=@]~m', $name ) ) {
$name = "'{$name}";
}
$data[] = array(
'id' => $customer->id,
'user_id' => $customer->user_id,
'name' => $name,
'email' => $customer->email,
'purchases' => $customer->purchase_count,
'amount' => edd_format_amount( $customer->purchase_value ),
'payment_ids' => $customer->payment_ids,
'date_created' => $customer->date_created,
);
}
}
// All customers.
} else {
$customers = edd_get_customers( array(
'number' => 30,
'offset' => 30 * ( $this->step - 1 ),
) );
$i = 0;
foreach ( $customers as $customer ) {
$name = ! empty( $customer->name ) ? $customer->name : '';
if ( preg_match( '~^[+\-=@]~m', $name ) ) {
$name = "'{$name}";
}
$data[ $i ]= array(
'id' => $customer->id,
'user_id' => $customer->user_id,
'name' => $name,
'email' => $customer->email,
'purchases' => $customer->purchase_count,
'amount' => edd_format_amount( $customer->purchase_value ),
'payment_ids' => $customer->payment_ids,
'date_created' => $customer->date_created,
);
$i++;
}
}
$data = apply_filters( 'edd_export_get_data', $data );
$data = apply_filters( 'edd_export_get_data_' . $this->export_type, $data );
return $data;
}
/**
* Return the calculated completion percentage.
*
* @since 2.4
*
* @return float Percentage complete.
*/
public function get_percentage_complete() {
$percentage = 0;
// We can't count the number when getting them for a specific download.
if ( empty( $this->download ) ) {
$total = edd_count_customers();
if ( $total > 0 ) {
$percentage = ( ( 30 * $this->step ) / $total ) * 100;
}
}
if ( $percentage > 100 ) {
$percentage = 100;
}
return $percentage;
}
/**
* Set the properties specific to the Customers export
*
* @since 2.4.2
*
* @param array $request Form data passed into the batch processing.
*/
public function set_properties( $request ) {
$this->taxonomy = isset( $request['taxonomy'] )
? absint( $request['taxonomy'] )
: null;
$this->download = isset( $request['download'] )
? absint( $request['download'] )
: null;
$this->price_id = ! empty( $request['edd_price_option'] ) && 0 !== $request['edd_price_option']
? absint( $request['edd_price_option'] )
: null;
}
}

View File

@ -0,0 +1,234 @@
<?php
/**
* Batch Downloads Export Class
*
* This class handles download products export
*
* @package EDD
* @subpackage Admin/Reporting/Export
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.5
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Batch_Downloads_Export Class
*
* @since 2.5
*/
class EDD_Batch_Downloads_Export extends EDD_Batch_Export {
/**
* Our export type. Used for export-type specific filters/actions
*
* @var string
* @since 2.5
*/
public $export_type = 'downloads';
/**
* Set the CSV columns.
*
* @since 2.5
*
* @return array $cols All the columns.
*/
public function csv_cols() {
$cols = array(
'ID' => __( 'ID', 'easy-digital-downloads' ),
'post_name' => __( 'Slug', 'easy-digital-downloads' ),
'post_title' => __( 'Name', 'easy-digital-downloads' ),
'post_date' => __( 'Date Created', 'easy-digital-downloads' ),
'post_author' => __( 'Author', 'easy-digital-downloads' ),
'post_content' => __( 'Description', 'easy-digital-downloads' ),
'post_excerpt' => __( 'Excerpt', 'easy-digital-downloads' ),
'post_status' => __( 'Status', 'easy-digital-downloads' ),
'categories' => __( 'Categories', 'easy-digital-downloads' ),
'tags' => __( 'Tags', 'easy-digital-downloads' ),
'edd_price' => __( 'Price', 'easy-digital-downloads' ),
'_edd_files' => __( 'Files', 'easy-digital-downloads' ),
'_edd_download_limit' => __( 'File Download Limit', 'easy-digital-downloads' ),
'_thumbnail_id' => __( 'Featured Image', 'easy-digital-downloads' ),
'edd_sku' => __( 'SKU', 'easy-digital-downloads' ),
'edd_product_notes' => __( 'Notes', 'easy-digital-downloads' ),
'_edd_download_sales' => __( 'Sales', 'easy-digital-downloads' ),
'_edd_download_earnings' => __( 'Earnings', 'easy-digital-downloads' ),
);
return $cols;
}
/**
* Get the export data.
*
* @since 2.5
*
* @return array $data The data for the CSV file.
*/
public function get_data() {
$data = array();
$meta = array(
'edd_price',
'_edd_files',
'_edd_download_limit',
'_thumbnail_id',
'edd_sku',
'edd_product_notes',
'_edd_download_sales',
'_edd_download_earnings',
);
$args = array(
'post_type' => 'download',
'posts_per_page' => 30,
'paged' => $this->step,
'orderby' => 'ID',
'order' => 'ASC',
);
if ( 0 !== $this->download ) {
$args['post__in'] = array( $this->download );
}
$downloads = new WP_Query( $args );
if ( $downloads->posts ) {
foreach ( $downloads->posts as $download ) {
$row = array();
foreach ( $this->csv_cols() as $key => $value ) {
// Setup default value
$row[ $key ] = '';
if ( in_array( $key, $meta ) ) {
switch ( $key ) {
case '_thumbnail_id' :
$image_id = get_post_thumbnail_id( $download->ID );
$row[ $key ] = wp_get_attachment_url( $image_id );
break;
case 'edd_price' :
if ( edd_has_variable_prices( $download->ID ) ) {
$prices = array();
foreach ( edd_get_variable_prices( $download->ID ) as $price ) {
$prices[] = $price['name'] . ': ' . $price['amount'];
}
$row[ $key ] = implode( ' | ', $prices );
} else {
$row[ $key ] = edd_get_download_price( $download->ID );
}
break;
case '_edd_files' :
$files = array();
foreach ( edd_get_download_files( $download->ID ) as $file ) {
$f = $file['file'];
if ( edd_has_variable_prices( $download->ID ) ) {
$condition = isset( $file['condition'] ) ? $file['condition'] : 'all';
$f .= ';' . $condition;
}
$files[] = $f;
unset( $file );
}
$row[ $key ] = implode( ' | ', $files );
break;
default :
$row[ $key ] = get_post_meta( $download->ID, $key, true );
break;
}
} elseif ( isset( $download->$key ) ) {
switch ( $key ) {
case 'post_author':
$row[ $key ] = get_the_author_meta( 'user_login', $download->post_author );
break;
default:
$row[ $key ] = $download->$key;
break;
}
} elseif ( 'tags' == $key ) {
$terms = get_the_terms( $download->ID, 'download_tag' );
if ( $terms ) {
$terms = wp_list_pluck( $terms, 'name' );
$row[ $key ] = implode( ' | ', $terms );
}
} elseif ( 'categories' == $key ) {
$terms = get_the_terms( $download->ID, 'download_category' );
if ( $terms ) {
$terms = wp_list_pluck( $terms, 'name' );
$row[ $key ] = implode( ' | ', $terms );
}
}
}
$data[] = $row;
}
$data = apply_filters( 'edd_export_get_data', $data );
$data = apply_filters( 'edd_export_get_data_' . $this->export_type, $data );
return $data;
}
return false;
}
/**
* Return the calculated completion percentage.
*
* @since 2.5
*
* @return int Percentage complete.
*/
public function get_percentage_complete() {
$args = array(
'post_type' => 'download',
'posts_per_page' => - 1,
'post_status' => 'any',
'fields' => 'ids',
);
if ( 0 !== $this->download ) {
$args['post__in'] = array( $this->download );
}
$downloads = new WP_Query( $args );
$total = (int) $downloads->post_count;
$percentage = 100;
if ( $total > 0 ) {
$percentage = ( ( 30 * $this->step ) / $total ) * 100;
}
if ( $percentage > 100 ) {
$percentage = 100;
}
return $percentage;
}
/**
* Set the properties specific to the downloads export.
*
* @since 3.0
*
* @param array $request Form data passed into the batch processor.
*/
public function set_properties( $request ) {
$this->download = isset( $request['download_id'] ) ? absint( $request['download_id'] ) : null;
}
}

View File

@ -0,0 +1,349 @@
<?php
/**
* Batch Earnings Report Export Class.
*
* This class handles earnings report export.
*
* @package EDD
* @subpackage Admin/Reporting/Export
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.7
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Earnings_Report_Export Class
*
* @since 2.7
*/
class EDD_Batch_Earnings_Report_Export extends EDD_Batch_Export {
/**
* Our export type. Used for export-type specific filters/actions.
*
* @since 2.7
* @var string
*/
public $export_type = 'earnings_report';
/**
* Set the export headers.
*
* @since 2.7
*/
public function headers() {
edd_set_time_limit();
nocache_headers();
header( 'Content-Type: text/csv; charset=utf-8' );
header( 'Content-Disposition: attachment; filename="' . apply_filters( 'edd_earnings_report_export_filename', 'edd-export-' . $this->export_type . '-' . date( 'm' ) . '-' . date( 'Y' ) ) . '.csv"' );
header( 'Expires: 0' );
}
/**
* Get the column headers for the Earnings Report.
*
* @since 2.8.18
*
* @return array CSV columns.
*/
public function get_csv_cols() {
// Always start with the date column.
$pre_status_columns = array(
__( 'Monthly Sales Activity', 'easy-digital-downloads' ),
__( 'Gross Activity', 'easy-digital-downloads' ),
);
$status_cols = $this->get_status_cols();
// Append the arrays together so it starts with the date, then include the status list.
$cols = array_merge( $pre_status_columns, $status_cols );
// Include the 'net' after all other columns.
$cols[] = __( 'Net Activity', 'easy-digital-downloads' );
return $cols;
}
/**
* Specifically retrieve the headers for supported order statuses.
*
* @since 2.8.18
*
* @return array Order status columns.
*/
public function get_status_cols() {
$status_cols = edd_get_payment_statuses();
$supported_statuses = $this->get_supported_statuses();
foreach ( $status_cols as $id => $label ) {
if ( ! in_array( $id, $supported_statuses ) ) {
unset( $status_cols[ $id ] );
}
}
return array_values( $status_cols );
}
/**
* Get a list of the statuses supported in this report.
*
* @since 2.8.18
*
* @return array The status keys supported (not labels).
*/
public function get_supported_statuses() {
$statuses = edd_get_payment_statuses();
// Unset a few statuses we don't need in the report:
unset( $statuses['pending'], $statuses['processing'], $statuses['preapproval'] );
$supported_statuses = array_keys( $statuses );
return apply_filters( 'edd_export_earnings_supported_statuses', $supported_statuses );
}
/**
* Output the CSV columns.
*
* We make use of this function to set up the header of the earnings report.
*
* @since 2.7
*
* @return string $col_data CSV cols.
*/
public function print_csv_cols() {
$cols = $this->get_csv_cols();
$col_data = '';
$column_count = count( $cols );
for ( $i = 0; $i < $column_count; $i++ ) {
$col_data .= $cols[ $i ];
// We don't need an extra space after the first column.
if ( $i == 0 ) {
$col_data .= ',';
continue;
}
if ( $i == ( $column_count - 1 ) ) {
$col_data .= "\r\n";
} else {
$col_data .= ",,";
}
}
$statuses = $this->get_supported_statuses();
$number_cols = count( $statuses ) + 2;
$col_data .= ',';
for ( $i = 1; $i <= $number_cols; $i++ ) {
$col_data .= __( 'Order Count', 'easy-digital-downloads' ) . ',';
$col_data .= __( 'Gross Amount', 'easy-digital-downloads' );
if ( $number_cols !== $i ) {
$col_data .= ',';
}
}
$col_data .= "\r\n";
$this->stash_step_data( $col_data );
return $col_data;
}
/**
* Print the CSV rows for the current step.
*
* @since 2.7
*
* @return mixed string|false
*/
public function print_csv_rows() {
$row_data = '';
$data = $this->get_data();
if ( $data ) {
$start_date = date( 'Y-m-d', strtotime( $this->start ) );
if ( $this->count() == 0 ) {
$end_date = date( 'Y-m-d', strtotime( $this->end ) );
} else {
$end_date = date( 'Y-m-d', strtotime( 'first day of +1 month', strtotime( $start_date ) ) );
}
if ( $this->step == 1 ) {
$row_data .= $start_date . ',';
} elseif ( $this->step > 1 ) {
$start_date = date( 'Y-m-d', strtotime( 'first day of +' . ( $this->step - 1 ) . ' month', strtotime( $start_date ) ) );
if ( date( 'Y-m', strtotime( $start_date ) ) == date( 'Y-m', strtotime( $this->end ) ) ) {
$end_date = date( 'Y-m-d', strtotime( $this->end ) );
$row_data .= $end_date . ',';
} else {
$row_data .= $start_date . ',';
}
}
$supported_statuses = $this->get_supported_statuses();
$gross_count = 0;
$gross_amount = 0;
foreach ( $supported_statuses as $status ) {
$gross_count += absint( $data[ $status ]['count'] );
$gross_amount += $data[ $status ]['amount'];
}
$row_data .= $gross_count . ',';
$row_data .= '"' . edd_format_amount( $gross_amount ) . '",';
foreach ( $data as $status => $status_data ) {
$row_data .= isset( $data[ $status ]['count'] ) ? $data[ $status ]['count'] . ',' : 0 . ',';
$column_amount = isset( $data[ $status ]['amount'] ) ? edd_format_amount( $data[ $status ]['amount'] ) : 0;
if ( ! empty( $column_amount ) && 'refunded' == $status ) {
$column_amount = '-' . $column_amount;
}
$row_data .= isset( $data[ $status ]['amount'] ) ? '"' . $column_amount . '"' . ',' : 0 . ',';
}
// Allows extensions with other 'completed' statuses to alter net earnings, like recurring.
$completed_statuses = apply_filters( 'edd_export_earnings_completed_statuses', edd_get_complete_order_statuses() );
$net_count = 0;
$net_amount = 0;
foreach ( $completed_statuses as $status ) {
if ( ! isset( $data[ $status ] ) ) {
continue;
}
$net_count += absint( $data[ $status ]['count'] );
$net_amount += floatval( $data[ $status ]['amount'] );
}
$row_data .= $net_count . ',';
$row_data .= '"' . edd_format_amount( $net_amount ) . '"';
$row_data .= "\r\n";
$this->stash_step_data( $row_data );
return $row_data;
}
return false;
}
/**
* Get the Export Data.
*
* @since 2.7
*
* @return array $data The data for the CSV file
*/
public function get_data() {
global $wpdb;
$data = array();
$start_date = date( 'Y-m-d 00:00:00', strtotime( $this->start ) );
$end_date = date( 'Y-m-t 23:59:59', strtotime( $this->start ) );
if ( $this->step > 1 ) {
$start_timestamp = strtotime( 'first day of +' . ( $this->step - 1 ) . ' month', strtotime( $start_date ) );
$start_date = date( 'Y-m-d 00:00:00', $start_timestamp );
$end_date = date( 'Y-m-t 23:59:59', $start_timestamp );
}
if ( strtotime( $start_date ) > strtotime( $this->end ) ) {
return false;
}
$statuses = $this->get_supported_statuses();
$totals = $wpdb->get_results( $wpdb->prepare(
"SELECT SUM(total) AS total, COUNT(DISTINCT id) AS count, status
FROM {$wpdb->edd_orders}
WHERE date_created >= %s AND date_created <= %s
GROUP BY YEAR(date_created), MONTH(date_created), status
ORDER by date_created ASC", $start_date, $end_date ), ARRAY_A );
$total_data = array();
foreach ( $totals as $row ) {
$total_data[ $row['status'] ] = array(
'count' => $row['count'],
'amount' => floatval( $row['total'] )
);
}
foreach ( $statuses as $status ) {
if ( ! isset( $total_data[ $status ] ) ) {
$data[ $status ] = array(
'count' => 0,
'amount' => 0,
);
} else {
$data[ $status ] = array(
'count' => $total_data[ $status ]['count'],
'amount' => $total_data[ $status ]['amount'],
);
}
}
$data = apply_filters( 'edd_export_get_data', $data );
$data = apply_filters( 'edd_export_get_data_' . $this->export_type, $data, $start_date, $end_date );
return $data;
}
/**
* Count the number of months we are dealing with.
*
* @since 2.7
* @access private
*
* @return void
*/
private function count() {
return abs( ( date( 'Y', strtotime( $this->end ) ) - date( 'Y', strtotime( $this->start ) ) ) * 12 + ( date( 'm', strtotime( $this->end ) ) - date( 'm', strtotime( $this->start ) ) ) );
}
/**
* Return the calculated completion percentage
*
* @since 2.7
*
* @return int Percentage of batch processing complete.
*/
public function get_percentage_complete() {
$percentage = 100;
$total = $this->count();
if ( $total > 0 ) {
$percentage = ( $this->step / $total ) * 100;
}
if ( $percentage > 100 ) {
$percentage = 100;
}
return $percentage;
}
/**
* Set the properties specific to the earnings report.
*
* @since 2.7
*
* @param array $request The Form Data passed into the batch processing
* @return void
*/
public function set_properties( $request ) {
$this->start = ( isset( $request['start_month'] ) && isset( $request['start_year'] ) ) ? sanitize_text_field( $request['start_year'] ) . '-' . sanitize_text_field( $request['start_month'] ) . '-1' : '';
$this->end = ( isset( $request['end_month'] ) && isset( $request['end_year'] ) ) ? sanitize_text_field( $request['end_year'] ) . '-' . sanitize_text_field( $request['end_month'] ) . '-' . cal_days_in_month( CAL_GREGORIAN, sanitize_text_field( $request['end_month'] ), sanitize_text_field( $request['end_year'] ) ) : '';
}
}

View File

@ -0,0 +1,162 @@
<?php
/**
* Batch File Downloads Export Class
*
* This class handles file downloads export
*
* @package EDD
* @subpackage Admin/Reporting/Export
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.4
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Batch_File_Downloads_Export Class
*
* @since 2.4
* @since 3.0 Refactored to use new query methods.
*/
class EDD_Batch_File_Downloads_Export extends EDD_Batch_Export {
/**
* Our export type. Used for export-type specific filters/actions.
*
* @var string
* @since 2.4
*/
public $export_type = 'file_downloads';
/**
* Set the CSV columns.
*
* @since 2.4
* @since 3.0 Updated to add 'User Agent' column.
*
* @return array $cols All the columns.
*/
public function csv_cols() {
$cols = array(
'date' => __( 'Date', 'easy-digital-downloads' ),
'user' => __( 'Downloaded by', 'easy-digital-downloads' ),
'ip' => __( 'IP Address', 'easy-digital-downloads' ),
'user_agent' => __( 'User Agent', 'easy-digital-downloads' ),
'download' => __( 'Product', 'easy-digital-downloads' ),
'file' => __( 'File', 'easy-digital-downloads' ),
);
return $cols;
}
/**
* Get the export data.
*
* @since 2.4
* @since 3.0 Refactored to use new query methods.
*
* @return array $data The data for the CSV file.
*/
public function get_data() {
$data = array();
$args = array(
'number' => 30,
'offset' => ( $this->step * 30 ) - 30,
);
if ( ! empty( $this->start ) || ! empty( $this->end ) ) {
$args['date_created_query'] = $this->get_date_query();
}
if ( 0 !== $this->download_id ) {
$args['product_id'] = $this->download_id;
}
$logs = edd_get_file_download_logs( $args );
foreach ( $logs as $log ) {
/** @var EDD\Logs\File_Download_Log $log */
$files = edd_get_download_files( $log->product_id );
$file_id = $log->file_id;
$file_name = isset( $files[ $file_id ]['name'] ) ? $files[ $file_id ]['name'] : null;
$customer = edd_get_customer( $log->customer_id );
if ( $customer ) {
$customer = $customer->email;
if ( ! empty( $customer->name ) ) {
$customer = $customer->name;
if ( preg_match( '~^[+\-=@]~m', $customer ) ) {
$customer = "'{$customer}";
}
}
} else {
$order = edd_get_order( $log->order_id );
if ( $order ) {
$customer = $order->email;
}
}
$data[] = array(
'date' => $log->date_created,
'user' => $customer,
'ip' => $log->ip,
'user_agent' => $log->user_agent,
'download' => get_the_title( $log->product_id ),
'file' => $file_name,
);
}
$data = apply_filters( 'edd_export_get_data', $data );
$data = apply_filters( 'edd_export_get_data_' . $this->export_type, $data );
return ! empty( $data )
? $data
: false;
}
/**
* Return the calculated completion percentage.
*
* @since 2.4
* @since 3.0 Updated to use new query methods.
*
* @return int Percentage complete.
*/
public function get_percentage_complete() {
$args = array(
'fields' => 'ids',
);
if ( ! empty( $this->start ) || ! empty( $this->end ) ) {
$args['date_created_query'] = $this->get_date_query();
}
if ( 0 !== $this->download_id ) {
$args['download_id'] = $this->download_id;
}
$total = edd_count_file_download_logs( $args );
$percentage = 100;
if ( $total > 0 ) {
$percentage = ( ( 30 * $this->step ) / $total ) * 100;
}
if ( $percentage > 100 ) {
$percentage = 100;
}
return $percentage;
}
public function set_properties( $request ) {
$this->start = isset( $request['file-download-export-start'] ) ? sanitize_text_field( $request['file-download-export-start'] ) : '';
$this->end = isset( $request['file-download-export-end'] ) ? sanitize_text_field( $request['file-download-export-end'] ) : '';
$this->download_id = isset( $request['download_id'] ) ? absint( $request['download_id'] ) : 0;
}
}

View File

@ -0,0 +1,280 @@
<?php
/**
* Payments Export Class
*
* This class handles payment export in batches
*
* @package EDD
* @subpackage Admin/Reporting/Export
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.4
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Batch_Payments_Export Class
*
* @since 2.4
*/
class EDD_Batch_Payments_Export extends EDD_Batch_Export {
/**
* Our export type. Used for export-type specific filters/actions.
*
* @var string
* @since 2.4
*/
public $export_type = 'orders';
/**
* Set the CSV columns
*
* @since 2.4
*
* @return array $cols All the columns
*/
public function csv_cols() {
$cols = array(
'id' => __( 'Order ID', 'easy-digital-downloads' ), // unaltered payment ID (use for querying)
'seq_id' => __( 'Order Number', 'easy-digital-downloads' ), // sequential payment ID
'email' => __( 'Email', 'easy-digital-downloads' ),
'customer_id' => __( 'Customer ID', 'easy-digital-downloads' ),
'name' => __( 'Customer Name', 'easy-digital-downloads' ),
'address1' => __( 'Address', 'easy-digital-downloads' ),
'address2' => __( 'Address (Line 2)', 'easy-digital-downloads' ),
'city' => __( 'City', 'easy-digital-downloads' ),
'state' => __( 'State', 'easy-digital-downloads' ),
'country' => __( 'Country', 'easy-digital-downloads' ),
'zip' => __( 'Zip / Postal Code', 'easy-digital-downloads' ),
'products' => __( 'Products (Verbose)', 'easy-digital-downloads' ),
'products_raw' => __( 'Products (Raw)', 'easy-digital-downloads' ),
'skus' => __( 'SKUs', 'easy-digital-downloads' ),
'currency' => __( 'Currency', 'easy-digital-downloads' ),
'amount' => __( 'Amount', 'easy-digital-downloads' ),
'tax' => __( 'Tax', 'easy-digital-downloads' ),
'discount' => __( 'Discount Code', 'easy-digital-downloads' ),
'gateway' => __( 'Payment Method', 'easy-digital-downloads' ),
'trans_id' => __( 'Transaction ID', 'easy-digital-downloads' ),
'key' => __( 'Purchase Key', 'easy-digital-downloads' ),
'date' => __( 'Date', 'easy-digital-downloads' ),
'user' => __( 'User', 'easy-digital-downloads' ),
'ip' => __( 'IP Address', 'easy-digital-downloads' ),
'mode' => __( 'Mode (Live|Test)', 'easy-digital-downloads' ),
'status' => __( 'Status', 'easy-digital-downloads' ),
'country_name' => __( 'Country Name', 'easy-digital-downloads' ),
'state_name' => __( 'State Name', 'easy-digital-downloads' ),
);
if ( ! edd_use_skus() ){
unset( $cols['skus'] );
}
if ( ! edd_get_option( 'enable_sequential' ) ) {
unset( $cols['seq_id'] );
}
return $cols;
}
/**
* Get the export data.
*
* @since 2.4
* @since 3.0 Updated to use new query methods.
*
* @return array $data The data for the CSV file.
*/
public function get_data() {
$data = array();
$args = array(
'number' => 30,
'offset' => ( $this->step * 30 ) - 30,
'status' => $this->status,
'order' => 'ASC',
'orderby' => 'date_created',
'type' => 'sale',
'status__not_in' => array( 'trash' ),
);
if ( ! empty( $this->start ) || ! empty( $this->end ) ) {
$args['date_query'] = $this->get_date_query();
}
if ( 'all' === $args['status'] ) {
unset( $args['status'] );
}
$orders = edd_get_orders( $args );
foreach ( $orders as $order ) {
/** @var EDD\Orders\Order $order */
$items = $order->get_items();
$address = $order->get_address();
$total = $order->total;
$user_id = $order->id && $order->id != - 1 ? $order->id : $order->email;
$customer = edd_get_customer( $order->customer_id );
$products = '';
$products_raw = '';
$skus = '';
$discounts = $order->get_discounts();
$discounts = ! empty( $discounts )
? implode( ', ', $discounts )
: __( 'none', 'easy-digital-downloads' );
foreach ( $items as $key => $item ) {
/** @var EDD\Orders\Order_Item $item */
// Setup item information.
$id = $item->product_id;
$qty = $item->quantity;
$price = $item->amount;
$tax = $item->tax;
$price_id = $item->price_id;
// Set up verbose product column.
$products .= html_entity_decode( get_the_title( $id ) );
if ( $qty > 1 ) {
$products .= html_entity_decode( ' (' . $qty . ')' );
}
$products .= ' - ';
if ( edd_use_skus() ) {
$sku = edd_get_download_sku( $id );
if ( ! empty( $sku ) ) {
$skus .= $sku;
}
}
if ( 0 < $item->price_id ) {
$products .= html_entity_decode( edd_get_price_option_name( $id, $item->price_id, $order->id ) ) . ' - ';
}
$products .= html_entity_decode( edd_currency_filter( edd_format_amount( $price ), $order->currency ) );
if ( $key != ( count( $items ) -1 ) ) {
$products .= ' / ';
if ( edd_use_skus() ) {
$skus .= ' / ';
}
}
// Set up raw products column; nothing but product names.
$products_raw .= html_entity_decode( get_the_title( $id ) ) . '|' . $price . '{' . $tax . '}';
// If we have a price ID, include it.
if ( false !== $price_id ) {
$products_raw .= '{' . $price_id . '}';
}
if ( $key != ( count( $items ) -1 ) ) {
$products_raw .= ' / ';
}
}
$user = is_numeric( $user_id )
? get_userdata( $user_id )
: false;
$name = ! empty( $customer->name ) ? $customer->name : '';
if ( preg_match( '~^[+\-=@]~m', $name ) ) {
$name = "'{$name}";
}
$data[] = array(
'id' => $order->id,
'seq_id' => $order->get_number(),
'email' => $order->email,
'customer_id' => $order->customer_id,
'name' => $name,
'address1' => isset( $address->address ) ? $address->address : '',
'address2' => isset( $address->address2 ) ? $address->address2 : '',
'city' => isset( $address->city ) ? $address->city : '',
'state' => isset( $address->region ) ? $address->region : '',
'country' => isset( $address->country ) ? $address->country : '',
'zip' => isset( $address->postal_code ) ? $address->postal_code : '',
'products' => $products,
'products_raw' => $products_raw,
'skus' => $skus,
'currency' => $order->currency,
'amount' => html_entity_decode( edd_format_amount( $total ) ), // The non-discounted item price
'tax' => html_entity_decode( edd_format_amount( $order->tax ) ),
'discount' => $discounts,
'gateway' => edd_get_gateway_admin_label( $order->gateway ),
'trans_id' => $order->get_transaction_id(),
'key' => $order->payment_key,
'date' => $order->date_created,
'user' => $user ? $user->display_name : __( 'guest', 'easy-digital-downloads' ),
'ip' => $order->ip,
'mode' => $order->mode,
'status' => $order->status,
'country_name' => isset( $address->country ) ? edd_get_country_name( $address->country ) : '',
'state_name' => isset( $address->country ) && isset( $address->region ) ? edd_get_state_name( $address->country, $address->region ) : '',
);
}
$data = apply_filters( 'edd_export_get_data', $data );
$data = apply_filters( 'edd_export_get_data_' . $this->export_type, $data );
return ! empty( $data )
? $data
: false;
}
/**
* Return the calculated completion percentage
*
* @since 2.4
* @since 3.0 Updated to use new query methods.
*
* @return int
*/
public function get_percentage_complete() {
$args = array(
'fields' => 'ids',
);
if ( ! empty( $this->start ) || ! empty( $this->end ) ) {
$args['date_query'] = $this->get_date_query();
}
if ( 'any' !== $this->status ) {
$args['status'] = $this->status;
}
$total = edd_count_orders( $args );
$percentage = 100;
if ( $total > 0 ) {
$percentage = ( ( 30 * $this->step ) / $total ) * 100;
}
if ( $percentage > 100 ) {
$percentage = 100;
}
return $percentage;
}
/**
* Set the properties specific to the payments export
*
* @since 2.4.2
*
* @param array $request The Form Data passed into the batch processing
*/
public function set_properties( $request ) {
$this->start = isset( $request['orders-export-start'] ) ? sanitize_text_field( $request['orders-export-start'] ) : '';
$this->end = isset( $request['orders-export-end'] ) ? sanitize_text_field( $request['orders-export-end'] ) : '';
$this->status = isset( $request['status'] ) ? sanitize_text_field( $request['status'] ) : 'complete';
}
}

View File

@ -0,0 +1,238 @@
<?php
/**
* Sales and Earnings Export Class.
*
* This class handles sales and earnings export on a day-by-day basis.
*
* @package EDD
* @subpackage Admin/Reporting/Export
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 3.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Batch_Payments_Export Class
*
* @since 2.4
*/
class EDD_Batch_Sales_And_Earnings_Export extends EDD_Batch_Export {
/**
* Our export type. Used for export-type specific filters/actions.
*
* @since 3.0
* @var string
*/
public $export_type = 'sales_and_earnings';
/**
* Download ID.
*
* @since 3.0
* @access protected
* @var int
*/
protected $download_id = 0;
/**
* Customer ID.
*
* @since 3.0
* @access protected
* @var int
*/
protected $customer_id = 0;
/**
* Set the CSV columns.
*
* @since 3.0
*
* @return array $cols CSV columns.
*/
public function csv_cols() {
$cols = array(
'date' => __( 'Date', 'easy-digital-downloads' ),
'sales' => __( 'Sales', 'easy-digital-downloads' ),
'earnings' => __( 'Earnings', 'easy-digital-downloads' ),
);
return $cols;
}
/**
* Get the export data.
*
* @since 3.0
*
* @return array $data The data for the CSV file.
*/
public function get_data() {
global $wpdb;
$data = array();
$args = array(
'number' => 30,
'offset' => ( $this->step * 30 ) - 30,
);
$status = "AND {$wpdb->edd_orders}.status IN ( '" . implode( "', '", $wpdb->_escape( edd_get_complete_order_statuses() ) ) . "' )";
$date_query_sql = '';
// Customer ID.
$customer_id = ! empty( $this->customer_id )
? $wpdb->prepare( "AND {$wpdb->edd_orders}.customer_id = %d", $this->customer_id )
: '';
// Download ID.
$download_id = ! empty( $this->download_id )
? $wpdb->prepare( "AND {$wpdb->edd_order_items}.product_id = %d", $this->download_id )
: '';
// Generate date query SQL if dates have been set.
if ( ! empty( $this->start ) || ! empty( $this->end ) ) {
// Fetch GMT offset.
$offset = EDD()->utils->get_gmt_offset();
$date_query_sql = 'AND ';
if ( ! empty( $this->start ) ) {
$this->start = date( 'Y-m-d 00:00:00', strtotime( $this->start ) );
$this->start = 0 < $offset
? EDD()->utils->date( $this->start )->subSeconds( $offset )->format( 'mysql' )
: EDD()->utils->date( $this->start )->addSeconds( $offset )->format( 'mysql' );
$date_query_sql .= $wpdb->prepare( "{$wpdb->edd_orders}.date_created >= %s", $this->start );
}
// Join dates with `AND` if start and end date set.
if ( ! empty( $this->start ) && ! empty( $this->end ) ) {
$this->end = date( 'Y-m-d 23:59:59', strtotime( $this->end ) );
$this->end = 0 < $offset
? EDD()->utils->date( $this->end )->addSeconds( $offset )->format( 'mysql' )
: EDD()->utils->date( $this->end )->subSeconds( $offset )->format( 'mysql' );
$date_query_sql .= ' AND ';
}
if ( ! empty( $this->end ) ) {
$date_query_sql .= $wpdb->prepare( "{$wpdb->edd_orders}.date_created <= %s", $this->end );
}
}
// Look in orders table if a product ID was not passed.
if ( 0 === $this->download_id ) {
$sql = "
SELECT COUNT(id) AS sales, SUM(total) AS earnings, date_created
FROM {$wpdb->edd_orders}
WHERE 1=1 {$status} {$customer_id} {$date_query_sql}
GROUP BY YEAR(date_created), MONTH(date_created), DAY(date_created)
ORDER BY YEAR(date_created), MONTH(date_created), DAY(date_created) ASC
LIMIT {$args['offset']}, {$args['number']}
";
// Join orders and order items table.
} else {
$sql = "
SELECT SUM({$wpdb->edd_order_items}.quantity) AS sales, SUM({$wpdb->edd_order_items}.total) AS earnings, {$wpdb->edd_orders}.date_created
FROM {$wpdb->edd_orders}
INNER JOIN {$wpdb->edd_order_items} ON {$wpdb->edd_orders}.id = {$wpdb->edd_order_items}.order_id
WHERE 1=1 {$status} {$download_id} {$date_query_sql}
GROUP BY YEAR({$wpdb->edd_orders}.date_created), MONTH({$wpdb->edd_orders}.date_created), DAY({$wpdb->edd_orders}.date_created)
ORDER BY YEAR({$wpdb->edd_orders}.date_created), MONTH({$wpdb->edd_orders}.date_created), DAY({$wpdb->edd_orders}.date_created) ASC
LIMIT {$args['offset']}, {$args['number']}
";
}
$results = $wpdb->get_results( $sql );
foreach ( $results as $result ) {
// Localize the returned time.
$d = EDD()->utils->date( $result->date_created, null, true )->format( 'date' );
$sales = isset( $result->sales )
? absint( $result->sales )
: 0;
$earnings = isset( $result->earnings )
? edd_format_amount( $result->earnings )
: floatval( 0 );
$data[] = array(
'date' => $d,
'sales' => $sales,
'earnings' => $earnings,
);
}
$data = apply_filters( 'edd_export_get_data', $data );
$data = apply_filters( 'edd_export_get_data_' . $this->export_type, $data );
return $data;
}
/**
* Return the calculated completion percentage
*
* @since 2.4
* @since 3.0 Updated to use new query methods.
*
* @return int
*/
public function get_percentage_complete() {
$args = array(
'fields' => 'ids',
);
if ( ! empty( $this->start ) || ! empty( $this->end ) ) {
$args['date_query'] = $this->get_date_query();
}
$total = edd_count_orders( $args );
$percentage = 100;
if ( $total > 0 ) {
$percentage = ( ( 30 * $this->step ) / $total ) * 100;
}
if ( $percentage > 100 ) {
$percentage = 100;
}
return $percentage;
}
/**
* Set the properties specific to the sales and earnings export.
*
* @since 3.0
*
* @param array $request Form data passed to the batch processor.
*/
public function set_properties( $request ) {
$this->start = isset( $request['order-export-start'] )
? sanitize_text_field( $request['order-export-start'] )
: '';
$this->end = isset( $request['order-export-end'] )
? sanitize_text_field( $request['order-export-end'] )
: '';
$this->download_id = isset( $request['download_id'] )
? absint( $request['download_id'] )
: 0;
$this->customer_id = isset( $request['customer_id'] )
? absint( $request['customer_id'] )
: 0;
}
}

View File

@ -0,0 +1,163 @@
<?php
/**
* Batch Sales Logs Export Class
*
* This class handles Sales logs export
*
* @package EDD
* @subpackage Admin/Reporting/Export
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.7
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Batch_Sales_Export Class
*
* @since 2.7
*/
class EDD_Batch_Sales_Export extends EDD_Batch_Export {
/**
* Our export type. Used for export-type specific filters/actions
*
* @var string
* @since 2.7
*/
public $export_type = 'sales';
/**
* Set the CSV columns
*
* @since 2.7
* @return array $cols All the columns
*/
public function csv_cols() {
$cols = array(
'ID' => __( 'Log ID', 'easy-digital-downloads' ),
'user_id' => __( 'User', 'easy-digital-downloads' ),
'customer_id' => __( 'Customer ID', 'easy-digital-downloads' ),
'email' => __( 'Email', 'easy-digital-downloads' ),
'first_name' => __( 'First Name', 'easy-digital-downloads' ),
'last_name' => __( 'Last Name', 'easy-digital-downloads' ),
'download' => edd_get_label_singular(),
'quantity' => __( 'Quantity', 'easy-digital-downloads' ),
'amount' => __( 'Item Amount', 'easy-digital-downloads' ),
'payment_id' => __( 'Payment ID', 'easy-digital-downloads' ),
'price_id' => __( 'Price ID', 'easy-digital-downloads' ),
'date' => __( 'Date', 'easy-digital-downloads' ),
);
return $cols;
}
/**
* Get the Export Data
*
* @since 2.7
* @since 3.0 Updated to use new query methods.
*
* @return array|bool The data for the CSV file, false if no data to return.
*/
public function get_data() {
$data = array();
$args = array(
'number' => 30,
'offset' => ( $this->step * 30 ) - 30,
'order' => 'ASC',
);
if ( ! empty( $this->start ) || ! empty( $this->end ) ) {
$args['date_query'] = $this->get_date_query();
}
if ( 0 !== $this->download_id ) {
$args['product_id'] = $this->download_id;
}
$items = edd_get_order_items( $args );
foreach ( $items as $item ) {
/** @var EDD\Orders\Order_Item $item */
$order = edd_get_order( $item->order_id );
$download = edd_get_download( $item->product_id );
$user_info = $order->get_user_info();
$download_title = $item->product_name;
// Maybe append variable price name.
if ( $download->has_variable_prices() ) {
$price_option = edd_get_price_option_name( $item->product_id, $item->price_id, $order->id );
$download_title .= ! empty( $price_option )
? ' - ' . $price_option
: '';
}
$data[] = array(
'ID' => $item->product_id,
'user_id' => $order->user_id,
'customer_id' => $order->customer_id,
'email' => $order->email,
'first_name' => isset( $user_info['first_name'] ) ? $user_info['first_name'] : '',
'last_name' => isset( $user_info['last_name'] ) ? $user_info['last_name'] : '',
'download' => $download_title,
'quantity' => $item->quantity,
'amount' => $order->total,
'payment_id' => $order->id,
'price_id' => $item->price_id,
'date' => $order->date_created,
);
}
$data = apply_filters( 'edd_export_get_data', $data );
$data = apply_filters( 'edd_export_get_data_' . $this->export_type, $data );
return ! empty( $data )
? $data
: false;
}
/**
* Return the calculated completion percentage.
*
* @since 2.7
* @since 3.0 Updated to use new query methods.
*
* @return int
*/
public function get_percentage_complete() {
$args = array(
'fields' => 'ids',
);
if ( ! empty( $this->start ) || ! empty( $this->end ) ) {
$args['date_query'] = $this->get_date_query();
}
if ( 0 !== $this->download_id ) {
$args['product_id'] = $this->download_id;
}
$total = edd_count_order_items( $args );
$percentage = 100;
if ( $total > 0 ) {
$percentage = ( ( 30 * $this->step ) / $total ) * 100;
}
if ( $percentage > 100 ) {
$percentage = 100;
}
return $percentage;
}
public function set_properties( $request ) {
$this->start = isset( $request['orders-export-start'] ) ? sanitize_text_field( $request['orders-export-start'] ) : '';
$this->end = isset( $request['orders-export-end'] ) ? sanitize_text_field( $request['orders-export-end'] ) . ' 23:59:59' : '';
$this->download_id = isset( $request['download_id'] ) ? absint( $request['download_id'] ) : 0;
}
}

View File

@ -0,0 +1,183 @@
<?php
/**
* Taxed Customers Export Class.
*
* This class handles the taxed orders export in batches.
*
* @package EDD
* @subpackage Admin/Reporting/Export
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 3.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Batch_Taxed_Orders_Export Class
*
* @since 3.0
*/
class EDD_Batch_Taxed_Customers_Export extends EDD_Batch_Export {
/**
* Our export type. Used for export-type specific filters/actions.
*
* @var string
* @since 3.0
*/
public $export_type = 'taxed_customers';
/**
* Set the CSV columns
*
* @since 3.0
*
* @return array $cols All the columns
*/
public function csv_cols() {
$cols = array(
'id' => __( 'ID', 'easy-digital-downloads' ),
'name' => __( 'Name', 'easy-digital-downloads' ),
'email' => __( 'Email', 'easy-digital-downloads' ),
'purchases' => __( 'Number of Purchases', 'easy-digital-downloads' ),
'amount' => __( 'Customer Value', 'easy-digital-downloads' ),
);
return $cols;
}
/**
* Get the export data.
*
* @since 3.0
*
* @return array $data The data for the CSV file.
*/
public function get_data() {
$data = array();
$args = array(
'number' => 30,
'offset' => ( $this->step * 30 ) - 30,
'status__in' => edd_get_complete_order_statuses(),
'order' => 'ASC',
'orderby' => 'date_created',
'fields' => 'customer_id',
);
if ( ! empty( $this->start ) || ! empty( $this->end ) ) {
$args['date_query'] = $this->get_date_query();
}
add_filter( 'edd_orders_query_clauses', array( $this, 'query_clauses' ), 10, 2 );
$customer_ids = edd_get_orders( $args );
remove_filter( 'edd_orders_query_clauses', array( $this, 'query_clauses' ), 10 );
$customer_ids = array_unique( $customer_ids );
asort( $customer_ids );
foreach ( $customer_ids as $customer_id ) {
// Bail if a customer ID was not set.
if ( 0 === $customer_id ) {
continue;
}
$customer = edd_get_customer( $customer_id );
// Bail if a customer record does not exist.
if ( ! $customer ) {
continue;
}
$name = ! empty( $customer->name ) ? $customer->name : '';
if ( preg_match( '~^[+\-=@]~m', $name ) ) {
$name = "'{$name}";
}
$data[] = array(
'id' => $customer->id,
'name' => $name,
'email' => $customer->email,
'purchases' => $customer->purchase_count,
'amount' => edd_format_amount( $customer->purchase_value ),
);
}
$data = apply_filters( 'edd_export_get_data', $data );
$data = apply_filters( 'edd_export_get_data_' . $this->export_type, $data );
return $data;
}
/**
* Return the calculated completion percentage.
*
* @since 3.0
*
* @return int
*/
public function get_percentage_complete() {
$args = array(
'fields' => 'ids',
'status__in' => edd_get_complete_order_statuses(),
);
if ( ! empty( $this->start ) || ! empty( $this->end ) ) {
$args['date_query'] = $this->get_date_query();
}
add_filter( 'edd_orders_query_clauses', array( $this, 'query_clauses' ), 10, 2 );
$total = edd_count_orders( $args );
remove_filter( 'edd_orders_query_clauses', array( $this, 'query_clauses' ), 10 );
$percentage = 100;
if ( $total > 0 ) {
$percentage = ( ( 30 * $this->step ) / $total ) * 100;
}
if ( $percentage > 100 ) {
$percentage = 100;
}
return $percentage;
}
/**
* Set the properties specific to the taxed orders export.
*
* @since 3.0
*
* @param array $request The form data passed into the batch processing.
*/
public function set_properties( $request ) {
$this->start = isset( $request['taxed-customers-export-start'] ) ? sanitize_text_field( $request['taxed-customers-export-start'] ) : '';
$this->end = isset( $request['taxed-customers-export-end'] ) ? sanitize_text_field( $request['taxed-customers-export-end'] ) : '';
}
/**
* Filter the database query to only return orders which have tax applied to them.
*
* @since 3.0
*
* @param array $clauses A compacted array of item query clauses.
* @param \EDD\Database\Query $base Instance passed by reference.
*
* @return array
*/
public function query_clauses( $clauses, $base ) {
$clauses['where'] = ! empty( $clauses['where'] )
? $clauses['where'] .= ' AND tax > 0'
: 'tax > 0';
return $clauses;
}
}

View File

@ -0,0 +1,306 @@
<?php
/**
* Taxed Orders Export Class.
*
* This class handles the taxed orders export in batches.
*
* @package EDD
* @subpackage Admin/Reporting/Export
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 3.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Batch_Taxed_Orders_Export Class
*
* @since 3.0
*/
class EDD_Batch_Taxed_Orders_Export extends EDD_Batch_Export {
/**
* Our export type. Used for export-type specific filters/actions.
*
* @var string
* @since 3.0
*/
public $export_type = 'taxed_orders';
/**
* Set the CSV columns
*
* @since 3.0
*
* @return array $cols All the columns
*/
public function csv_cols() {
$cols = array(
'id' => __( 'Order ID', 'easy-digital-downloads' ), // unaltered payment ID (use for querying)
'seq_id' => __( 'Order Number', 'easy-digital-downloads' ), // sequential payment ID
'email' => __( 'Email', 'easy-digital-downloads' ),
'customer_id' => __( 'Customer ID', 'easy-digital-downloads' ),
'first' => __( 'First Name', 'easy-digital-downloads' ),
'last' => __( 'Last Name', 'easy-digital-downloads' ),
'address1' => __( 'Address', 'easy-digital-downloads' ),
'address2' => __( 'Address (Line 2)', 'easy-digital-downloads' ),
'city' => __( 'City', 'easy-digital-downloads' ),
'state' => __( 'State', 'easy-digital-downloads' ),
'country' => __( 'Country', 'easy-digital-downloads' ),
'zip' => __( 'Zip / Postal Code', 'easy-digital-downloads' ),
'products' => __( 'Products (Verbose)', 'easy-digital-downloads' ),
'products_raw' => __( 'Products (Raw)', 'easy-digital-downloads' ),
'skus' => __( 'SKUs', 'easy-digital-downloads' ),
'amount' => __( 'Amount', 'easy-digital-downloads' ) . ' (' . html_entity_decode( edd_currency_filter( '' ) ) . ')',
'tax' => __( 'Tax', 'easy-digital-downloads' ) . ' (' . html_entity_decode( edd_currency_filter( '' ) ) . ')',
'discount' => __( 'Discount Code', 'easy-digital-downloads' ),
'gateway' => __( 'Gateway', 'easy-digital-downloads' ),
'trans_id' => __( 'Transaction ID', 'easy-digital-downloads' ),
'key' => __( 'Purchase Key', 'easy-digital-downloads' ),
'date' => __( 'Date', 'easy-digital-downloads' ),
'user' => __( 'User', 'easy-digital-downloads' ),
'currency' => __( 'Currency', 'easy-digital-downloads' ),
'ip' => __( 'IP Address', 'easy-digital-downloads' ),
'mode' => __( 'Mode (Live|Test)', 'easy-digital-downloads' ),
'status' => __( 'Status', 'easy-digital-downloads' ),
'country_name' => __( 'Country Name', 'easy-digital-downloads' ),
);
if ( ! edd_use_skus() ) {
unset( $cols['skus'] );
}
if ( ! edd_get_option( 'enable_sequential' ) ) {
unset( $cols['seq_id'] );
}
return $cols;
}
/**
* Get the export data.
*
* @since 3.0
*
* @return array $data The data for the CSV file.
*/
public function get_data() {
$data = array();
$args = array(
'number' => 30,
'offset' => ( $this->step * 30 ) - 30,
'status' => $this->status,
'order' => 'ASC',
'orderby' => 'date_created',
'status__not_in' => array( 'trash' ),
);
if ( ! empty( $this->start ) || ! empty( $this->end ) ) {
$args['date_created_query'] = $this->get_date_query();
}
if ( 'any' === $args['status'] || 'all' === $args['status'] ) {
unset( $args['status'] );
}
add_filter( 'edd_orders_query_clauses', array( $this, 'query_clauses' ), 10, 2 );
$orders = edd_get_orders( $args );
remove_filter( 'edd_orders_query_clauses', array( $this, 'query_clauses' ), 10 );
foreach ( $orders as $order ) {
/** @var EDD\Orders\Order $order */
$items = $order->get_items();
$address = $order->get_address();
$total = $order->total;
$user_id = $order->user_id;
$products = '';
$products_raw = '';
$skus = '';
$discounts = $order->get_discounts();
$discounts = ! empty( $discounts )
? implode( ', ', $discounts )
: __( 'none', 'easy-digital-downloads' );
foreach ( $items as $key => $item ) {
/** @var EDD\Orders\Order_Item $item */
// Setup item information.
$id = $item->product_id;
$qty = $item->quantity;
$price = $item->amount;
$tax = $item->tax;
$price_id = $item->price_id;
// Set up verbose product column.
$products .= html_entity_decode( get_the_title( $id ) );
if ( $qty > 1 ) {
$products .= html_entity_decode( ' (' . $qty . ')' );
}
$products .= ' - ';
if ( edd_use_skus() ) {
$sku = edd_get_download_sku( $id );
if ( ! empty( $sku ) ) {
$skus .= $sku;
}
}
if ( 0 < $item->price_id ) {
$products .= html_entity_decode( edd_get_price_option_name( $id, $item->price_id, $order->id ) ) . ' - ';
}
$products .= html_entity_decode( edd_currency_filter( edd_format_amount( $price ) ) );
if ( ( count( $items ) - 1 ) !== $key ) {
$products .= ' / ';
if ( edd_use_skus() ) {
$skus .= ' / ';
}
}
// Set up raw products column; nothing but product names.
$products_raw .= html_entity_decode( get_the_title( $id ) ) . '|' . $price . '{' . $tax . '}';
// If we have a price ID, include it.
if ( false !== $price_id ) {
$products_raw .= '{' . $price_id . '}';
}
if ( ( count( $items ) - 1 ) !== $key ) {
$products_raw .= ' / ';
}
}
$user = is_numeric( $user_id )
? get_userdata( $user_id )
: false;
$data[] = array(
'id' => $order->id,
'seq_id' => $order->get_number(),
'email' => $order->email,
'customer_id' => $order->customer_id,
'first' => $address->first_name,
'last' => $address->last_name,
'address1' => $address->address,
'address2' => $address->address2,
'city' => $address->city,
'state' => $address->region,
'country' => $address->country,
'zip' => $address->postal_code,
'products' => $products,
'products_raw' => $products_raw,
'skus' => $skus,
'amount' => html_entity_decode( edd_format_amount( $total ) ), // The non-discounted item price
'tax' => html_entity_decode( edd_format_amount( $order->tax ) ),
'discount' => $discounts,
'gateway' => edd_get_gateway_admin_label( $order->gateway ),
'trans_id' => $order->get_transaction_id(),
'key' => $order->payment_key,
'date' => $order->date_created,
'user' => $user ? $user->display_name : __( 'guest', 'easy-digital-downloads' ),
'currency' => $order->currency,
'ip' => $order->ip,
'mode' => $order->mode,
'status' => $order->status,
'country_name' => isset( $user_info['address']['country'] ) ? edd_get_country_name( $user_info['address']['country'] ) : '',
);
}
$data = apply_filters( 'edd_export_get_data', $data );
$data = apply_filters( 'edd_export_get_data_' . $this->export_type, $data );
return ! empty( $data )
? $data
: false;
}
/**
* Return the calculated completion percentage.
*
* @since 3.0
*
* @return int
*/
public function get_percentage_complete() {
$args = array(
'fields' => 'ids',
);
if ( ! empty( $this->start ) || ! empty( $this->end ) ) {
$args['date_created_query'] = $this->get_date_query();
}
if ( 'any' !== $this->status ) {
$args['status'] = $this->status;
}
$total = edd_count_orders( $args );
$percentage = 100;
if ( $total > 0 ) {
$percentage = ( ( 30 * $this->step ) / $total ) * 100;
}
if ( $percentage > 100 ) {
$percentage = 100;
}
return $percentage;
}
/**
* Set the properties specific to the taxed orders export.
*
* @since 3.0
*
* @param array $request The form data passed into the batch processing.
*/
public function set_properties( $request ) {
$this->start = isset( $request['taxed-orders-export-start'] ) ? sanitize_text_field( $request['taxed-orders-export-start'] ) : '';
$this->end = isset( $request['taxed-orders-export-end'] ) ? sanitize_text_field( $request['taxed-orders-export-end'] ) : '';
$this->status = isset( $request['status'] ) ? sanitize_text_field( $request['status'] ) : 'complete';
$this->country = isset( $request['country'] ) ? sanitize_text_field( $request['country'] ) : '';
$this->region = isset( $request['region'] ) ? sanitize_text_field( $request['region'] ) : '';
}
/**
* Filter the database query to only return orders which have tax applied to them.
*
* @since 3.0
*
* @param array $clauses A compacted array of item query clauses.
* @param \EDD\Database\Query $base Instance passed by reference.
*
* @return array
*/
public function query_clauses( $clauses, $base ) {
global $wpdb;
$clauses['where'] = ! empty( $clauses['where'] )
? $clauses['where'] .= ' AND edd_o.tax > 0'
: 'edd_o.tax > 0';
if ( ! empty( $this->country ) ) {
$clauses['join'] = " INNER JOIN {$wpdb->edd_order_addresses} edd_oa ON edd_o.id = edd_oa.order_id";
$clauses['where'] .= $wpdb->prepare( ' AND edd_oa.country = %s', $this->country );
}
if ( ! empty( $this->region ) ) {
$clauses['where'] .= $wpdb->prepare( ' AND edd_oa.region = %s', $this->region );
}
return $clauses;
}
}

View File

@ -0,0 +1,340 @@
<?php
/**
* Batch Export Class
*
* This is the base class for all batch export methods. Each data export type (customers, payments, etc) extend this class
*
* @package EDD
* @subpackage Admin/Export
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.4
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* EDD_Export Class
*
* @since 2.4
*/
class EDD_Batch_Export extends EDD_Export {
/**
* Whether or not we're done processing.
*
* @var bool
*/
public $done;
/**
* The file the data is stored in
*
* @since 2.4
*/
protected $file;
/**
* The name of the file the data is stored in
*
* @since 2.4
*/
public $filename;
/**
* The file type, typically .csv
*
* @since 2.4
*/
public $filetype;
/**
* The current step being processed
*
* @since 2.4
*/
public $step;
/**
* Start date, Y-m-d H:i:s
*
* @since 2.4
*/
public $start;
/**
* End date, Y-m-d H:i:s
*
* @since 2.4
*/
public $end;
/**
* Status to export
*
* @since 2.4
*/
public $status;
/**
* Download to export data for
*
* @since 2.4
*/
public $download = null;
/**
* Download Price ID to export data for
*
* @since 2.4
*/
public $price_id = null;
/**
* Is the export file writable
*
* @since 2.4.4
*/
public $is_writable = true;
/**
* Is the export file empty
*
* @since 2.4.4
*/
public $is_empty = false;
/**
* The data to return to the script.
*
* @since 3.0
* @var array
*/
public $result_data = array();
/**
* Get things started
*
* @param $_step int The step to process
* @since 2.4
*/
public function __construct( $_step = 1 ) {
$upload_dir = wp_upload_dir();
$this->filetype = '.csv';
$this->filename = 'edd-' . $this->export_type . $this->filetype;
$this->file = trailingslashit( $upload_dir['basedir'] ) . $this->filename;
if ( ! is_writeable( $upload_dir['basedir'] ) ) {
$this->is_writable = false;
}
$this->step = $_step;
$this->done = false;
}
/**
* Process a step
*
* @since 2.4
* @return bool
*/
public function process_step() {
if ( ! $this->can_export() ) {
wp_die( __( 'You do not have permission to export data.', 'easy-digital-downloads' ), __( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
if( $this->step < 2 ) {
// Make sure we start with a fresh file on step 1
if ( file_exists( $this->file ) ) {
unlink( $this->file );
}
$this->print_csv_cols();
}
$rows = $this->print_csv_rows();
if( $rows ) {
return true;
} else {
return false;
}
}
/**
* Output the CSV columns
*
* @since 2.4
* @uses EDD_Export::get_csv_cols()
* @return string
*/
public function print_csv_cols() {
$col_data = '';
$cols = $this->get_csv_cols();
$i = 1;
foreach( $cols as $col_id => $column ) {
$col_data .= '"' . addslashes( $column ) . '"';
$col_data .= $i == count( $cols ) ? '' : ',';
$i++;
}
$col_data .= "\r\n";
$this->stash_step_data( $col_data );
return $col_data;
}
/**
* Print the CSV rows for the current step
*
* @since 2.4
* @return string|false
*/
public function print_csv_rows() {
$row_data = '';
$data = $this->get_data();
$cols = $this->get_csv_cols();
if( $data ) {
// Output each row
foreach ( $data as $row ) {
$i = 1;
foreach ( $row as $col_id => $column ) {
// Make sure the column is valid
if ( array_key_exists( $col_id, $cols ) ) {
$row_data .= '"' . addslashes( preg_replace( "/\"/","'", $column ) ) . '"';
$row_data .= $i == count( $cols ) ? '' : ',';
$i++;
}
}
$row_data .= "\r\n";
}
$this->stash_step_data( $row_data );
return $row_data;
}
return false;
}
/**
* Return the calculated completion percentage
*
* @since 2.4
* @return int
*/
public function get_percentage_complete() {
return 100;
}
/**
* Retrieve the file data is written to
*
* @since 2.4
* @return string
*/
protected function get_file() {
$file = '';
if ( @file_exists( $this->file ) ) {
if ( ! is_writeable( $this->file ) ) {
$this->is_writable = false;
}
$file = @file_get_contents( $this->file );
} else {
@file_put_contents( $this->file, '' );
@chmod( $this->file, 0664 );
}
return $file;
}
/**
* Append data to export file
*
* @since 2.4
* @param $data string The data to add to the file
* @return void
*/
protected function stash_step_data( $data = '' ) {
$file = $this->get_file();
$file .= $data;
@file_put_contents( $this->file, $file );
// If we have no rows after this step, mark it as an empty export
$file_rows = file( $this->file, FILE_SKIP_EMPTY_LINES);
$default_cols = $this->get_csv_cols();
$default_cols = empty( $default_cols ) ? 0 : 1;
$this->is_empty = count( $file_rows ) == $default_cols ? true : false;
}
/**
* Perform the export
*
* @since 2.4
* @return void
*/
public function export() {
// Set headers
$this->headers();
$file = $this->get_file();
@unlink( $this->file );
echo $file;
die();
}
/*
* Set the properties specific to the export
*
* @since 2.4.2
* @param array $request The Form Data passed into the batch processing
*/
public function set_properties( $request ) {}
/**
* Allow for prefetching of data for the remainder of the exporter
*
* @since 2.5
* @return void
*/
public function pre_fetch() {}
/**
* Gets the date query.
*
* @since 3.0
* @return array
*/
protected function get_date_query() {
return array(
array(
'after' => $this->start ? date( 'Y-m-d 00:00:00', strtotime( $this->start ) ) : '',
'before' => $this->end ? date( 'Y-m-d 23:59:59', strtotime( $this->end ) ) : '',
'inclusive' => true,
),
);
}
}

View File

@ -0,0 +1,309 @@
<?php
/**
* Exports Actions
*
* These are actions related to exporting data from Easy Digital Downloads.
*
* @package EDD
* @subpackage Admin/Export
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 2.4
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Process the download file generated by a batch export.
*
* @since 2.4
*/
function edd_process_batch_export_download() {
if ( ! wp_verify_nonce( $_REQUEST['nonce'], 'edd-batch-export' ) ) {
wp_die( esc_html__( 'Nonce verification failed', 'easy-digital-downloads' ), esc_html__( 'Error', 'easy-digital-downloads' ), array( 'response' => 403 ) );
}
require_once EDD_PLUGIN_DIR . 'includes/admin/reporting/export/class-batch-export.php';
do_action( 'edd_batch_export_class_include', $_REQUEST['class'] );
if ( class_exists( $_REQUEST['class'] ) && 'EDD_Batch_Export' === get_parent_class( $_REQUEST['class'] ) ) {
$export = new $_REQUEST['class']();
$export->export();
}
}
add_action( 'edd_download_batch_export', 'edd_process_batch_export_download' );
/**
* Export all the customers to a CSV file.
*
* Note: The WordPress Database API is being used directly for performance
* reasons (workaround of calling all posts and fetch data respectively)
*
* @since 1.4.4
* @return void
*/
function edd_export_all_customers() {
require_once EDD_PLUGIN_DIR . 'includes/admin/reporting/class-export-customers.php';
$customer_export = new EDD_Customers_Export();
$customer_export->export();
}
add_action( 'edd_email_export', 'edd_export_all_customers' );
/**
* Exports all the downloads to a CSV file using the EDD_Export class.
*
* @since 1.4.4
* @return void
*/
function edd_export_all_downloads_history() {
require_once EDD_PLUGIN_DIR . 'includes/admin/reporting/class-export-download-history.php';
$file_download_export = new EDD_Download_History_Export();
$file_download_export->export();
}
add_action( 'edd_downloads_history_export', 'edd_export_all_downloads_history' );
/**
* Add a hook allowing extensions to register a hook on the batch export process
*
* @since 2.4.2
* @return void
*/
function edd_register_batch_exporters() {
if ( is_admin() ) {
do_action( 'edd_register_batch_exporter' );
}
}
add_action( 'plugins_loaded', 'edd_register_batch_exporters', 99 );
/**
* Register the payments batch exporter
* @since 2.4.2
*/
function edd_register_payments_batch_export() {
add_action( 'edd_batch_export_class_include', 'edd_include_payments_batch_processor', 10, 1 );
}
add_action( 'edd_register_batch_exporter', 'edd_register_payments_batch_export', 10 );
/**
* Loads the payments batch processor if needed.
*
* @since 2.4.2
*
* @param string $class The class being requested to run for the batch export
*/
function edd_include_payments_batch_processor( $class ) {
if ( 'EDD_Batch_Payments_Export' === $class ) {
require_once EDD_PLUGIN_DIR . 'includes/admin/reporting/export/class-batch-export-payments.php';
}
}
/**
* Register the customers batch exporter.
*
* @since 2.4.2
*/
function edd_register_customers_batch_export() {
add_action( 'edd_batch_export_class_include', 'edd_include_customers_batch_processor', 10, 1 );
}
add_action( 'edd_register_batch_exporter', 'edd_register_customers_batch_export', 10 );
/**
* Loads the customers batch processor if needed.
*
* @since 2.4.2
*
* @param string $class The class being requested to run for the batch export.
*/
function edd_include_customers_batch_processor( $class ) {
if ( 'EDD_Batch_Customers_Export' === $class ) {
require_once EDD_PLUGIN_DIR . 'includes/admin/reporting/export/class-batch-export-customers.php';
}
}
/**
* Register the download products batch exporter
*
* @since 2.5
*/
function edd_register_downloads_batch_export() {
add_action( 'edd_batch_export_class_include', 'edd_include_downloads_batch_processor', 10, 1 );
}
add_action( 'edd_register_batch_exporter', 'edd_register_downloads_batch_export', 10 );
/**
* Loads the file downloads batch process if needed
*
* @since 2.5
* @param string $class The class being requested to run for the batch export
* @return void
*/
function edd_include_downloads_batch_processor( $class ) {
if ( 'EDD_Batch_Downloads_Export' === $class ) {
require_once EDD_PLUGIN_DIR . 'includes/admin/reporting/export/class-batch-export-downloads.php';
}
}
/**
* Register the file downloads batch exporter
* @since 2.4.2
*/
function edd_register_file_downloads_batch_export() {
add_action( 'edd_batch_export_class_include', 'edd_include_file_downloads_batch_processor', 10, 1 );
}
add_action( 'edd_register_batch_exporter', 'edd_register_file_downloads_batch_export', 10 );
/**
* Loads the file downloads batch process if needed
*
* @since 2.4.2
* @param string $class The class being requested to run for the batch export
* @return void
*/
function edd_include_file_downloads_batch_processor( $class ) {
if ( 'EDD_Batch_File_Downloads_Export' === $class ) {
require_once EDD_PLUGIN_DIR . 'includes/admin/reporting/export/class-batch-export-file-downloads.php';
}
}
/**
* Register the sales batch exporter.
*
* @since 2.7
*/
function edd_register_sales_export_batch_export() {
add_action( 'edd_batch_export_class_include', 'edd_include_sales_export_batch_processor', 10, 1 );
}
add_action( 'edd_register_batch_exporter', 'edd_register_sales_export_batch_export', 10 );
/**
* Loads the sales export batch process if needed
*
* @since 2.7
* @param string $class The class being requested to run for the batch export
* @return void
*/
function edd_include_sales_export_batch_processor( $class ) {
if ( 'EDD_Batch_Sales_Export' === $class ) {
require_once EDD_PLUGIN_DIR . 'includes/admin/reporting/export/class-batch-export-sales.php';
}
}
/**
* Register the earnings report batch exporter
*
* @since 2.7
*/
function edd_register_earnings_report_batch_export() {
add_action( 'edd_batch_export_class_include', 'edd_include_earnings_report_batch_processor', 10, 1 );
}
add_action( 'edd_register_batch_exporter', 'edd_register_earnings_report_batch_export', 10 );
/**
* Loads the earnings report batch process if needed
*
* @since 2.7
* @param string $class The class being requested to run for the batch export
* @return void
*/
function edd_include_earnings_report_batch_processor( $class ) {
if ( 'EDD_Batch_Earnings_Report_Export' === $class ) {
require_once EDD_PLUGIN_DIR . 'includes/admin/reporting/export/class-batch-export-earnings-report.php';
}
}
/**
* Register the API requests batch exporter
*
* @since 2.7
*/
function edd_register_api_requests_batch_export() {
add_action( 'edd_batch_export_class_include', 'edd_include_api_requests_batch_processor', 10, 1 );
}
add_action( 'edd_register_batch_exporter', 'edd_register_api_requests_batch_export', 10 );
/**
* Loads the API requests batch process if needed
*
* @since 2.7
* @param string $class The class being requested to run for the batch export
* @return void
*/
function edd_include_api_requests_batch_processor( $class ) {
if ( 'EDD_Batch_API_Requests_Export' === $class ) {
require_once EDD_PLUGIN_DIR . 'includes/admin/reporting/export/class-batch-export-api-requests.php';
}
}
/**
* Register the taxed orders report batch exporter.
*
* @since 3.0
*/
function edd_register_taxed_orders_batch_export() {
add_action( 'edd_batch_export_class_include', 'edd_include_taxed_orders_batch_processor', 10, 1 );
}
add_action( 'edd_register_batch_exporter', 'edd_register_taxed_orders_batch_export', 10 );
/**
* Loads the taxed orders report batch process if needed.
*
* @since 3.0
*
* @param string $class The class being requested to run for the batch export
*/
function edd_include_taxed_orders_batch_processor( $class ) {
if ( 'EDD_Batch_Taxed_Orders_Export' === $class ) {
require_once EDD_PLUGIN_DIR . 'includes/admin/reporting/export/class-batch-export-taxed-orders.php';
}
}
/**
* Register the taxed orders report batch exporter.
*
* @since 3.0
*/
function edd_register_taxed_customers_batch_export() {
add_action( 'edd_batch_export_class_include', 'edd_include_taxed_customers_batch_processor', 10, 1 );
}
add_action( 'edd_register_batch_exporter', 'edd_register_taxed_customers_batch_export', 10 );
/**
* Loads the taxed customers report batch process if needed.
*
* @since 3.0
*
* @param string $class The class being requested to run for the batch export
*/
function edd_include_taxed_customers_batch_processor( $class ) {
if ( 'EDD_Batch_Taxed_Customers_Export' === $class ) {
require_once EDD_PLUGIN_DIR . 'includes/admin/reporting/export/class-batch-export-taxed-customers.php';
}
}
/**
* Register the sales and earnings report batch exporter.
*
* @since 3.0
*/
function edd_register_sales_and_earnings_batch_export() {
add_action( 'edd_batch_export_class_include', 'edd_include_sales_and_earnings_batch_processor', 10, 1 );
}
add_action( 'edd_register_batch_exporter', 'edd_register_sales_and_earnings_batch_export', 10 );
/**
* Loads the sales and earnings batch process if needed.
*
* @since 3.0
*
* @param string $class The class being requested to run for the batch export
*/
function edd_include_sales_and_earnings_batch_processor( $class ) {
if ( 'EDD_Batch_Sales_And_Earnings_Export' === $class ) {
require_once EDD_PLUGIN_DIR . 'includes/admin/reporting/export/class-batch-export-sales-and-earnings.php';
}
}

View File

@ -0,0 +1,112 @@
<?php
/**
* Exports Functions
*
* These are functions are used for exporting data from Easy Digital Downloads.
*
* @package EDD
* @subpackage Admin/Export
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
require_once EDD_PLUGIN_DIR . 'includes/admin/reporting/class-export.php';
require_once EDD_PLUGIN_DIR . 'includes/admin/reporting/export/export-actions.php';
/**
* Process batch exports via AJAX.
*
* @since 2.4
*/
function edd_do_ajax_export() {
require_once EDD_PLUGIN_DIR . 'includes/admin/reporting/export/class-batch-export.php';
parse_str( $_POST['form'], $form ); // WPCS: CSRF ok.
$_REQUEST = $form;
$form = (array) $form;
if ( ! wp_verify_nonce( $_REQUEST['edd_ajax_export'], 'edd_ajax_export' ) ) {
die( '-2' );
}
do_action( 'edd_batch_export_class_include', $form['edd-export-class'] );
$step = absint( $_POST['step'] );
$class = sanitize_text_field( $form['edd-export-class'] );
/** @var \EDD_Batch_Export $export */
$export = new $class( $step );
if ( ! $export->can_export() ) {
die( '-1' );
}
if ( ! $export->is_writable ) {
echo wp_json_encode( array(
'error' => true,
'message' => __( 'Export location or file not writable', 'easy-digital-downloads' ),
));
exit;
}
$export->set_properties( $_REQUEST );
// Added in 2.5 to allow a bulk processor to pre-fetch some data to speed up the remaining steps and cache data.
$export->pre_fetch();
$ret = $export->process_step();
$percentage = $export->get_percentage_complete();
if ( $ret ) {
$step++;
echo wp_json_encode( array(
'step' => absint( $step ),
'percentage' => esc_attr( $percentage ),
) );
exit;
} elseif ( true === $export->is_empty ) {
echo wp_json_encode( array(
'error' => true,
'message' => __( 'No data found for export parameters', 'easy-digital-downloads' ),
) );
exit;
} elseif ( true === $export->done && true === $export->is_void ) {
$message = ! empty( $export->message )
? $export->message
: __( 'Batch Processing Complete', 'easy-digital-downloads' );
echo wp_json_encode( array(
'success' => true,
'message' => $message,
'data' => $export->result_data,
) );
exit;
} else {
$args = array_merge( $_REQUEST, array(
'step' => absint( $step ),
'class' => urlencode( $class ),
'nonce' => wp_create_nonce( 'edd-batch-export' ),
'edd_action' => 'download_batch_export',
) );
$download_url = add_query_arg( $args, admin_url() );
echo wp_json_encode( array(
'step' => 'done',
'url' => esc_url_raw( $download_url ),
) );
exit;
}
}
add_action( 'wp_ajax_edd_do_ajax_export', 'edd_do_ajax_export' );

View File

@ -0,0 +1,775 @@
<?php
/**
* Graphing Functions
*
* @package EDD
* @subpackage Admin/Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.0
*/
use EDD\Reports;
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Show report graphs
*
* @since 1.3
* @return void
*/
function edd_reports_graph() {
// Retrieve the queried dates
$dates = Reports\get_dates_filter( 'objects' );
$day_by_day = Reports\get_dates_filter_day_by_day();
$earnings_totals = 0.00; // Total earnings for time period shown
$sales_totals = 0; // Total sales for time period shown
$include_taxes = empty( $_GET['exclude_taxes'] ) ? true : false;
if ( $dates['range'] == 'today' || $dates['range'] == 'yesterday' ) {
// Hour by hour
$hour = 0;
$month = $dates['start']->month;
$i = 0;
$j = 0;
$start = $dates['start']->format( 'Y-m-d' );
$end = $dates['end']->format( 'Y-m-d' );
$sales = EDD()->payment_stats->get_sales_by_range( $dates['range'], true, $start, $end );
$earnings = EDD()->payment_stats->get_earnings_by_range( $dates['range'], true, $start, $end, $include_taxes );
while ( $hour <= 23 ) {
$date = mktime( $hour, 0, 0, $month, $dates['start']->day, $dates['start']->year ) * 1000;
if ( isset( $earnings[ $i ] ) && $earnings[ $i ]['h'] == $hour ) {
$earnings_data[] = array( $date, $earnings[ $i ]['total'] );
$earnings_totals += $earnings[ $i ]['total'];
$i++;
} else {
$earnings_data[] = array( $date, 0 );
}
if ( isset( $sales[ $j ] ) && $sales[ $j ]['h'] == $hour ) {
$sales_data[] = array( $date, $sales[ $j ]['count'] );
$sales_totals += $sales[ $j ]['count'];
$j++;
} else {
$sales_data[] = array( $date, 0 );
}
$hour++;
}
} elseif ( $dates['range'] == 'this_week' || $dates['range'] == 'last_week' ) {
$report_dates = array();
$i = 0;
while ( $i <= 6 ) {
if ( ( $dates['start']->day + $i ) <= $dates['end']->day ) {
$report_dates[ $i ] = array(
'day' => (string) $dates['start']->day + $i,
'month' => $dates['start']->month,
'year' => $dates['start']->year,
);
} else {
$report_dates[ $i ] = array(
'day' => (string) $i,
'month' => $dates['end']->month,
'year' => $dates['end']->year,
);
}
$i++;
}
$start_date = $report_dates[0];
$end_date = end( $report_dates );
$sales = EDD()->payment_stats->get_sales_by_range( $dates['range'], true, $start_date['year'] . '-' . $start_date['month'] . '-' . $start_date['day'], $end_date['year'] . '-' . $end_date['month'] . '-' . $end_date['day'] );
$earnings = EDD()->payment_stats->get_earnings_by_range( $dates['range'], true, $start_date['year'] . '-' . $start_date['month'] . '-' . $start_date['day'], $end_date['year'] . '-' . $end_date['month'] . '-' . $end_date['day'], $include_taxes );
$i = 0;
$j = 0;
foreach ( $report_dates as $report_date ) {
$date = mktime( 0, 0, 0, $report_date['month'], $report_date['day'], $report_date['year'] ) * 1000;
if ( array_key_exists( $i, $sales ) && $report_date['day'] == $sales[ $i ]['d'] && $report_date['month'] == $sales[ $i ]['m'] && $report_date['year'] == $sales[ $i ]['y'] ) {
$sales_data[] = array( $date, $sales[ $i ]['count'] );
$sales_totals += $sales[ $i ]['count'];
$i++;
} else {
$sales_data[] = array( $date, 0 );
}
if ( array_key_exists( $j, $earnings ) && $report_date['day'] == $earnings[ $j ]['d'] && $report_date['month'] == $earnings[ $j ]['m'] && $report_date['year'] == $earnings[ $j ]['y'] ) {
$earnings_data[] = array( $date, $earnings[ $j ]['total'] );
$earnings_totals += $earnings[ $j ]['total'];
$j++;
} else {
$earnings_data[] = array( $date, 0 );
}
}
} else {
$date_start = $dates['start']->format( 'Y-m-d' );
$date_end = $dates['end']->format( 'Y-m-d' );
$sales = EDD()->payment_stats->get_sales_by_range( $dates['range'], $day_by_day, $date_start, $date_end );
$earnings = EDD()->payment_stats->get_earnings_by_range( $dates['range'], $day_by_day, $date_start, $date_end, $include_taxes );
$temp_data = array(
'sales' => array(),
'earnings' => array(),
);
foreach ( $sales as $sale ) {
if ( $day_by_day ) {
$temp_data['sales'][ $sale['y'] ][ $sale['m'] ][ $sale['d'] ] = $sale['count'];
} else {
$temp_data['sales'][ $sale['y'] ][ $sale['m'] ] = $sale['count'];
}
$sales_totals += $sale['count'];
}
foreach ( $earnings as $earning ) {
if ( $day_by_day ) {
$temp_data['earnings'][ $earning['y'] ][ $earning['m'] ][ $earning['d'] ] = $earning['total'];
} else {
$temp_data['earnings'][ $earning['y'] ][ $earning['m'] ] = $earning['total'];
}
$earnings_totals += $earning['total'];
}
while ( $day_by_day && ( strtotime( $date_start ) <= strtotime( $date_end ) ) ) {
$d = $dates['start']->day;
$m = $dates['start']->month;
$y = $dates['start']->year;
if ( ! isset( $temp_data['sales'][ $y ][ $m ][ $d ] ) ) {
$temp_data['sales'][ $y ][ $m ][ $d ] = 0;
}
if ( ! isset( $temp_data['earnings'][ $y ][ $m ][ $d ] ) ) {
$temp_data['earnings'][ $y ][ $m ][ $d ] = 0;
}
$date_start = $dates['start']->addDays( 1 )->format( 'Y-m-d' );
}
while ( ! $day_by_day && ( strtotime( $date_start ) <= strtotime( $date_end ) ) ) {
$m = $dates['start']->month;
$y = $dates['start']->year;
if ( ! isset( $temp_data['sales'][ $y ][ $m ] ) ) {
$temp_data['sales'][ $y ][ $m ] = 0;
}
if ( ! isset( $temp_data['earnings'][ $y ][ $m ] ) ) {
$temp_data['earnings'][ $y ][ $m ] = 0;
}
$date_start = $dates['start']->addMonths( 1 )->format( 'Y-m' );
}
$sales_data = array();
$earnings_data = array();
// When using 3 months or smaller as the custom range, show each day individually on the graph
if ( $day_by_day ) {
foreach ( $temp_data['sales'] as $year => $months ) {
foreach ( $months as $month => $days ) {
foreach ( $days as $day => $count ) {
$date = mktime( 0, 0, 0, $month, $day, $year ) * 1000;
$sales_data[] = array( $date, $count );
}
}
}
foreach ( $temp_data['earnings'] as $year => $months ) {
foreach ( $months as $month => $days ) {
foreach ( $days as $day => $total ) {
$date = mktime( 0, 0, 0, $month, $day, $year ) * 1000;
$earnings_data[] = array( $date, $total );
}
}
}
// Sort dates in ascending order
foreach ( $sales_data as $key => $value ) {
$timestamps[ $key ] = $value[0];
}
if ( ! empty( $timestamps ) ) {
array_multisort( $timestamps, SORT_ASC, $sales_data );
}
foreach ( $earnings_data as $key => $value ) {
$earnings_timestamps[ $key ] = $value[0];
}
if ( ! empty( $earnings_timestamps ) ) {
array_multisort( $earnings_timestamps, SORT_ASC, $earnings_data );
}
// When showing more than 3 months of results, group them by month, by the first (except for the last month, group on the last day of the month selected)
} else {
foreach ( $temp_data['sales'] as $year => $months ) {
$month_keys = array_keys( $months );
$last_month = end( $month_keys );
if ( $day_by_day ) {
foreach ( $months as $month => $days ) {
$day_keys = array_keys( $days );
$last_day = end( $day_keys );
$month_keys = array_keys( $months );
$consolidated_date = $month === end( $month_keys ) ? cal_days_in_month( CAL_GREGORIAN, $month, $year ) : 1;
$sales = array_sum( $days );
$date = mktime( 0, 0, 0, $month, $consolidated_date, $year ) * 1000;
$sales_data[] = array( $date, $sales );
}
} else {
foreach ( $months as $month => $count ) {
$month_keys = array_keys( $months );
$consolidated_date = $month === end( $month_keys ) ? cal_days_in_month( CAL_GREGORIAN, $month, $year ) : 1;
$date = mktime( 0, 0, 0, $month, $consolidated_date, $year ) * 1000;
$sales_data[] = array( $date, $count );
}
}
}
// Sort dates in ascending order
foreach ( $sales_data as $key => $value ) {
$timestamps[ $key ] = $value[0];
}
if ( ! empty( $timestamps ) ) {
array_multisort( $timestamps, SORT_ASC, $sales_data );
}
foreach ( $temp_data['earnings'] as $year => $months ) {
$month_keys = array_keys( $months );
$last_month = end( $month_keys );
if ( $day_by_day ) {
foreach ( $months as $month => $days ) {
$day_keys = array_keys( $days );
$last_day = end( $day_keys );
$month_keys = array_keys( $months );
$consolidated_date = $month === end( $month_keys )
? cal_days_in_month( CAL_GREGORIAN, $month, $year )
: 1;
$earnings = array_sum( $days );
$date = mktime( 0, 0, 0, $month, $consolidated_date, $year ) * 1000;
$earnings_data[] = array( $date, $earnings );
}
} else {
foreach ( $months as $month => $count ) {
$month_keys = array_keys( $months );
$consolidated_date = $month === end( $month_keys )
? cal_days_in_month( CAL_GREGORIAN, $month, $year )
: 1;
$date = mktime( 0, 0, 0, $month, $consolidated_date, $year ) * 1000;
$earnings_data[] = array( $date, $count );
}
}
}
// Sort dates in ascending order
foreach ( $earnings_data as $key => $value ) {
$earnings_timestamps[ $key ] = $value[0];
}
if ( ! empty( $earnings_timestamps ) ) {
array_multisort( $earnings_timestamps, SORT_ASC, $earnings_data );
}
}
}
$data = array(
__( 'Earnings', 'easy-digital-downloads' ) => $earnings_data,
__( 'Sales', 'easy-digital-downloads' ) => $sales_data
);
// start our own output buffer
ob_start();
do_action( 'edd_reports_graph_before' ); ?>
<div id="edd-dashboard-widgets-wrap">
<div class="metabox-holder" style="padding-top: 0;">
<div class="postbox">
<h3><span><?php _e('Earnings Over Time','easy-digital-downloads' ); ?></span></h3>
<div class="inside">
<?php
$graph = new EDD_Graph( $data );
$graph->set( 'x_mode', 'time' );
$graph->set( 'multiple_y_axes', true );
$graph->display();
if( ! empty( $dates['range'] ) && 'this_month' == $dates['range'] ) {
$estimated = edd_estimated_monthly_stats( $include_taxes );
}
?>
<p class="edd_graph_totals">
<strong>
<?php
_e( 'Total earnings for period shown: ', 'easy-digital-downloads' );
echo edd_currency_filter( edd_format_amount( $earnings_totals ) );
?>
</strong>
<?php if ( ! $include_taxes ) : ?>
<sup>&dagger;</sup>
<?php endif; ?>
</p>
<p class="edd_graph_totals"><strong><?php _e( 'Total sales for period shown: ', 'easy-digital-downloads' ); echo edd_format_amount( $sales_totals, false ); ?></strong></p>
<?php if( ! empty( $dates['range'] ) && 'this_month' == $dates['range'] ) : ?>
<p class="edd_graph_totals">
<strong>
<?php
_e( 'Estimated monthly earnings: ', 'easy-digital-downloads' );
echo edd_currency_filter( edd_format_amount( $estimated['earnings'] ) );
?>
</strong>
<?php if ( ! $include_taxes ) : ?>
<sup>&dagger;</sup>
<?php endif; ?>
</p>
<p class="edd_graph_totals"><strong><?php _e( 'Estimated monthly sales: ', 'easy-digital-downloads' ); echo edd_format_amount( $estimated['sales'], false ); ?></strong></p>
<?php endif; ?>
<?php do_action( 'edd_reports_graph_additional_stats' ); ?>
<p class="edd_graph_notes">
<?php if ( false === $include_taxes ) : ?>
<em><sup>&dagger;</sup> <?php _e( 'Excludes sales tax.', 'easy-digital-downloads' ); ?></em>
<?php endif; ?>
</p>
</div>
</div>
</div>
</div>
<?php do_action( 'edd_reports_graph_after' );
// Output the buffer
echo ob_get_clean();
}
/**
* Show report graphs of a specific product
*
* @since 1.9
* @return void
*/
function edd_reports_graph_of_download( $download_id = 0 ) {
// Retrieve the queried dates
$dates = edd_get_report_dates();
// Determine graph options
switch ( $dates['range'] ) {
case 'today' :
case 'yesterday' :
$day_by_day = true;
break;
case 'last_year' :
case 'this_year' :
$day_by_day = false;
break;
case 'last_quarter' :
case 'this_quarter' :
$day_by_day = false;
break;
case 'other' :
if ( $dates['m_start'] == 12 && $dates['m_end'] == 1 ) {
$day_by_day = true;
} elseif ( $dates['m_end'] - $dates['m_start'] >= 3 || ( $dates['year_end'] > $dates['year'] && ( $dates['m_start'] - $dates['m_end'] ) != 10 ) ) {
$day_by_day = false;
} else {
$day_by_day = true;
}
break;
default:
$day_by_day = true;
break;
}
$earnings_totals = (float) 0.00; // Total earnings for time period shown
$sales_totals = 0; // Total sales for time period shown
$include_taxes = empty( $_GET['exclude_taxes'] ) ? true : false;
$earnings_data = array();
$sales_data = array();
if ( $dates['range'] == 'today' || $dates['range'] == 'yesterday' ) {
// Hour by hour
$month = $dates['m_start'];
$hour = 0;
$minute = 0;
$second = 0;
while ( $hour <= 23 ) :
if ( $hour == 23 ) {
$minute = $second = 59;
}
$date = mktime( $hour, $minute, $second, $month, $dates['day'], $dates['year'] );
$date_end = mktime( $hour + 1, $minute, $second, $month, $dates['day'], $dates['year'] );
$sales = EDD()->payment_stats->get_sales( $download_id, $date, $date_end );
$sales_totals += $sales;
$earnings = EDD()->payment_stats->get_earnings( $download_id, $date, $date_end, $include_taxes );
$earnings_totals += $earnings;
$sales_data[] = array( $date * 1000, $sales );
$earnings_data[] = array( $date * 1000, $earnings );
$hour++;
endwhile;
} elseif( $dates['range'] == 'this_week' || $dates['range'] == 'last_week' ) {
$num_of_days = cal_days_in_month( CAL_GREGORIAN, $dates['m_start'], $dates['year'] );
$report_dates = array();
$i = 0;
while ( $i <= 6 ) {
if ( ( $dates['day'] + $i ) <= $num_of_days ) {
$report_dates[ $i ] = array(
'day' => (string) $dates['day'] + $i,
'month' => $dates['m_start'],
'year' => $dates['year'],
);
} else {
$report_dates[ $i ] = array(
'day' => (string) $i,
'month' => $dates['m_end'],
'year' => $dates['year_end'],
);
}
$i++;
}
foreach ( $report_dates as $report_date ) {
$date = mktime( 0, 0, 0, $report_date['month'], $report_date['day'], $report_date['year'] );
$date_end = mktime( 23, 59, 59, $report_date['month'], $report_date['day'], $report_date['year'] );
$sales = EDD()->payment_stats->get_sales( $download_id, $date, $date_end );
$sales_totals += $sales;
$earnings = EDD()->payment_stats->get_earnings( $download_id, $date, $date_end, $include_taxes );
$earnings_totals += $earnings;
$sales_data[] = array( $date * 1000, $sales );
$earnings_data[] = array( $date * 1000, $earnings );
}
} else {
$y = $dates['year'];
$temp_data = array();
while( $y <= $dates['year_end'] ) {
$last_year = false;
if( $dates['year'] == $dates['year_end'] ) {
$month_start = $dates['m_start'];
$month_end = $dates['m_end'];
$last_year = true;
} elseif( $y == $dates['year'] ) {
$month_start = $dates['m_start'];
$month_end = 12;
} elseif ( $y == $dates['year_end'] ) {
$month_start = 1;
$month_end = $dates['m_end'];
} else {
$month_start = 1;
$month_end = 12;
}
$i = $month_start;
while ( $i <= $month_end ) {
$d = $dates['day'];
if ( $i == $month_end ) {
$num_of_days = $dates['day_end'];
if ( $month_start < $month_end ) {
$d = 1;
}
} elseif ( $i > $month_start && $i < $month_end ) {
$num_of_days = cal_days_in_month( CAL_GREGORIAN, $i, $y );
$d = 1;
} else {
$num_of_days = cal_days_in_month( CAL_GREGORIAN, $i, $y );
}
while ( $d <= $num_of_days ) {
$date = mktime( 0, 0, 0, $i, $d, $y );
$end_date = mktime( 23, 59, 59, $i, $d, $y );
$earnings = EDD()->payment_stats->get_earnings( $download_id, $date, $end_date, $include_taxes );
$earnings_totals += $earnings;
$sales = EDD()->payment_stats->get_sales( $download_id, $date, $end_date );
$sales_totals += $sales;
$temp_data['earnings'][ $y ][ $i ][ $d ] = $earnings;
$temp_data['sales'][ $y ][ $i ][ $d ] = $sales;
$d++;
}
$i++;
}
$y++;
}
$sales_data = array();
$earnings_data = array();
// When using 2 months or smaller as the custom range, show each day individually on the graph
if ( $day_by_day ) {
foreach ( $temp_data[ 'sales' ] as $year => $months ) {
foreach( $months as $month => $dates ) {
foreach ( $dates as $day => $sales ) {
$date = mktime( 0, 0, 0, $month, $day, $year ) * 1000;
$sales_data[] = array( $date, $sales );
}
}
}
foreach ( $temp_data[ 'earnings' ] as $year => $months ) {
foreach( $months as $month => $dates ) {
foreach ( $dates as $day => $earnings ) {
$date = mktime( 0, 0, 0, $month, $day, $year ) * 1000;
$earnings_data[] = array( $date, $earnings );
}
}
}
// When showing more than 2 months of results, group them by month, by the first (except for the last month, group on the last day of the month selected)
} else {
foreach ( $temp_data[ 'sales' ] as $year => $months ) {
$month_keys = array_keys( $months );
$last_month = end( $month_keys );
foreach ( $months as $month => $days ) {
$day_keys = array_keys( $days );
$last_day = end( $day_keys );
$consolidated_date = $month === $last_month ? $last_day : 1;
$sales = array_sum( $days );
$date = mktime( 0, 0, 0, $month, $consolidated_date, $year ) * 1000;
$sales_data[] = array( $date, $sales );
}
}
foreach ( $temp_data[ 'earnings' ] as $year => $months ) {
$month_keys = array_keys( $months );
$last_month = end( $month_keys );
foreach ( $months as $month => $days ) {
$day_keys = array_keys( $days );
$last_day = end( $day_keys );
$consolidated_date = $month === $last_month ? $last_day : 1;
$earnings = array_sum( $days );
$date = mktime( 0, 0, 0, $month, $consolidated_date, $year ) * 1000;
$earnings_data[] = array( $date, $earnings );
}
}
}
}
$data = array(
__( 'Earnings', 'easy-digital-downloads' ) => $earnings_data,
__( 'Sales', 'easy-digital-downloads' ) => $sales_data
);
?>
<div class="metabox-holder" style="padding-top: 0;">
<div class="postbox">
<h3><span><?php printf( __('Earnings Over Time for %s', 'easy-digital-downloads' ), get_the_title( $download_id ) ); ?></span></h3>
<div class="inside">
<?php
$graph = new EDD_Graph( $data );
$graph->set( 'x_mode', 'time' );
$graph->set( 'multiple_y_axes', true );
$graph->display();
?>
<p class="edd_graph_totals"><strong><?php _e( 'Total earnings for period shown: ', 'easy-digital-downloads' ); echo edd_currency_filter( edd_format_amount( $earnings_totals ) ); ?></strong></p>
<p class="edd_graph_totals"><strong><?php _e( 'Total sales for period shown: ', 'easy-digital-downloads' ); echo $sales_totals; ?></strong></p>
<p class="edd_graph_totals"><strong><?php printf( __( 'Average monthly earnings: %s', 'easy-digital-downloads' ), edd_currency_filter( edd_format_amount( edd_get_average_monthly_download_earnings( $download_id ) ) ) ); ?>
<p class="edd_graph_totals"><strong><?php printf( __( 'Average monthly sales: %s', 'easy-digital-downloads' ), number_format( edd_get_average_monthly_download_sales( $download_id ), 0 ) ); ?>
</div>
</div>
</div>
<?php
echo ob_get_clean();
}
/**
* Grabs all of the selected date info and then redirects appropriately
*
* @since 1.3
*
* @param array $form_data POSTed data from the filters form.
*/
function edd_parse_report_dates( $form_data ) {
// Load the Reports API dependencies.
Reports\Init::bootstrap();
$filters = Reports\get_filters();
$redirect = ! empty( $form_data['edd_redirect'] )
? $form_data['edd_redirect']
: edd_get_admin_url( array(
'page' => 'edd-reports',
) );
$filter_args = array();
// Parse and validate filters.
foreach ( $filters as $filter => $attributes ) {
switch ( $filter ) {
case 'dates':
if ( ! empty( $form_data['range'] ) ) {
$range = sanitize_key( $form_data['range'] );
$relative_range = sanitize_key( $form_data['relative_range'] );
} else {
$range = Reports\get_dates_filter_range();
$relative_range = Reports\get_relative_dates_filter_range();
}
if ( 'other' === $range ) {
try {
/*
* This validates the input dates before saving. If they're not valid, an exception
* will be thrown.
*/
EDD()->utils->date( $form_data['filter_from'] );
EDD()->utils->date( $form_data['filter_to'] );
} catch ( \Exception $e ) {
wp_die(
esc_html__( 'Invalid date format. Please enter a date in the format: YYYY-mm-dd.', 'easy-digital-downloads' ),
esc_html__( 'Invalid Date Error', 'easy-digital-downloads' ),
array( 'response' => 400, 'back_link' => true )
);
}
$filter_args = array_merge(
array(
'filter_from' => ! empty( $form_data['filter_from'] )
? sanitize_text_field( $form_data['filter_from'] )
: '',
'filter_to' => ! empty( $form_data['filter_to'] )
? sanitize_text_field( $form_data['filter_to'] )
: '',
'range' => 'other',
'relative_range' => 'previous_period',
),
$filter_args
);
} else {
$dates = Reports\parse_dates_for_range( $range );
$filter_args = array_merge(
array(
'filter_from' => $dates['start']->format( 'date-mysql' ),
'filter_to' => $dates['end']->format( 'date-mysql' ),
'range' => $range,
'relative_range' => $relative_range,
),
$filter_args
);
}
break;
case 'taxes':
$filter_args = array_merge(
array(
'exclude_taxes' => isset( $form_data['exclude_taxes'] ),
),
$filter_args
);
break;
default:
$filter_arg = isset( $form_data[ $filter ] )
? $form_data[ $filter ]
: array();
if ( ! empty( $filter_arg ) ) {
$filter_args[ $filter ] = $filter_arg;
}
break;
}
}
// Redirect back to report.
$redirect = add_query_arg( $filter_args, $redirect );
edd_redirect( $redirect );
}
add_action( 'edd_filter_reports', 'edd_parse_report_dates' );
/**
* EDD Reports Refresh Button
* @since 2.7
* @description: Outputs a "Refresh Reports" button for graphs
*/
function edd_reports_refresh_button() {
$url = wp_nonce_url( add_query_arg( array(
'edd_action' => 'refresh_reports_transients',
'edd-message' => 'refreshed-reports'
) ), 'edd-refresh-reports' );
echo '<a href="' . esc_url( $url ) . '" title="' . esc_html__( 'Clicking this will clear the reports cache', 'easy-digital-downloads' ) . '" class="button edd-refresh-reports-button">' . esc_html__( 'Refresh Reports', 'easy-digital-downloads' ) . '</a>';
}
add_action( 'edd_reports_graph_after', 'edd_reports_refresh_button' );
/**
* EDD trigger the refresh of reports transients
*
* @since 2.7
*
* @param array $data Parameters sent from Settings page
* @return void
*/
function edd_run_refresh_reports_transients( $data ) {
if ( ! wp_verify_nonce( $data['_wpnonce'], 'edd-refresh-reports' ) ) {
return;
}
// Delete transients
delete_transient( 'edd_stats_earnings' );
delete_transient( 'edd_stats_sales' );
delete_transient( 'edd_estimated_monthly_stats' );
delete_transient( 'edd_earnings_total' );
delete_transient( md5( 'edd_earnings_this_monththis_month' ) );
delete_transient( md5( 'edd_earnings_todaytoday' ) );
}
add_action( 'edd_refresh_reports_transients', 'edd_run_refresh_reports_transients' );

View File

@ -0,0 +1,23 @@
<?php
/**
* Logs UI (moved)
*
* @package EDD
* @subpackage Admin/Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.4
* @deprecated 3.0
*/
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
_edd_deprecated_file(
__FILE__,
'3.0',
'includes/admin/tools/logs.php',
__( 'The logs tab has been moved to the Tools screen.', 'easy-digital-downloads' )
);
require_once EDD_PLUGIN_DIR . 'includes/admin/tools/logs.php';

View File

@ -0,0 +1,298 @@
<?php
/**
* Reports functions.
*
* @package EDD
* @subpackage Admin/Reports
* @copyright Copyright (c) 2018, Easy Digital Downloads, LLC
* @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
* @since 1.0
* @since 3.0 Full refactor of Reports.
*/
use EDD\Reports;
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;
/**
* The callback function which fetches the data for the overview_sales_earnings_chart reports endpoint.
*
* @since 3.0
*/
function edd_overview_sales_earnings_chart() {
global $wpdb;
$dates = Reports\get_dates_filter( 'objects' );
$chart_dates = Reports\parse_dates_for_range( null, 'now', false );
$day_by_day = Reports\get_dates_filter_day_by_day();
$hour_by_hour = Reports\get_dates_filter_hour_by_hour();
$column = Reports\get_taxes_excluded_filter() ? '(total - tax)' : 'total';
$currency = Reports\get_filter_value( 'currencies' );
if ( empty( $currency ) || 'convert' === $currency ) {
$column .= ' / rate';
}
$sql_clauses = array(
'select' => 'date_created AS date',
'where' => '',
'groupby' => '',
);
// Default to 'monthly'.
$sql_clauses['groupby'] = Reports\get_groupby_date_string( 'MONTH', 'date_created' );
$sql_clauses['orderby'] = 'MONTH(date_created)';
// Now drill down to the smallest unit.
if ( $hour_by_hour ) {
$sql_clauses['groupby'] = Reports\get_groupby_date_string( 'HOUR', 'date_created' );
$sql_clauses['orderby'] = 'HOUR(date_created)';
} elseif ( $day_by_day ) {
$sql_clauses['groupby'] = Reports\get_groupby_date_string( 'DATE', 'date_created' );
$sql_clauses['orderby'] = 'DATE(date_created)';
}
if ( ! empty( $currency ) && array_key_exists( strtoupper( $currency ), edd_get_currencies() ) ) {
$sql_clauses['where'] = $wpdb->prepare( " AND currency = %s ", strtoupper( $currency ) );
}
// Revenue calculations should include gross statuses to negate refunds properly.
$statuses = edd_get_gross_order_statuses();
$statuses = apply_filters( 'edd_payment_stats_post_statuses', $statuses );
$statuses = "'" . implode( "', '", $statuses ) . "'";
$earnings_results = $wpdb->get_results(
$wpdb->prepare(
"SELECT SUM({$column}) AS earnings, {$sql_clauses['select']}
FROM {$wpdb->edd_orders} edd_o
WHERE date_created >= %s AND date_created <= %s
AND status IN( {$statuses} )
AND type IN ( 'sale', 'refund' )
{$sql_clauses['where']}
GROUP BY {$sql_clauses['groupby']}
ORDER BY {$sql_clauses['orderby']} ASC",
$dates['start']->copy()->format( 'mysql' ),
$dates['end']->copy()->format( 'mysql' )
)
);
// Sales counts should count by 'net' statuses, which excludes refunds.
$statuses = edd_get_net_order_statuses();
$statuses = apply_filters( 'edd_payment_stats_post_statuses', $statuses );
$statuses = "'" . implode( "', '", $statuses ) . "'";
$sales_results = $wpdb->get_results(
$wpdb->prepare(
"SELECT COUNT(id) AS sales, {$sql_clauses['select']}
FROM {$wpdb->edd_orders} edd_o
WHERE date_created >= %s AND date_created <= %s
AND status IN( {$statuses} )
AND type = 'sale'
{$sql_clauses['where']}
GROUP BY {$sql_clauses['groupby']}
ORDER BY {$sql_clauses['orderby']} ASC",
$dates['start']->copy()->format( 'mysql' ),
$dates['end']->copy()->format( 'mysql' )
)
);
$sales = array();
$earnings = array();
/**
* Initialise all arrays with timestamps and set values to 0.
*
* We use the Chart based dates for this loop, so the graph shows in the proper date ranges while the actual DB queries are all UTC based.
*/
while ( strtotime( $chart_dates['start']->copy()->format( 'mysql' ) ) <= strtotime( $chart_dates['end']->copy()->format( 'mysql' ) ) ) {
$timestamp = $chart_dates['start']->copy()->format( 'U' );
$date_on_chart = $chart_dates['start'];
$sales[ $timestamp ][0] = $date_on_chart->format( 'Y-m-d H:i:s' );
$sales[ $timestamp ][1] = 0;
$earnings[ $timestamp ][0] = $date_on_chart->format( 'Y-m-d H:i:s' );
$earnings[ $timestamp ][1] = 0.00;
// Loop through each date there were sales/earnings, which we queried from the database.
foreach ( $earnings_results as $earnings_result ) {
$date_of_db_value = edd_get_edd_timezone_equivalent_date_from_utc( EDD()->utils->date( $earnings_result->date ) );
// Add any sales/earnings that happened during this hour.
if ( $hour_by_hour ) {
// If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result.
if ( $date_of_db_value->format( 'Y-m-d H' ) === $date_on_chart->format( 'Y-m-d H' ) ) {
$earnings[ $timestamp ][1] += $earnings_result->earnings;
}
// Add any sales/earnings that happened during this day.
} elseif ( $day_by_day ) {
// If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result.
if ( $date_of_db_value->format( 'Y-m-d' ) === $date_on_chart->format( 'Y-m-d' ) ) {
$earnings[ $timestamp ][1] += $earnings_result->earnings;
}
// Add any sales/earnings that happened during this month.
} else {
// If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result.
if ( $date_of_db_value->format( 'Y-m' ) === $date_on_chart->format( 'Y-m' ) ) {
$earnings[ $timestamp ][1] += $earnings_result->earnings;
}
}
}
// Loop through each date there were sales/earnings, which we queried from the database.
foreach ( $sales_results as $sales_result ) {
$date_of_db_value = edd_get_edd_timezone_equivalent_date_from_utc( EDD()->utils->date( $sales_result->date ) );
// Add any sales/earnings that happened during this hour.
if ( $hour_by_hour ) {
// If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result.
if ( $date_of_db_value->format( 'Y-m-d H' ) === $date_on_chart->format( 'Y-m-d H' ) ) {
$sales[ $timestamp ][1] += $sales_result->sales;
}
// Add any sales/earnings that happened during this day.
} elseif ( $day_by_day ) {
// If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result.
if ( $date_of_db_value->format( 'Y-m-d' ) === $date_on_chart->format( 'Y-m-d' ) ) {
$sales[ $timestamp ][1] += $sales_result->sales;
}
// Add any sales/earnings that happened during this month.
} else {
// If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result.
if ( $date_of_db_value->format( 'Y-m' ) === $date_on_chart->format( 'Y-m' ) ) {
$sales[ $timestamp ][1] += $sales_result->sales;
}
}
}
// Move the chart along to the next hour/day/month to get ready for the next loop.
if ( $hour_by_hour ) {
$chart_dates['start']->addHour( 1 );
} elseif ( $day_by_day ) {
$chart_dates['start']->addDays( 1 );
} else {
$chart_dates['start']->addMonth( 1 );
}
}
return array(
'sales' => array_values( $sales ),
'earnings' => array_values( $earnings ),
);
}
/**
* The callback function which fetches the data for the edd_overview_refunds_chart reports endpoint.
*
* @since 3.0
*/
function edd_overview_refunds_chart() {
global $wpdb;
$dates = Reports\get_dates_filter( 'objects' );
$chart_dates = Reports\parse_dates_for_range( null, 'now', false );
$day_by_day = Reports\get_dates_filter_day_by_day();
$hour_by_hour = Reports\get_dates_filter_hour_by_hour();
$column = Reports\get_taxes_excluded_filter() ? 'total - tax' : 'total';
$currency = Reports\get_filter_value( 'currencies' );
$sql_clauses = array(
'select' => 'date_created AS date',
'where' => '',
);
// Default to 'monthly'.
$sql_clauses['groupby'] = Reports\get_groupby_date_string( 'MONTH', 'date_created' );
$sql_clauses['orderby'] = 'MONTH(date_created)';
// Now drill down to the smallest unit.
if ( $hour_by_hour ) {
$sql_clauses['groupby'] = Reports\get_groupby_date_string( 'HOUR', 'date_created' );
$sql_clauses['orderby'] = 'HOUR(date_created)';
} elseif ( $day_by_day ) {
$sql_clauses['groupby'] = Reports\get_groupby_date_string( 'DATE', 'date_created' );
$sql_clauses['orderby'] = 'DATE(date_created)';
}
if ( empty( $currency ) || 'convert' === $currency ) {
$column = sprintf( '(%s) / rate', $column );
} else {
$sql_clauses['where'] = $wpdb->prepare( " AND currency = %s ", strtoupper( $currency ) );
}
$results = $wpdb->get_results(
$wpdb->prepare(
"SELECT COUNT(id) AS number, SUM({$column}) AS amount, {$sql_clauses['select']}
FROM {$wpdb->edd_orders} edd_o
WHERE status IN (%s, %s) AND date_created >= %s AND date_created <= %s AND type = 'refund'
{$sql_clauses['where']}
GROUP BY {$sql_clauses['groupby']}
ORDER BY {$sql_clauses['orderby']} ASC",
esc_sql( 'complete' ),
esc_sql( 'partially_refunded' ),
$dates['start']->copy()->format( 'mysql' ),
$dates['end']->copy()->format( 'mysql' )
)
);
$number = array();
$amount = array();
// Initialise all arrays with timestamps and set values to 0.
while ( strtotime( $chart_dates['start']->copy()->format( 'mysql' ) ) <= strtotime( $chart_dates['end']->copy()->format( 'mysql' ) ) ) {
$timestamp = $chart_dates['start']->copy()->format( 'U' );
$date_on_chart = $chart_dates['start'];
$number[ $timestamp ][0] = $date_on_chart->format( 'Y-m-d H:i:s' );
$number[ $timestamp ][1] = 0;
$amount[ $timestamp ][0] = $date_on_chart->format( 'Y-m-d H:i:s' );
$amount[ $timestamp ][1] = 0.00;
// Loop through each date there were refunds, which we queried from the database.
foreach ( $results as $result ) {
$date_of_db_value = edd_get_edd_timezone_equivalent_date_from_utc( EDD()->utils->date( $result->date ) );
// Add any refunds that happened during this hour.
if ( $hour_by_hour ) {
// If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result.
if ( $date_of_db_value->format( 'Y-m-d H' ) === $date_on_chart->format( 'Y-m-d H' ) ) {
$number[ $timestamp ][1] += $result->number;
$amount[ $timestamp ][1] += abs( $result->amount );
}
// Add any refunds that happened during this day.
} elseif ( $day_by_day ) {
// If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result.
if ( $date_of_db_value->format( 'Y-m-d' ) === $date_on_chart->format( 'Y-m-d' ) ) {
$number[ $timestamp ][1] += $result->number;
$amount[ $timestamp ][1] += abs( $result->amount );
}
// Add any refunds that happened during this month.
} else {
// If the date of this db value matches the date on this line graph/chart, set the y axis value for the chart to the number in the DB result.
if ( $date_of_db_value->format( 'Y-m' ) === $date_on_chart->format( 'Y-m' ) ) {
$number[ $timestamp ][1] += $result->number;
$amount[ $timestamp ][1] += abs( $result->amount );
}
}
}
// Move the chart along to the next hour/day/month to get ready for the next loop.
if ( $hour_by_hour ) {
$chart_dates['start']->addHour( 1 );
} elseif ( $day_by_day ) {
$chart_dates['start']->addDays( 1 );
} else {
$chart_dates['start']->addMonth( 1 );
}
}
return array(
'number' => array_values( $number ),
'amount' => array_values( $amount ),
);
}

File diff suppressed because it is too large Load Diff

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