WooCommerce PDF Invoices & Packing Slips - Version 2.0.13

Version Description

  • Fix: Minor XSS issue on settings screens by escaping and sanitizing 'tab' & 'section' GET variables. Discovered by Detectify.
  • Fix: Pakistani Rupee Symbol
  • Feature: Automatically enable extended currency symbol support for currencies not supported by Open Sans
  • Dev: added wpo_wcpdf_document_number_settings filter
Download this release

Release Info

Developer pomegranate
Plugin Icon 128x128 WooCommerce PDF Invoices & Packing Slips
Version 2.0.13
Comparing to
See all releases

Code changes from version 2.0.12 to 2.0.13

includes/class-wcpdf-install.php CHANGED
@@ -80,12 +80,59 @@ class Install {
80
  WPO_WCPDF()->main->init_tmp( $tmp_base );
81
  }
82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  // set default settings
84
  $settings_defaults = array(
85
  'wpo_wcpdf_settings_general' => array(
86
  'download_display' => 'display',
87
  'template_path' => WPO_WCPDF()->plugin_path() . '/templates/Simple',
88
- // 'currency_font' => '',
89
  'paper_size' => 'a4',
90
  // 'header_logo' => '',
91
  // 'shop_name' => array(),
80
  WPO_WCPDF()->main->init_tmp( $tmp_base );
81
  }
82
 
83
+ // Unsupported currency symbols
84
+ $unsupported_symbols = array (
85
+ 'AED',
86
+ 'AFN',
87
+ 'BDT',
88
+ 'BHD',
89
+ 'BTC',
90
+ 'CRC',
91
+ 'DZD',
92
+ 'GEL',
93
+ 'GHS',
94
+ 'ILS',
95
+ 'INR',
96
+ 'IQD',
97
+ 'IRR',
98
+ 'IRT',
99
+ 'JOD',
100
+ 'KHR',
101
+ 'KPW',
102
+ 'KRW',
103
+ 'KWD',
104
+ 'LAK',
105
+ 'LBP',
106
+ 'LKR',
107
+ 'LYD',
108
+ 'MAD',
109
+ 'MNT',
110
+ 'MUR',
111
+ 'MVR',
112
+ 'NPR',
113
+ 'OMR',
114
+ 'PHP',
115
+ 'PKR',
116
+ 'PYG',
117
+ 'QAR',
118
+ 'RUB',
119
+ 'SAR',
120
+ 'SCR',
121
+ 'SDG',
122
+ 'SYP',
123
+ 'THB',
124
+ 'TND',
125
+ 'TRY',
126
+ 'UAH',
127
+ 'YER',
128
+ );
129
+
130
  // set default settings
131
  $settings_defaults = array(
132
  'wpo_wcpdf_settings_general' => array(
133
  'download_display' => 'display',
134
  'template_path' => WPO_WCPDF()->plugin_path() . '/templates/Simple',
135
+ 'currency_font' => ( in_array( get_woocommerce_currency(), $unsupported_symbols ) ) ? 1 : '',
136
  'paper_size' => 'a4',
137
  // 'header_logo' => '',
138
  // 'shop_name' => array(),
includes/class-wcpdf-main.php CHANGED
@@ -161,11 +161,11 @@ class Main {
161
  }
162
 
163
  // Generate the output
164
- $document_type = $_GET['document_type'];
165
 
166
- $order_ids = (array) explode('x',$_GET['order_ids']);
167
  // Process oldest first: reverse $order_ids array
168
- $order_ids = array_reverse($order_ids);
169
 
170
  // set default is allowed
171
  $allowed = true;
161
  }
162
 
163
  // Generate the output
164
+ $document_type = sanitize_text_field( $_GET['document_type'] );
165
 
166
+ $order_ids = (array) array_map( 'absint', explode( 'x', $_GET['order_ids'] ) );
167
  // Process oldest first: reverse $order_ids array
168
+ $order_ids = array_reverse( $order_ids );
169
 
170
  // set default is allowed
171
  $allowed = true;
includes/class-wcpdf-settings.php CHANGED
@@ -1,255 +1,255 @@
1
- <?php
2
- namespace WPO\WC\PDF_Invoices;
3
-
4
- use WPO\WC\PDF_Invoices\Documents\Sequential_Number_Store;
5
-
6
- if ( ! defined( 'ABSPATH' ) ) {
7
- exit; // Exit if accessed directly
8
- }
9
-
10
- if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Settings' ) ) :
11
-
12
- class Settings {
13
- public $options_page_hook;
14
-
15
- function __construct() {
16
- $this->callbacks = include( 'class-wcpdf-settings-callbacks.php' );
17
-
18
- // include settings classes
19
- $this->general = include( 'class-wcpdf-settings-general.php' );
20
- $this->documents = include( 'class-wcpdf-settings-documents.php' );
21
- $this->debug = include( 'class-wcpdf-settings-debug.php' );
22
-
23
-
24
- // Settings menu item
25
- add_action( 'admin_menu', array( $this, 'menu' ) ); // Add menu.
26
- // Links on plugin page
27
- add_filter( 'plugin_action_links_'.WPO_WCPDF()->plugin_basename, array( $this, 'add_settings_link' ) );
28
- add_filter( 'plugin_row_meta', array( $this, 'add_support_links' ), 10, 2 );
29
-
30
- // settings capabilities
31
- add_filter( 'option_page_capability_wpo_wcpdf_general_settings', array( $this, 'settings_capabilities' ) );
32
-
33
- $this->general_settings = get_option('wpo_wcpdf_settings_general');
34
- $this->debug_settings = get_option('wpo_wcpdf_settings_debug');
35
-
36
- // admin notice for auto_increment_increment
37
- // add_action( 'admin_notices', array( $this, 'check_auto_increment_increment') );
38
-
39
- // AJAX set number store
40
- add_action( 'wp_ajax_wpo_wcpdf_set_next_number', array($this, 'set_number_store' ));
41
- }
42
-
43
- public function menu() {
44
- $parent_slug = 'woocommerce';
45
-
46
- $this->options_page_hook = add_submenu_page(
47
- $parent_slug,
48
- __( 'PDF Invoices', 'woocommerce-pdf-invoices-packing-slips' ),
49
- __( 'PDF Invoices', 'woocommerce-pdf-invoices-packing-slips' ),
50
- 'manage_woocommerce',
51
- 'wpo_wcpdf_options_page',
52
- array( $this, 'settings_page' )
53
- );
54
- }
55
-
56
- /**
57
- * Add settings link to plugins page
58
- */
59
- public function add_settings_link( $links ) {
60
- $action_links = array(
61
- 'settings' => '<a href="admin.php?page=wpo_wcpdf_options_page">'. __( 'Settings', 'woocommerce' ) . '</a>',
62
- );
63
-
64
- return array_merge( $action_links, $links );
65
- }
66
-
67
- /**
68
- * Add various support links to plugin page
69
- * after meta (version, authors, site)
70
- */
71
- public function add_support_links( $links, $file ) {
72
- if ( $file == WPO_WCPDF()->plugin_basename ) {
73
- $row_meta = array(
74
- 'docs' => '<a href="http://docs.wpovernight.com/woocommerce-pdf-invoices-packing-slips/" target="_blank" title="' . __( 'Documentation', 'woocommerce-pdf-invoices-packing-slips' ) . '">' . __( 'Documentation', 'woocommerce-pdf-invoices-packing-slips' ) . '</a>',
75
- 'support' => '<a href="https://wordpress.org/support/plugin/woocommerce-pdf-invoices-packing-slips" target="_blank" title="' . __( 'Support Forum', 'woocommerce-pdf-invoices-packing-slips' ) . '">' . __( 'Support Forum', 'woocommerce-pdf-invoices-packing-slips' ) . '</a>',
76
- );
77
-
78
- return array_merge( $links, $row_meta );
79
- }
80
- return (array) $links;
81
- }
82
-
83
- function check_auto_increment_increment() {
84
- global $wpdb;
85
- $row = $wpdb->get_row("SHOW VARIABLES LIKE 'auto_increment_increment'");
86
- if ( !empty($row) && !empty($row->Value) && $row->Value != 1 ) {
87
- $error = sprintf( __( "<strong>Warning!</strong> Your database has an AUTO_INCREMENT step size of %s, your invoice numbers may not be sequential. Enable the 'Calculate document numbers (slow)' setting in the Status tab to use an alternate method." , 'woocommerce-pdf-invoices-packing-slips' ), $row->Value );
88
- printf( '<div class="error"><p>%s</p></div>', $error );
89
- }
90
- }
91
-
92
-
93
- public function settings_page() {
94
- $settings_tabs = apply_filters( 'wpo_wcpdf_settings_tabs', array (
95
- 'general' => __('General', 'woocommerce-pdf-invoices-packing-slips' ),
96
- 'documents' => __('Documents', 'woocommerce-pdf-invoices-packing-slips' ),
97
- )
98
- );
99
-
100
- // add status tab last in row
101
- $settings_tabs['debug'] = __('Status', 'woocommerce-pdf-invoices-packing-slips' );
102
-
103
- $active_tab = isset( $_GET[ 'tab' ] ) ? $_GET[ 'tab' ] : 'general';
104
- $active_section = isset( $_GET[ 'section' ] ) ? $_GET[ 'section' ] : '';
105
-
106
- include('views/wcpdf-settings-page.php');
107
- }
108
-
109
- public function add_settings_fields( $settings_fields, $page, $option_group, $option_name ) {
110
- foreach ( $settings_fields as $settings_field ) {
111
- if (!isset($settings_field['callback'])) {
112
- continue;
113
- } elseif ( is_callable( array( $this->callbacks, $settings_field['callback'] ) ) ) {
114
- $callback = array( $this->callbacks, $settings_field['callback'] );
115
- } elseif ( is_callable( $settings_field['callback'] ) ) {
116
- $callback = $settings_field['callback'];
117
- } else {
118
- continue;
119
- }
120
-
121
- if ( $settings_field['type'] == 'section' ) {
122
- add_settings_section(
123
- $settings_field['id'],
124
- $settings_field['title'],
125
- $callback,
126
- $page
127
- );
128
- } else {
129
- add_settings_field(
130
- $settings_field['id'],
131
- $settings_field['title'],
132
- $callback,
133
- $page,
134
- $settings_field['section'],
135
- $settings_field['args']
136
- );
137
- // register option separately for singular options
138
- if (is_string($settings_field['callback']) && $settings_field['callback'] == 'singular_text_element') {
139
- register_setting( $option_group, $settings_field['args']['option_name'], array( $this->callbacks, 'validate' ) );
140
- }
141
- }
142
- }
143
- // $page, $option_group & $option_name are all the same...
144
- register_setting( $option_group, $option_name, array( $this->callbacks, 'validate' ) );
145
- add_filter( 'option_page_capability_'.$page, array( $this, 'settings_capabilities' ) );
146
-
147
- }
148
-
149
- /**
150
- * Set capability for settings page
151
- */
152
- public function settings_capabilities() {
153
- return 'manage_woocommerce';
154
- }
155
-
156
- public function get_common_document_settings() {
157
- $common_settings = array(
158
- 'paper_size' => isset( $this->general_settings['paper_size'] ) ? $this->general_settings['paper_size'] : '',
159
- 'font_subsetting' => isset( $this->general_settings['font_subsetting'] ) || ( defined("DOMPDF_ENABLE_FONTSUBSETTING") && DOMPDF_ENABLE_FONTSUBSETTING === true ) ? true : false,
160
- 'header_logo' => isset( $this->general_settings['header_logo'] ) ? $this->general_settings['header_logo'] : '',
161
- 'shop_name' => isset( $this->general_settings['shop_name'] ) ? $this->general_settings['shop_name'] : '',
162
- 'shop_address' => isset( $this->general_settings['shop_address'] ) ? $this->general_settings['shop_address'] : '',
163
- 'footer' => isset( $this->general_settings['footer'] ) ? $this->general_settings['footer'] : '',
164
- 'extra_1' => isset( $this->general_settings['extra_1'] ) ? $this->general_settings['extra_1'] : '',
165
- 'extra_2' => isset( $this->general_settings['extra_2'] ) ? $this->general_settings['extra_2'] : '',
166
- 'extra_3' => isset( $this->general_settings['extra_3'] ) ? $this->general_settings['extra_3'] : '',
167
- );
168
- return $common_settings;
169
- }
170
-
171
- public function get_document_settings( $document_type ) {
172
- $documents = WPO_WCPDF()->documents->get_documents('all');
173
- foreach ($documents as $document) {
174
- if ( $document->get_type() == $document_type ) {
175
- return $document->settings;
176
- }
177
- }
178
- return false;
179
- }
180
-
181
- public function get_output_format() {
182
- if ( isset( $this->debug_settings['html_output'] ) ) {
183
- $output_format = 'html';
184
- } else {
185
- $output_format = 'pdf';
186
- }
187
- return $output_format;
188
- }
189
-
190
- public function get_output_mode() {
191
- if ( isset( WPO_WCPDF()->settings->general_settings['download_display'] ) ) {
192
- switch ( WPO_WCPDF()->settings->general_settings['download_display'] ) {
193
- case 'display':
194
- $output_mode = 'inline';
195
- break;
196
- case 'download':
197
- default:
198
- $output_mode = 'download';
199
- break;
200
- }
201
- } else {
202
- $output_mode = 'download';
203
- }
204
- return $output_mode;
205
- }
206
-
207
- public function get_template_path( $document_type = NULL ) {
208
- $template_path = isset( $this->general_settings['template_path'] )?$this->general_settings['template_path']:'';
209
- // forward slash for consistency
210
- $template_path = str_replace('\\','/', $template_path);
211
-
212
- // add base path, checking if it's not already there
213
- // alternative setups like Bedrock have WP_CONTENT_DIR & ABSPATH separated
214
- if ( defined('WP_CONTENT_DIR') && strpos( WP_CONTENT_DIR, ABSPATH ) !== false ) {
215
- $forwardslash_basepath = str_replace('\\','/', ABSPATH);
216
- } else {
217
- // bedrock e.a
218
- $forwardslash_basepath = str_replace('\\','/', WP_CONTENT_DIR);
219
- }
220
-
221
- if ( strpos( $template_path, $forwardslash_basepath ) === false ) {
222
- $template_path = $forwardslash_basepath . $template_path;
223
- }
224
-
225
- return $template_path;
226
- }
227
-
228
- public function set_number_store() {
229
- check_ajax_referer( "wpo_wcpdf_next_{$_POST['store']}", 'security' );
230
- $number = isset( $_POST['number'] ) ? (int) $_POST['number'] : 0;
231
- $number_store_method = $this->get_sequential_number_store_method();
232
- $number_store = new Sequential_Number_Store( $_POST['store'], $number_store_method );
233
- $number_store->set_next( $number );
234
- echo "next number ({$_POST['store']}) set to {$number}";
235
- die();
236
- }
237
-
238
- public function get_sequential_number_store_method() {
239
- global $wpdb;
240
- $method = isset( $this->debug_settings['calculate_document_numbers'] ) ? 'calculate' : 'auto_increment';
241
-
242
- // safety first - always use calculate when auto_increment_increment is not 1
243
- $row = $wpdb->get_row("SHOW VARIABLES LIKE 'auto_increment_increment'");
244
- if ( !empty($row) && !empty($row->Value) && $row->Value != 1 ) {
245
- $method = 'calculate';
246
- }
247
-
248
- return $method;
249
- }
250
-
251
- }
252
-
253
- endif; // class_exists
254
-
255
  return new Settings();
1
+ <?php
2
+ namespace WPO\WC\PDF_Invoices;
3
+
4
+ use WPO\WC\PDF_Invoices\Documents\Sequential_Number_Store;
5
+
6
+ if ( ! defined( 'ABSPATH' ) ) {
7
+ exit; // Exit if accessed directly
8
+ }
9
+
10
+ if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Settings' ) ) :
11
+
12
+ class Settings {
13
+ public $options_page_hook;
14
+
15
+ function __construct() {
16
+ $this->callbacks = include( 'class-wcpdf-settings-callbacks.php' );
17
+
18
+ // include settings classes
19
+ $this->general = include( 'class-wcpdf-settings-general.php' );
20
+ $this->documents = include( 'class-wcpdf-settings-documents.php' );
21
+ $this->debug = include( 'class-wcpdf-settings-debug.php' );
22
+
23
+
24
+ // Settings menu item
25
+ add_action( 'admin_menu', array( $this, 'menu' ) ); // Add menu.
26
+ // Links on plugin page
27
+ add_filter( 'plugin_action_links_'.WPO_WCPDF()->plugin_basename, array( $this, 'add_settings_link' ) );
28
+ add_filter( 'plugin_row_meta', array( $this, 'add_support_links' ), 10, 2 );
29
+
30
+ // settings capabilities
31
+ add_filter( 'option_page_capability_wpo_wcpdf_general_settings', array( $this, 'settings_capabilities' ) );
32
+
33
+ $this->general_settings = get_option('wpo_wcpdf_settings_general');
34
+ $this->debug_settings = get_option('wpo_wcpdf_settings_debug');
35
+
36
+ // admin notice for auto_increment_increment
37
+ // add_action( 'admin_notices', array( $this, 'check_auto_increment_increment') );
38
+
39
+ // AJAX set number store
40
+ add_action( 'wp_ajax_wpo_wcpdf_set_next_number', array($this, 'set_number_store' ));
41
+ }
42
+
43
+ public function menu() {
44
+ $parent_slug = 'woocommerce';
45
+
46
+ $this->options_page_hook = add_submenu_page(
47
+ $parent_slug,
48
+ __( 'PDF Invoices', 'woocommerce-pdf-invoices-packing-slips' ),
49
+ __( 'PDF Invoices', 'woocommerce-pdf-invoices-packing-slips' ),
50
+ 'manage_woocommerce',
51
+ 'wpo_wcpdf_options_page',
52
+ array( $this, 'settings_page' )
53
+ );
54
+ }
55
+
56
+ /**
57
+ * Add settings link to plugins page
58
+ */
59
+ public function add_settings_link( $links ) {
60
+ $action_links = array(
61
+ 'settings' => '<a href="admin.php?page=wpo_wcpdf_options_page">'. __( 'Settings', 'woocommerce' ) . '</a>',
62
+ );
63
+
64
+ return array_merge( $action_links, $links );
65
+ }
66
+
67
+ /**
68
+ * Add various support links to plugin page
69
+ * after meta (version, authors, site)
70
+ */
71
+ public function add_support_links( $links, $file ) {
72
+ if ( $file == WPO_WCPDF()->plugin_basename ) {
73
+ $row_meta = array(
74
+ 'docs' => '<a href="http://docs.wpovernight.com/woocommerce-pdf-invoices-packing-slips/" target="_blank" title="' . __( 'Documentation', 'woocommerce-pdf-invoices-packing-slips' ) . '">' . __( 'Documentation', 'woocommerce-pdf-invoices-packing-slips' ) . '</a>',
75
+ 'support' => '<a href="https://wordpress.org/support/plugin/woocommerce-pdf-invoices-packing-slips" target="_blank" title="' . __( 'Support Forum', 'woocommerce-pdf-invoices-packing-slips' ) . '">' . __( 'Support Forum', 'woocommerce-pdf-invoices-packing-slips' ) . '</a>',
76
+ );
77
+
78
+ return array_merge( $links, $row_meta );
79
+ }
80
+ return (array) $links;
81
+ }
82
+
83
+ function check_auto_increment_increment() {
84
+ global $wpdb;
85
+ $row = $wpdb->get_row("SHOW VARIABLES LIKE 'auto_increment_increment'");
86
+ if ( !empty($row) && !empty($row->Value) && $row->Value != 1 ) {
87
+ $error = sprintf( __( "<strong>Warning!</strong> Your database has an AUTO_INCREMENT step size of %s, your invoice numbers may not be sequential. Enable the 'Calculate document numbers (slow)' setting in the Status tab to use an alternate method." , 'woocommerce-pdf-invoices-packing-slips' ), $row->Value );
88
+ printf( '<div class="error"><p>%s</p></div>', $error );
89
+ }
90
+ }
91
+
92
+
93
+ public function settings_page() {
94
+ $settings_tabs = apply_filters( 'wpo_wcpdf_settings_tabs', array (
95
+ 'general' => __('General', 'woocommerce-pdf-invoices-packing-slips' ),
96
+ 'documents' => __('Documents', 'woocommerce-pdf-invoices-packing-slips' ),
97
+ )
98
+ );
99
+
100
+ // add status tab last in row
101
+ $settings_tabs['debug'] = __('Status', 'woocommerce-pdf-invoices-packing-slips' );
102
+
103
+ $active_tab = isset( $_GET[ 'tab' ] ) ? sanitize_text_field( $_GET[ 'tab' ] ) : 'general';
104
+ $active_section = isset( $_GET[ 'section' ] ) ? sanitize_text_field( $_GET[ 'section' ] ) : '';
105
+
106
+ include('views/wcpdf-settings-page.php');
107
+ }
108
+
109
+ public function add_settings_fields( $settings_fields, $page, $option_group, $option_name ) {
110
+ foreach ( $settings_fields as $settings_field ) {
111
+ if (!isset($settings_field['callback'])) {
112
+ continue;
113
+ } elseif ( is_callable( array( $this->callbacks, $settings_field['callback'] ) ) ) {
114
+ $callback = array( $this->callbacks, $settings_field['callback'] );
115
+ } elseif ( is_callable( $settings_field['callback'] ) ) {
116
+ $callback = $settings_field['callback'];
117
+ } else {
118
+ continue;
119
+ }
120
+
121
+ if ( $settings_field['type'] == 'section' ) {
122
+ add_settings_section(
123
+ $settings_field['id'],
124
+ $settings_field['title'],
125
+ $callback,
126
+ $page
127
+ );
128
+ } else {
129
+ add_settings_field(
130
+ $settings_field['id'],
131
+ $settings_field['title'],
132
+ $callback,
133
+ $page,
134
+ $settings_field['section'],
135
+ $settings_field['args']
136
+ );
137
+ // register option separately for singular options
138
+ if (is_string($settings_field['callback']) && $settings_field['callback'] == 'singular_text_element') {
139
+ register_setting( $option_group, $settings_field['args']['option_name'], array( $this->callbacks, 'validate' ) );
140
+ }
141
+ }
142
+ }
143
+ // $page, $option_group & $option_name are all the same...
144
+ register_setting( $option_group, $option_name, array( $this->callbacks, 'validate' ) );
145
+ add_filter( 'option_page_capability_'.$page, array( $this, 'settings_capabilities' ) );
146
+
147
+ }
148
+
149
+ /**
150
+ * Set capability for settings page
151
+ */
152
+ public function settings_capabilities() {
153
+ return 'manage_woocommerce';
154
+ }
155
+
156
+ public function get_common_document_settings() {
157
+ $common_settings = array(
158
+ 'paper_size' => isset( $this->general_settings['paper_size'] ) ? $this->general_settings['paper_size'] : '',
159
+ 'font_subsetting' => isset( $this->general_settings['font_subsetting'] ) || ( defined("DOMPDF_ENABLE_FONTSUBSETTING") && DOMPDF_ENABLE_FONTSUBSETTING === true ) ? true : false,
160
+ 'header_logo' => isset( $this->general_settings['header_logo'] ) ? $this->general_settings['header_logo'] : '',
161
+ 'shop_name' => isset( $this->general_settings['shop_name'] ) ? $this->general_settings['shop_name'] : '',
162
+ 'shop_address' => isset( $this->general_settings['shop_address'] ) ? $this->general_settings['shop_address'] : '',
163
+ 'footer' => isset( $this->general_settings['footer'] ) ? $this->general_settings['footer'] : '',
164
+ 'extra_1' => isset( $this->general_settings['extra_1'] ) ? $this->general_settings['extra_1'] : '',
165
+ 'extra_2' => isset( $this->general_settings['extra_2'] ) ? $this->general_settings['extra_2'] : '',
166
+ 'extra_3' => isset( $this->general_settings['extra_3'] ) ? $this->general_settings['extra_3'] : '',
167
+ );
168
+ return $common_settings;
169
+ }
170
+
171
+ public function get_document_settings( $document_type ) {
172
+ $documents = WPO_WCPDF()->documents->get_documents('all');
173
+ foreach ($documents as $document) {
174
+ if ( $document->get_type() == $document_type ) {
175
+ return $document->settings;
176
+ }
177
+ }
178
+ return false;
179
+ }
180
+
181
+ public function get_output_format() {
182
+ if ( isset( $this->debug_settings['html_output'] ) ) {
183
+ $output_format = 'html';
184
+ } else {
185
+ $output_format = 'pdf';
186
+ }
187
+ return $output_format;
188
+ }
189
+
190
+ public function get_output_mode() {
191
+ if ( isset( WPO_WCPDF()->settings->general_settings['download_display'] ) ) {
192
+ switch ( WPO_WCPDF()->settings->general_settings['download_display'] ) {
193
+ case 'display':
194
+ $output_mode = 'inline';
195
+ break;
196
+ case 'download':
197
+ default:
198
+ $output_mode = 'download';
199
+ break;
200
+ }
201
+ } else {
202
+ $output_mode = 'download';
203
+ }
204
+ return $output_mode;
205
+ }
206
+
207
+ public function get_template_path( $document_type = NULL ) {
208
+ $template_path = isset( $this->general_settings['template_path'] )?$this->general_settings['template_path']:'';
209
+ // forward slash for consistency
210
+ $template_path = str_replace('\\','/', $template_path);
211
+
212
+ // add base path, checking if it's not already there
213
+ // alternative setups like Bedrock have WP_CONTENT_DIR & ABSPATH separated
214
+ if ( defined('WP_CONTENT_DIR') && strpos( WP_CONTENT_DIR, ABSPATH ) !== false ) {
215
+ $forwardslash_basepath = str_replace('\\','/', ABSPATH);
216
+ } else {
217
+ // bedrock e.a
218
+ $forwardslash_basepath = str_replace('\\','/', WP_CONTENT_DIR);
219
+ }
220
+
221
+ if ( strpos( $template_path, $forwardslash_basepath ) === false ) {
222
+ $template_path = $forwardslash_basepath . $template_path;
223
+ }
224
+
225
+ return $template_path;
226
+ }
227
+
228
+ public function set_number_store() {
229
+ check_ajax_referer( "wpo_wcpdf_next_{$_POST['store']}", 'security' );
230
+ $number = isset( $_POST['number'] ) ? (int) $_POST['number'] : 0;
231
+ $number_store_method = $this->get_sequential_number_store_method();
232
+ $number_store = new Sequential_Number_Store( $_POST['store'], $number_store_method );
233
+ $number_store->set_next( $number );
234
+ echo "next number ({$_POST['store']}) set to {$number}";
235
+ die();
236
+ }
237
+
238
+ public function get_sequential_number_store_method() {
239
+ global $wpdb;
240
+ $method = isset( $this->debug_settings['calculate_document_numbers'] ) ? 'calculate' : 'auto_increment';
241
+
242
+ // safety first - always use calculate when auto_increment_increment is not 1
243
+ $row = $wpdb->get_row("SHOW VARIABLES LIKE 'auto_increment_increment'");
244
+ if ( !empty($row) && !empty($row->Value) && $row->Value != 1 ) {
245
+ $method = 'calculate';
246
+ }
247
+
248
+ return $method;
249
+ }
250
+
251
+ }
252
+
253
+ endif; // class_exists
254
+
255
  return new Settings();
includes/documents/abstract-wcpdf-order-document.php CHANGED
@@ -1,676 +1,676 @@
1
- <?php
2
- namespace WPO\WC\PDF_Invoices\Documents;
3
-
4
- use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
5
- use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
6
- use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
7
- use WPO\WC\PDF_Invoices\Compatibility\WC_DateTime;
8
-
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- exit; // Exit if accessed directly
11
- }
12
-
13
- if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Documents\\Order_Document' ) ) :
14
-
15
- /**
16
- * Abstract Document
17
- *
18
- * Handles generic pdf document & order data and database interaction
19
- * which is extended by both Invoices & Packing Slips
20
- *
21
- * @class \WPO\WC\PDF_Invoices\Documents\Order_Document
22
- * @version 2.0
23
- * @category Class
24
- * @author Ewout Fernhout
25
- */
26
-
27
- abstract class Order_Document {
28
- /**
29
- * Document type.
30
- * @var String
31
- */
32
- public $type;
33
-
34
- /**
35
- * Document slug.
36
- * @var String
37
- */
38
- public $slug;
39
-
40
- /**
41
- * Document title.
42
- * @var string
43
- */
44
- public $title;
45
-
46
- /**
47
- * Document icon.
48
- * @var string
49
- */
50
- public $icon;
51
-
52
- /**
53
- * WC Order object
54
- * @var object
55
- */
56
- public $order;
57
-
58
- /**
59
- * WC Order ID
60
- * @var object
61
- */
62
- public $order_id;
63
-
64
- /**
65
- * Document settings.
66
- * @var array
67
- */
68
- public $settings;
69
-
70
- /**
71
- * TRUE if document is enabled.
72
- * @var bool
73
- */
74
- public $enabled;
75
-
76
- /**
77
- * Linked documents, used for data retrieval
78
- * @var array
79
- */
80
- protected $linked_documents = array();
81
-
82
- /**
83
- * Core data for this object. Name value pairs (name + default value).
84
- * @var array
85
- */
86
- protected $data = array();
87
-
88
- /**
89
- * Init/load the order object.
90
- *
91
- * @param int|object|WC_Order $order Order to init.
92
- */
93
- public function __construct( $order = 0 ) {
94
- if ( is_numeric( $order ) && $order > 0 ) {
95
- $this->order_id = $order;
96
- $this->order = WCX::get_order( $order_id );
97
- } elseif ( $order instanceof \WC_Order || is_subclass_of( $order, '\WC_Abstract_Order') ) {
98
- $this->order_id = WCX_Order::get_id( $order );
99
- $this->order = $order;
100
- }
101
-
102
- // set properties
103
- $this->slug = str_replace('-', '_', $this->type);
104
-
105
- // load settings
106
- $this->settings = $this->get_settings();
107
- $this->enabled = $this->get_setting( 'enabled', false );
108
-
109
- // load data
110
- if ( $this->order ) {
111
- $this->read_data( $this->order );
112
- if ( WPO_WCPDF()->legacy_mode_enabled() ) {
113
- global $wpo_wcpdf;
114
- $wpo_wcpdf->export->order = $this->order;
115
- $wpo_wcpdf->export->document = $this;
116
- $wpo_wcpdf->export->order_id = $this->order_id;
117
- $wpo_wcpdf->export->template_type = $this->type;
118
- }
119
- }
120
-
121
- }
122
-
123
- public function init_settings() {
124
- return ;
125
- }
126
-
127
- public function get_settings() {
128
- $common_settings = WPO_WCPDF()->settings->get_common_document_settings();
129
- $document_settings = get_option( 'wpo_wcpdf_documents_settings_'.$this->get_type() );
130
- return (array) $document_settings + (array) $common_settings;
131
- }
132
-
133
- public function get_setting( $key, $default = '' ) {
134
- $setting = isset( $this->settings[$key] ) ? $this->settings[$key] : $default;
135
- return $setting;
136
- }
137
-
138
- public function get_attach_to_email_ids() {
139
- $email_ids = isset( $this->settings['attach_to_email_ids'] ) ? array_keys( $this->settings['attach_to_email_ids'] ) : array();
140
- return $email_ids;
141
- }
142
-
143
- public function get_type() {
144
- return $this->type;
145
- }
146
-
147
- public function is_enabled() {
148
- return apply_filters( 'wpo_wcpdf_document_is_enabled', $this->enabled, $this->type );
149
- }
150
-
151
- public function get_hook_prefix() {
152
- return 'wpo_wcpdf_' . $this->slug . '_get_';
153
- }
154
-
155
- public function read_data( $order ) {
156
- $number = WCX_Order::get_meta( $order, "_wcpdf_{$this->slug}_number_data", true );
157
- // fallback to legacy data for number
158
- if ( empty( $number ) ) {
159
- $number = WCX_Order::get_meta( $order, "_wcpdf_{$this->slug}_number", true );
160
- $formatted_number = WCX_Order::get_meta( $order, "_wcpdf_formatted_{$this->slug}_number", true );
161
- if (!empty($formatted_number)) {
162
- $number = compact( 'number', 'formatted_number' );
163
- }
164
- }
165
-
166
- // pass data to setter functions
167
- $this->set_data( array(
168
- // always load date before number, because date is used in number formatting
169
- 'date' => WCX_Order::get_meta( $order, "_wcpdf_{$this->slug}_date", true ),
170
- 'number' => $number,
171
- ), $order );
172
-
173
- return;
174
- }
175
-
176
- public function init() {
177
- $this->set_date( current_time( 'timestamp', true ) );
178
- }
179
-
180
- public function save( $order = null ) {
181
- $order = empty( $order ) ? $this->order : $order;
182
- if ( empty( $order ) ) {
183
- return; // nowhere to save to...
184
- }
185
-
186
- foreach ($this->data as $key => $value) {
187
- if ( empty( $value ) ) {
188
- WCX_Order::delete_meta_data( $order, "_wcpdf_{$this->slug}_{$key}" );
189
- if ( $key == 'date' ) {
190
- WCX_Order::delete_meta_data( $order, "_wcpdf_{$this->slug}_{$key}_formatted" );
191
- } elseif ( $key == 'number' ) {
192
- WCX_Order::delete_meta_data( $order, "_wcpdf_{$this->slug}_{$key}_data" );
193
- }
194
- } else {
195
- if ( $key == 'date' ) {
196
- // store dates as timestamp and formatted as mysql time
197
- WCX_Order::update_meta_data( $order, "_wcpdf_{$this->slug}_{$key}", $value->getTimestamp() );
198
- WCX_Order::update_meta_data( $order, "_wcpdf_{$this->slug}_{$key}_formatted", $value->date( 'Y-m-d H:i:s' ) );
199
- } elseif ( $key == 'number' ) {
200
- // store both formatted number and number data
201
- WCX_Order::update_meta_data( $order, "_wcpdf_{$this->slug}_{$key}", $value->formatted_number );
202
- WCX_Order::update_meta_data( $order, "_wcpdf_{$this->slug}_{$key}_data", $value->to_array() );
203
- }
204
- }
205
- }
206
- }
207
-
208
- public function exists() {
209
- return !empty( $this->data['number'] );
210
- }
211
-
212
- /*
213
- |--------------------------------------------------------------------------
214
- | Data getters
215
- |--------------------------------------------------------------------------
216
- */
217
-
218
- public function get_data( $key, $document_type = '', $order = null, $context = 'view' ) {
219
- $document_type = empty( $document_type ) ? $this->type : $document_type;
220
- $order = empty( $order ) ? $this->order : $order;
221
-
222
- // redirect get_data call for linked documents
223
- if ( $document_type != $this->type ) {
224
- if ( !isset( $this->linked_documents[ $document_type ] ) ) {
225
- // always assume parent for documents linked to credit notes
226
- if ($this->type == 'credit-note') {
227
- $order = $this->get_refund_parent( $order );
228
- }
229
- // order is not loaded to avoid overhead - we pass this by reference directly to the read_data method instead
230
- $this->linked_documents[ $document_type ] = wcpdf_get_document( $document_type, null );
231
- $this->linked_documents[ $document_type ]->read_data( $order );
232
- }
233
- return $this->linked_documents[ $document_type ]->get_data( $key, $document_type );
234
- }
235
-
236
- $value = null;
237
-
238
- if ( array_key_exists( $key, $this->data ) ) {
239
- $value = $this->data[ $key ];
240
-
241
- if ( 'view' === $context ) {
242
- $value = apply_filters( $this->get_hook_prefix() . $key, $value, $this );
243
- }
244
- }
245
-
246
- return $value;
247
- }
248
-
249
- public function get_number( $document_type = '', $order = null, $context = 'view' ) {
250
- return $this->get_data( 'number', $document_type, $order, $context );
251
- }
252
-
253
- public function get_date( $document_type = '', $order = null, $context = 'view' ) {
254
- return $this->get_data( 'date', $document_type, $order, $context );
255
- }
256
-
257
- public function get_title() {
258
- return apply_filters( "wpo_wcpdf_{$this->slug}_title", $this->title );
259
- }
260
-
261
- /*
262
- |--------------------------------------------------------------------------
263
- | Data setters
264
- |--------------------------------------------------------------------------
265
- |
266
- | Functions for setting order data. These should not update anything in the
267
- | order itself and should only change what is stored in the class
268
- | object.
269
- */
270
-
271
- public function set_data( $data, $order ) {
272
- $order = empty( $order ) ? $this->order : $order;
273
- foreach ($data as $key => $value) {
274
- $setter = "set_$key";
275
- if ( is_callable( array( $this, $setter ) ) ) {
276
- $this->$setter( $value, $order );
277
- } else {
278
- $this->data[ $key ] = $value;
279
- }
280
- }
281
- }
282
-
283
- public function set_date( $value, $order = null ) {
284
- $order = empty( $order ) ? $this->order : $order;
285
- try {
286
- if ( empty( $value ) ) {
287
- $this->data[ 'date' ] = null;
288
- return;
289
- }
290
-
291
- if ( is_a( $value, 'WC_DateTime' ) ) {
292
- $datetime = $value;
293
- } elseif ( is_numeric( $value ) ) {
294
- // Timestamps are handled as UTC timestamps in all cases.
295
- $datetime = new WC_DateTime( "@{$value}", new \DateTimeZone( 'UTC' ) );
296
- } else {
297
- // Strings are defined in local WP timezone. Convert to UTC.
298
- if ( 1 === preg_match( '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(Z|((-|\+)\d{2}:\d{2}))$/', $value, $date_bits ) ) {
299
- $offset = ! empty( $date_bits[7] ) ? iso8601_timezone_to_offset( $date_bits[7] ) : wc_timezone_offset();
300
- $timestamp = gmmktime( $date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1] ) - $offset;
301
- } else {
302
- $timestamp = wc_string_to_timestamp( get_gmt_from_date( gmdate( 'Y-m-d H:i:s', wc_string_to_timestamp( $value ) ) ) );
303
- }
304
- $datetime = new WC_DateTime( "@{$timestamp}", new \DateTimeZone( 'UTC' ) );
305
- }
306
-
307
- // Set local timezone or offset.
308
- if ( get_option( 'timezone_string' ) ) {
309
- $datetime->setTimezone( new \DateTimeZone( wc_timezone_string() ) );
310
- } else {
311
- $datetime->set_utc_offset( wc_timezone_offset() );
312
- }
313
-
314
- $this->data[ 'date' ] = $datetime;
315
- } catch ( Exception $e ) {}
316
-
317
-
318
- }
319
-
320
- public function set_number( $value, $order = null ) {
321
- $order = empty( $order ) ? $this->order : $order;
322
-
323
- if ( is_array( $value ) ) {
324
- $filtered_value = array_filter( $value );
325
- }
326
-
327
- if ( empty( $value ) || ( is_array( $value ) && empty( $filtered_value ) ) ) {
328
- $document_number = null;
329
- } elseif ( $value instanceof Document_Number ) {
330
- // WCPDF 2.0 number data
331
- $document_number = $value;
332
- } elseif ( is_array( $value ) ) {
333
- // WCPDF 2.0 number data as array
334
- $document_number = new Document_Number( $value, $this->get_number_settings(), $this, $order );
335
- } else {
336
- // plain number
337
- $document_number = new Document_Number( $value, $this->get_number_settings(), $this, $order );
338
- }
339
-
340
- $this->data[ 'number' ] = $document_number;
341
- }
342
-
343
- /*
344
- |--------------------------------------------------------------------------
345
- | Settings getters / outputters
346
- |--------------------------------------------------------------------------
347
- */
348
-
349
- public function get_number_settings() {
350
- $number_settings = isset($this->settings['number_format'])?$this->settings['number_format']:array();
351
- return $number_settings;
352
- }
353
-
354
- /**
355
- * Output template styles
356
- */
357
- public function template_styles() {
358
- $css = apply_filters( 'wpo_wcpdf_template_styles_file', $this->locate_template_file( "style.css" ) );
359
-
360
- ob_start();
361
- if (file_exists($css)) {
362
- include($css);
363
- }
364
- $css = ob_get_clean();
365
- $css = apply_filters( 'wpo_wcpdf_template_styles', $css, $this );
366
-
367
- echo $css;
368
- }
369
-
370
- public function has_header_logo() {
371
- return !empty( $this->settings['header_logo'] );
372
- }
373
-
374
- /**
375
- * Return logo id
376
- */
377
- public function get_header_logo_id() {
378
- if ( !empty( $this->settings['header_logo'] ) ) {
379
- return apply_filters( 'wpo_wcpdf_header_logo_id', $this->settings['header_logo'], $this );
380
- }
381
- }
382
-
383
- /**
384
- * Show logo html
385
- */
386
- public function header_logo() {
387
- if ($this->get_header_logo_id()) {
388
- $attachment_id = $this->get_header_logo_id();
389
- $company = $this->get_shop_name();
390
- if( $attachment_id ) {
391
- $attachment = wp_get_attachment_image_src( $attachment_id, 'full', false );
392
-
393
- $attachment_src = $attachment[0];
394
- $attachment_width = $attachment[1];
395
- $attachment_height = $attachment[2];
396
-
397
- $attachment_path = get_attached_file( $attachment_id );
398
-
399
- if ( apply_filters('wpo_wcpdf_use_path', true) && file_exists($attachment_path) ) {
400
- $src = $attachment_path;
401
- } else {
402
- $src = $attachment_src;
403
- }
404
-
405
- printf('<img src="%1$s" width="%2$d" height="%3$d" alt="%4$s" />', $src, $attachment_width, $attachment_height, esc_attr( $company ) );
406
- }
407
- }
408
- }
409
-
410
- public function get_settings_text( $settings_key, $default = false, $autop = true ) {
411
- if ( !empty( $this->settings[$settings_key]['default'] ) ) {
412
- $text = wptexturize( trim( $this->settings[$settings_key]['default'] ) );
413
- if ($autop === true) {
414
- $text = wpautop( $text );
415
- }
416
- } else {
417
- $text = $default;
418
- }
419
- return apply_filters( "wpo_wcpdf_{$settings_key}", $text, $this );
420
- }
421
-
422
- /**
423
- * Return/Show custom company name or default to blog name
424
- */
425
- public function get_shop_name() {
426
- $default = get_bloginfo( 'name' );
427
- return $this->get_settings_text( 'shop_name', $default, false );
428
- }
429
- public function shop_name() {
430
- echo $this->get_shop_name();
431
- }
432
-
433
- /**
434
- * Return/Show shop/company address if provided
435
- */
436
- public function get_shop_address() {
437
- return $this->get_settings_text( 'shop_address' );
438
- }
439
- public function shop_address() {
440
- echo $this->get_shop_address();
441
- }
442
-
443
- /**
444
- * Return/Show shop/company footer imprint, copyright etc.
445
- */
446
- public function get_footer() {
447
- return $this->get_settings_text( 'footer' );
448
- }
449
- public function footer() {
450
- echo $this->get_footer();
451
- }
452
-
453
- /**
454
- * Return/Show Extra field 1
455
- */
456
- public function get_extra_1() {
457
- return $this->get_settings_text( 'extra_1' );
458
-
459
- }
460
- public function extra_1() {
461
- echo $this->get_extra_1();
462
- }
463
-
464
- /**
465
- * Return/Show Extra field 2
466
- */
467
- public function get_extra_2() {
468
- return $this->get_settings_text( 'extra_2' );
469
- }
470
- public function extra_2() {
471
- echo $this->get_extra_2();
472
- }
473
-
474
- /**
475
- * Return/Show Extra field 3
476
- */
477
- public function get_extra_3() {
478
- return $this->get_settings_text( 'extra_3' );
479
- }
480
- public function extra_3() {
481
- echo $this->get_extra_3();
482
- }
483
-
484
- /*
485
- |--------------------------------------------------------------------------
486
- | Output functions
487
- |--------------------------------------------------------------------------
488
- */
489
-
490
- public function get_pdf() {
491
- do_action( 'wpo_wcpdf_before_pdf', $this->get_type(), $this );
492
-
493
- $pdf_settings = array(
494
- 'paper_size' => apply_filters( 'wpo_wcpdf_paper_format', $this->get_setting( 'paper_size', 'A4' ), $this->get_type() ),
495
- 'paper_orientation' => apply_filters( 'wpo_wcpdf_paper_orientation', 'portrait', $this->get_type() ),
496
- 'font_subsetting' => $this->get_setting( 'font_subsetting', false ),
497
- );
498
- $pdf_maker = wcpdf_get_pdf_maker( $this->get_html(), $pdf_settings );
499
- $pdf = $pdf_maker->output();
500
-
501
- do_action( 'wpo_wcpdf_after_pdf', $this->get_type(), $this );
502
- do_action( 'wpo_wcpdf_pdf_created', $pdf, $this );
503
-
504
- return $pdf;
505
- }
506
-
507
- public function get_html( $args = array() ) {
508
- do_action( 'wpo_wcpdf_before_html', $this->get_type(), $this );
509
- $default_args = array (
510
- 'wrap_html_content' => true,
511
- );
512
- $args = $args + $default_args;
513
-
514
- $html = $this->render_template( $this->locate_template_file( "{$this->type}.php" ) );
515
- if ($args['wrap_html_content']) {
516
- $html = $this->wrap_html_content( $html );
517
- }
518
-
519
- // clean up special characters
520
- if ( function_exists('utf8_decode') && function_exists('mb_convert_encoding') ) {
521
- $html = utf8_decode(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
522
- }
523
-
524
- do_action( 'wpo_wcpdf_after_html', $this->get_type(), $this );
525
-
526
- return apply_filters( 'wpo_wcpdf_get_html', $html, $this );
527
- }
528
-
529
- public function output_pdf( $output_mode = 'download' ) {
530
- $pdf = $this->get_pdf();
531
- wcpdf_pdf_headers( $this->get_filename(), $output_mode, $pdf );
532
- echo $pdf;
533
- die();
534
- }
535
-
536
- public function output_html() {
537
- echo $this->get_html();
538
- die();
539
- }
540
-
541
- public function wrap_html_content( $content ) {
542
- if ( WPO_WCPDF()->legacy_mode_enabled() ) {
543
- $GLOBALS['wpo_wcpdf']->export->output_body = $content;
544
- }
545
-
546
- $html = $this->render_template( $this->locate_template_file( "html-document-wrapper.php" ), array(
547
- 'content' => $content,
548
- )
549
- );
550
- return $html;
551
- }
552
-
553
- public function get_filename( $context = 'download', $args = array() ) {
554
- $order_count = isset($args['order_ids']) ? count($args['order_ids']) : 1;
555
-
556
- $name = $this->get_type();
557
- if ( get_post_type( $this->order_id ) == 'shop_order_refund' ) {
558
- $number = $this->order_id;
559
- } else {
560
- $number = method_exists( $this->order, 'get_order_number' ) ? $this->order->get_order_number() : '';
561
- }
562
-
563
- if ( $order_count == 1 ) {
564
- $suffix = $number;
565
- } else {
566
- $suffix = date('Y-m-d'); // 2020-11-11
567
- }
568
-
569
- $filename = $name . '-' . $suffix . '.pdf';
570
-
571
- // Filter filename
572
- $order_ids = isset($args['order_ids']) ? $args['order_ids'] : array( $this->order_id );
573
- $filename = apply_filters( 'wpo_wcpdf_filename', $filename, $this->get_type(), $order_ids, $context );
574
-
575
- // sanitize filename (after filters to prevent human errors)!
576
- return sanitize_file_name( $filename );
577
- }
578
-
579
- public function get_template_path() {
580
- return WPO_WCPDF()->settings->get_template_path();
581
- }
582
-
583
- public function locate_template_file( $file ) {
584
- if (empty($file)) {
585
- $file = $this->type.'.php';
586
- }
587
- $path = WPO_WCPDF()->settings->get_template_path( $file );
588
- $file_path = "{$path}/{$file}";
589
-
590
- $fallback_file_path = WPO_WCPDF()->plugin_path() . '/templates/Simple/' . $file;
591
- if ( !file_exists( $file_path ) && file_exists( $fallback_file_path ) ) {
592
- $file_path = $fallback_file_path;
593
- }
594
-
595
- $file_path = apply_filters( 'wpo_wcpdf_template_file', $file_path, $this->type, $this->order );
596
-
597
- return $file_path;
598
- }
599
-
600
- public function render_template( $file, $args = array() ) {
601
- do_action( 'wpo_wcpdf_process_template', $this->get_type(), $this );
602
-
603
- if ( ! empty( $args ) && is_array( $args ) ) {
604
- extract( $args );
605
- }
606
- ob_start();
607
- if (file_exists($file)) {
608
- include($file);
609
- }
610
- return ob_get_clean();
611
- }
612
-
613
- /*
614
- |--------------------------------------------------------------------------
615
- | Settings helper functions
616
- |--------------------------------------------------------------------------
617
- */
618
-
619
- /**
620
- * get all emails registered in WooCommerce
621
- * @param boolean $remove_defaults switch to remove default woocommerce emails
622
- * @return array $emails list of all email ids/slugs and names
623
- */
624
- public function get_wc_emails() {
625
- // get emails from WooCommerce
626
- global $woocommerce;
627
- $mailer = $woocommerce->mailer();
628
- $wc_emails = $mailer->get_emails();
629
-
630
- $non_order_emails = array(
631
- 'customer_note',
632
- 'customer_reset_password',
633
- 'customer_new_account'
634
- );
635
-
636
- $emails = array();
637
- foreach ($wc_emails as $class => $email) {
638
- if ( !in_array( $email->id, $non_order_emails ) ) {
639
- switch ($email->id) {
640
- case 'new_order':
641
- $emails[$email->id] = sprintf('%s (%s)', $email->title, __( 'Admin email', 'woocommerce-pdf-invoices-packing-slips' ) );
642
- break;
643
- case 'customer_invoice':
644
- $emails[$email->id] = sprintf('%s (%s)', $email->title, __( 'Manual email', 'woocommerce-pdf-invoices-packing-slips' ) );
645
- break;
646
- default:
647
- $emails[$email->id] = $email->title;
648
- break;
649
- }
650
- }
651
- }
652
-
653
- return apply_filters( 'wpo_wcpdf_wc_emails', $emails );
654
- }
655
-
656
- // get list of WooCommerce statuses
657
- public function get_wc_order_status_list() {
658
- if ( version_compare( WOOCOMMERCE_VERSION, '2.2', '<' ) ) {
659
- $statuses = (array) get_terms( 'shop_order_status', array( 'hide_empty' => 0, 'orderby' => 'id' ) );
660
- foreach ( $statuses as $status ) {
661
- $order_statuses[esc_attr( $status->slug )] = esc_html__( $status->name, 'woocommerce' );
662
- }
663
- } else {
664
- $statuses = wc_get_order_statuses();
665
- foreach ( $statuses as $status_slug => $status ) {
666
- $status_slug = 'wc-' === substr( $status_slug, 0, 3 ) ? substr( $status_slug, 3 ) : $status_slug;
667
- $order_statuses[$status_slug] = $status;
668
- }
669
- }
670
- return $order_statuses;
671
- }
672
-
673
-
674
- }
675
-
676
- endif; // class_exists
1
+ <?php
2
+ namespace WPO\WC\PDF_Invoices\Documents;
3
+
4
+ use WPO\WC\PDF_Invoices\Compatibility\WC_Core as WCX;
5
+ use WPO\WC\PDF_Invoices\Compatibility\Order as WCX_Order;
6
+ use WPO\WC\PDF_Invoices\Compatibility\Product as WCX_Product;
7
+ use WPO\WC\PDF_Invoices\Compatibility\WC_DateTime;
8
+
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ exit; // Exit if accessed directly
11
+ }
12
+
13
+ if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Documents\\Order_Document' ) ) :
14
+
15
+ /**
16
+ * Abstract Document
17
+ *
18
+ * Handles generic pdf document & order data and database interaction
19
+ * which is extended by both Invoices & Packing Slips
20
+ *
21
+ * @class \WPO\WC\PDF_Invoices\Documents\Order_Document
22
+ * @version 2.0
23
+ * @category Class
24
+ * @author Ewout Fernhout
25
+ */
26
+
27
+ abstract class Order_Document {
28
+ /**
29
+ * Document type.
30
+ * @var String
31
+ */
32
+ public $type;
33
+
34
+ /**
35
+ * Document slug.
36
+ * @var String
37
+ */
38
+ public $slug;
39
+
40
+ /**
41
+ * Document title.
42
+ * @var string
43
+ */
44
+ public $title;
45
+
46
+ /**
47
+ * Document icon.
48
+ * @var string
49
+ */
50
+ public $icon;
51
+
52
+ /**
53
+ * WC Order object
54
+ * @var object
55
+ */
56
+ public $order;
57
+
58
+ /**
59
+ * WC Order ID
60
+ * @var object
61
+ */
62
+ public $order_id;
63
+
64
+ /**
65
+ * Document settings.
66
+ * @var array
67
+ */
68
+ public $settings;
69
+
70
+ /**
71
+ * TRUE if document is enabled.
72
+ * @var bool
73
+ */
74
+ public $enabled;
75
+
76
+ /**
77
+ * Linked documents, used for data retrieval
78
+ * @var array
79
+ */
80
+ protected $linked_documents = array();
81
+
82
+ /**
83
+ * Core data for this object. Name value pairs (name + default value).
84
+ * @var array
85
+ */
86
+ protected $data = array();
87
+
88
+ /**
89
+ * Init/load the order object.
90
+ *
91
+ * @param int|object|WC_Order $order Order to init.
92
+ */
93
+ public function __construct( $order = 0 ) {
94
+ if ( is_numeric( $order ) && $order > 0 ) {
95
+ $this->order_id = $order;
96
+ $this->order = WCX::get_order( $order_id );
97
+ } elseif ( $order instanceof \WC_Order || is_subclass_of( $order, '\WC_Abstract_Order') ) {
98
+ $this->order_id = WCX_Order::get_id( $order );
99
+ $this->order = $order;
100
+ }
101
+
102
+ // set properties
103
+ $this->slug = str_replace('-', '_', $this->type);
104
+
105
+ // load settings
106
+ $this->settings = $this->get_settings();
107
+ $this->enabled = $this->get_setting( 'enabled', false );
108
+
109
+ // load data
110
+ if ( $this->order ) {
111
+ $this->read_data( $this->order );
112
+ if ( WPO_WCPDF()->legacy_mode_enabled() ) {
113
+ global $wpo_wcpdf;
114
+ $wpo_wcpdf->export->order = $this->order;
115
+ $wpo_wcpdf->export->document = $this;
116
+ $wpo_wcpdf->export->order_id = $this->order_id;
117
+ $wpo_wcpdf->export->template_type = $this->type;
118
+ }
119
+ }
120
+
121
+ }
122
+
123
+ public function init_settings() {
124
+ return ;
125
+ }
126
+
127
+ public function get_settings() {
128
+ $common_settings = WPO_WCPDF()->settings->get_common_document_settings();
129
+ $document_settings = get_option( 'wpo_wcpdf_documents_settings_'.$this->get_type() );
130
+ return (array) $document_settings + (array) $common_settings;
131
+ }
132
+
133
+ public function get_setting( $key, $default = '' ) {
134
+ $setting = isset( $this->settings[$key] ) ? $this->settings[$key] : $default;
135
+ return $setting;
136
+ }
137
+
138
+ public function get_attach_to_email_ids() {
139
+ $email_ids = isset( $this->settings['attach_to_email_ids'] ) ? array_keys( $this->settings['attach_to_email_ids'] ) : array();
140
+ return $email_ids;
141
+ }
142
+
143
+ public function get_type() {
144
+ return $this->type;
145
+ }
146
+
147
+ public function is_enabled() {
148
+ return apply_filters( 'wpo_wcpdf_document_is_enabled', $this->enabled, $this->type );
149
+ }
150
+
151
+ public function get_hook_prefix() {
152
+ return 'wpo_wcpdf_' . $this->slug . '_get_';
153
+ }
154
+
155
+ public function read_data( $order ) {
156
+ $number = WCX_Order::get_meta( $order, "_wcpdf_{$this->slug}_number_data", true );
157
+ // fallback to legacy data for number
158
+ if ( empty( $number ) ) {
159
+ $number = WCX_Order::get_meta( $order, "_wcpdf_{$this->slug}_number", true );
160
+ $formatted_number = WCX_Order::get_meta( $order, "_wcpdf_formatted_{$this->slug}_number", true );
161
+ if (!empty($formatted_number)) {
162
+ $number = compact( 'number', 'formatted_number' );
163
+ }
164
+ }
165
+
166
+ // pass data to setter functions
167
+ $this->set_data( array(
168
+ // always load date before number, because date is used in number formatting
169
+ 'date' => WCX_Order::get_meta( $order, "_wcpdf_{$this->slug}_date", true ),
170
+ 'number' => $number,
171
+ ), $order );
172
+
173
+ return;
174
+ }
175
+
176
+ public function init() {
177
+ $this->set_date( current_time( 'timestamp', true ) );
178
+ }
179
+
180
+ public function save( $order = null ) {
181
+ $order = empty( $order ) ? $this->order : $order;
182
+ if ( empty( $order ) ) {
183
+ return; // nowhere to save to...
184
+ }
185
+
186
+ foreach ($this->data as $key => $value) {
187
+ if ( empty( $value ) ) {
188
+ WCX_Order::delete_meta_data( $order, "_wcpdf_{$this->slug}_{$key}" );
189
+ if ( $key == 'date' ) {
190
+ WCX_Order::delete_meta_data( $order, "_wcpdf_{$this->slug}_{$key}_formatted" );
191
+ } elseif ( $key == 'number' ) {
192
+ WCX_Order::delete_meta_data( $order, "_wcpdf_{$this->slug}_{$key}_data" );
193
+ }
194
+ } else {
195
+ if ( $key == 'date' ) {
196
+ // store dates as timestamp and formatted as mysql time
197
+ WCX_Order::update_meta_data( $order, "_wcpdf_{$this->slug}_{$key}", $value->getTimestamp() );
198
+ WCX_Order::update_meta_data( $order, "_wcpdf_{$this->slug}_{$key}_formatted", $value->date( 'Y-m-d H:i:s' ) );
199
+ } elseif ( $key == 'number' ) {
200
+ // store both formatted number and number data
201
+ WCX_Order::update_meta_data( $order, "_wcpdf_{$this->slug}_{$key}", $value->formatted_number );
202
+ WCX_Order::update_meta_data( $order, "_wcpdf_{$this->slug}_{$key}_data", $value->to_array() );
203
+ }
204
+ }
205
+ }
206
+ }
207
+
208
+ public function exists() {
209
+ return !empty( $this->data['number'] );
210
+ }
211
+
212
+ /*
213
+ |--------------------------------------------------------------------------
214
+ | Data getters
215
+ |--------------------------------------------------------------------------
216
+ */
217
+
218
+ public function get_data( $key, $document_type = '', $order = null, $context = 'view' ) {
219
+ $document_type = empty( $document_type ) ? $this->type : $document_type;
220
+ $order = empty( $order ) ? $this->order : $order;
221
+
222
+ // redirect get_data call for linked documents
223
+ if ( $document_type != $this->type ) {
224
+ if ( !isset( $this->linked_documents[ $document_type ] ) ) {
225
+ // always assume parent for documents linked to credit notes
226
+ if ($this->type == 'credit-note') {
227
+ $order = $this->get_refund_parent( $order );
228
+ }
229
+ // order is not loaded to avoid overhead - we pass this by reference directly to the read_data method instead
230
+ $this->linked_documents[ $document_type ] = wcpdf_get_document( $document_type, null );
231
+ $this->linked_documents[ $document_type ]->read_data( $order );
232
+ }
233
+ return $this->linked_documents[ $document_type ]->get_data( $key, $document_type );
234
+ }
235
+
236
+ $value = null;
237
+
238
+ if ( array_key_exists( $key, $this->data ) ) {
239
+ $value = $this->data[ $key ];
240
+
241
+ if ( 'view' === $context ) {
242
+ $value = apply_filters( $this->get_hook_prefix() . $key, $value, $this );
243
+ }
244
+ }
245
+
246
+ return $value;
247
+ }
248
+
249
+ public function get_number( $document_type = '', $order = null, $context = 'view' ) {
250
+ return $this->get_data( 'number', $document_type, $order, $context );
251
+ }
252
+
253
+ public function get_date( $document_type = '', $order = null, $context = 'view' ) {
254
+ return $this->get_data( 'date', $document_type, $order, $context );
255
+ }
256
+
257
+ public function get_title() {
258
+ return apply_filters( "wpo_wcpdf_{$this->slug}_title", $this->title );
259
+ }
260
+
261
+ /*
262
+ |--------------------------------------------------------------------------
263
+ | Data setters
264
+ |--------------------------------------------------------------------------
265
+ |
266
+ | Functions for setting order data. These should not update anything in the
267
+ | order itself and should only change what is stored in the class
268
+ | object.
269
+ */
270
+
271
+ public function set_data( $data, $order ) {
272
+ $order = empty( $order ) ? $this->order : $order;
273
+ foreach ($data as $key => $value) {
274
+ $setter = "set_$key";
275
+ if ( is_callable( array( $this, $setter ) ) ) {
276
+ $this->$setter( $value, $order );
277
+ } else {
278
+ $this->data[ $key ] = $value;
279
+ }
280
+ }
281
+ }
282
+
283
+ public function set_date( $value, $order = null ) {
284
+ $order = empty( $order ) ? $this->order : $order;
285
+ try {
286
+ if ( empty( $value ) ) {
287
+ $this->data[ 'date' ] = null;
288
+ return;
289
+ }
290
+
291
+ if ( is_a( $value, 'WC_DateTime' ) ) {
292
+ $datetime = $value;
293
+ } elseif ( is_numeric( $value ) ) {
294
+ // Timestamps are handled as UTC timestamps in all cases.
295
+ $datetime = new WC_DateTime( "@{$value}", new \DateTimeZone( 'UTC' ) );
296
+ } else {
297
+ // Strings are defined in local WP timezone. Convert to UTC.
298
+ if ( 1 === preg_match( '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(Z|((-|\+)\d{2}:\d{2}))$/', $value, $date_bits ) ) {
299
+ $offset = ! empty( $date_bits[7] ) ? iso8601_timezone_to_offset( $date_bits[7] ) : wc_timezone_offset();
300
+ $timestamp = gmmktime( $date_bits[4], $date_bits[5], $date_bits[6], $date_bits[2], $date_bits[3], $date_bits[1] ) - $offset;
301
+ } else {
302
+ $timestamp = wc_string_to_timestamp( get_gmt_from_date( gmdate( 'Y-m-d H:i:s', wc_string_to_timestamp( $value ) ) ) );
303
+ }
304
+ $datetime = new WC_DateTime( "@{$timestamp}", new \DateTimeZone( 'UTC' ) );
305
+ }
306
+
307
+ // Set local timezone or offset.
308
+ if ( get_option( 'timezone_string' ) ) {
309
+ $datetime->setTimezone( new \DateTimeZone( wc_timezone_string() ) );
310
+ } else {
311
+ $datetime->set_utc_offset( wc_timezone_offset() );
312
+ }
313
+
314
+ $this->data[ 'date' ] = $datetime;
315
+ } catch ( Exception $e ) {}
316
+
317
+
318
+ }
319
+
320
+ public function set_number( $value, $order = null ) {
321
+ $order = empty( $order ) ? $this->order : $order;
322
+
323
+ if ( is_array( $value ) ) {
324
+ $filtered_value = array_filter( $value );
325
+ }
326
+
327
+ if ( empty( $value ) || ( is_array( $value ) && empty( $filtered_value ) ) ) {
328
+ $document_number = null;
329
+ } elseif ( $value instanceof Document_Number ) {
330
+ // WCPDF 2.0 number data
331
+ $document_number = $value;
332
+ } elseif ( is_array( $value ) ) {
333
+ // WCPDF 2.0 number data as array
334
+ $document_number = new Document_Number( $value, $this->get_number_settings(), $this, $order );
335
+ } else {
336
+ // plain number
337
+ $document_number = new Document_Number( $value, $this->get_number_settings(), $this, $order );
338
+ }
339
+
340
+ $this->data[ 'number' ] = $document_number;
341
+ }
342
+
343
+ /*
344
+ |--------------------------------------------------------------------------
345
+ | Settings getters / outputters
346
+ |--------------------------------------------------------------------------
347
+ */
348
+
349
+ public function get_number_settings() {
350
+ $number_settings = isset($this->settings['number_format'])?$this->settings['number_format']:array();
351
+ return apply_filters( 'wpo_wcpdf_document_number_settings', $number_settings, $this );
352
+ }
353
+
354
+ /**
355
+ * Output template styles
356
+ */
357
+ public function template_styles() {
358
+ $css = apply_filters( 'wpo_wcpdf_template_styles_file', $this->locate_template_file( "style.css" ) );
359
+
360
+ ob_start();
361
+ if (file_exists($css)) {
362
+ include($css);
363
+ }
364
+ $css = ob_get_clean();
365
+ $css = apply_filters( 'wpo_wcpdf_template_styles', $css, $this );
366
+
367
+ echo $css;
368
+ }
369
+
370
+ public function has_header_logo() {
371
+ return !empty( $this->settings['header_logo'] );
372
+ }
373
+
374
+ /**
375
+ * Return logo id
376
+ */
377
+ public function get_header_logo_id() {
378
+ if ( !empty( $this->settings['header_logo'] ) ) {
379
+ return apply_filters( 'wpo_wcpdf_header_logo_id', $this->settings['header_logo'], $this );
380
+ }
381
+ }
382
+
383
+ /**
384
+ * Show logo html
385
+ */
386
+ public function header_logo() {
387
+ if ($this->get_header_logo_id()) {
388
+ $attachment_id = $this->get_header_logo_id();
389
+ $company = $this->get_shop_name();
390
+ if( $attachment_id ) {
391
+ $attachment = wp_get_attachment_image_src( $attachment_id, 'full', false );
392
+
393
+ $attachment_src = $attachment[0];
394
+ $attachment_width = $attachment[1];
395
+ $attachment_height = $attachment[2];
396
+
397
+ $attachment_path = get_attached_file( $attachment_id );
398
+
399
+ if ( apply_filters('wpo_wcpdf_use_path', true) && file_exists($attachment_path) ) {
400
+ $src = $attachment_path;
401
+ } else {
402
+ $src = $attachment_src;
403
+ }
404
+
405
+ printf('<img src="%1$s" width="%2$d" height="%3$d" alt="%4$s" />', $src, $attachment_width, $attachment_height, esc_attr( $company ) );
406
+ }
407
+ }
408
+ }
409
+
410
+ public function get_settings_text( $settings_key, $default = false, $autop = true ) {
411
+ if ( !empty( $this->settings[$settings_key]['default'] ) ) {
412
+ $text = wptexturize( trim( $this->settings[$settings_key]['default'] ) );
413
+ if ($autop === true) {
414
+ $text = wpautop( $text );
415
+ }
416
+ } else {
417
+ $text = $default;
418
+ }
419
+ return apply_filters( "wpo_wcpdf_{$settings_key}", $text, $this );
420
+ }
421
+
422
+ /**
423
+ * Return/Show custom company name or default to blog name
424
+ */
425
+ public function get_shop_name() {
426
+ $default = get_bloginfo( 'name' );
427
+ return $this->get_settings_text( 'shop_name', $default, false );
428
+ }
429
+ public function shop_name() {
430
+ echo $this->get_shop_name();
431
+ }
432
+
433
+ /**
434
+ * Return/Show shop/company address if provided
435
+ */
436
+ public function get_shop_address() {
437
+ return $this->get_settings_text( 'shop_address' );
438
+ }
439
+ public function shop_address() {
440
+ echo $this->get_shop_address();
441
+ }
442
+
443
+ /**
444
+ * Return/Show shop/company footer imprint, copyright etc.
445
+ */
446
+ public function get_footer() {
447
+ return $this->get_settings_text( 'footer' );
448
+ }
449
+ public function footer() {
450
+ echo $this->get_footer();
451
+ }
452
+
453
+ /**
454
+ * Return/Show Extra field 1
455
+ */
456
+ public function get_extra_1() {
457
+ return $this->get_settings_text( 'extra_1' );
458
+
459
+ }
460
+ public function extra_1() {
461
+ echo $this->get_extra_1();
462
+ }
463
+
464
+ /**
465
+ * Return/Show Extra field 2
466
+ */
467
+ public function get_extra_2() {
468
+ return $this->get_settings_text( 'extra_2' );
469
+ }
470
+ public function extra_2() {
471
+ echo $this->get_extra_2();
472
+ }
473
+
474
+ /**
475
+ * Return/Show Extra field 3
476
+ */
477
+ public function get_extra_3() {
478
+ return $this->get_settings_text( 'extra_3' );
479
+ }
480
+ public function extra_3() {
481
+ echo $this->get_extra_3();
482
+ }
483
+
484
+ /*
485
+ |--------------------------------------------------------------------------
486
+ | Output functions
487
+ |--------------------------------------------------------------------------
488
+ */
489
+
490
+ public function get_pdf() {
491
+ do_action( 'wpo_wcpdf_before_pdf', $this->get_type(), $this );
492
+
493
+ $pdf_settings = array(
494
+ 'paper_size' => apply_filters( 'wpo_wcpdf_paper_format', $this->get_setting( 'paper_size', 'A4' ), $this->get_type() ),
495
+ 'paper_orientation' => apply_filters( 'wpo_wcpdf_paper_orientation', 'portrait', $this->get_type() ),
496
+ 'font_subsetting' => $this->get_setting( 'font_subsetting', false ),
497
+ );
498
+ $pdf_maker = wcpdf_get_pdf_maker( $this->get_html(), $pdf_settings );
499
+ $pdf = $pdf_maker->output();
500
+
501
+ do_action( 'wpo_wcpdf_after_pdf', $this->get_type(), $this );
502
+ do_action( 'wpo_wcpdf_pdf_created', $pdf, $this );
503
+
504
+ return $pdf;
505
+ }
506
+
507
+ public function get_html( $args = array() ) {
508
+ do_action( 'wpo_wcpdf_before_html', $this->get_type(), $this );
509
+ $default_args = array (
510
+ 'wrap_html_content' => true,
511
+ );
512
+ $args = $args + $default_args;
513
+
514
+ $html = $this->render_template( $this->locate_template_file( "{$this->type}.php" ) );
515
+ if ($args['wrap_html_content']) {
516
+ $html = $this->wrap_html_content( $html );
517
+ }
518
+
519
+ // clean up special characters
520
+ if ( function_exists('utf8_decode') && function_exists('mb_convert_encoding') ) {
521
+ $html = utf8_decode(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
522
+ }
523
+
524
+ do_action( 'wpo_wcpdf_after_html', $this->get_type(), $this );
525
+
526
+ return apply_filters( 'wpo_wcpdf_get_html', $html, $this );
527
+ }
528
+
529
+ public function output_pdf( $output_mode = 'download' ) {
530
+ $pdf = $this->get_pdf();
531
+ wcpdf_pdf_headers( $this->get_filename(), $output_mode, $pdf );
532
+ echo $pdf;
533
+ die();
534
+ }
535
+
536
+ public function output_html() {
537
+ echo $this->get_html();
538
+ die();
539
+ }
540
+
541
+ public function wrap_html_content( $content ) {
542
+ if ( WPO_WCPDF()->legacy_mode_enabled() ) {
543
+ $GLOBALS['wpo_wcpdf']->export->output_body = $content;
544
+ }
545
+
546
+ $html = $this->render_template( $this->locate_template_file( "html-document-wrapper.php" ), array(
547
+ 'content' => $content,
548
+ )
549
+ );
550
+ return $html;
551
+ }
552
+
553
+ public function get_filename( $context = 'download', $args = array() ) {
554
+ $order_count = isset($args['order_ids']) ? count($args['order_ids']) : 1;
555
+
556
+ $name = $this->get_type();
557
+ if ( get_post_type( $this->order_id ) == 'shop_order_refund' ) {
558
+ $number = $this->order_id;
559
+ } else {
560
+ $number = method_exists( $this->order, 'get_order_number' ) ? $this->order->get_order_number() : '';
561
+ }
562
+
563
+ if ( $order_count == 1 ) {
564
+ $suffix = $number;
565
+ } else {
566
+ $suffix = date('Y-m-d'); // 2020-11-11
567
+ }
568
+
569
+ $filename = $name . '-' . $suffix . '.pdf';
570
+
571
+ // Filter filename
572
+ $order_ids = isset($args['order_ids']) ? $args['order_ids'] : array( $this->order_id );
573
+ $filename = apply_filters( 'wpo_wcpdf_filename', $filename, $this->get_type(), $order_ids, $context );
574
+
575
+ // sanitize filename (after filters to prevent human errors)!
576
+ return sanitize_file_name( $filename );
577
+ }
578
+
579
+ public function get_template_path() {
580
+ return WPO_WCPDF()->settings->get_template_path();
581
+ }
582
+
583
+ public function locate_template_file( $file ) {
584
+ if (empty($file)) {
585
+ $file = $this->type.'.php';
586
+ }
587
+ $path = WPO_WCPDF()->settings->get_template_path( $file );
588
+ $file_path = "{$path}/{$file}";
589
+
590
+ $fallback_file_path = WPO_WCPDF()->plugin_path() . '/templates/Simple/' . $file;
591
+ if ( !file_exists( $file_path ) && file_exists( $fallback_file_path ) ) {
592
+ $file_path = $fallback_file_path;
593
+ }
594
+
595
+ $file_path = apply_filters( 'wpo_wcpdf_template_file', $file_path, $this->type, $this->order );
596
+
597
+ return $file_path;
598
+ }
599
+
600
+ public function render_template( $file, $args = array() ) {
601
+ do_action( 'wpo_wcpdf_process_template', $this->get_type(), $this );
602
+
603
+ if ( ! empty( $args ) && is_array( $args ) ) {
604
+ extract( $args );
605
+ }
606
+ ob_start();
607
+ if (file_exists($file)) {
608
+ include($file);
609
+ }
610
+ return ob_get_clean();
611
+ }
612
+
613
+ /*
614
+ |--------------------------------------------------------------------------
615
+ | Settings helper functions
616
+ |--------------------------------------------------------------------------
617
+ */
618
+
619
+ /**
620
+ * get all emails registered in WooCommerce
621
+ * @param boolean $remove_defaults switch to remove default woocommerce emails
622
+ * @return array $emails list of all email ids/slugs and names
623
+ */
624
+ public function get_wc_emails() {
625
+ // get emails from WooCommerce
626
+ global $woocommerce;
627
+ $mailer = $woocommerce->mailer();
628
+ $wc_emails = $mailer->get_emails();
629
+
630
+ $non_order_emails = array(
631
+ 'customer_note',
632
+ 'customer_reset_password',
633
+ 'customer_new_account'
634
+ );
635
+
636
+ $emails = array();
637
+ foreach ($wc_emails as $class => $email) {
638
+ if ( !in_array( $email->id, $non_order_emails ) ) {
639
+ switch ($email->id) {
640
+ case 'new_order':
641
+ $emails[$email->id] = sprintf('%s (%s)', $email->title, __( 'Admin email', 'woocommerce-pdf-invoices-packing-slips' ) );
642
+ break;
643
+ case 'customer_invoice':
644
+ $emails[$email->id] = sprintf('%s (%s)', $email->title, __( 'Manual email', 'woocommerce-pdf-invoices-packing-slips' ) );
645
+ break;
646
+ default:
647
+ $emails[$email->id] = $email->title;
648
+ break;
649
+ }
650
+ }
651
+ }
652
+
653
+ return apply_filters( 'wpo_wcpdf_wc_emails', $emails );
654
+ }
655
+
656
+ // get list of WooCommerce statuses
657
+ public function get_wc_order_status_list() {
658
+ if ( version_compare( WOOCOMMERCE_VERSION, '2.2', '<' ) ) {
659
+ $statuses = (array) get_terms( 'shop_order_status', array( 'hide_empty' => 0, 'orderby' => 'id' ) );
660
+ foreach ( $statuses as $status ) {
661
+ $order_statuses[esc_attr( $status->slug )] = esc_html__( $status->name, 'woocommerce' );
662
+ }
663
+ } else {
664
+ $statuses = wc_get_order_statuses();
665
+ foreach ( $statuses as $status_slug => $status ) {
666
+ $status_slug = 'wc-' === substr( $status_slug, 0, 3 ) ? substr( $status_slug, 3 ) : $status_slug;
667
+ $order_statuses[$status_slug] = $status;
668
+ }
669
+ }
670
+ return $order_statuses;
671
+ }
672
+
673
+
674
+ }
675
+
676
+ endif; // class_exists
includes/views/wcpdf-settings-page.php CHANGED
@@ -1,50 +1,51 @@
1
- <script type="text/javascript">
2
- jQuery( function( $ ) {
3
- $("#footer-thankyou").html("If you like <strong>WooCommerce PDF Invoices & Packing Slips</strong> please leave us a <a href='https://wordpress.org/support/view/plugin-reviews/woocommerce-pdf-invoices-packing-slips?rate=5#postform'>★★★★★</a> rating. A huge thank you in advance!");
4
- });
5
- </script>
6
- <div class="wrap">
7
- <div class="icon32" id="icon-options-general"><br /></div>
8
- <h2><?php _e( 'WooCommerce PDF Invoices', 'woocommerce-pdf-invoices-packing-slips' ); ?></h2>
9
- <h2 class="nav-tab-wrapper">
10
- <?php
11
- foreach ($settings_tabs as $tab_slug => $tab_title ) {
12
- printf('<a href="?page=wpo_wcpdf_options_page&tab=%1$s" class="nav-tab nav-tab-%1$s %2$s">%3$s</a>', $tab_slug, (($active_tab == $tab_slug) ? 'nav-tab-active' : ''), $tab_title);
13
- }
14
- ?>
15
- </h2>
16
-
17
- <?php
18
- do_action( 'wpo_wcpdf_before_settings_page', $active_tab, $active_section );
19
-
20
- // save or check option to hide extensions ad
21
- if ( isset( $_GET['wpo_wcpdf_hide_extensions_ad'] ) ) {
22
- update_option( 'wpo_wcpdf_hide_extensions_ad', true );
23
- $hide_ad = true;
24
- } else {
25
- $hide_ad = get_option( 'wpo_wcpdf_hide_extensions_ad' );
26
- }
27
-
28
- if ( !$hide_ad && !( class_exists('WooCommerce_PDF_IPS_Pro') && class_exists('WooCommerce_PDF_IPS_Dropbox') && class_exists('WooCommerce_PDF_IPS_Templates') && class_exists('WooCommerce_Ext_PrintOrders') ) ) {
29
- include('wcpdf-extensions.php');
30
- }
31
-
32
- ?>
33
- <form method="post" action="options.php" id="wpo-wcpdf-settings" class="<?php echo "{$active_tab} {$active_section}"; ?>">
34
- <?php
35
- do_action( 'wpo_wcpdf_before_settings', $active_tab, $active_section );
36
- if ( has_action( 'wpo_wcpdf_settings_output_'.$active_tab ) ) {
37
- do_action( 'wpo_wcpdf_settings_output_'.$active_tab, $active_section );
38
- } else {
39
- // legacy settings
40
- settings_fields( "wpo_wcpdf_{$active_tab}_settings" );
41
- do_settings_sections( "wpo_wcpdf_{$active_tab}_settings" );
42
-
43
- submit_button();
44
- }
45
- do_action( 'wpo_wcpdf_after_settings', $active_tab, $active_section );
46
- ?>
47
-
48
- </form>
49
- <?php do_action( 'wpo_wcpdf_after_settings_page', $active_tab, $active_section ); ?>
50
- </div>
 
1
+ <script type="text/javascript">
2
+ jQuery( function( $ ) {
3
+ $("#footer-thankyou").html("If you like <strong>WooCommerce PDF Invoices & Packing Slips</strong> please leave us a <a href='https://wordpress.org/support/view/plugin-reviews/woocommerce-pdf-invoices-packing-slips?rate=5#postform'>★★★★★</a> rating. A huge thank you in advance!");
4
+ });
5
+ </script>
6
+ <div class="wrap">
7
+ <div class="icon32" id="icon-options-general"><br /></div>
8
+ <h2><?php _e( 'WooCommerce PDF Invoices', 'woocommerce-pdf-invoices-packing-slips' ); ?></h2>
9
+ <h2 class="nav-tab-wrapper">
10
+ <?php
11
+ foreach ($settings_tabs as $tab_slug => $tab_title ) {
12
+ $tab_link = esc_url("?page=wpo_wcpdf_options_page&tab={$tab_slug}");
13
+ printf('<a href="%1$s" class="nav-tab nav-tab-%2$s %3$s">%4$s</a>', $tab_link, $tab_slug, (($active_tab == $tab_slug) ? 'nav-tab-active' : ''), $tab_title);
14
+ }
15
+ ?>
16
+ </h2>
17
+
18
+ <?php
19
+ do_action( 'wpo_wcpdf_before_settings_page', $active_tab, $active_section );
20
+
21
+ // save or check option to hide extensions ad
22
+ if ( isset( $_GET['wpo_wcpdf_hide_extensions_ad'] ) ) {
23
+ update_option( 'wpo_wcpdf_hide_extensions_ad', true );
24
+ $hide_ad = true;
25
+ } else {
26
+ $hide_ad = get_option( 'wpo_wcpdf_hide_extensions_ad' );
27
+ }
28
+
29
+ if ( !$hide_ad && !( class_exists('WooCommerce_PDF_IPS_Pro') && class_exists('WooCommerce_PDF_IPS_Dropbox') && class_exists('WooCommerce_PDF_IPS_Templates') && class_exists('WooCommerce_Ext_PrintOrders') ) ) {
30
+ include('wcpdf-extensions.php');
31
+ }
32
+
33
+ ?>
34
+ <form method="post" action="options.php" id="wpo-wcpdf-settings" class="<?php echo "{$active_tab} {$active_section}"; ?>">
35
+ <?php
36
+ do_action( 'wpo_wcpdf_before_settings', $active_tab, $active_section );
37
+ if ( has_action( 'wpo_wcpdf_settings_output_'.$active_tab ) ) {
38
+ do_action( 'wpo_wcpdf_settings_output_'.$active_tab, $active_section );
39
+ } else {
40
+ // legacy settings
41
+ settings_fields( "wpo_wcpdf_{$active_tab}_settings" );
42
+ do_settings_sections( "wpo_wcpdf_{$active_tab}_settings" );
43
+
44
+ submit_button();
45
+ }
46
+ do_action( 'wpo_wcpdf_after_settings', $active_tab, $active_section );
47
+ ?>
48
+
49
+ </form>
50
+ <?php do_action( 'wpo_wcpdf_after_settings_page', $active_tab, $active_section ); ?>
51
+ </div>
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-
4
  Tags: woocommerce, pdf, invoices, packing slips, print, delivery notes, invoice, packing slip, export, email, bulk, automatic
5
  Requires at least: 3.5
6
  Tested up to: 4.8
7
- Stable tag: 2.0.12
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -104,6 +104,12 @@ There's a setting on the Status tab of the settings page that allows you to togg
104
 
105
  **2.0 is a BIG update! Make a full site backup before upgrading**
106
 
 
 
 
 
 
 
107
  = 2.0.12 =
108
  * Option: Use different HTML parser (debug settings)
109
 
@@ -192,5 +198,5 @@ There's a setting on the Status tab of the settings page that allows you to togg
192
 
193
  == Upgrade Notice ==
194
 
195
- = 2.0.12 =
196
  **2.0 is a BIG update! Make a full site backup before upgrading!**
4
  Tags: woocommerce, pdf, invoices, packing slips, print, delivery notes, invoice, packing slip, export, email, bulk, automatic
5
  Requires at least: 3.5
6
  Tested up to: 4.8
7
+ Stable tag: 2.0.13
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
104
 
105
  **2.0 is a BIG update! Make a full site backup before upgrading**
106
 
107
+ = 2.0.13 =
108
+ * Fix: Minor XSS issue on settings screens by escaping and sanitizing 'tab' & 'section' GET variables. Discovered by Detectify.
109
+ * Fix: Pakistani Rupee Symbol
110
+ * Feature: Automatically enable extended currency symbol support for currencies not supported by Open Sans
111
+ * Dev: added `wpo_wcpdf_document_number_settings` filter
112
+
113
  = 2.0.12 =
114
  * Option: Use different HTML parser (debug settings)
115
 
198
 
199
  == Upgrade Notice ==
200
 
201
+ = 2.0.13 =
202
  **2.0 is a BIG update! Make a full site backup before upgrading!**
vendor/dompdf/dompdf/lib/fonts/currencies.ttf CHANGED
Binary file
woocommerce-pdf-invoices-packingslips.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: WooCommerce PDF Invoices & Packing Slips
4
  * Plugin URI: http://www.wpovernight.com
5
  * Description: Create, print & email PDF invoices & packing slips for WooCommerce orders.
6
- * Version: 2.0.12
7
  * Author: Ewout Fernhout
8
  * Author URI: http://www.wpovernight.com
9
  * License: GPLv2 or later
@@ -19,7 +19,7 @@ if ( !class_exists( 'WPO_WCPDF' ) ) :
19
 
20
  class WPO_WCPDF {
21
 
22
- public $version = '2.0.12';
23
  public $plugin_basename;
24
  public $legacy_mode;
25
 
3
  * Plugin Name: WooCommerce PDF Invoices & Packing Slips
4
  * Plugin URI: http://www.wpovernight.com
5
  * Description: Create, print & email PDF invoices & packing slips for WooCommerce orders.
6
+ * Version: 2.0.13
7
  * Author: Ewout Fernhout
8
  * Author URI: http://www.wpovernight.com
9
  * License: GPLv2 or later
19
 
20
  class WPO_WCPDF {
21
 
22
+ public $version = '2.0.13';
23
  public $plugin_basename;
24
  public $legacy_mode;
25