plugin install

This commit is contained in:
Tony Volpe
2024-06-18 17:29:05 -04:00
parent e1aaedd1ae
commit 41f50eacc4
5880 changed files with 1057631 additions and 39681 deletions

View File

@@ -0,0 +1,313 @@
<?php
namespace Gravity_Forms\Gravity_Forms\Orders;
use Gravity_Forms\Gravity_Forms\Orders\Items\GF_Order_Item;
use Gravity_Forms\Gravity_Forms\Orders\Items\GF_Form_Product_Item;
final class GF_Order {
/**
* The order items.
*
* Contains the items grouped by item type.
*
* @since 2.6
*
* @var GF_Order_Item[]
*/
private $items = array();
/**
* Contains all the calculated totals for the order, like sub total, discounts, and final total.
*
* @since 2.6
*
* @var array
*/
private $totals = array();
/**
* The order items groups.
*
* Contains the items grouped by their location in the final order summary.
*
* @since 2.6
*
* @var array[]
*/
private $groups = array(
'body' => array(),
'footer' => array(),
);
/**
* The order currency.
*
* @since 2.6
*
* @var string
*/
public $currency;
/**
* Adds a group of items to the order.
*
* @since 2.6
*
* @param GF_Order_Item[] $items
*/
public function add_items( $items = array() ) {
foreach ( $items as $item ) {
$this->add_item( $item );
}
}
/**
* Adds a single item to the order.
*
* @since 2.6
*
* @param GF_Order_Item $item
*
* @return bool
*/
public function add_item( $item ) {
if ( ! is_a( $item, 'Gravity_Forms\Gravity_Forms\Orders\Items\GF_Order_Item' ) ) {
return false;
}
$item->currency = $this->currency;
if ( ! isset( $this->items[ $item->type ] ) ) {
$this->items[ $item->type ] = array();
}
if ( isset( $this->items[ $item->type ][ $item->get_id() ] ) ) {
return false;
}
$this->items[ $item->type ][ $item->get_id() ] = $item;
if ( ! isset( $this->groups[ $item->belongs_to ] ) ) {
$this->groups[ $item->belongs_to ] = array();
}
$this->groups[ $item->belongs_to ][ $item->get_id() ] = $item;
return true;
}
/**
* Returns a collection of items by item type or all items.
*
* @since 2.6
*
* @param null|string $type The item type to look for.
*
* @return array|GF_Order_Item An empty array if no items found or the items.
*/
public function get_items( $type = null ) {
if ( $type ) {
return $this->items[ $type ];
} else {
$all_items = array();
foreach ( $this->items as $items ) {
foreach ( $items as $item ) {
$all_items[ $item->get_id() ] = $item;
}
}
return $all_items;
}
return array();
}
/**
* Returns a collection of items by item class type.
*
* Item types can be represented by a string, for example a collection of GF_Form_Product_Item added to a type called "recurring".
*
* @since 2.6
*
* @param string $type The item class to look for.
*
* @return array|GF_Order_Item An empty array if no items found or the items.
*/
public function get_items_by_class_type( $type ) {
$items = $this->get_items();
$filtered = array();
foreach ( $items as $item ) {
if ( is_a( $item, $type ) ) {
$filtered[ $item->get_id() ] = $item;
}
}
return $filtered;
}
/**
* Returns a collection of items excluding a certain class type.
*
* @since 2.6
*
* @param string $type The item class to look for.
*
* @return array|GF_Order_Item An empty array if no items found or the items.
*/
public function get_items_exclude_class_type( $type ) {
$items = $this->get_items();
$filtered = $this->get_items_by_class_type( $type );
return array_diff_key( $items, $filtered );
}
/**
* Deletes an item from the order.
*
* @since 2.6
*
* @param string $id The item ID.
*
* @return bool
*/
public function delete_item( $id ) {
$item = $this->get_item( $id );
if ( $item ) {
unset( $this->items[ $item->type ][ $id ] );
unset( $this->groups[ $item->belongs_to ][ $id ] );
$this->totals = array();
return true;
}
return false;
}
/**
* Gets an item from the order.
*
* @since 2.6
*
* @param string $id The item ID.
*
* @return false|GF_Order_Item The found item or false.
*/
public function get_item( $id ) {
return $this->loop_items_return(
function ( $item ) use ( $id ) {
return $id == $item->get_id();
}
);
}
/**
* Searches for an item by a property and its values and returns the first found item.
*
* @since 2.6
*
* @param string $property The property name to look for.
* @param string $value The property value to look for.
*
* @return false|GF_Order_Item false if no items are found or the order item.
*/
public function get_item_by_property( $property, $value ) {
return $this->loop_items_return(
function( $item ) use ( $property, $value ) {
return $item->{$property} == $value;
}
);
}
/**
* Loops over the order item and returns one if it matches the provided callback.
*
* @since 2.6
*
* @param callable $callback The callback to use to evaluate the item.
*
* @return false|GF_Order_Item false if no items are found or the order item.
*/
protected function loop_items_return( $callback ) {
foreach ( $this->items as $item_type => $items ) {
foreach ( $items as $item_id => $item ) {
if ( $callback( $item ) ) {
return $item;
}
}
}
return false;
}
/**
* Returns the order items in a certain group.
*
* @since 2.6
*
* @param string $group The group to look for.
*
* @return array|GF_Order_Item An empty array if no items found or the items.
*/
public function get_group( $group ) {
return rgar( $this->groups, $group, array() );
}
/**
* Returns the order groups.
*
* @since 2.6
*
* @return array[]
*/
public function get_groups() {
return $this->groups;
}
/**
* Calculates the order totals.
*
* @since 2.6
*
* @return array an assoc array that contains each total label and its value.
*/
public function get_totals() {
if ( empty( $this->totals ) ) {
// Sub total is all items in the summary body.
$this->totals['sub_total'] = array_sum(
array_map(
function ( $item ) {
return $item->get_total();
},
$this->groups['body']
)
);
// Final total is the sub total plus everything in footer.
$this->totals['total'] = $this->totals['sub_total'] + array_sum(
array_map(
function ( $item ) {
return $item->get_total();
},
$this->groups['footer']
)
);
// Later on there will be dynamic totals, which will be calculated depending on certain conditions.
}
// Make sure we don't have negative totals.
foreach ( $this->totals as $label => $total_value ) {
$this->totals[ $label ] = max( $total_value, 0 );
}
return $this->totals;
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace Gravity_Forms\Gravity_Forms\Orders\Exporters;
use \Gravity_Forms\Gravity_Forms\Orders\GF_Order;
use \Gravity_Forms\Gravity_Forms\Orders\Exporters\GF_Order_Exporter;
use \GFCommon;
class GF_Entry_Details_Order_Exporter extends GF_Order_Exporter {
/**
* GF_Entry_Details_Order_Formatter constructor.
*
* @param GF_Order $order The order to be formatted.
* @param array $config Any specific configurations required while formatting the order.
*/
public function __construct( $order, $config = array() ) {
parent::__construct( $order, $config );
$this->data['rows'] = array();
}
/**
* Extracts a set of raw data from the order.
*
* @since 2.6
*/
protected function format() {
foreach ( $this->order->get_items() as $item ) {
$this->data['rows'][ $item->belongs_to ][] = $this->filter_item_data(
$item,
array(),
array(
'price_money' => GFCommon::to_money( $item->get_base_price(), $this->order->currency ),
'sub_total_money' => GFCommon::to_money( $item->sub_total, $this->order->currency ),
)
);
}
foreach ( $this->data['totals'] as $label => $total ) {
$this->data['totals'][ $label . '_money' ] = GFCommon::to_money( $total, $this->order->currency );
}
}
}

View File

@@ -0,0 +1,110 @@
<?php
namespace Gravity_Forms\Gravity_Forms\Orders\Exporters;
use \Gravity_Forms\Gravity_Forms\Orders\GF_Order;
use \Gravity_Forms\Gravity_Forms\Orders\Items\GF_Order_Item;
use \GFCommon;
class GF_Order_Exporter {
/**
* The order to be formatted.
*
* @since 2.6
*
* @var GF_Order
*/
protected $order;
/**
* Any specific configurations required while formatting the order.
*
* @since 2.6
*
* @var array
*/
protected $config;
/**
* An array containing the extracted order data.
*
* @since 2.6
*
* @var array[]
*/
protected $data = array(
'totals' => array(),
);
/**
* GF_Order_Formatter constructor.
*
* @param GF_Order $order The order to be formatted.
* @param array $config Any specific configurations required while formatting the order.
*/
public function __construct( $order, $config = array() ) {
$this->order = $order;
$this->data['totals'] = $this->order->get_totals();
}
/**
* Extracts a set of raw data from the order.
*
* @since 2.6
*/
protected function format() {
}
/**
* Filters the item data and keeps only the required values.
*
* @since 2.6
*
* @param GF_Order_Item $item The order item.
* @param array $exclude A set of properties to exclude from the item.
* @param array $add More rows to be added to the item data.
*
* @return array The filtered data.
*/
protected function filter_item_data( $item, $exclude = array(), $add = array() ) {
$data = $item->to_array();
if ( is_array( $exclude ) && ! empty( $exclude ) ) {
$data = array_diff_key( $data, array_flip( $exclude ) );
}
if ( is_array( $add ) && ! empty( $add ) ) {
$data = array_merge( $data, $add );
}
return array_filter( $data );
}
/**
* Returns the extracted data.
*
* @since 2.6
*
* @param string|callable $output What format to use when exporting the data, or a function to execute on the formatted data.
*
* @return mixed|array[]
*/
public function export( $output = 'ARRAY' ) {
try {
$this->format();
} catch ( \Exception $ex ) {
$this->data['errors'] = array( $ex->getMessage(), $ex->getCode() );
}
if ( is_callable( $output ) ) {
return $output( $this->data );
}
return $output === 'json' ? json_encode( $this->data ) : $this->data;
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace Gravity_Forms\Gravity_Forms\Orders\Exporters;
use \Gravity_Forms\Gravity_Forms\Orders\GF_Order;
use \Gravity_Forms\Gravity_Forms\Orders\Items\GF_Order_Item;
use \Gravity_Forms\Gravity_Forms\Orders\Items\GF_Form_Product_Item;
use \GFCommon;
class GF_Save_Entry_Order_Exporter extends GF_Order_Exporter {
/**
* GF_Default_Order_Formatter constructor.
*
* @param GF_Order $order The order to be formatted.
* @param array $config Any specific configurations required while formatting the order.
*/
public function __construct( $order, $config = array() ) {
parent::__construct( $order, $config );
}
/**
* Extracts a set of raw data from the order.
*
* @since 2.6
*/
protected function format() {
foreach ( $this->order->get_items() as $item ) {
if ( ! isset( $this->data['rows'][ $item->belongs_to ] ) ) {
$this->data['rows'][ $item->belongs_to ] = array();
}
if ( $item->is_line_item ) {
// If form product item, we don't need to store pricing info, name and options as they are already stored.
$exclude_properties = is_a( $item, GF_Form_Product_Item::class ) ? array( 'name', 'price', 'quantity', 'sub_total', 'options' ) : array();
$this->data['rows'][ $item->belongs_to ][] = $this->filter_item_data( $item, $exclude_properties );
}
}
// No need to save totals now as they will be calculated later.
// In the future when we have dynamically calculated totals we may save them.
unset( $this->data['totals'] );
// Versioning will help identify this when it changes later, which is very likely.
$this->data['v'] = '0.1';
}
}

View File

@@ -0,0 +1,345 @@
<?php
namespace Gravity_Forms\Gravity_Forms\Orders\Factories;
use \Gravity_Forms\Gravity_Forms\Orders\GF_Order;
use \Gravity_Forms\Gravity_Forms\Orders\Items\GF_Order_Item;
use \Gravity_Forms\Gravity_Forms\Orders\Items\GF_Form_Product_Item;
use \Gravity_Forms\Gravity_Forms\Orders\Exporters\GF_Save_Entry_Order_Exporter;
use \GFCommon;
final class GF_Order_Factory {
/**
* Creates an order from an entry.
*
* This method tries to locate the saved order meta in the entry meta first, and if no order meta found it creates an order from the entry products.
*
* @since 2.6
*
* @param array $form The form object.
* @param array $entry The entry object.
* @param bool $use_choice_text If the product field has choices, this decided if the choice text should be retrieved along with the product name or not.
* @param bool $use_admin_labels Whether to use the product admin label or the front end label.
* @param bool $receipt Whether to show only the line items paid for in the order or all products in the form.
*
* @return GF_Order
*/
public static function create_from_entry( $form, $entry, $use_choice_text = false, $use_admin_labels = false, $receipt = false ) {
if ( ! self::validate( $form, $entry ) ) {
return new GF_Order();
}
$order_meta = gform_get_meta( $entry['id'], 'gform_order' );
$order = self::create_from_form( $form, $entry, $use_choice_text, $use_admin_labels );
if ( ! $order_meta ) {
return $order;
}
$order_meta['currency'] = rgar( $order_meta, 'currency', $entry['currency'] );
$products = self::get_products( $form, $entry, $use_choice_text, $use_admin_labels );
$meta_rows = rgar( $order_meta, 'rows' );
// Load line items and custom items.
foreach ( $meta_rows as $group_label => $items ) {
foreach ( $items as $item ) {
$item['belongs_to'] = $group_label;
// When saving line items to the order meta, name and options are deleted as they already exist in form product fields.
// Leave the name and options, but override all other properties that don't exist in the product field, like is_trial for example.
if ( ! rgar( $item, 'name' ) && $order->get_item( $item['id'] ) ) {
$order->get_item( $item['id'] )->override_properties( $item, array( 'options', 'name', 'price', 'sub_total', 'quantity' ) );
unset( $products[ $item['id'] ] );
} else {
$order->add_item( new GF_Order_Item( $item['id'], $item ) );
}
}
}
// Delete any product that was not paid for if only a receipt is required.
if ( $receipt ) {
foreach ( $products as $id => $product ) {
$order->delete_item( $product->get_id() );
}
}
return $order;
}
/**
* Creates an order from a form.
*
* This method creates an order from the form product fields.
*
* @since 2.6
*
* @param array $form The form object.
* @param array $entry The entry object.
* @param bool $use_choice_text If the product field has choices, this decided if the choice text should be retrieved along with the product name or not.
* @param bool $use_admin_labels Whether to use the product admin label or the front end label.
*
* @return GF_Order
*/
public static function create_from_form( $form, $entry, $use_choice_text = false, $use_admin_labels = false ) {
if ( ! self::validate( $form, $entry ) ) {
return new GF_Order();
}
$order = new GF_Order();
$order->currency = $entry['currency'];
$products = self::get_products( $form, $entry, $use_choice_text, $use_admin_labels );
$order->add_items( $products );
return $order;
}
/**
* Creates an order from a feed.
*
* This method created the order from the form products then adds/removes/replaces order items according to the feed settings.
*
* @since 2.6
*
* @param array $feed The feed object.
* @param array $form The form object.
* @param array $entry The entry object.
* @param array $submission The submitted data.
* @param \GFPaymentAddOn|null $addon An instance of the addon that is creating the order.
*
* @return GF_Order
*/
public static function create_from_feed( $feed, $form, $entry, $submission, $addon = null ) {
if ( ! self::validate( $form, $entry ) ) {
return new GF_Order();
}
$order = self::create_from_form( $form, $entry );
// If trial is enabled, add required additional items to the order.
if ( rgars( $feed, 'meta/transactionType' ) === 'subscription' && rgars( $feed, 'meta/trial_enabled' ) ) {
$trial_discount = GFCommon::to_number( $submission['payment_amount'], $entry['currency'] ) * -1;
/**
* Filter the trial discount amount.
*
* @since 2.6
*
* @param float $trial_discount The trial discount amount.
* @param array $form The Form object to filter through
* @param array $feed The Form object to filter through
* @param array $entry The entry object to filter through
*/
$trial_discount = gf_apply_filters( array( 'gforms_order_trial_discount_item_price', $form['id'] ), $trial_discount, $form, $feed, $entry );
$trial_discount_description = __( 'Trial Discount', 'gravityforms' );
/**
* Filter the description of the trial discount.
*
* @since 2.6
*
* @param string $trial_discount_description The trial discount description.
* @param array $form The Form object to filter through
* @param array $feed The Form object to filter through
* @param array $entry The entry object to filter through
*/
$trial_discount_description = gf_apply_filters( array( 'gforms_order_trial_discount_item_description', $form['id'] ), $trial_discount_description, $form, $feed, $entry );
$order->add_item(
new GF_Order_Item(
'trial_discount',
array(
'name' => $trial_discount_description,
'price' => abs( GFCommon::to_number( $trial_discount, $entry['currency'] ) ) * -1,
'belongs_to' => 'footer',
'is_line_item' => true,
'is_discount' => true,
)
)
);
// If the trial product is a form product not a custom amount, set it as line item so it is always shown.
if ( is_numeric( rgars( $feed, 'meta/trial_product' ) ) ) {
$order->get_item( rgars( $feed, 'meta/trial_product' ) )->is_trial = true;
$order->get_item( rgars( $feed, 'meta/trial_product' ) )->is_line_item = true;
}
// If trial is free or custom amount, add a custom item for it.
if (
! rgars( $feed, 'meta/trial_product' )
|| rgars( $feed, 'meta/trial_product' ) === 'enter_amount'
|| rgars( $feed, 'meta/trial_product' ) === 'free_trial'
) {
$price = rgars( $feed, 'meta/trial_product' ) === 'enter_amount' ? GFCommon::to_number( rgars( $feed, 'meta/trial_amount' ), $order->currency ) : 0;
/**
* Filter the price of the custom trial item.
*
* @since 2.6
*
* @param float $price The trial price.
* @param array $form The Form object to filter through
* @param array $feed The Form object to filter through
* @param array $entry The entry object to filter through
*/
$price = gf_apply_filters( array( 'gforms_order_trial_item_price', $form['id'] ), $price, $form, $feed, $entry );
$trial_description = $price ? ( rgars( $feed, 'meta/trial_amount' ) . ' ' . __( 'Trial', 'gravityforms' ) ) : __( 'Free Trial', 'gravityforms' );
/**
* Filter the description that appears in the subscription details box of the custom trial item.
*
* @since 2.6
*
* @param string $trial_description The trial description.
* @param array $form The Form object to filter through
* @param array $feed The Form object to filter through
* @param array $entry The entry object to filter through
*/
$trial_description = gf_apply_filters( array( 'gforms_order_trial_item_description', $form['id'] ), $trial_description, $form, $feed, $entry );
$trial_item_name = $price ? __( 'Trial', 'gravityforms' ) : __( 'Free Trial', 'gravityforms' );
/**
* Filter the name that appears in the order summary of the custom trial item.
*
* @since 2.6
*
* @param string $trial_item_name The trial item name.
* @param array $form The Form object to filter through
* @param array $feed The Form object to filter through
* @param array $entry The entry object to filter through
*/
$trial_item_name = gf_apply_filters( array( 'gforms_order_trial_discount_item_name', $form['id'] ), $trial_item_name, $form, $feed, $entry );
$order->add_item(
new GF_Order_Item(
'trial',
array(
'price' => $price,
'name' => $trial_item_name,
'is_trial' => true,
'is_line_item' => true,
'description' => $trial_description,
)
)
);
}
}
// If the trial product is a form product not a custom amount, set it as line item so it is always shown.
if ( rgars( $feed, 'meta/setupFee_enabled' ) ) {
if ( $item = $order->get_item( rgars( $feed, 'meta/setupFee_product' ) ) ) {
$item->is_setup = true;
$item->is_line_item = true;
}
}
// If payment amount is not set to form total, set only the selected products as line items, otherwise mark all as line items.
if ( rgars( $feed, 'meta/transactionType' ) === 'product' && is_numeric( rgars( $feed, 'meta/paymentAmount' ) ) ) {
$order->get_item( rgars( $feed, 'meta/paymentAmount' ) )->is_line_item = true;
} elseif ( rgars( $feed, 'meta/transactionType' ) === 'subscription' && is_numeric( rgars( $feed, 'meta/recurringAmount' ) ) ) {
$order->get_item( rgars( $feed, 'meta/recurringAmount' ) )->is_recurring = true;
$order->get_item( rgars( $feed, 'meta/recurringAmount' ) )->is_line_item = true;
} else {
foreach ( $order->get_items() as $id => $item ) {
$order->get_item( $id )->is_line_item = true;
}
}
/**
* Allows adding additional items to the order before it is saved.
*
* @since 2.6
*
* @param array $additional_items An associative array that represents the ID of the item as key and the data of the item as a value array.
* @param array $form The Form object to filter through
* @param array $feed The Form object to filter through
* @param array $entry The entry object to filter through
*/
$additional_items = gf_apply_filters( array( 'gform_order_additional_items', $form['id'] ), array(), $form, $feed, $entry );
foreach ( $additional_items as $id => $data ) {
if ( ! $id || ! is_array( $data ) || empty( $data ) ) {
continue;
}
$order->add_item( new GF_Order_Item( $id, $data ) );
}
return $order;
}
/**
/**
* Gets the product fields in the form as GF_Form_Product_Item objects.
*
* @since 2.6
*
* @param array $form The form object.
* @param array $entry The entry object.
* @param bool $use_choice_text If the product field has choices, this decided if the choice text should be retrieved along with the product name or not.
* @param bool $use_admin_labels Whether to use the product admin label or the front end label.
*
* @return array|GF_Form_Product_Item[] and empty array if the form has no products, or the products array.
*/
public static function get_products( $form, $entry, $use_choice_text, $use_admin_labels ) {
$form_product_items = GFCommon::get_product_fields( $form, $entry, $use_choice_text, $use_admin_labels );
$products = rgar( $form_product_items, 'products' );
if ( ! $products ) {
return array();
}
$shipping = rgar( $form_product_items, 'shipping' );
if ( ! empty( $shipping['name'] ) && ! empty( $shipping['price'] ) ) {
$shipping['is_shipping'] = true;
$shipping['is_line_item'] = true;
$shipping['belongs_to'] = 'footer';
$products[ $shipping['id'] ] = $shipping;
}
$product_items = array();
foreach ( $products as $id => $product ) {
$product['id'] = $id;
$product_items[ $product['id'] ] = new GF_Form_Product_Item( $product['id'], $product );
}
return $product_items;
}
/**
* Validates that the form and entry have the required information to creates an order.
*
* @param array $form The form the order is being created from.
* @param array $entry The entry the order is being created from.
*
* @return bool
*/
public static function validate( $form, $entry ) {
self::load_dependencies();
if ( ! rgar( $form, 'id' ) || ! rgar( $form, 'fields' ) || empty( $entry ) ) {
return false;
}
return true;
}
/**
* Includes the required classes.
*
* @since 2.6
*/
public static function load_dependencies() {
if ( ! class_exists( 'GF_Order' ) ) {
require_once GFCommon::get_base_path() . '/includes/orders/class-gf-order.php';
require_once GFCommon::get_base_path() . '/includes/orders/items/class-gf-order-item.php';
require_once GFCommon::get_base_path() . '/includes/orders/items/class-gf-form-product-order-item.php';
require_once GFCommon::get_base_path() . '/includes/orders/exporters/class-gf-order-exporter.php';
require_once GFCommon::get_base_path() . '/includes/orders/exporters/class-gf-entry-details-order-exporter.php';
require_once GFCommon::get_base_path() . '/includes/orders/exporters/class-gf-save-entry-order-exporter.php';
require_once GFCommon::get_base_path() . '/includes/orders/summaries/class-gf-order-summary.php';
}
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace Gravity_Forms\Gravity_Forms\Orders\Items;
use \GFCommon;
final class GF_Form_Product_Item extends GF_Order_Item {
/**
* GF_Form_Product_Item constructor.
*
* @since 2.6
*
* @param string|int $id The product ID
* @param array $data The product data.
*/
public function __construct( $id, $data = array() ) {
parent::__construct( $id, $data );
}
/**
* Returns the base price of the item.
*
* @since 2.6
*
* @return float
*/
public function get_base_price() {
$this->price = GFCommon::to_number( $this->price, $this->currency );
return $this->price + $this->get_options_total();
}
/**
* Calculates and returns the total price of the product options.
*
* @since 2.6
*
* @return float
*/
private function get_options_total() {
$options_total = 0;
if ( is_array( $this->options ) ) {
foreach ( $this->options as $option ) {
$option['price'] = GFCommon::to_number( $option['price'], $this->currency );
$options_total += $option['price'];
}
}
return $options_total;
}
}

View File

@@ -0,0 +1,192 @@
<?php
namespace Gravity_Forms\Gravity_Forms\Orders\Items;
use \GFCommon;
class GF_Order_Item {
/**
* The item ID.
*
* @since 2.6
*
* @var string|int
*/
private $id;
/**
* A collection of item properties.
*
* @since 2.6
*
* @var array
*/
protected $data;
/**
* Returns the default item properties.
*
* No properties can be set other than these ones.
*
* @since 2.6
*
* @return array
*/
protected final function get_default_properties() {
return array(
'is_discount' => false,
'is_shipping' => false,
'is_trial' => false,
'is_setup' => false,
'is_line_item' => false,
'is_recurring' => false,
'belongs_to' => 'body',
'price' => 0,
'quantity' => 1,
'sub_total' => 0,
'currency' => GFCommon::get_currency(),
'name' => '',
'description' => '',
'options' => array(),
'type' => '',
);
}
/**
* GF_Order_Item constructor.
*
* @since 2.6
*
* @param string|int $id The item ID.
* @param array $data The item data.
*/
public function __construct( $id, $data = array() ) {
if ( ! is_array( $data ) ) {
$data = array();
}
$this->id = $id;
if ( ! isset( $data['type'] ) ) {
$data['type'] = static::class;
}
$this->data = array_intersect_key( $data, $this->get_default_properties() );
}
/**
* Returns the item ID.
*
* @since 2.6
*
* @return int|string The item ID
*/
public function get_id() {
return $this->id;
}
/**
* Returns the base price of the item.
*
* @since 2.6
*
* @return float
*/
public function get_base_price() {
$this->price = GFCommon::to_number( $this->price, $this->currency );
return $this->price;
}
/**
* Calculates the item final total.
*
* @since 2.6
*
* @return float|int The item final total.
*/
public function get_total() {
$this->quantity = GFCommon::to_number( $this->quantity, $this->currency );
$this->sub_total = $this->get_base_price() * $this->quantity;
return $this->sub_total;
}
/**
* Overrides the item properties with a new set of properties.
*
* @since 2.6
*
* @param array $data The new data.
* @param array $except A group of keys to be skipped while overriding.
*/
public final function override_properties( $data, $except = array() ) {
$except = array_merge( array( 'id', 'currency' ), $except );
$data = array_filter(
$data,
function ( $value, $key ) use ( $except ) {
if ( in_array( $key, $except ) ) {
return false;
}
return true;
},
1
);
$this->data = array_merge( $this->data, $data );
}
/**
* Returns the item properties as an array.
*
* @since 2.6
*
* @return array The item properties
*/
public final function to_array() {
$this->get_total();
$data = array();
foreach ( array_keys( $this->get_default_properties() ) as $key ) {
$data[ $key ] = $this->__get( $key );
}
$data['id'] = $this->get_id();
return $data;
}
/**
* Returns a property from the item's data.
*
* @since 2.6
*
* @param string $key The property name to look for.
*
* @return mixed|null The property value or null if nothing found.
*/
public final function __get( $key ) {
if ( ! array_key_exists( $key, $this->get_default_properties() ) ) {
return null;
}
return rgar( $this->data, $key, rgar( $this->get_default_properties(), $key ) );
}
/**
* Sets the value of a property in the item's data.
*
* @since 2.6
*
* @param string $key The property name to look for.
* @param mixed $value The property value.
*/
public final function __set( $key, $value ) {
if ( array_key_exists( $key, $this->get_default_properties() ) ) {
$this->data[ $key ] = $value;
}
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace Gravity_Forms\Gravity_Forms\Orders\Summaries;
use \Gravity_Forms\Gravity_Forms\Orders\GF_Order;
use \Gravity_Forms\Gravity_Forms\Orders\Factories\GF_Order_Factory;
use \Gravity_Forms\Gravity_Forms\Orders\Exporters\GF_Entry_Details_Order_Exporter;
final class GF_Order_Summary {
/**
* Contains any specific configurations for rendering the summary, for example showing only a receipt.
*
* @since 2.6
*
* @var array
*/
public static $configurations;
/**
* Renders the summary markup using the provided data and view.
*
* @since 2.6
*
* @param array $form The form object.
* @param array $entry The entry object.
* @param string $view The view to be used for rendering the order.
* @param bool $use_choice_text If the product field has choices, this decided if the choice text should be retrieved along with the product name or not.
* @param bool $use_admin_labels Whether to use the product admin label or the front end label.
* @param bool $receipt Whether to show only the line items paid for in the order or all products in the form.
*
* @return string The summary markup.
*/
public static function render( $form, $entry, $view = 'order-summary', $use_choice_text = false, $use_admin_labels = false, $receipt = false ) {
GF_Order_Factory::load_dependencies();
$order = GF_Order_Factory::create_from_entry( $form, $entry, $use_choice_text, $use_admin_labels, rgar( self::$configurations, 'receipt' ) );
$order_summary = ( new GF_Entry_Details_Order_Exporter( $order ) )->export();
if ( empty( $order_summary['rows'] ) ) {
return '';
}
$order_summary['labels'] = self::get_labels( $form );
ob_start();
include 'views/view-' . $view . '.php';
return ob_get_clean();
}
/**
* Return the labels used in the summary view.
*
* @since 2.6
*
* @param array form The form object.
*
* @return array
*/
public static function get_labels( $form ) {
return array(
'order_label' => gf_apply_filters( array( 'gform_order_label', $form['id'] ), __( 'Order', 'gravityforms' ), $form['id'] ),
'product' => gf_apply_filters( array( 'gform_product', $form['id'] ), __( 'Product', 'gravityforms' ), $form['id'] ),
'product_qty' => gf_apply_filters( array( 'gform_product_qty', $form['id'] ), __( 'Qty', 'gravityforms' ), $form['id'] ),
'product_unitprice' => gf_apply_filters( array( 'gform_product_unitprice', $form['id'] ), __( 'Unit Price', 'gravityforms' ), $form['id'] ),
'product_price' => gf_apply_filters( array( 'gform_product_price', $form['id'] ), __( 'Price', 'gravityforms' ), $form['id'] ),
);
}
}

View File

@@ -0,0 +1,67 @@
<tr>
<td colspan="2" class="entry-view-field-name"><?php echo esc_html( $order_summary['labels']['order_label'] ); ?></td>
</tr>
<tr>
<td colspan="2" class="entry-view-field-value lastrow">
<table class="entry-products" cellspacing="0" width="97%">
<colgroup>
<col class="entry-products-col1" />
<col class="entry-products-col2" />
<col class="entry-products-col3" />
<col class="entry-products-col4" />
</colgroup>
<thead>
<th scope="col"><?php echo esc_html( $order_summary['labels']['product'] ); ?></th>
<th scope="col" class="textcenter"><?php echo esc_html( $order_summary['labels']['product_qty'] ); ?></th>
<th scope="col"><?php echo esc_html( $order_summary['labels']['product_unitprice'] ); ?></th>
<th scope="col"><?php echo esc_html( $order_summary['labels']['product_price'] ); ?></th>
</thead>
<tbody>
<?php
foreach ( rgars( $order_summary, 'rows/body', array() ) as $row ) {
?>
<tr>
<td>
<div class="product_name"><?php echo esc_html( rgar( $row, 'name' ) ); ?></div>
<ul class="product_options">
<?php
if ( is_array( rgar( $row, 'options' ) ) ) {
$count = sizeof( $row['options'] );
for ( $i = 0; $i < $count; $i++ ) {
?>
<li <?php echo ( $i === ( $count - 1 ) ? "class='lastitem'" : '' ); ?>><?php echo rgar( $row['options'][ $i ], 'option_label' );?></li>
<?php
}
}
?>
</ul>
</td>
<td class="textcenter"><?php echo esc_html( rgar( $row, 'quantity' ) ); ?></td>
<td><?php echo rgar( $row, 'price_money' ); ?></td>
<td><?php echo rgar( $row, 'sub_total_money' ); ?></td>
</tr>
<?php } ?>
</tbody>
<tfoot>
<tr>
<td colspan="2" class="emptycell">&nbsp;</td>
<td class="subtotal"><?php esc_html_e( 'Sub Total', 'gravityforms' ); ?></td>
<td class="subtotal_amount"><?php echo $order_summary['totals']['sub_total_money']; ?></td>
</tr>
<?php foreach ( rgars( $order_summary, 'rows/footer', array() ) as $row ) { ?>
<tr>
<td colspan="2" class="emptycell">&nbsp;</td>
<td class="footer_row"><?php echo esc_html( rgar( $row, 'name' ) ); ?></td>
<td class="footer_row_amount"><?php echo rgar( $row, 'price_money' ); ?>&nbsp;</td>
</tr>
<?php } ?>
<tr>
<td colspan="2" class="emptycell">&nbsp;</td>
<td class="grandtotal"><?php esc_html_e( 'Total', 'gravityforms' ); ?></td>
<td class="grandtotal_amount"><?php echo $order_summary['totals']['total_money']; ?></td>
</tr>
</tfoot>
</table>
</td>
</tr>

View File

@@ -0,0 +1,68 @@
<tr stayle="background-color:#EAF2FA">
<td colspan="2">
<strong style="font-family: sans-serif; font-size:12px;"><?php echo esc_html( $order_summary['labels']['order_label'] ); ?></strong>
</td>
</tr>
<tr style="background-color: #FFFFFF">
<td style="width: 20px">&nbsp;</td>
<td>
<table cellspacing="0" style="border-left:1px solid #DFDFDF; border-top:1px solid #DFDFDF; width: 97%">
<thead>
<tr>
<th style="background-color:#F4F4F4; border-bottom:1px solid #DFDFDF; border-right:1px solid #DFDFDF; padding:7px; font-family: sans-serif; font-size:12px; text-align:left"><?php echo esc_html( $order_summary['labels']['product'] ); ?></th>
<th style="background-color:#F4F4F4; border-bottom:1px solid #DFDFDF; border-right:1px solid #DFDFDF; padding:7px; width:50px; font-family: sans-serif; font-size:12px; text-align:center"><?php echo esc_html( $order_summary['labels']['product_qty'] ); ?></th>
<th style="background-color:#F4F4F4; border-bottom:1px solid #DFDFDF; border-right:1px solid #DFDFDF; padding:7px; width:155px; font-family: sans-serif; font-size:12px; text-align:left"><?php echo esc_html( $order_summary['labels']['product_unitprice'] ); ?></th>
<th style="background-color:#F4F4F4; border-bottom:1px solid #DFDFDF; border-right:1px solid #DFDFDF; padding:7px; width:155px; font-family: sans-serif; font-size:12px; text-align:left"><?php echo esc_html( $order_summary['labels']['product_price'] ); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ( rgars( $order_summary, 'rows/body', array() ) as $row ) { ?>
<tr>
<td style="border-bottom:1px solid #DFDFDF; border-right:1px solid #DFDFDF; padding:7px; font-family: sans-serif; font-size:11px;">
<strong style="color:#BF461E; font-size:12px; margin-bottom:5px"><?php echo \GFCommon::maybe_wp_kses( rgar( $row, 'name' ) ); ?></strong>
<ul style="margin:0">
<?php if ( is_array( rgar( $row, 'options' ) ) ) { ?>
<?php
$count = sizeof( $row['options'] );
for ( $i = 0; $i < $count; $i ++ ) {
?>
<li style="padding:4px 0 4px 0"><?php echo esc_html( rgar( $row['options'][ $i ], 'option_label' ) );?></li>
<?php } ?>
<?php } ?>
</ul>
</td>
<td style="border-bottom:1px solid #DFDFDF; border-right:1px solid #DFDFDF; padding:7px; text-align:center; width:50px; font-family: sans-serif; font-size:11px;"><?php echo esc_html( rgar( $row, 'quantity', 1 ) ); ?></td>
<td style="border-bottom:1px solid #DFDFDF; border-right:1px solid #DFDFDF; padding:7px; width:155px; font-family: sans-serif; font-size:11px;"><?php echo rgar( $row, 'price_money' ); ?></td>
<td style="border-bottom:1px solid #DFDFDF; border-right:1px solid #DFDFDF; padding:7px; width:155px; font-family: sans-serif; font-size:11px;"><?php echo rgar( $row, 'sub_total_money' ); ?></td>
</tr>
<?php } ?>
</tbody>
<tfoot>
<tr>
<td colspan="2" style="background-color:#F4F4F4; border-bottom:1px solid #DFDFDF; border-right:1px solid #DFDFDF; padding:7px; font-size:11px;">&nbsp;</td>
<td style="border-bottom:1px solid #DFDFDF; border-right:1px solid #DFDFDF; padding:7px; text-align:right; width:155px; font-family: sans-serif;">
<strong style="font-size:12px;"><?php esc_html_e( 'Sub Total', 'gravityforms' ); ?></strong></td>
<td style="border-bottom:1px solid #DFDFDF; border-right:1px solid #DFDFDF; padding:7px; width:155px; font-family: sans-serif;">
<strong style="font-size:12px;"><?php echo $order_summary['totals']['sub_total_money']; ?></strong>
</td>
</tr>
<?php foreach ( rgars( $order_summary, 'rows/footer', array() ) as $row ) { ?>
<tr>
<td colspan="2" style="background-color:#F4F4F4; border-bottom:1px solid #DFDFDF; border-right:1px solid #DFDFDF; padding:7px; font-size:11px;">&nbsp;</td>
<td style="border-bottom:1px solid #DFDFDF; border-right:1px solid #DFDFDF; padding:7px; width:155px; font-family: sans-serif;font-size:12px;text-align:right;"><?php echo \GFCommon::maybe_wp_kses( rgar( $row, 'name' ) ); ?></td>
<td style="border-bottom:1px solid #DFDFDF; border-right:1px solid #DFDFDF; padding:7px; width:155px; font-family: sans-serif; font-size:11px;">
<?php echo rgar( $row, 'sub_total_money' ); ?>
</td>
</tr>
<?php } ?>
<tr>
<td colspan="2" style="background-color:#F4F4F4; border-bottom:1px solid #DFDFDF; border-right:1px solid #DFDFDF; padding:7px; font-size:11px;">&nbsp;</td>
<td style="border-bottom:1px solid #DFDFDF; border-right:1px solid #DFDFDF; padding:7px; text-align:right; width:155px; font-family: sans-serif;">
<strong style="font-size:12px;"><?php esc_html_e( 'Total', 'gravityforms' ); ?></strong></td>
<td style="border-bottom:1px solid #DFDFDF; border-right:1px solid #DFDFDF; padding:7px; width:155px; font-family: sans-serif;">
<strong style="font-size:12px;"><?php echo $order_summary['totals']['total_money']; ?></strong></td>
</tr>
</tfoot>
</table>
</td>
</tr>

View File

@@ -0,0 +1,25 @@
<?php echo "--------------------------------\n";
echo esc_html( $order_summary['labels']['order_label'] ) . "\n\n";
foreach ( rgars( $order_summary, 'rows/body', array() ) as $row ) {
if ( ! empty( $row['options'] ) ) {
$row['name'] .= ' (' . implode(
', ',
array_map(
function( $option ) {
return rgar( $option, 'option_name', '' );
},
rgar( $row, 'options', array() )
)
) . ')';
}
echo rgar( $row, 'quantity' ) . ' ' . rgar( $row, 'name' ) . ': ' . rgar( $row, 'sub_total_money', 0 ) . "\n\n";
}
foreach ( rgars( $order_summary, 'rows/footer', array() ) as $row ) {
echo rgar( $row, 'name' ) . ': ' . rgar( $row, 'sub_total_money', 0 ) . "\n\n";
}
echo esc_html__( 'Sub Total', 'gravityforms' ) . ': ' . $order_summary['totals']['sub_total_money'] . "\n\n";
echo esc_html__( 'Total', 'gravityforms' ) . ': ' . $order_summary['totals']['total_money'] . "\n\n";