/** * Adds support for Customer/Order CSV Export by adding data for admin order fields * * @since 1.2.0 * @param array $order_data generated order data matching the column keys in the header * @param WC_Order $order order being exported * @param \WC_Customer_Order_CSV_Export_Generator $csv_generator instance * @return array */ public function add_fields_to_csv_export_column_data($order_data, $order, $csv_generator) { $field_data = array(); foreach (wc_admin_custom_order_fields()->get_order_fields($order->id) as $field_id => $field) { $field_data['admin_custom_order_field_' . $field_id] = $field->get_value_formatted(); } $new_order_data = array(); if (isset($csv_generator->order_format) && ('default_one_row_per_item' == $csv_generator->order_format || 'legacy_one_row_per_item' == $csv_generator->order_format)) { foreach ($order_data as $data) { $new_order_data[] = array_merge($field_data, (array) $data); } } else { $new_order_data = array_merge($field_data, $order_data); } return $new_order_data; }
/** * Render the custom order fields editor table * * @since 1.0 */ private function render_editor() { ?> <div class="wc-admin-custom-order-fields-editor-content"> <table class="widefat wc-admin-custom-order-fields-editor"> <thead> <tr> <th class="check-column"><input type="checkbox" /></th> <th class="wc-custom-order-field-label"><?php _e('Label', WC_Admin_Custom_Order_Fields::TEXT_DOMAIN); ?> </th> <th width="1%" class="wc-custom-order-field-type"><?php _e('Type', WC_Admin_Custom_Order_Fields::TEXT_DOMAIN); ?> </th> <th class="wc-custom-order-field-description"><?php _e('Description', WC_Admin_Custom_Order_Fields::TEXT_DOMAIN); ?> </th> <th class="wc-custom-order-field-default-values"><?php _e('Default / Values', WC_Admin_Custom_Order_Fields::TEXT_DOMAIN); ?> </th> <th class="wc-custom-order-field-attributes"><?php _e('Attributes', WC_Admin_Custom_Order_Fields::TEXT_DOMAIN); ?> </th> <th class="js-wc-custom-order-field-draggable"></th> </tr> </thead> <tfoot> <tr> <th colspan="3"> <button type="button" class="button button-secondary js-wc-admin-custom-order-fields-add-field"> + <?php _e('Add Field', WC_Admin_Custom_Order_Fields::TEXT_DOMAIN); ?> </button> <button type="button" class="button button-secondary js-wc-admin-custom-order-fields-remove"><?php _e('Remove Selected', WC_Admin_Custom_Order_Fields::TEXT_DOMAIN); ?> </button> </th> <th colspan="5"><input type="submit" class="button-primary" value="<?php _e('Save Fields', WC_Admin_Custom_Order_Fields::TEXT_DOMAIN); ?> "/></th> </tr> </tfoot> <tbody> <?php $index = 0; foreach (wc_admin_custom_order_fields()->get_order_fields() as $custom_field_id => $custom_field) { echo $this->get_row_html($index, $custom_field_id, $custom_field); $index++; } ?> </tbody> </table> </div> <?php wp_nonce_field(__FILE__); }
<td class="wc-custom-order-field-attributes"> <select name="wc-custom-order-field-attributes[<?php echo $index; ?> ][]" class="js-wc-custom-order-field-attributes wc-enhanced-select chosen_select" multiple="multiple" style="width: 250px;"> <?php foreach ($field_attributes as $value => $label) { ?> <option value="<?php echo esc_attr($value); ?> " <?php selected(isset($field->{$value}) ? $field->{$value} : null); ?> ><?php echo esc_html($label); ?> </option> <?php } ?> </select> </td> <td class="js-wc-custom-order-field-draggable"> <img src="<?php echo wc_admin_custom_order_fields()->get_plugin_url(); ?> /assets/images/draggable-handle.png" /> </td> </tr>
function init_woocommerce_admin_custom_order_fields() { /** * # WooCommerce Admin Custom Order Fields Main Plugin Class * * ## Plugin Overview * * The WooCommerce Admin Custom Order Fields allows custom order fields to be * defined and configured by a shop manager and displaed within the WooCommerce Order Admin * * ## Admin Considerations * * A 'Custom Order Fields' sub-menu item added to the 'WooCommerce' menu item, along with a meta-box on the Edit Order * page, used for entering data into the custom fields defined * * ## Frontend Considerations * * If a custom field has the `visible` attribute, it will be displayed after the order table, in both emails and * the my account > orders > view order screen * * ## Database * * ### Custom Fields * * + `wc_admin_custom_order_fields` - a serialized array containing all the custom fields defined, in this format: * * ``` * [ int|field_id ] => { * label => string, field title * type => string, text|textarea|select|multiselect|radio|checkbox|date * description => string, text to display as a help bubble * default => string, available if type is text or textarea or date ('now' is a special indicator for date fields) * options => array of available options, if type is select or multiselect or radio or checkbox { * default => bool, true if option is a default, false otherwise * label => string, the label for the option * value => string, the sanitized value for the option, generated using sanitize_key() on the label * } * required => bool, true if the field is required. Note this doesn't prevent the order from being saved, but simply adds a red star next to the field label * visible => bool, true to show in order emails & frontend * listable => bool, true to show field in the Orders list page, false otherwise * sortable => bool, true if the field is sortable (text/textarea/radio/date only, listable fields only) * filterable => bool, true if the field is filterable (listable fields only, no textarea) * is_numeric => bool, true if field value/default is numeric (available if the type is 'text') * scope => string `order` by default at the moment, not editable by admin * } * ``` * * ### Options table * * + `wc_admin_custom_order_fields_version` - the current plugin version, set on install/upgrade * + `wc_admin_custom_order_fields_next_field_id` - a sequential counter for the custom field IDs * + `wc_admin_custom_order_fields_welcome` - a flag to display the welcome notice on the field editor screen * * ### Order Meta * * When information is entered into the custom fields and the order is saved, the field data is saved to the order meta * using the `_wc_acof_<field_id>` meta key. This allow field labels to be changed without affecting the saved data. * */ class WC_Admin_Custom_Order_Fields extends SV_WC_Plugin { /** plugin version number */ const VERSION = '1.3.2-1'; /** @var WC_Admin_Custom_Order_Fields single instance of this plugin */ protected static $instance; /** plugin id */ const PLUGIN_ID = 'admin_custom_order_fields'; /** plugin text domain */ const TEXT_DOMAIN = 'woocommerce-admin-custom-order-fields'; /** @var \WC_Admin_Custom_Order_Fields_Admin instance */ public $admin; /** @var \WC_Admin_Custom_Order_Fields_Export_Handler instance */ public $export_handler; /** * Initializes the plugin * * @since 1.0 * @return \WC_Admin_Custom_Order_Fields */ public function __construct() { parent::__construct(self::PLUGIN_ID, self::VERSION, self::TEXT_DOMAIN); // include required files $this->includes(); // display any publicly-visible custom order data in the frontend/emails add_action('woocommerce_order_details_after_order_table', array($this, 'add_order_details_after_order_table')); add_action('woocommerce_email_after_order_table', array($this, 'add_order_details_after_order_table')); // custom ajax handler for AJAX search add_action('wp_ajax_wc_admin_custom_order_fields_json_search_field', array($this, 'add_json_search_field')); // save default field values when order is created add_action('wp_insert_post', array($this, 'save_default_field_values'), 10, 2); } /** * Include required files * * @since 1.0 */ private function includes() { require_once 'includes/class-wc-custom-order-field.php'; require_once 'includes/class-wc-custom-order-fields-export-handler.php'; $this->export_handler = new WC_Admin_Custom_Order_Fields_Export_Handler(); if (is_admin() && !defined('DOING_AJAX')) { $this->admin_includes(); } } /** * Include required admin files * * @since 1.0 */ private function admin_includes() { // load order list table/edit order customizations require_once 'includes/admin/class-wc-admin-custom-order-fields-admin.php'; $this->admin = new WC_Admin_Custom_Order_Fields_Admin(); } /** * Load plugin text domain. * * @since 1.1 * @see SV_WC_Plugin::load_translation() */ public function load_translation() { load_plugin_textdomain('woocommerce-admin-custom-order-fields', false, dirname(plugin_basename($this->get_file())) . '/i18n/languages'); } /** Frontend methods ******************************************************/ /** * Display any publicly viewable order fields in the frontend/order emails * * @since 1.0 * @param WC_Order $order the order object */ public function add_order_details_after_order_table($order) { $visible = true; foreach ($this->get_order_fields($order->id) as $order_field) { if ($order_field->is_visible() && $order_field->get_value_formatted()) { if (!$visible) { echo '<dl id="order-custom-fields">'; $visible = true; } echo '<dt>' . __($order_field->label, self::TEXT_DOMAIN) . '</dt><dd>' . __($order_field->get_value_formatted(), self::TEXT_DOMAIN) . '</dd>'; } } if ($visible) { echo "</dl>"; } } /** Admin methods ******************************************************/ /** * AJAX search handler for chosen fields. Searches for custom order admin fields * and returns the results. * * @since 1.0 */ public function add_json_search_field() { global $wpdb; check_ajax_referer('search-field', 'security'); // the search term $term = isset($_GET['term']) ? urldecode(stripslashes(strip_tags($_GET['term']))) : ''; // the field to search $field_name = isset($_GET['request_data']['field_name']) ? urldecode(stripslashes(strip_tags($_GET['request_data']['field_name']))) : ''; if (empty($term) || empty($field_name)) { die; } $default = isset($_GET['request_data']['default']) ? $_GET['request_data']['default'] : ''; $found_values = SV_WC_Plugin_Compatibility::is_wc_version_gte_2_3() ? array() : array('' => $default); $results = $wpdb->get_results($wpdb->prepare("SELECT meta_value FROM " . $wpdb->postmeta . " WHERE meta_key = %s and meta_value LIKE %s", $field_name, '%' . $term . '%')); if ($results) { foreach ($results as $result) { $found_values[$result->meta_value] = $result->meta_value; } } echo json_encode($found_values); die; } /** * Render a notice for the user to read the docs before adding custom fields * * @since 1.1.4 * @see SV_WC_Plugin::add_admin_notices() */ public function add_admin_notices() { // show any dependency notices parent::add_admin_notices(); // add notice for selecting export format if ($this->is_plugin_settings()) { $this->get_admin_notice_handler()->add_admin_notice(sprintf(__('Thanks for installing Admin Custom Order Fields! Before you get started, please %sread the documentation%s', WC_Admin_Custom_Order_Fields::TEXT_DOMAIN), '<a href="' . $this->get_documentation_url() . '">', '</a>'), 'read-the-docs', array('always_show_on_settings' => false)); } } /** * Save the default field values when an order is created * * @since 1.3.2-1 * @param int $post_id new order ID * @param object $post the post object */ public function save_default_field_values($post_id, $post) { if ('shop_order' == $post->post_type) { foreach ($this->get_order_fields($post_id) as $order_field) { if ($order_field->default) { // force unique, because oddly this can be invoked when changing the status of an existing order add_post_meta($post_id, $order_field->get_meta_key(), $order_field->default, true); } } } } /** Helper methods ******************************************************/ /** * Main Admin Custom Order Fields Instance, ensures only one instance is/can be loaded * * @since 1.3.0 * @see wc_admin_custom_order_fields() * @return WC_Admin_Custom_Order_Fields */ public static function instance() { if (is_null(self::$instance)) { self::$instance = new self(); } return self::$instance; } /** * Returns any configured order fields * * @since 1.0 * @param int $order_id optional order identifier, if provided any set values are loaded * @return array of WC_Custom_Order_Field objects */ public function get_order_fields($order_id = null) { $order_fields = array(); // get the order object if we can $order = $order_id ? SV_WC_Plugin_Compatibility::wc_get_order($order_id) : null; $custom_order_fields = get_option('wc_admin_custom_order_fields'); if (!is_array($custom_order_fields)) { $custom_order_fields = array(); } foreach ($custom_order_fields as $field_id => $field) { $order_field = new WC_Custom_Order_Field($field_id, $field); // if getting the fields for an order, does the order have a value set? if ($order instanceof WC_Order) { // WC 2.1+ uses magic methods which already prefix the key with a leading underscore // which needs to be removed prior to getting the value $meta_key = ltrim($order_field->get_meta_key(), '_'); if (isset($order->{$meta_key})) { $order_field->set_value(maybe_unserialize($order->{$meta_key})); } } $order_fields[$field_id] = $order_field; } return $order_fields; } /** * Returns the plugin name, localized * * @since 1.1 * @see SV_WC_Plugin::get_plugin_name() * @return string the plugin name */ public function get_plugin_name() { return __('WooCommerce Admin Custom Order Fields', self::TEXT_DOMAIN); } /** * Returns __FILE__ * * @since 1.1 * @see SV_WC_Plugin::get_file() * @return string the full path and filename of the plugin file */ protected function get_file() { return __FILE__; } /** * Gets the URL to the settings page * * @since 1.1 * @see SV_WC_Plugin::is_plugin_settings() * @param string $_ unused * @return string URL to the settings page */ public function get_settings_url($_ = '') { return admin_url('admin.php?page=wc_admin_custom_order_fields'); } /** * Returns true if on the gateway settings page * * @since 1.1 * @see SV_WC_Plugin::is_plugin_settings() * @return boolean true if on the settings page */ public function is_plugin_settings() { return isset($_GET['page']) && 'wc_admin_custom_order_fields' == $_GET['page']; } /** Lifecycle methods ******************************************************/ /** * Install default settings * * @since 1.1 * @see SV_WC_Plugin::install() */ protected function install() { add_option('wc_admin_custom_order_fields_next_field_id', 1); add_option('wc_admin_custom_order_fields_welcome', 1); } /** * Upgrade to $installed_version * * @since 1.1 * @param string $installed_version * @see SV_WC_Plugin::upgrade() */ protected function upgrade($installed_version) { // upgrade to 1.1 if (version_compare($installed_version, '1.1', '<')) { delete_option('wc_admin_custom_order_fields_welcome'); } } } // end \WC_Admin_Custom_Order_Fields class /** * Returns the One True Instance of <plugin> * * @since 1.3.0 * @return WC_Admin_Custom_Order_Fields */ function wc_admin_custom_order_fields() { return WC_Admin_Custom_Order_Fields::instance(); } /** * The WC_Admin_Custom_Order_Fields global object * * @deprecated 1.3.0 * @name $wc_admin_custom_order_fields * @global WC_Admin_Custom_Order_Fields $GLOBALS['wc_admin_custom_order_fields'] */ $GLOBALS['wc_admin_custom_order_fields'] = wc_admin_custom_order_fields(); }
/** * Persist any order custom fields * * @since 1.0 * @param int $order_id the WC_Order ID */ public function save($order_id) { if (empty($_POST['wc-admin-custom-order-fields'])) { return; } $order_fields = wc_admin_custom_order_fields()->get_order_fields(); foreach ($_POST['wc-admin-custom-order-fields'] as $field_id => $field_value) { if (!isset($order_fields[$field_id])) { continue; } if ('date' === $order_fields[$field_id]->type) { $field_value = strtotime($field_value); $order_fields[$field_id]->set_value($field_value); // this column is used so that date fields can be searchable. not a perfect solution, but a compromise update_post_meta($order_id, $order_fields[$field_id]->get_meta_key() . '_formatted', $order_fields[$field_id]->get_value_formatted()); } update_post_meta($order_id, $order_fields[$field_id]->get_meta_key(), $field_value); } }
/** * Add our custom order fields to the set of search fields so that * the admin search functionality is maintained * * @since 1.0 * @param array $search_fields array of post meta fields to search by * @return array of post meta fields to search by */ public function add_search_fields($search_fields) { foreach (wc_admin_custom_order_fields()->get_order_fields() as $order_field) { if ('date' == $order_field->type) { array_push($search_fields, $order_field->get_meta_key() . '_formatted'); } else { array_push($search_fields, $order_field->get_meta_key()); } } return $search_fields; }