WooCommerce PDF Invoices & Packing Slips - Version 2.0.7

Version Description

  • Feature: Added button to delete legacy settings
  • Feature: Option to enable font subsetting
  • Fix: Invoice number sequence for databases with alternative auto_increment_increment settings
  • Fix: Fallback function for MB String (mb_stripos)
Download this release

Release Info

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

Code changes from version 2.0.6 to 2.0.7

includes/class-wcpdf-pdf-maker.php CHANGED
@@ -1,55 +1,57 @@
1
- <?php
2
- namespace WPO\WC\PDF_Invoices;
3
-
4
- use Dompdf\Dompdf;
5
- use Dompdf\Options;
6
-
7
- if ( ! defined( 'ABSPATH' ) ) {
8
- exit; // Exit if accessed directly
9
- }
10
-
11
- if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\PDF_Maker' ) ) :
12
-
13
- class PDF_Maker {
14
- public $html;
15
- public $settings;
16
-
17
- public function __construct( $html, $settings = array() ) {
18
- $this->html = $html;
19
-
20
- $default_settings = array(
21
- 'paper_size' => 'A4',
22
- 'paper_orientation' => 'portrait',
23
- );
24
- $this->settings = $settings + $default_settings;
25
- }
26
-
27
- public function output() {
28
- if ( empty( $this->html ) ) {
29
- return;
30
- }
31
-
32
- require WPO_WCPDF()->plugin_path() . '/vendor/autoload.php';
33
-
34
- // set options
35
- $options = new Options();
36
- $options->setdefaultFont( 'dejavu sans');
37
- $options->setTempDir( WPO_WCPDF()->main->get_tmp_path('dompdf') );
38
- $options->setLogOutputFile( WPO_WCPDF()->main->get_tmp_path('dompdf') . "/log.htm");
39
- $options->setFontDir( WPO_WCPDF()->main->get_tmp_path('fonts') );
40
- $options->setFontCache( WPO_WCPDF()->main->get_tmp_path('fonts') );
41
- $options->setIsRemoteEnabled( true );
42
-
43
- // instantiate and use the dompdf class
44
- $dompdf = new Dompdf( $options );
45
- $dompdf->loadHtml( $this->html );
46
- $dompdf->setPaper( $this->settings['paper_size'], $this->settings['paper_orientation'] );
47
- $dompdf = apply_filters( 'wpo_wcpdf_before_dompdf_render', $dompdf, $this->html );
48
- $dompdf->render();
49
- $dompdf = apply_filters( 'wpo_wcpdf_after_dompdf_render', $dompdf, $this->html );
50
-
51
- return $dompdf->output();
52
- }
53
- }
54
-
55
- endif; // class_exists
 
 
1
+ <?php
2
+ namespace WPO\WC\PDF_Invoices;
3
+
4
+ use Dompdf\Dompdf;
5
+ use Dompdf\Options;
6
+
7
+ if ( ! defined( 'ABSPATH' ) ) {
8
+ exit; // Exit if accessed directly
9
+ }
10
+
11
+ if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\PDF_Maker' ) ) :
12
+
13
+ class PDF_Maker {
14
+ public $html;
15
+ public $settings;
16
+
17
+ public function __construct( $html, $settings = array() ) {
18
+ $this->html = $html;
19
+
20
+ $default_settings = array(
21
+ 'paper_size' => 'A4',
22
+ 'paper_orientation' => 'portrait',
23
+ 'font_subsetting' => false,
24
+ );
25
+ $this->settings = $settings + $default_settings;
26
+ }
27
+
28
+ public function output() {
29
+ if ( empty( $this->html ) ) {
30
+ return;
31
+ }
32
+
33
+ require WPO_WCPDF()->plugin_path() . '/vendor/autoload.php';
34
+
35
+ // set options
36
+ $options = new Options();
37
+ $options->setdefaultFont( 'dejavu sans');
38
+ $options->setTempDir( WPO_WCPDF()->main->get_tmp_path('dompdf') );
39
+ $options->setLogOutputFile( WPO_WCPDF()->main->get_tmp_path('dompdf') . "/log.htm");
40
+ $options->setFontDir( WPO_WCPDF()->main->get_tmp_path('fonts') );
41
+ $options->setFontCache( WPO_WCPDF()->main->get_tmp_path('fonts') );
42
+ $options->setIsRemoteEnabled( true );
43
+ $options->setIsFontSubsettingEnabled( $this->settings['font_subsetting'] );
44
+
45
+ // instantiate and use the dompdf class
46
+ $dompdf = new Dompdf( $options );
47
+ $dompdf->loadHtml( $this->html );
48
+ $dompdf->setPaper( $this->settings['paper_size'], $this->settings['paper_orientation'] );
49
+ $dompdf = apply_filters( 'wpo_wcpdf_before_dompdf_render', $dompdf, $this->html );
50
+ $dompdf->render();
51
+ $dompdf = apply_filters( 'wpo_wcpdf_after_dompdf_render', $dompdf, $this->html );
52
+
53
+ return $dompdf->output();
54
+ }
55
+ }
56
+
57
+ endif; // class_exists
includes/class-wcpdf-settings-callbacks.php CHANGED
@@ -1,503 +1,504 @@
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_Callbacks' ) ) :
11
-
12
- class Settings_Callbacks {
13
- /**
14
- * Section null callback.
15
- *
16
- * @return void.
17
- */
18
- public function section() {
19
- }
20
-
21
- /**
22
- * Debug section callback.
23
- *
24
- * @return void.
25
- */
26
- public function debug_section() {
27
- _e( '<b>Warning!</b> The settings below are meant for debugging/development only. Do not use them on a live website!' , 'woocommerce-pdf-invoices-packing-slips' );
28
- }
29
-
30
- /**
31
- * Custom fields section callback.
32
- *
33
- * @return void.
34
- */
35
- public function custom_fields_section() {
36
- _e( 'These are used for the (optional) footer columns in the <em>Modern (Premium)</em> template, but can also be used for other elements in your custom template' , 'woocommerce-pdf-invoices-packing-slips' );
37
- }
38
-
39
- /**
40
- * Checkbox callback.
41
- *
42
- * args:
43
- * option_name - name of the main option
44
- * id - key of the setting
45
- * value - value if not 1 (optional)
46
- * default - default setting (optional)
47
- * description - description (optional)
48
- *
49
- * @return void.
50
- */
51
- public function checkbox( $args ) {
52
- extract( $this->normalize_settings_args( $args ) );
53
-
54
- // output checkbox
55
- printf( '<input type="checkbox" id="%1$s" name="%2$s" value="%3$s"%4$s />', $id, $setting_name, $value, checked( $value, $current, false ) );
56
-
57
- // output description.
58
- if ( isset( $description ) ) {
59
- printf( '<p class="description">%s</p>', $description );
60
- }
61
- }
62
-
63
- /**
64
- * Text input callback.
65
- *
66
- * args:
67
- * option_name - name of the main option
68
- * id - key of the setting
69
- * size - size of the text input (em)
70
- * default - default setting (optional)
71
- * description - description (optional)
72
- * type - type (optional)
73
- *
74
- * @return void.
75
- */
76
- public function text_input( $args ) {
77
- extract( $this->normalize_settings_args( $args ) );
78
-
79
- if (empty($type)) {
80
- $type = 'text';
81
- }
82
-
83
- printf( '<input type="%1$s" id="%2$s" name="%3$s" value="%4$s" size="%5$s" placeholder="%6$s"/>', $type, $id, $setting_name, esc_attr( $current ), $size, $placeholder );
84
-
85
- // output description.
86
- if ( isset( $description ) ) {
87
- printf( '<p class="description">%s</p>', $description );
88
- }
89
- }
90
-
91
- // Single text option (not part of any settings array)
92
- public function singular_text_element( $args ) {
93
- $option_name = $args['option_name'];
94
- $id = $args['id'];
95
- $size = isset( $args['size'] ) ? $args['size'] : '25';
96
- $class = isset( $args['translatable'] ) && $args['translatable'] === true ? 'translatable' : '';
97
-
98
- $option = get_option( $option_name );
99
-
100
- if ( isset( $option ) ) {
101
- $current = $option;
102
- } else {
103
- $current = isset( $args['default'] ) ? $args['default'] : '';
104
- }
105
-
106
- $html = sprintf( '<input type="text" id="%1$s" name="%2$s" value="%3$s" size="%4$s" class="%5$s"/>', $id, $option_name, $current, $size, $class );
107
-
108
- // Displays option description.
109
- if ( isset( $args['description'] ) ) {
110
- $html .= sprintf( '<p class="description">%s</p>', $args['description'] );
111
- }
112
-
113
- echo $html;
114
- }
115
-
116
-
117
- /**
118
- * Textarea callback.
119
- *
120
- * args:
121
- * option_name - name of the main option
122
- * id - key of the setting
123
- * width - width of the text input (em)
124
- * height - height of the text input (lines)
125
- * default - default setting (optional)
126
- * description - description (optional)
127
- *
128
- * @return void.
129
- */
130
- public function textarea( $args ) {
131
- extract( $this->normalize_settings_args( $args ) );
132
-
133
- printf( '<textarea id="%1$s" name="%2$s" cols="%4$s" rows="%5$s" placeholder="%6$s"/>%3$s</textarea>', $id, $setting_name, esc_textarea( $current ), $width, $height, $placeholder );
134
-
135
- // output description.
136
- if ( isset( $description ) ) {
137
- printf( '<p class="description">%s</p>', $description );
138
- }
139
- }
140
-
141
- /**
142
- * Select element callback.
143
- *
144
- * @param array $args Field arguments.
145
- *
146
- * @return string Select field.
147
- */
148
- public function select( $args ) {
149
- extract( $this->normalize_settings_args( $args ) );
150
-
151
- printf( '<select id="%1$s" name="%2$s">', $id, $setting_name );
152
-
153
- foreach ( $options as $key => $label ) {
154
- printf( '<option value="%s"%s>%s</option>', $key, selected( $current, $key, false ), $label );
155
- }
156
-
157
- echo '</select>';
158
-
159
- if (isset($custom)) {
160
- printf( '<div class="%1$s_custom custom">', $id );
161
-
162
- if (is_callable( array( $this, $custom['type'] ) ) ) {
163
- $this->{$custom['type']}( $custom['args'] );
164
- }
165
- echo '</div>';
166
- ?>
167
- <script type="text/javascript">
168
- jQuery(document).ready(function($) {
169
- function check_<?php echo $id; ?>_custom() {
170
- var custom = $('#<?php echo $id; ?>').val();
171
- console.log(custom);
172
- if (custom == 'custom') {
173
- $( '.<?php echo $id; ?>_custom').show();
174
- } else {
175
- $( '.<?php echo $id; ?>_custom').hide();
176
- }
177
- }
178
-
179
- check_<?php echo $id; ?>_custom();
180
-
181
- $( '#<?php echo $id; ?>' ).change(function() {
182
- check_<?php echo $id; ?>_custom();
183
- });
184
-
185
- });
186
- </script>
187
- <?php
188
- }
189
-
190
- // Displays option description.
191
- if ( isset( $args['description'] ) ) {
192
- printf( '<p class="description">%s</p>', $args['description'] );
193
- }
194
-
195
- }
196
-
197
- public function radio_button( $args ) {
198
- extract( $this->normalize_settings_args( $args ) );
199
-
200
- foreach ( $options as $key => $label ) {
201
- printf( '<input type="radio" class="radio" id="%1$s[%3$s]" name="%2$s" value="%3$s"%4$s />', $id, $setting_name, $key, checked( $current, $key, false ) );
202
- printf( '<label for="%1$s[%3$s]"> %4$s</label><br>', $id, $setting_name, $key, $label);
203
- }
204
-
205
-
206
- // Displays option description.
207
- if ( isset( $args['description'] ) ) {
208
- printf( '<p class="description">%s</p>', $args['description'] );
209
- }
210
-
211
- }
212
-
213
- /**
214
- * Multiple text element callback.
215
- * @param array $args Field arguments.
216
- * @return string Text input field.
217
- */
218
- public function multiple_text_input( $args ) {
219
- extract( $this->normalize_settings_args( $args ) );
220
-
221
- if (!empty($header)) {
222
- echo "<p><strong>{$header}</strong>:</p>";
223
- }
224
-
225
- printf('<p class="%s multiple-text-input">', $id);
226
- foreach ($fields as $name => $field) {
227
- $size = $field['size'];
228
- $placeholder = isset( $field['placeholder'] ) ? $field['placeholder'] : '';
229
-
230
- if (isset($field['label_width'])) {
231
- $style = sprintf( 'style="display:inline-block; width:%1$s;"', $field['label_width'] );
232
- } else {
233
- $style = '';
234
- }
235
-
236
- $description = isset( $field['description'] ) ? '<span style="font-style:italic;">'.$field['description'].'</span>' : '';
237
-
238
- // output field label
239
- if (isset($field['label'])) {
240
- printf( '<label for="%1$s_%2$s" %3$s>%4$s:</label>', $id, $name, $style, $field['label'] );
241
- }
242
-
243
- // output field
244
- $field_current = isset($current[$name]) ? $current[$name] : '';
245
- $type = isset( $field['type'] ) ? $field['type'] : 'text';
246
- printf( '<input type="%1$s" id="%2$s_%4$s" name="%3$s[%4$s]" value="%5$s" size="%6$s" placeholder="%7$s"/> %8$s<br/>', $type, $id, $setting_name, $name, esc_attr( $field_current ), $size, $placeholder, $description );
247
- }
248
- echo "</p>";
249
-
250
- // Displays option description.
251
- if ( isset( $args['description'] ) ) {
252
- printf( '<p class="description">%s</p>', $args['description'] );
253
- }
254
- }
255
-
256
- /**
257
- * Multiple text element callback.
258
- * @param array $args Field arguments.
259
- * @return string Text input field.
260
- */
261
- public function multiple_checkboxes( $args ) {
262
- extract( $this->normalize_settings_args( $args ) );
263
-
264
- foreach ($fields as $name => $label) {
265
- // $label = $field['label'];
266
-
267
- // output checkbox
268
- $field_current = isset($current[$name]) ? $current[$name] : '';
269
- printf( '<input type="checkbox" id="%1$s_%3$s" name="%2$s[%3$s]" value="%4$s"%5$s />', $id, $setting_name, $name, $value, checked( $value, $field_current, false ) );
270
-
271
- // output field label
272
- printf( '<label for="%1$s_%2$s">%3$s</label><br>', $id, $name, $label );
273
-
274
- }
275
-
276
- // Displays option description.
277
- if ( isset( $args['description'] ) ) {
278
- printf( '<p class="description">%s</p>', $args['description'] );
279
- }
280
- }
281
-
282
- /**
283
- * Media upload callback.
284
- *
285
- * @param array $args Field arguments.
286
- *
287
- * @return string Media upload button & preview.
288
- */
289
- public function media_upload( $args ) {
290
- extract( $this->normalize_settings_args( $args ) );
291
-
292
- if( !empty($current) ) {
293
- $attachment = wp_get_attachment_image_src( $current, 'thumbnail', false );
294
-
295
- $attachment_src = $attachment[0];
296
- $attachment_width = $attachment[1];
297
- $attachment_height = $attachment[2];
298
- $attachment_resolution = round($attachment_height/(3/2.54));
299
-
300
- printf('<img src="%1$s" style="display:block" id="img-%4$s"/>', $attachment_src, $attachment_width, $attachment_height, $id );
301
- printf('<div class="attachment-resolution"><p class="description">%s: %sdpi (default height = 3cm)</p></div>', __('Image resolution'), $attachment_resolution );
302
- printf('<span class="button wpo_remove_image_button" data-input_id="%1$s">%2$s</span>', $id, $remove_button_text );
303
- }
304
-
305
- printf( '<input id="%1$s" name="%2$s" type="hidden" value="%3$s" />', $id, $setting_name, $current );
306
-
307
- printf( '<span class="button wpo_upload_image_button %4$s" data-uploader_title="%1$s" data-uploader_button_text="%2$s" data-remove_button_text="%3$s" data-input_id="%4$s">%2$s</span>', $uploader_title, $uploader_button_text, $remove_button_text, $id );
308
-
309
- // Displays option description.
310
- if ( isset( $description ) ) {
311
- printf( '<p class="description">%s</p>', $description );
312
- }
313
- }
314
-
315
- /**
316
- * Next document number edit callback.
317
- *
318
- * @param array $args Field arguments.
319
- */
320
- public function next_number_edit( $args ) {
321
- extract( $args );
322
- $number_store = new Sequential_Number_Store( $store );
323
- $next_number = $number_store->get_next();
324
- $nonce = wp_create_nonce( "wpo_wcpdf_next_{$store}" );
325
- printf( '<input id="next_%1$s" class="next-number-input" type="text" size="%2$s" value="%3$s" disabled="disabled" data-store="%1$s" data-nonce="%4$s"/> <span class="edit-next-number dashicons dashicons-edit"></span><span class="save-next-number button secondary" style="display:none;">%5$s</span>', $store, $size, $next_number, $nonce, __( 'Save', 'woocommerce-pdf-invoices-packing-slips' ) );
326
- // Displays option description.
327
- if ( isset( $description ) ) {
328
- printf( '<p class="description">%s</p>', $description );
329
- }
330
- }
331
-
332
- /**
333
- * Wrapper function to create tabs for settings in different languages
334
- * @param [type] $args [description]
335
- * @param [type] $callback [description]
336
- * @return [type] [description]
337
- */
338
- public function i18n_wrap ( $args ) {
339
- extract( $this->normalize_settings_args( $args ) );
340
-
341
- if ( $languages = $this->get_languages() ) {
342
- printf( '<div id="%s-%s-translations" class="translations">', $option_name, $id)
343
- ?>
344
- <ul>
345
- <?php foreach ( $languages as $lang_code => $language_name ) {
346
- $translation_id = "{$option_name}_{$id}_{$lang_code}";
347
- printf('<li><a href="#%s">%s</a></li>', $translation_id, $language_name );
348
- }
349
- ?>
350
- </ul>
351
- <?php foreach ( $languages as $lang_code => $language_name ) {
352
- $translation_id = "{$option_name}_{$id}_{$lang_code}";
353
- printf( '<div id="%s">', $translation_id );
354
- $args['lang'] = $lang_code;
355
- // don't use internationalized placeholders since they're not translated,
356
- // to avoid confusion (user thinking they're all the same)
357
- if ( $callback == 'multiple_text_input' ) {
358
- foreach ($fields as $key => $field_args) {
359
- if (!empty($field_args['placeholder']) && isset($field_args['i18n_placeholder'])) {
360
- $args['fields'][$key]['placeholder'] = '';
361
- }
362
- }
363
- } else {
364
- if (!empty($args['placeholder']) && isset($args['i18n_placeholder'])) {
365
- $args['placeholder'] = '';
366
- }
367
- }
368
- // specific description for internationalized fields (to compensate for missing placeholder)
369
- if (!empty($args['i18n_description'])) {
370
- $args['description'] = $args['i18n_description'];
371
- }
372
- call_user_func( array( $this, $callback ), $args );
373
- echo '</div>';
374
- }
375
- ?>
376
-
377
- </div>
378
- <?php
379
- } else {
380
- $args['lang'] = 'default';
381
- call_user_func( array( $this, $callback ), $args );
382
- }
383
- }
384
-
385
- public function get_languages () {
386
- $multilingual = function_exists('icl_get_languages');
387
- // $multilingual = true; // for development
388
-
389
- if ($multilingual) {
390
- // use this instead of function call for development outside of WPML
391
- // $icl_get_languages = 'a:3:{s:2:"en";a:8:{s:2:"id";s:1:"1";s:6:"active";s:1:"1";s:11:"native_name";s:7:"English";s:7:"missing";s:1:"0";s:15:"translated_name";s:7:"English";s:13:"language_code";s:2:"en";s:16:"country_flag_url";s:43:"http://yourdomain/wpmlpath/res/flags/en.png";s:3:"url";s:23:"http://yourdomain/about";}s:2:"fr";a:8:{s:2:"id";s:1:"4";s:6:"active";s:1:"0";s:11:"native_name";s:9:"Français";s:7:"missing";s:1:"0";s:15:"translated_name";s:6:"French";s:13:"language_code";s:2:"fr";s:16:"country_flag_url";s:43:"http://yourdomain/wpmlpath/res/flags/fr.png";s:3:"url";s:29:"http://yourdomain/fr/a-propos";}s:2:"it";a:8:{s:2:"id";s:2:"27";s:6:"active";s:1:"0";s:11:"native_name";s:8:"Italiano";s:7:"missing";s:1:"0";s:15:"translated_name";s:7:"Italian";s:13:"language_code";s:2:"it";s:16:"country_flag_url";s:43:"http://yourdomain/wpmlpath/res/flags/it.png";s:3:"url";s:26:"http://yourdomain/it/circa";}}';
392
- // $icl_get_languages = unserialize($icl_get_languages);
393
-
394
- $icl_get_languages = icl_get_languages('skip_missing=0');
395
- $languages = array();
396
- foreach ($icl_get_languages as $lang => $data) {
397
- $languages[$data['language_code']] = $data['native_name'];
398
- }
399
- } else {
400
- return false;
401
- }
402
-
403
- return $languages;
404
- }
405
-
406
- public function normalize_settings_args ( $args ) {
407
- $args['value'] = isset( $args['value'] ) ? $args['value'] : 1;
408
-
409
- $args['placeholder'] = isset( $args['placeholder'] ) ? $args['placeholder'] : '';
410
-
411
- // get main settings array
412
- $option = get_option( $args['option_name'] );
413
-
414
- $args['setting_name'] = "{$args['option_name']}[{$args['id']}]";
415
-
416
- if ( !isset($args['lang']) && !empty($args['translatable']) ) {
417
- $args['lang'] = 'default';
418
- }
419
-
420
- if (isset($args['lang'])) {
421
- // i18n settings name
422
- $args['setting_name'] = "{$args['setting_name']}[{$args['lang']}]";
423
- // copy current option value if set
424
-
425
- if ( $args['lang'] == 'default' && !empty($option[$args['id']]) && !isset( $option[$args['id']]['default'] ) ) {
426
- // we're switching back from WPML to normal
427
- // try english first
428
- if ( isset( $option[$args['id']]['en'] ) ) {
429
- $args['current'] = $option[$args['id']]['en'];
430
- } elseif ( is_array( $option[$args['id']] ) ) {
431
- // fallback to the first language if english not found
432
- $first = array_shift($option[$args['id']]);
433
- if (!empty($first)) {
434
- $args['current'] = $first;
435
- }
436
- } elseif ( is_string( $option[$args['id']] ) ) {
437
- $args['current'] = $option[$args['id']];
438
- } else {
439
- // nothing, really?
440
- $args['current'] = '';
441
- }
442
- } else {
443
- if ( isset( $option[$args['id']][$args['lang']] ) ) {
444
- $args['current'] = $option[$args['id']][$args['lang']];
445
- } elseif (isset( $option[$args['id']]['default'] )) {
446
- $args['current'] = $option[$args['id']]['default'];
447
- }
448
- }
449
- } else {
450
- // copy current option value if set
451
- if ( isset( $option[$args['id']] ) ) {
452
- $args['current'] = $option[$args['id']];
453
- }
454
- }
455
-
456
- // falback to default or empty if no value in option
457
- if ( !isset($args['current']) ) {
458
- $args['current'] = isset( $args['default'] ) ? $args['default'] : '';
459
- }
460
-
461
- return $args;
462
- }
463
-
464
- /**
465
- * Validate options.
466
- *
467
- * @param array $input options to valid.
468
- *
469
- * @return array validated options.
470
- */
471
- public function validate( $input ) {
472
- // echo '<pre>';var_dump($input);die('</pre>');
473
- // Create our array for storing the validated options.
474
- $output = array();
475
-
476
- if (empty($input) || !is_array($input)) {
477
- return $input;
478
- }
479
-
480
- // Loop through each of the incoming options.
481
- foreach ( $input as $key => $value ) {
482
-
483
- // Check to see if the current option has a value. If so, process it.
484
- if ( isset( $input[$key] ) ) {
485
- if ( is_array( $input[$key] ) ) {
486
- foreach ( $input[$key] as $sub_key => $sub_value ) {
487
- $output[$key][$sub_key] = $input[$key][$sub_key];
488
- }
489
- } else {
490
- $output[$key] = $input[$key];
491
- }
492
- }
493
- }
494
-
495
- // Return the array processing any additional functions filtered by this action.
496
- return apply_filters( 'wpo_wcpdf_validate_input', $output, $input );
497
- }
498
- }
499
-
500
-
501
- endif; // class_exists
502
-
 
503
  return new Settings_Callbacks();
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_Callbacks' ) ) :
11
+
12
+ class Settings_Callbacks {
13
+ /**
14
+ * Section null callback.
15
+ *
16
+ * @return void.
17
+ */
18
+ public function section() {
19
+ }
20
+
21
+ /**
22
+ * Debug section callback.
23
+ *
24
+ * @return void.
25
+ */
26
+ public function debug_section() {
27
+ _e( '<b>Warning!</b> The settings below are meant for debugging/development only. Do not use them on a live website!' , 'woocommerce-pdf-invoices-packing-slips' );
28
+ }
29
+
30
+ /**
31
+ * Custom fields section callback.
32
+ *
33
+ * @return void.
34
+ */
35
+ public function custom_fields_section() {
36
+ _e( 'These are used for the (optional) footer columns in the <em>Modern (Premium)</em> template, but can also be used for other elements in your custom template' , 'woocommerce-pdf-invoices-packing-slips' );
37
+ }
38
+
39
+ /**
40
+ * Checkbox callback.
41
+ *
42
+ * args:
43
+ * option_name - name of the main option
44
+ * id - key of the setting
45
+ * value - value if not 1 (optional)
46
+ * default - default setting (optional)
47
+ * description - description (optional)
48
+ *
49
+ * @return void.
50
+ */
51
+ public function checkbox( $args ) {
52
+ extract( $this->normalize_settings_args( $args ) );
53
+
54
+ // output checkbox
55
+ printf( '<input type="checkbox" id="%1$s" name="%2$s" value="%3$s"%4$s />', $id, $setting_name, $value, checked( $value, $current, false ) );
56
+
57
+ // output description.
58
+ if ( isset( $description ) ) {
59
+ printf( '<p class="description">%s</p>', $description );
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Text input callback.
65
+ *
66
+ * args:
67
+ * option_name - name of the main option
68
+ * id - key of the setting
69
+ * size - size of the text input (em)
70
+ * default - default setting (optional)
71
+ * description - description (optional)
72
+ * type - type (optional)
73
+ *
74
+ * @return void.
75
+ */
76
+ public function text_input( $args ) {
77
+ extract( $this->normalize_settings_args( $args ) );
78
+
79
+ if (empty($type)) {
80
+ $type = 'text';
81
+ }
82
+
83
+ printf( '<input type="%1$s" id="%2$s" name="%3$s" value="%4$s" size="%5$s" placeholder="%6$s"/>', $type, $id, $setting_name, esc_attr( $current ), $size, $placeholder );
84
+
85
+ // output description.
86
+ if ( isset( $description ) ) {
87
+ printf( '<p class="description">%s</p>', $description );
88
+ }
89
+ }
90
+
91
+ // Single text option (not part of any settings array)
92
+ public function singular_text_element( $args ) {
93
+ $option_name = $args['option_name'];
94
+ $id = $args['id'];
95
+ $size = isset( $args['size'] ) ? $args['size'] : '25';
96
+ $class = isset( $args['translatable'] ) && $args['translatable'] === true ? 'translatable' : '';
97
+
98
+ $option = get_option( $option_name );
99
+
100
+ if ( isset( $option ) ) {
101
+ $current = $option;
102
+ } else {
103
+ $current = isset( $args['default'] ) ? $args['default'] : '';
104
+ }
105
+
106
+ $html = sprintf( '<input type="text" id="%1$s" name="%2$s" value="%3$s" size="%4$s" class="%5$s"/>', $id, $option_name, $current, $size, $class );
107
+
108
+ // Displays option description.
109
+ if ( isset( $args['description'] ) ) {
110
+ $html .= sprintf( '<p class="description">%s</p>', $args['description'] );
111
+ }
112
+
113
+ echo $html;
114
+ }
115
+
116
+
117
+ /**
118
+ * Textarea callback.
119
+ *
120
+ * args:
121
+ * option_name - name of the main option
122
+ * id - key of the setting
123
+ * width - width of the text input (em)
124
+ * height - height of the text input (lines)
125
+ * default - default setting (optional)
126
+ * description - description (optional)
127
+ *
128
+ * @return void.
129
+ */
130
+ public function textarea( $args ) {
131
+ extract( $this->normalize_settings_args( $args ) );
132
+
133
+ printf( '<textarea id="%1$s" name="%2$s" cols="%4$s" rows="%5$s" placeholder="%6$s"/>%3$s</textarea>', $id, $setting_name, esc_textarea( $current ), $width, $height, $placeholder );
134
+
135
+ // output description.
136
+ if ( isset( $description ) ) {
137
+ printf( '<p class="description">%s</p>', $description );
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Select element callback.
143
+ *
144
+ * @param array $args Field arguments.
145
+ *
146
+ * @return string Select field.
147
+ */
148
+ public function select( $args ) {
149
+ extract( $this->normalize_settings_args( $args ) );
150
+
151
+ printf( '<select id="%1$s" name="%2$s">', $id, $setting_name );
152
+
153
+ foreach ( $options as $key => $label ) {
154
+ printf( '<option value="%s"%s>%s</option>', $key, selected( $current, $key, false ), $label );
155
+ }
156
+
157
+ echo '</select>';
158
+
159
+ if (isset($custom)) {
160
+ printf( '<div class="%1$s_custom custom">', $id );
161
+
162
+ if (is_callable( array( $this, $custom['type'] ) ) ) {
163
+ $this->{$custom['type']}( $custom['args'] );
164
+ }
165
+ echo '</div>';
166
+ ?>
167
+ <script type="text/javascript">
168
+ jQuery(document).ready(function($) {
169
+ function check_<?php echo $id; ?>_custom() {
170
+ var custom = $('#<?php echo $id; ?>').val();
171
+ console.log(custom);
172
+ if (custom == 'custom') {
173
+ $( '.<?php echo $id; ?>_custom').show();
174
+ } else {
175
+ $( '.<?php echo $id; ?>_custom').hide();
176
+ }
177
+ }
178
+
179
+ check_<?php echo $id; ?>_custom();
180
+
181
+ $( '#<?php echo $id; ?>' ).change(function() {
182
+ check_<?php echo $id; ?>_custom();
183
+ });
184
+
185
+ });
186
+ </script>
187
+ <?php
188
+ }
189
+
190
+ // Displays option description.
191
+ if ( isset( $args['description'] ) ) {
192
+ printf( '<p class="description">%s</p>', $args['description'] );
193
+ }
194
+
195
+ }
196
+
197
+ public function radio_button( $args ) {
198
+ extract( $this->normalize_settings_args( $args ) );
199
+
200
+ foreach ( $options as $key => $label ) {
201
+ printf( '<input type="radio" class="radio" id="%1$s[%3$s]" name="%2$s" value="%3$s"%4$s />', $id, $setting_name, $key, checked( $current, $key, false ) );
202
+ printf( '<label for="%1$s[%3$s]"> %4$s</label><br>', $id, $setting_name, $key, $label);
203
+ }
204
+
205
+
206
+ // Displays option description.
207
+ if ( isset( $args['description'] ) ) {
208
+ printf( '<p class="description">%s</p>', $args['description'] );
209
+ }
210
+
211
+ }
212
+
213
+ /**
214
+ * Multiple text element callback.
215
+ * @param array $args Field arguments.
216
+ * @return string Text input field.
217
+ */
218
+ public function multiple_text_input( $args ) {
219
+ extract( $this->normalize_settings_args( $args ) );
220
+
221
+ if (!empty($header)) {
222
+ echo "<p><strong>{$header}</strong>:</p>";
223
+ }
224
+
225
+ printf('<p class="%s multiple-text-input">', $id);
226
+ foreach ($fields as $name => $field) {
227
+ $size = $field['size'];
228
+ $placeholder = isset( $field['placeholder'] ) ? $field['placeholder'] : '';
229
+
230
+ if (isset($field['label_width'])) {
231
+ $style = sprintf( 'style="display:inline-block; width:%1$s;"', $field['label_width'] );
232
+ } else {
233
+ $style = '';
234
+ }
235
+
236
+ $description = isset( $field['description'] ) ? '<span style="font-style:italic;">'.$field['description'].'</span>' : '';
237
+
238
+ // output field label
239
+ if (isset($field['label'])) {
240
+ printf( '<label for="%1$s_%2$s" %3$s>%4$s:</label>', $id, $name, $style, $field['label'] );
241
+ }
242
+
243
+ // output field
244
+ $field_current = isset($current[$name]) ? $current[$name] : '';
245
+ $type = isset( $field['type'] ) ? $field['type'] : 'text';
246
+ printf( '<input type="%1$s" id="%2$s_%4$s" name="%3$s[%4$s]" value="%5$s" size="%6$s" placeholder="%7$s"/> %8$s<br/>', $type, $id, $setting_name, $name, esc_attr( $field_current ), $size, $placeholder, $description );
247
+ }
248
+ echo "</p>";
249
+
250
+ // Displays option description.
251
+ if ( isset( $args['description'] ) ) {
252
+ printf( '<p class="description">%s</p>', $args['description'] );
253
+ }
254
+ }
255
+
256
+ /**
257
+ * Multiple text element callback.
258
+ * @param array $args Field arguments.
259
+ * @return string Text input field.
260
+ */
261
+ public function multiple_checkboxes( $args ) {
262
+ extract( $this->normalize_settings_args( $args ) );
263
+
264
+ foreach ($fields as $name => $label) {
265
+ // $label = $field['label'];
266
+
267
+ // output checkbox
268
+ $field_current = isset($current[$name]) ? $current[$name] : '';
269
+ printf( '<input type="checkbox" id="%1$s_%3$s" name="%2$s[%3$s]" value="%4$s"%5$s />', $id, $setting_name, $name, $value, checked( $value, $field_current, false ) );
270
+
271
+ // output field label
272
+ printf( '<label for="%1$s_%2$s">%3$s</label><br>', $id, $name, $label );
273
+
274
+ }
275
+
276
+ // Displays option description.
277
+ if ( isset( $args['description'] ) ) {
278
+ printf( '<p class="description">%s</p>', $args['description'] );
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Media upload callback.
284
+ *
285
+ * @param array $args Field arguments.
286
+ *
287
+ * @return string Media upload button & preview.
288
+ */
289
+ public function media_upload( $args ) {
290
+ extract( $this->normalize_settings_args( $args ) );
291
+
292
+ if( !empty($current) ) {
293
+ $attachment = wp_get_attachment_image_src( $current, 'thumbnail', false );
294
+
295
+ $attachment_src = $attachment[0];
296
+ $attachment_width = $attachment[1];
297
+ $attachment_height = $attachment[2];
298
+ $attachment_resolution = round($attachment_height/(3/2.54));
299
+
300
+ printf('<img src="%1$s" style="display:block" id="img-%4$s"/>', $attachment_src, $attachment_width, $attachment_height, $id );
301
+ printf('<div class="attachment-resolution"><p class="description">%s: %sdpi (default height = 3cm)</p></div>', __('Image resolution'), $attachment_resolution );
302
+ printf('<span class="button wpo_remove_image_button" data-input_id="%1$s">%2$s</span>', $id, $remove_button_text );
303
+ }
304
+
305
+ printf( '<input id="%1$s" name="%2$s" type="hidden" value="%3$s" />', $id, $setting_name, $current );
306
+
307
+ printf( '<span class="button wpo_upload_image_button %4$s" data-uploader_title="%1$s" data-uploader_button_text="%2$s" data-remove_button_text="%3$s" data-input_id="%4$s">%2$s</span>', $uploader_title, $uploader_button_text, $remove_button_text, $id );
308
+
309
+ // Displays option description.
310
+ if ( isset( $description ) ) {
311
+ printf( '<p class="description">%s</p>', $description );
312
+ }
313
+ }
314
+
315
+ /**
316
+ * Next document number edit callback.
317
+ *
318
+ * @param array $args Field arguments.
319
+ */
320
+ public function next_number_edit( $args ) {
321
+ extract( $args );
322
+ $number_store_method = WPO_WCPDF()->settings->get_sequential_number_store_method();
323
+ $number_store = new Sequential_Number_Store( $store, $number_store_method );
324
+ $next_number = $number_store->get_next();
325
+ $nonce = wp_create_nonce( "wpo_wcpdf_next_{$store}" );
326
+ printf( '<input id="next_%1$s" class="next-number-input" type="text" size="%2$s" value="%3$s" disabled="disabled" data-store="%1$s" data-nonce="%4$s"/> <span class="edit-next-number dashicons dashicons-edit"></span><span class="save-next-number button secondary" style="display:none;">%5$s</span>', $store, $size, $next_number, $nonce, __( 'Save', 'woocommerce-pdf-invoices-packing-slips' ) );
327
+ // Displays option description.
328
+ if ( isset( $description ) ) {
329
+ printf( '<p class="description">%s</p>', $description );
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Wrapper function to create tabs for settings in different languages
335
+ * @param [type] $args [description]
336
+ * @param [type] $callback [description]
337
+ * @return [type] [description]
338
+ */
339
+ public function i18n_wrap ( $args ) {
340
+ extract( $this->normalize_settings_args( $args ) );
341
+
342
+ if ( $languages = $this->get_languages() ) {
343
+ printf( '<div id="%s-%s-translations" class="translations">', $option_name, $id)
344
+ ?>
345
+ <ul>
346
+ <?php foreach ( $languages as $lang_code => $language_name ) {
347
+ $translation_id = "{$option_name}_{$id}_{$lang_code}";
348
+ printf('<li><a href="#%s">%s</a></li>', $translation_id, $language_name );
349
+ }
350
+ ?>
351
+ </ul>
352
+ <?php foreach ( $languages as $lang_code => $language_name ) {
353
+ $translation_id = "{$option_name}_{$id}_{$lang_code}";
354
+ printf( '<div id="%s">', $translation_id );
355
+ $args['lang'] = $lang_code;
356
+ // don't use internationalized placeholders since they're not translated,
357
+ // to avoid confusion (user thinking they're all the same)
358
+ if ( $callback == 'multiple_text_input' ) {
359
+ foreach ($fields as $key => $field_args) {
360
+ if (!empty($field_args['placeholder']) && isset($field_args['i18n_placeholder'])) {
361
+ $args['fields'][$key]['placeholder'] = '';
362
+ }
363
+ }
364
+ } else {
365
+ if (!empty($args['placeholder']) && isset($args['i18n_placeholder'])) {
366
+ $args['placeholder'] = '';
367
+ }
368
+ }
369
+ // specific description for internationalized fields (to compensate for missing placeholder)
370
+ if (!empty($args['i18n_description'])) {
371
+ $args['description'] = $args['i18n_description'];
372
+ }
373
+ call_user_func( array( $this, $callback ), $args );
374
+ echo '</div>';
375
+ }
376
+ ?>
377
+
378
+ </div>
379
+ <?php
380
+ } else {
381
+ $args['lang'] = 'default';
382
+ call_user_func( array( $this, $callback ), $args );
383
+ }
384
+ }
385
+
386
+ public function get_languages () {
387
+ $multilingual = function_exists('icl_get_languages');
388
+ // $multilingual = true; // for development
389
+
390
+ if ($multilingual) {
391
+ // use this instead of function call for development outside of WPML
392
+ // $icl_get_languages = 'a:3:{s:2:"en";a:8:{s:2:"id";s:1:"1";s:6:"active";s:1:"1";s:11:"native_name";s:7:"English";s:7:"missing";s:1:"0";s:15:"translated_name";s:7:"English";s:13:"language_code";s:2:"en";s:16:"country_flag_url";s:43:"http://yourdomain/wpmlpath/res/flags/en.png";s:3:"url";s:23:"http://yourdomain/about";}s:2:"fr";a:8:{s:2:"id";s:1:"4";s:6:"active";s:1:"0";s:11:"native_name";s:9:"Français";s:7:"missing";s:1:"0";s:15:"translated_name";s:6:"French";s:13:"language_code";s:2:"fr";s:16:"country_flag_url";s:43:"http://yourdomain/wpmlpath/res/flags/fr.png";s:3:"url";s:29:"http://yourdomain/fr/a-propos";}s:2:"it";a:8:{s:2:"id";s:2:"27";s:6:"active";s:1:"0";s:11:"native_name";s:8:"Italiano";s:7:"missing";s:1:"0";s:15:"translated_name";s:7:"Italian";s:13:"language_code";s:2:"it";s:16:"country_flag_url";s:43:"http://yourdomain/wpmlpath/res/flags/it.png";s:3:"url";s:26:"http://yourdomain/it/circa";}}';
393
+ // $icl_get_languages = unserialize($icl_get_languages);
394
+
395
+ $icl_get_languages = icl_get_languages('skip_missing=0');
396
+ $languages = array();
397
+ foreach ($icl_get_languages as $lang => $data) {
398
+ $languages[$data['language_code']] = $data['native_name'];
399
+ }
400
+ } else {
401
+ return false;
402
+ }
403
+
404
+ return $languages;
405
+ }
406
+
407
+ public function normalize_settings_args ( $args ) {
408
+ $args['value'] = isset( $args['value'] ) ? $args['value'] : 1;
409
+
410
+ $args['placeholder'] = isset( $args['placeholder'] ) ? $args['placeholder'] : '';
411
+
412
+ // get main settings array
413
+ $option = get_option( $args['option_name'] );
414
+
415
+ $args['setting_name'] = "{$args['option_name']}[{$args['id']}]";
416
+
417
+ if ( !isset($args['lang']) && !empty($args['translatable']) ) {
418
+ $args['lang'] = 'default';
419
+ }
420
+
421
+ if (isset($args['lang'])) {
422
+ // i18n settings name
423
+ $args['setting_name'] = "{$args['setting_name']}[{$args['lang']}]";
424
+ // copy current option value if set
425
+
426
+ if ( $args['lang'] == 'default' && !empty($option[$args['id']]) && !isset( $option[$args['id']]['default'] ) ) {
427
+ // we're switching back from WPML to normal
428
+ // try english first
429
+ if ( isset( $option[$args['id']]['en'] ) ) {
430
+ $args['current'] = $option[$args['id']]['en'];
431
+ } elseif ( is_array( $option[$args['id']] ) ) {
432
+ // fallback to the first language if english not found
433
+ $first = array_shift($option[$args['id']]);
434
+ if (!empty($first)) {
435
+ $args['current'] = $first;
436
+ }
437
+ } elseif ( is_string( $option[$args['id']] ) ) {
438
+ $args['current'] = $option[$args['id']];
439
+ } else {
440
+ // nothing, really?
441
+ $args['current'] = '';
442
+ }
443
+ } else {
444
+ if ( isset( $option[$args['id']][$args['lang']] ) ) {
445
+ $args['current'] = $option[$args['id']][$args['lang']];
446
+ } elseif (isset( $option[$args['id']]['default'] )) {
447
+ $args['current'] = $option[$args['id']]['default'];
448
+ }
449
+ }
450
+ } else {
451
+ // copy current option value if set
452
+ if ( isset( $option[$args['id']] ) ) {
453
+ $args['current'] = $option[$args['id']];
454
+ }
455
+ }
456
+
457
+ // falback to default or empty if no value in option
458
+ if ( !isset($args['current']) ) {
459
+ $args['current'] = isset( $args['default'] ) ? $args['default'] : '';
460
+ }
461
+
462
+ return $args;
463
+ }
464
+
465
+ /**
466
+ * Validate options.
467
+ *
468
+ * @param array $input options to valid.
469
+ *
470
+ * @return array validated options.
471
+ */
472
+ public function validate( $input ) {
473
+ // echo '<pre>';var_dump($input);die('</pre>');
474
+ // Create our array for storing the validated options.
475
+ $output = array();
476
+
477
+ if (empty($input) || !is_array($input)) {
478
+ return $input;
479
+ }
480
+
481
+ // Loop through each of the incoming options.
482
+ foreach ( $input as $key => $value ) {
483
+
484
+ // Check to see if the current option has a value. If so, process it.
485
+ if ( isset( $input[$key] ) ) {
486
+ if ( is_array( $input[$key] ) ) {
487
+ foreach ( $input[$key] as $sub_key => $sub_value ) {
488
+ $output[$key][$sub_key] = $input[$key][$sub_key];
489
+ }
490
+ } else {
491
+ $output[$key] = $input[$key];
492
+ }
493
+ }
494
+ }
495
+
496
+ // Return the array processing any additional functions filtered by this action.
497
+ return apply_filters( 'wpo_wcpdf_validate_input', $output, $input );
498
+ }
499
+ }
500
+
501
+
502
+ endif; // class_exists
503
+
504
  return new Settings_Callbacks();
includes/class-wcpdf-settings-debug.php CHANGED
@@ -1,155 +1,185 @@
1
- <?php
2
- namespace WPO\WC\PDF_Invoices;
3
-
4
- if ( ! defined( 'ABSPATH' ) ) {
5
- exit; // Exit if accessed directly
6
- }
7
-
8
- if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Settings_Debug' ) ) :
9
-
10
- class Settings_Debug {
11
-
12
- function __construct() {
13
- add_action( 'admin_init', array( $this, 'init_settings' ) );
14
- add_action( 'wpo_wcpdf_settings_output_debug', array( $this, 'output' ), 10, 1 );
15
- add_action( 'wpo_wcpdf_after_settings_page', array( $this, 'debug_tools' ), 10, 2 );
16
- }
17
-
18
- public function output( $section ) {
19
- settings_fields( "wpo_wcpdf_settings_debug" );
20
- do_settings_sections( "wpo_wcpdf_settings_debug" );
21
-
22
- submit_button();
23
- }
24
-
25
- public function debug_tools( $tab, $section ) {
26
- if ($tab !== 'debug') {
27
- return;
28
- }
29
- ?>
30
- <form method="post">
31
- <input type="hidden" name="wpo_wcpdf_debug_tools_action" value="install_fonts">
32
- <input type="submit" name="submit" id="submit" class="button" value="<?php _e( 'Reinstall fonts', 'woocommerce-pdf-invoices-packing-slips' ); ?>">
33
- <?php
34
- if (isset($_POST['wpo_wcpdf_debug_tools_action']) && $_POST['wpo_wcpdf_debug_tools_action'] == 'install_fonts') {
35
- $font_path = WPO_WCPDF()->main->get_tmp_path( 'fonts' );
36
-
37
- // clear folder first
38
- if ( function_exists("glob") && $files = glob( $font_path.'/*.*' ) ) {
39
- $exclude_files = array( 'index.php', '.htaccess' );
40
- foreach($files as $file) {
41
- if( is_file($file) && !in_array( basename($file), $exclude_files ) ) {
42
- unlink($file);
43
- }
44
- }
45
- }
46
-
47
- WPO_WCPDF()->main->copy_fonts( $font_path );
48
- printf('<div class="notice notice-success"><p>%s</p></div>', __( 'Fonts reinstalled!', 'woocommerce-pdf-invoices-packing-slips' ) );
49
- }
50
- ?>
51
- </form>
52
- <form method="post">
53
- <input type="hidden" name="wpo_wcpdf_debug_tools_action" value="clear_tmp">
54
- <input type="submit" name="submit" id="submit" class="button" value="<?php _e( 'Remove temporary files', 'woocommerce-pdf-invoices-packing-slips' ); ?>">
55
- <?php
56
- if (isset($_POST['wpo_wcpdf_debug_tools_action']) && $_POST['wpo_wcpdf_debug_tools_action'] == 'clear_tmp') {
57
- $tmp_path = WPO_WCPDF()->main->get_tmp_path('attachments');
58
-
59
- if ( !function_exists("glob") ) {
60
- // glob is disabled
61
- printf('<div class="notice notice-error"><p>%s<br><code>%s</code></p></div>', __( "Unable to read temporary folder contents!", 'woocommerce-pdf-invoices-packing-slips' ), $tmp_path);
62
- } else {
63
- $success = 0;
64
- $error = 0;
65
- if ( $files = glob($tmp_path.'*.pdf') ) { // get all pdf files
66
- foreach($files as $file) {
67
- if(is_file($file)) {
68
- // delete file
69
- if ( unlink($file) === true ) {
70
- $success++;
71
- } else {
72
- $error++;
73
- }
74
- }
75
- }
76
-
77
- if ($error > 0) {
78
- $message = sprintf( __( 'Unable to delete %d files! (deleted %d)', 'woocommerce-pdf-invoices-packing-slips' ), $error, $success);
79
- printf('<div class="notice notice-error"><p>%s</p></div>', $message);
80
- } else {
81
- $message = sprintf( __( 'Successfully deleted %d files!', 'woocommerce-pdf-invoices-packing-slips' ), $success );
82
- printf('<div class="notice notice-success"><p>%s</p></div>', $message);
83
- }
84
- } else {
85
- printf('<div class="notice notice-success"><p>%s</p></div>', __( 'Nothing to delete!', 'woocommerce-pdf-invoices-packing-slips' ) );
86
- }
87
- }
88
- }
89
- ?>
90
- </form>
91
- <?php
92
- include( WPO_WCPDF()->plugin_path() . '/includes/views/dompdf-status.php' );
93
- }
94
-
95
- public function init_settings() {
96
- // Register settings.
97
- $page = $option_group = $option_name = 'wpo_wcpdf_settings_debug';
98
-
99
- $settings_fields = array(
100
- array(
101
- 'type' => 'section',
102
- 'id' => 'debug_settings',
103
- 'title' => __( 'Debug settings', 'woocommerce-pdf-invoices-packing-slips' ),
104
- 'callback' => 'section',
105
- ),
106
- array(
107
- 'type' => 'setting',
108
- 'id' => 'legacy_mode',
109
- 'title' => __( 'Legacy mode', 'woocommerce-pdf-invoices-packing-slips' ),
110
- 'callback' => 'checkbox',
111
- 'section' => 'debug_settings',
112
- 'args' => array(
113
- 'option_name' => $option_name,
114
- 'id' => 'legacy_mode',
115
- 'description' => __( "Legacy mode ensures compatibility with templates and filters from previous versions.", 'woocommerce-pdf-invoices-packing-slips' ),
116
- )
117
- ),
118
- array(
119
- 'type' => 'setting',
120
- 'id' => 'enable_debug',
121
- 'title' => __( 'Enable debug output', 'woocommerce-pdf-invoices-packing-slips' ),
122
- 'callback' => 'checkbox',
123
- 'section' => 'debug_settings',
124
- 'args' => array(
125
- 'option_name' => $option_name,
126
- 'id' => 'enable_debug',
127
- 'description' => __( "Enable this option to output plugin errors if you're getting a blank page or other PDF generation issues", 'woocommerce-pdf-invoices-packing-slips' ) . '<br>' .
128
- __( '<b>Caution!</b> This setting may reveal errors (from other plugins) in other places on your site too, therefor this is not recommended to leave it enabled on live sites.', 'woocommerce-pdf-invoices-packing-slips' ),
129
- )
130
- ),
131
- array(
132
- 'type' => 'setting',
133
- 'id' => 'html_output',
134
- 'title' => __( 'Output to HTML', 'woocommerce-pdf-invoices-packing-slips' ),
135
- 'callback' => 'checkbox',
136
- 'section' => 'debug_settings',
137
- 'args' => array(
138
- 'option_name' => $option_name,
139
- 'id' => 'html_output',
140
- 'description' => __( 'Send the template output as HTML to the browser instead of creating a PDF.', 'woocommerce-pdf-invoices-packing-slips' ),
141
- )
142
- ),
143
- );
144
-
145
- // allow plugins to alter settings fields
146
- $settings_fields = apply_filters( 'wpo_wcpdf_settings_fields_debug', $settings_fields, $page, $option_group, $option_name );
147
- WPO_WCPDF()->settings->add_settings_fields( $settings_fields, $page, $option_group, $option_name );
148
- return;
149
- }
150
-
151
- }
152
-
153
- endif; // class_exists
154
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  return new Settings_Debug();
1
+ <?php
2
+ namespace WPO\WC\PDF_Invoices;
3
+
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit; // Exit if accessed directly
6
+ }
7
+
8
+ if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Settings_Debug' ) ) :
9
+
10
+ class Settings_Debug {
11
+
12
+ function __construct() {
13
+ add_action( 'admin_init', array( $this, 'init_settings' ) );
14
+ add_action( 'wpo_wcpdf_settings_output_debug', array( $this, 'output' ), 10, 1 );
15
+ add_action( 'wpo_wcpdf_after_settings_page', array( $this, 'debug_tools' ), 10, 2 );
16
+ }
17
+
18
+ public function output( $section ) {
19
+ settings_fields( "wpo_wcpdf_settings_debug" );
20
+ do_settings_sections( "wpo_wcpdf_settings_debug" );
21
+
22
+ submit_button();
23
+ }
24
+
25
+ public function debug_tools( $tab, $section ) {
26
+ if ($tab !== 'debug') {
27
+ return;
28
+ }
29
+ ?>
30
+ <form method="post">
31
+ <input type="hidden" name="wpo_wcpdf_debug_tools_action" value="install_fonts">
32
+ <input type="submit" name="submit" id="submit" class="button" value="<?php _e( 'Reinstall fonts', 'woocommerce-pdf-invoices-packing-slips' ); ?>">
33
+ <?php
34
+ if (isset($_POST['wpo_wcpdf_debug_tools_action']) && $_POST['wpo_wcpdf_debug_tools_action'] == 'install_fonts') {
35
+ $font_path = WPO_WCPDF()->main->get_tmp_path( 'fonts' );
36
+
37
+ // clear folder first
38
+ if ( function_exists("glob") && $files = glob( $font_path.'/*.*' ) ) {
39
+ $exclude_files = array( 'index.php', '.htaccess' );
40
+ foreach($files as $file) {
41
+ if( is_file($file) && !in_array( basename($file), $exclude_files ) ) {
42
+ unlink($file);
43
+ }
44
+ }
45
+ }
46
+
47
+ WPO_WCPDF()->main->copy_fonts( $font_path );
48
+ printf('<div class="notice notice-success"><p>%s</p></div>', __( 'Fonts reinstalled!', 'woocommerce-pdf-invoices-packing-slips' ) );
49
+ }
50
+ ?>
51
+ </form>
52
+ <form method="post">
53
+ <input type="hidden" name="wpo_wcpdf_debug_tools_action" value="clear_tmp">
54
+ <input type="submit" name="submit" id="submit" class="button" value="<?php _e( 'Remove temporary files', 'woocommerce-pdf-invoices-packing-slips' ); ?>">
55
+ <?php
56
+ if (isset($_POST['wpo_wcpdf_debug_tools_action']) && $_POST['wpo_wcpdf_debug_tools_action'] == 'clear_tmp') {
57
+ $tmp_path = WPO_WCPDF()->main->get_tmp_path('attachments');
58
+
59
+ if ( !function_exists("glob") ) {
60
+ // glob is disabled
61
+ printf('<div class="notice notice-error"><p>%s<br><code>%s</code></p></div>', __( "Unable to read temporary folder contents!", 'woocommerce-pdf-invoices-packing-slips' ), $tmp_path);
62
+ } else {
63
+ $success = 0;
64
+ $error = 0;
65
+ if ( $files = glob($tmp_path.'*.pdf') ) { // get all pdf files
66
+ foreach($files as $file) {
67
+ if(is_file($file)) {
68
+ // delete file
69
+ if ( unlink($file) === true ) {
70
+ $success++;
71
+ } else {
72
+ $error++;
73
+ }
74
+ }
75
+ }
76
+
77
+ if ($error > 0) {
78
+ $message = sprintf( __( 'Unable to delete %d files! (deleted %d)', 'woocommerce-pdf-invoices-packing-slips' ), $error, $success);
79
+ printf('<div class="notice notice-error"><p>%s</p></div>', $message);
80
+ } else {
81
+ $message = sprintf( __( 'Successfully deleted %d files!', 'woocommerce-pdf-invoices-packing-slips' ), $success );
82
+ printf('<div class="notice notice-success"><p>%s</p></div>', $message);
83
+ }
84
+ } else {
85
+ printf('<div class="notice notice-success"><p>%s</p></div>', __( 'Nothing to delete!', 'woocommerce-pdf-invoices-packing-slips' ) );
86
+ }
87
+ }
88
+ }
89
+ ?>
90
+ </form>
91
+ <form method="post">
92
+ <input type="hidden" name="wpo_wcpdf_debug_tools_action" value="delete_legacy_settings">
93
+ <input type="submit" name="submit" id="submit" class="button" value="<?php _e( 'Delete legacy (1.X) settings', 'woocommerce-pdf-invoices-packing-slips' ); ?>">
94
+ <?php
95
+ if (isset($_POST['wpo_wcpdf_debug_tools_action']) && $_POST['wpo_wcpdf_debug_tools_action'] == 'delete_legacy_settings') {
96
+ // delete options
97
+ delete_option( 'wpo_wcpdf_general_settings' );
98
+ delete_option( 'wpo_wcpdf_template_settings' );
99
+ delete_option( 'wpo_wcpdf_debug_settings' );
100
+ // and delete cache of these options, just in case...
101
+ wp_cache_delete( 'wpo_wcpdf_general_settings','options' );
102
+ wp_cache_delete( 'wpo_wcpdf_template_settings','options' );
103
+ wp_cache_delete( 'wpo_wcpdf_debug_settings','options' );
104
+
105
+ printf('<div class="notice notice-success"><p>%s</p></div>', __( 'Legacy settings deleted!', 'woocommerce-pdf-invoices-packing-slips' ) );
106
+ }
107
+ ?>
108
+ </form>
109
+ <?php
110
+ include( WPO_WCPDF()->plugin_path() . '/includes/views/dompdf-status.php' );
111
+ }
112
+
113
+ public function init_settings() {
114
+ // Register settings.
115
+ $page = $option_group = $option_name = 'wpo_wcpdf_settings_debug';
116
+
117
+ $settings_fields = array(
118
+ array(
119
+ 'type' => 'section',
120
+ 'id' => 'debug_settings',
121
+ 'title' => __( 'Debug settings', 'woocommerce-pdf-invoices-packing-slips' ),
122
+ 'callback' => 'section',
123
+ ),
124
+ array(
125
+ 'type' => 'setting',
126
+ 'id' => 'legacy_mode',
127
+ 'title' => __( 'Legacy mode', 'woocommerce-pdf-invoices-packing-slips' ),
128
+ 'callback' => 'checkbox',
129
+ 'section' => 'debug_settings',
130
+ 'args' => array(
131
+ 'option_name' => $option_name,
132
+ 'id' => 'legacy_mode',
133
+ 'description' => __( "Legacy mode ensures compatibility with templates and filters from previous versions.", 'woocommerce-pdf-invoices-packing-slips' ),
134
+ )
135
+ ),
136
+ array(
137
+ 'type' => 'setting',
138
+ 'id' => 'calculate_document_numbers',
139
+ 'title' => __( 'Calculate document numbers (slow)', 'woocommerce-pdf-invoices-packing-slips' ),
140
+ 'callback' => 'checkbox',
141
+ 'section' => 'debug_settings',
142
+ 'args' => array(
143
+ 'option_name' => $option_name,
144
+ 'id' => 'calculate_document_numbers',
145
+ 'description' => __( "Document numbers (such as invoice numbers) are generated using AUTO_INCREMENT by default. Use this setting if your database auto increments with more than 1.", 'woocommerce-pdf-invoices-packing-slips' ),
146
+ )
147
+ ),
148
+ array(
149
+ 'type' => 'setting',
150
+ 'id' => 'enable_debug',
151
+ 'title' => __( 'Enable debug output', 'woocommerce-pdf-invoices-packing-slips' ),
152
+ 'callback' => 'checkbox',
153
+ 'section' => 'debug_settings',
154
+ 'args' => array(
155
+ 'option_name' => $option_name,
156
+ 'id' => 'enable_debug',
157
+ 'description' => __( "Enable this option to output plugin errors if you're getting a blank page or other PDF generation issues", 'woocommerce-pdf-invoices-packing-slips' ) . '<br>' .
158
+ __( '<b>Caution!</b> This setting may reveal errors (from other plugins) in other places on your site too, therefor this is not recommended to leave it enabled on live sites.', 'woocommerce-pdf-invoices-packing-slips' ),
159
+ )
160
+ ),
161
+ array(
162
+ 'type' => 'setting',
163
+ 'id' => 'html_output',
164
+ 'title' => __( 'Output to HTML', 'woocommerce-pdf-invoices-packing-slips' ),
165
+ 'callback' => 'checkbox',
166
+ 'section' => 'debug_settings',
167
+ 'args' => array(
168
+ 'option_name' => $option_name,
169
+ 'id' => 'html_output',
170
+ 'description' => __( 'Send the template output as HTML to the browser instead of creating a PDF.', 'woocommerce-pdf-invoices-packing-slips' ),
171
+ )
172
+ ),
173
+ );
174
+
175
+ // allow plugins to alter settings fields
176
+ $settings_fields = apply_filters( 'wpo_wcpdf_settings_fields_debug', $settings_fields, $page, $option_group, $option_name );
177
+ WPO_WCPDF()->settings->add_settings_fields( $settings_fields, $page, $option_group, $option_name );
178
+ return;
179
+ }
180
+
181
+ }
182
+
183
+ endif; // class_exists
184
+
185
  return new Settings_Debug();
includes/class-wcpdf-settings-general.php CHANGED
@@ -1,258 +1,270 @@
1
- <?php
2
- namespace WPO\WC\PDF_Invoices;
3
-
4
- if ( ! defined( 'ABSPATH' ) ) {
5
- exit; // Exit if accessed directly
6
- }
7
-
8
- if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Settings_General' ) ) :
9
-
10
- class Settings_General {
11
-
12
- function __construct() {
13
- add_action( 'admin_init', array( $this, 'init_settings' ) );
14
- add_action( 'wpo_wcpdf_settings_output_general', array( $this, 'output' ), 10, 1 );
15
- }
16
-
17
- public function output( $section ) {
18
- settings_fields( "wpo_wcpdf_settings_general" );
19
- do_settings_sections( "wpo_wcpdf_settings_general" );
20
-
21
- submit_button();
22
- }
23
-
24
- public function init_settings() {
25
- $page = $option_group = $option_name = 'wpo_wcpdf_settings_general';
26
-
27
- $template_base_path = ( defined( 'WC_TEMPLATE_PATH' ) ? WC_TEMPLATE_PATH : $GLOBALS['woocommerce']->template_url );
28
- $theme_template_path = get_stylesheet_directory() . '/' . $template_base_path;
29
- $wp_content_dir = str_replace( ABSPATH, '', WP_CONTENT_DIR );
30
- $theme_template_path = substr($theme_template_path, strpos($theme_template_path, $wp_content_dir)) . 'pdf/yourtemplate';
31
- $plugin_template_path = "{$wp_content_dir}/plugins/woocommerce-pdf-invoices-packing-slips/templates/Simple";
32
-
33
- $settings_fields = array(
34
- array(
35
- 'type' => 'section',
36
- 'id' => 'general_settings',
37
- 'title' => __( 'General settings', 'woocommerce-pdf-invoices-packing-slips' ),
38
- 'callback' => 'section',
39
- ),
40
- array(
41
- 'type' => 'setting',
42
- 'id' => 'download_display',
43
- 'title' => __( 'How do you want to view the PDF?', 'woocommerce-pdf-invoices-packing-slips' ),
44
- 'callback' => 'select',
45
- 'section' => 'general_settings',
46
- 'args' => array(
47
- 'option_name' => $option_name,
48
- 'id' => 'download_display',
49
- 'options' => array(
50
- 'download' => __( 'Download the PDF' , 'woocommerce-pdf-invoices-packing-slips' ),
51
- 'display' => __( 'Open the PDF in a new browser tab/window' , 'woocommerce-pdf-invoices-packing-slips' ),
52
- ),
53
- )
54
- ),
55
- array(
56
- 'type' => 'setting',
57
- 'id' => 'template_path',
58
- 'title' => __( 'Choose a template', 'woocommerce-pdf-invoices-packing-slips' ),
59
- 'callback' => 'select',
60
- 'section' => 'general_settings',
61
- 'args' => array(
62
- 'option_name' => $option_name,
63
- 'id' => 'template_path',
64
- 'options' => $this->find_templates(),
65
- 'description' => sprintf( __( 'Want to use your own template? Copy all the files from <code>%s</code> to your (child) theme in <code>%s</code> to customize them' , 'woocommerce-pdf-invoices-packing-slips' ), $plugin_template_path, $theme_template_path),
66
- )
67
- ),
68
- array(
69
- 'type' => 'setting',
70
- 'id' => 'paper_size',
71
- 'title' => __( 'Paper size', 'woocommerce-pdf-invoices-packing-slips' ),
72
- 'callback' => 'select',
73
- 'section' => 'general_settings',
74
- 'args' => array(
75
- 'option_name' => $option_name,
76
- 'id' => 'paper_size',
77
- 'options' => apply_filters( 'wpo_wcpdf_template_settings_paper_size', array(
78
- 'a4' => __( 'A4' , 'woocommerce-pdf-invoices-packing-slips' ),
79
- 'letter' => __( 'Letter' , 'woocommerce-pdf-invoices-packing-slips' ),
80
- ) ),
81
- )
82
- ),
83
- array(
84
- 'type' => 'setting',
85
- 'id' => 'currency_font',
86
- 'title' => __( 'Extended currency symbol support', 'woocommerce-pdf-invoices-packing-slips' ),
87
- 'callback' => 'checkbox',
88
- 'section' => 'general_settings',
89
- 'args' => array(
90
- 'option_name' => $option_name,
91
- 'id' => 'currency_font',
92
- 'description' => __( 'Enable this if your currency symbol is not displaying properly' , 'woocommerce-pdf-invoices-packing-slips' ),
93
- )
94
- ),
95
- array(
96
- 'type' => 'setting',
97
- 'id' => 'header_logo',
98
- 'title' => __( 'Shop header/logo', 'woocommerce-pdf-invoices-packing-slips' ),
99
- 'callback' => 'media_upload',
100
- 'section' => 'general_settings',
101
- 'args' => array(
102
- 'option_name' => $option_name,
103
- 'id' => 'header_logo',
104
- 'uploader_title' => __( 'Select or upload your invoice header/logo', 'woocommerce-pdf-invoices-packing-slips' ),
105
- 'uploader_button_text' => __( 'Set image', 'woocommerce-pdf-invoices-packing-slips' ),
106
- 'remove_button_text' => __( 'Remove image', 'woocommerce-pdf-invoices-packing-slips' ),
107
- //'description' => __( '...', 'woocommerce-pdf-invoices-packing-slips' ),
108
- )
109
- ),
110
- array(
111
- 'type' => 'setting',
112
- 'id' => 'shop_name',
113
- 'title' => __( 'Shop Name', 'woocommerce-pdf-invoices-packing-slips' ),
114
- 'callback' => 'text_input',
115
- 'section' => 'general_settings',
116
- 'args' => array(
117
- 'option_name' => $option_name,
118
- 'id' => 'shop_name',
119
- 'size' => '72',
120
- 'translatable' => true,
121
- )
122
- ),
123
- array(
124
- 'type' => 'setting',
125
- 'id' => 'shop_address',
126
- 'title' => __( 'Shop Address', 'woocommerce-pdf-invoices-packing-slips' ),
127
- 'callback' => 'textarea',
128
- 'section' => 'general_settings',
129
- 'args' => array(
130
- 'option_name' => $option_name,
131
- 'id' => 'shop_address',
132
- 'width' => '72',
133
- 'height' => '8',
134
- 'translatable' => true,
135
- //'description' => __( '...', 'woocommerce-pdf-invoices-packing-slips' ),
136
- )
137
- ),
138
- array(
139
- 'type' => 'setting',
140
- 'id' => 'footer',
141
- 'title' => __( 'Footer: terms & conditions, policies, etc.', 'woocommerce-pdf-invoices-packing-slips' ),
142
- 'callback' => 'textarea',
143
- 'section' => 'general_settings',
144
- 'args' => array(
145
- 'option_name' => $option_name,
146
- 'id' => 'footer',
147
- 'width' => '72',
148
- 'height' => '4',
149
- 'translatable' => true,
150
- //'description' => __( '...', 'woocommerce-pdf-invoices-packing-slips' ),
151
- )
152
- ),
153
- array(
154
- 'type' => 'section',
155
- 'id' => 'extra_template_fields',
156
- 'title' => __( 'Extra template fields', 'woocommerce-pdf-invoices-packing-slips' ),
157
- 'callback' => 'custom_fields_section',
158
- ),
159
- array(
160
- 'type' => 'setting',
161
- 'id' => 'extra_1',
162
- 'title' => __( 'Extra field 1', 'woocommerce-pdf-invoices-packing-slips' ),
163
- 'callback' => 'textarea',
164
- 'section' => 'extra_template_fields',
165
- 'args' => array(
166
- 'option_name' => $option_name,
167
- 'id' => 'extra_1',
168
- 'width' => '72',
169
- 'height' => '8',
170
- 'description' => __( 'This is footer column 1 in the <i>Modern (Premium)</i> template', 'woocommerce-pdf-invoices-packing-slips' ),
171
- 'translatable' => true,
172
- )
173
- ),
174
- array(
175
- 'type' => 'setting',
176
- 'id' => 'extra_2',
177
- 'title' => __( 'Extra field 2', 'woocommerce-pdf-invoices-packing-slips' ),
178
- 'callback' => 'textarea',
179
- 'section' => 'extra_template_fields',
180
- 'args' => array(
181
- 'option_name' => $option_name,
182
- 'id' => 'extra_2',
183
- 'width' => '72',
184
- 'height' => '8',
185
- 'description' => __( 'This is footer column 2 in the <i>Modern (Premium)</i> template', 'woocommerce-pdf-invoices-packing-slips' ),
186
- 'translatable' => true,
187
- )
188
- ),
189
- array(
190
- 'type' => 'setting',
191
- 'id' => 'extra_3',
192
- 'title' => __( 'Extra field 3', 'woocommerce-pdf-invoices-packing-slips' ),
193
- 'callback' => 'textarea',
194
- 'section' => 'extra_template_fields',
195
- 'args' => array(
196
- 'option_name' => $option_name,
197
- 'id' => 'extra_3',
198
- 'width' => '72',
199
- 'height' => '8',
200
- 'description' => __( 'This is footer column 3 in the <i>Modern (Premium)</i> template', 'woocommerce-pdf-invoices-packing-slips' ),
201
- 'translatable' => true,
202
- )
203
- ),
204
- );
205
-
206
- // allow plugins to alter settings fields
207
- $settings_fields = apply_filters( 'wpo_wcpdf_settings_fields_general', $settings_fields, $page, $option_group, $option_name );
208
- WPO_WCPDF()->settings->add_settings_fields( $settings_fields, $page, $option_group, $option_name );
209
- return;
210
- }
211
-
212
- /**
213
- * List templates in plugin folder, theme folder & child theme folder
214
- * @return array template path => template name
215
- */
216
- public function find_templates() {
217
- $installed_templates = array();
218
-
219
- // get base paths
220
- $template_base_path = ( defined( 'WC_TEMPLATE_PATH' ) ? WC_TEMPLATE_PATH : $GLOBALS['woocommerce']->template_url );
221
- $template_base_path = untrailingslashit( $template_base_path );
222
- $template_paths = array (
223
- // note the order: child-theme before theme, so that array_unique filters out parent doubles
224
- 'default' => WPO_WCPDF()->plugin_path() . '/templates/',
225
- 'child-theme' => get_stylesheet_directory() . "/{$template_base_path}/pdf/",
226
- 'theme' => get_template_directory() . "/{$template_base_path}/pdf/",
227
- );
228
-
229
- $template_paths = apply_filters( 'wpo_wcpdf_template_paths', $template_paths );
230
-
231
- foreach ($template_paths as $template_source => $template_path) {
232
- $dirs = (array) glob( $template_path . '*' , GLOB_ONLYDIR);
233
-
234
- foreach ($dirs as $dir) {
235
- // we're stripping abspath to make the plugin settings more portable
236
- $forwardslash_abspath = str_replace('\\','/', ABSPATH);
237
- $forwardslash_dir = str_replace('\\','/', $dir);
238
- $installed_templates[ str_replace( $forwardslash_abspath, '', $forwardslash_dir ) ] = basename($dir);
239
- }
240
- }
241
-
242
- // remove parent doubles
243
- $installed_templates = array_unique($installed_templates);
244
-
245
- if (empty($installed_templates)) {
246
- // fallback to Simple template for servers with glob() disabled
247
- $simple_template_path = str_replace( ABSPATH, '', $template_paths['default'] . 'Simple' );
248
- $installed_templates[$simple_template_path] = 'Simple';
249
- }
250
-
251
- return apply_filters( 'wpo_wcpdf_templates', $installed_templates );
252
- }
253
-
254
- }
255
-
256
- endif; // class_exists
257
-
 
 
 
 
 
 
 
 
 
 
 
 
258
  return new Settings_General();
1
+ <?php
2
+ namespace WPO\WC\PDF_Invoices;
3
+
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit; // Exit if accessed directly
6
+ }
7
+
8
+ if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Settings_General' ) ) :
9
+
10
+ class Settings_General {
11
+
12
+ function __construct() {
13
+ add_action( 'admin_init', array( $this, 'init_settings' ) );
14
+ add_action( 'wpo_wcpdf_settings_output_general', array( $this, 'output' ), 10, 1 );
15
+ }
16
+
17
+ public function output( $section ) {
18
+ settings_fields( "wpo_wcpdf_settings_general" );
19
+ do_settings_sections( "wpo_wcpdf_settings_general" );
20
+
21
+ submit_button();
22
+ }
23
+
24
+ public function init_settings() {
25
+ $page = $option_group = $option_name = 'wpo_wcpdf_settings_general';
26
+
27
+ $template_base_path = ( defined( 'WC_TEMPLATE_PATH' ) ? WC_TEMPLATE_PATH : $GLOBALS['woocommerce']->template_url );
28
+ $theme_template_path = get_stylesheet_directory() . '/' . $template_base_path;
29
+ $wp_content_dir = str_replace( ABSPATH, '', WP_CONTENT_DIR );
30
+ $theme_template_path = substr($theme_template_path, strpos($theme_template_path, $wp_content_dir)) . 'pdf/yourtemplate';
31
+ $plugin_template_path = "{$wp_content_dir}/plugins/woocommerce-pdf-invoices-packing-slips/templates/Simple";
32
+
33
+ $settings_fields = array(
34
+ array(
35
+ 'type' => 'section',
36
+ 'id' => 'general_settings',
37
+ 'title' => __( 'General settings', 'woocommerce-pdf-invoices-packing-slips' ),
38
+ 'callback' => 'section',
39
+ ),
40
+ array(
41
+ 'type' => 'setting',
42
+ 'id' => 'download_display',
43
+ 'title' => __( 'How do you want to view the PDF?', 'woocommerce-pdf-invoices-packing-slips' ),
44
+ 'callback' => 'select',
45
+ 'section' => 'general_settings',
46
+ 'args' => array(
47
+ 'option_name' => $option_name,
48
+ 'id' => 'download_display',
49
+ 'options' => array(
50
+ 'download' => __( 'Download the PDF' , 'woocommerce-pdf-invoices-packing-slips' ),
51
+ 'display' => __( 'Open the PDF in a new browser tab/window' , 'woocommerce-pdf-invoices-packing-slips' ),
52
+ ),
53
+ )
54
+ ),
55
+ array(
56
+ 'type' => 'setting',
57
+ 'id' => 'template_path',
58
+ 'title' => __( 'Choose a template', 'woocommerce-pdf-invoices-packing-slips' ),
59
+ 'callback' => 'select',
60
+ 'section' => 'general_settings',
61
+ 'args' => array(
62
+ 'option_name' => $option_name,
63
+ 'id' => 'template_path',
64
+ 'options' => $this->find_templates(),
65
+ 'description' => sprintf( __( 'Want to use your own template? Copy all the files from <code>%s</code> to your (child) theme in <code>%s</code> to customize them' , 'woocommerce-pdf-invoices-packing-slips' ), $plugin_template_path, $theme_template_path),
66
+ )
67
+ ),
68
+ array(
69
+ 'type' => 'setting',
70
+ 'id' => 'paper_size',
71
+ 'title' => __( 'Paper size', 'woocommerce-pdf-invoices-packing-slips' ),
72
+ 'callback' => 'select',
73
+ 'section' => 'general_settings',
74
+ 'args' => array(
75
+ 'option_name' => $option_name,
76
+ 'id' => 'paper_size',
77
+ 'options' => apply_filters( 'wpo_wcpdf_template_settings_paper_size', array(
78
+ 'a4' => __( 'A4' , 'woocommerce-pdf-invoices-packing-slips' ),
79
+ 'letter' => __( 'Letter' , 'woocommerce-pdf-invoices-packing-slips' ),
80
+ ) ),
81
+ )
82
+ ),
83
+ array(
84
+ 'type' => 'setting',
85
+ 'id' => 'currency_font',
86
+ 'title' => __( 'Extended currency symbol support', 'woocommerce-pdf-invoices-packing-slips' ),
87
+ 'callback' => 'checkbox',
88
+ 'section' => 'general_settings',
89
+ 'args' => array(
90
+ 'option_name' => $option_name,
91
+ 'id' => 'currency_font',
92
+ 'description' => __( 'Enable this if your currency symbol is not displaying properly' , 'woocommerce-pdf-invoices-packing-slips' ),
93
+ )
94
+ ),
95
+ array(
96
+ 'type' => 'setting',
97
+ 'id' => 'font_subsetting',
98
+ 'title' => __( 'Enable font subsetting', 'woocommerce-pdf-invoices-packing-slips' ),
99
+ 'callback' => 'checkbox',
100
+ 'section' => 'general_settings',
101
+ 'args' => array(
102
+ 'option_name' => $option_name,
103
+ 'id' => 'font_subsetting',
104
+ 'description' => __( "Font subsetting can reduce file size by only including the characters that are used in the PDF, but limits the ability to edit PDF files later. Recommended if you're using an Asian font." , 'woocommerce-pdf-invoices-packing-slips' ),
105
+ )
106
+ ),
107
+ array(
108
+ 'type' => 'setting',
109
+ 'id' => 'header_logo',
110
+ 'title' => __( 'Shop header/logo', 'woocommerce-pdf-invoices-packing-slips' ),
111
+ 'callback' => 'media_upload',
112
+ 'section' => 'general_settings',
113
+ 'args' => array(
114
+ 'option_name' => $option_name,
115
+ 'id' => 'header_logo',
116
+ 'uploader_title' => __( 'Select or upload your invoice header/logo', 'woocommerce-pdf-invoices-packing-slips' ),
117
+ 'uploader_button_text' => __( 'Set image', 'woocommerce-pdf-invoices-packing-slips' ),
118
+ 'remove_button_text' => __( 'Remove image', 'woocommerce-pdf-invoices-packing-slips' ),
119
+ //'description' => __( '...', 'woocommerce-pdf-invoices-packing-slips' ),
120
+ )
121
+ ),
122
+ array(
123
+ 'type' => 'setting',
124
+ 'id' => 'shop_name',
125
+ 'title' => __( 'Shop Name', 'woocommerce-pdf-invoices-packing-slips' ),
126
+ 'callback' => 'text_input',
127
+ 'section' => 'general_settings',
128
+ 'args' => array(
129
+ 'option_name' => $option_name,
130
+ 'id' => 'shop_name',
131
+ 'size' => '72',
132
+ 'translatable' => true,
133
+ )
134
+ ),
135
+ array(
136
+ 'type' => 'setting',
137
+ 'id' => 'shop_address',
138
+ 'title' => __( 'Shop Address', 'woocommerce-pdf-invoices-packing-slips' ),
139
+ 'callback' => 'textarea',
140
+ 'section' => 'general_settings',
141
+ 'args' => array(
142
+ 'option_name' => $option_name,
143
+ 'id' => 'shop_address',
144
+ 'width' => '72',
145
+ 'height' => '8',
146
+ 'translatable' => true,
147
+ //'description' => __( '...', 'woocommerce-pdf-invoices-packing-slips' ),
148
+ )
149
+ ),
150
+ array(
151
+ 'type' => 'setting',
152
+ 'id' => 'footer',
153
+ 'title' => __( 'Footer: terms & conditions, policies, etc.', 'woocommerce-pdf-invoices-packing-slips' ),
154
+ 'callback' => 'textarea',
155
+ 'section' => 'general_settings',
156
+ 'args' => array(
157
+ 'option_name' => $option_name,
158
+ 'id' => 'footer',
159
+ 'width' => '72',
160
+ 'height' => '4',
161
+ 'translatable' => true,
162
+ //'description' => __( '...', 'woocommerce-pdf-invoices-packing-slips' ),
163
+ )
164
+ ),
165
+ array(
166
+ 'type' => 'section',
167
+ 'id' => 'extra_template_fields',
168
+ 'title' => __( 'Extra template fields', 'woocommerce-pdf-invoices-packing-slips' ),
169
+ 'callback' => 'custom_fields_section',
170
+ ),
171
+ array(
172
+ 'type' => 'setting',
173
+ 'id' => 'extra_1',
174
+ 'title' => __( 'Extra field 1', 'woocommerce-pdf-invoices-packing-slips' ),
175
+ 'callback' => 'textarea',
176
+ 'section' => 'extra_template_fields',
177
+ 'args' => array(
178
+ 'option_name' => $option_name,
179
+ 'id' => 'extra_1',
180
+ 'width' => '72',
181
+ 'height' => '8',
182
+ 'description' => __( 'This is footer column 1 in the <i>Modern (Premium)</i> template', 'woocommerce-pdf-invoices-packing-slips' ),
183
+ 'translatable' => true,
184
+ )
185
+ ),
186
+ array(
187
+ 'type' => 'setting',
188
+ 'id' => 'extra_2',
189
+ 'title' => __( 'Extra field 2', 'woocommerce-pdf-invoices-packing-slips' ),
190
+ 'callback' => 'textarea',
191
+ 'section' => 'extra_template_fields',
192
+ 'args' => array(
193
+ 'option_name' => $option_name,
194
+ 'id' => 'extra_2',
195
+ 'width' => '72',
196
+ 'height' => '8',
197
+ 'description' => __( 'This is footer column 2 in the <i>Modern (Premium)</i> template', 'woocommerce-pdf-invoices-packing-slips' ),
198
+ 'translatable' => true,
199
+ )
200
+ ),
201
+ array(
202
+ 'type' => 'setting',
203
+ 'id' => 'extra_3',
204
+ 'title' => __( 'Extra field 3', 'woocommerce-pdf-invoices-packing-slips' ),
205
+ 'callback' => 'textarea',
206
+ 'section' => 'extra_template_fields',
207
+ 'args' => array(
208
+ 'option_name' => $option_name,
209
+ 'id' => 'extra_3',
210
+ 'width' => '72',
211
+ 'height' => '8',
212
+ 'description' => __( 'This is footer column 3 in the <i>Modern (Premium)</i> template', 'woocommerce-pdf-invoices-packing-slips' ),
213
+ 'translatable' => true,
214
+ )
215
+ ),
216
+ );
217
+
218
+ // allow plugins to alter settings fields
219
+ $settings_fields = apply_filters( 'wpo_wcpdf_settings_fields_general', $settings_fields, $page, $option_group, $option_name );
220
+ WPO_WCPDF()->settings->add_settings_fields( $settings_fields, $page, $option_group, $option_name );
221
+ return;
222
+ }
223
+
224
+ /**
225
+ * List templates in plugin folder, theme folder & child theme folder
226
+ * @return array template path => template name
227
+ */
228
+ public function find_templates() {
229
+ $installed_templates = array();
230
+
231
+ // get base paths
232
+ $template_base_path = ( defined( 'WC_TEMPLATE_PATH' ) ? WC_TEMPLATE_PATH : $GLOBALS['woocommerce']->template_url );
233
+ $template_base_path = untrailingslashit( $template_base_path );
234
+ $template_paths = array (
235
+ // note the order: child-theme before theme, so that array_unique filters out parent doubles
236
+ 'default' => WPO_WCPDF()->plugin_path() . '/templates/',
237
+ 'child-theme' => get_stylesheet_directory() . "/{$template_base_path}/pdf/",
238
+ 'theme' => get_template_directory() . "/{$template_base_path}/pdf/",
239
+ );
240
+
241
+ $template_paths = apply_filters( 'wpo_wcpdf_template_paths', $template_paths );
242
+
243
+ foreach ($template_paths as $template_source => $template_path) {
244
+ $dirs = (array) glob( $template_path . '*' , GLOB_ONLYDIR);
245
+
246
+ foreach ($dirs as $dir) {
247
+ // we're stripping abspath to make the plugin settings more portable
248
+ $forwardslash_abspath = str_replace('\\','/', ABSPATH);
249
+ $forwardslash_dir = str_replace('\\','/', $dir);
250
+ $installed_templates[ str_replace( $forwardslash_abspath, '', $forwardslash_dir ) ] = basename($dir);
251
+ }
252
+ }
253
+
254
+ // remove parent doubles
255
+ $installed_templates = array_unique($installed_templates);
256
+
257
+ if (empty($installed_templates)) {
258
+ // fallback to Simple template for servers with glob() disabled
259
+ $simple_template_path = str_replace( ABSPATH, '', $template_paths['default'] . 'Simple' );
260
+ $installed_templates[$simple_template_path] = 'Simple';
261
+ }
262
+
263
+ return apply_filters( 'wpo_wcpdf_templates', $installed_templates );
264
+ }
265
+
266
+ }
267
+
268
+ endif; // class_exists
269
+
270
  return new Settings_General();
includes/class-wcpdf-settings.php CHANGED
@@ -1,222 +1,249 @@
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
- // AJAX set number store
37
- add_action( 'wp_ajax_wpo_wcpdf_set_next_number', array($this, 'set_number_store' ));
38
- }
39
-
40
- public function menu() {
41
- $parent_slug = 'woocommerce';
42
-
43
- $this->options_page_hook = add_submenu_page(
44
- $parent_slug,
45
- __( 'PDF Invoices', 'woocommerce-pdf-invoices-packing-slips' ),
46
- __( 'PDF Invoices', 'woocommerce-pdf-invoices-packing-slips' ),
47
- 'manage_woocommerce',
48
- 'wpo_wcpdf_options_page',
49
- array( $this, 'settings_page' )
50
- );
51
- }
52
-
53
- /**
54
- * Add settings link to plugins page
55
- */
56
- public function add_settings_link( $links ) {
57
- $action_links = array(
58
- 'settings' => '<a href="admin.php?page=wpo_wcpdf_options_page">'. __( 'Settings', 'woocommerce' ) . '</a>',
59
- );
60
-
61
- return array_merge( $action_links, $links );
62
- }
63
-
64
- /**
65
- * Add various support links to plugin page
66
- * after meta (version, authors, site)
67
- */
68
- public function add_support_links( $links, $file ) {
69
- if ( $file == WPO_WCPDF()->plugin_basename ) {
70
- $row_meta = array(
71
- '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>',
72
- '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>',
73
- );
74
-
75
- return array_merge( $links, $row_meta );
76
- }
77
- return (array) $links;
78
- }
79
-
80
-
81
- public function settings_page() {
82
- $settings_tabs = apply_filters( 'wpo_wcpdf_settings_tabs', array (
83
- 'general' => __('General', 'woocommerce-pdf-invoices-packing-slips' ),
84
- 'documents' => __('Documents', 'woocommerce-pdf-invoices-packing-slips' ),
85
- )
86
- );
87
-
88
- // add status tab last in row
89
- $settings_tabs['debug'] = __('Status', 'woocommerce-pdf-invoices-packing-slips' );
90
-
91
- $active_tab = isset( $_GET[ 'tab' ] ) ? $_GET[ 'tab' ] : 'general';
92
- $active_section = isset( $_GET[ 'section' ] ) ? $_GET[ 'section' ] : '';
93
-
94
- include('views/wcpdf-settings-page.php');
95
- }
96
-
97
- public function add_settings_fields( $settings_fields, $page, $option_group, $option_name ) {
98
- foreach ( $settings_fields as $settings_field ) {
99
- if (!isset($settings_field['callback'])) {
100
- continue;
101
- } elseif ( is_callable( array( $this->callbacks, $settings_field['callback'] ) ) ) {
102
- $callback = array( $this->callbacks, $settings_field['callback'] );
103
- } elseif ( is_callable( $settings_field['callback'] ) ) {
104
- $callback = $settings_field['callback'];
105
- } else {
106
- continue;
107
- }
108
-
109
- if ( $settings_field['type'] == 'section' ) {
110
- add_settings_section(
111
- $settings_field['id'],
112
- $settings_field['title'],
113
- $callback,
114
- $page
115
- );
116
- } else {
117
- add_settings_field(
118
- $settings_field['id'],
119
- $settings_field['title'],
120
- $callback,
121
- $page,
122
- $settings_field['section'],
123
- $settings_field['args']
124
- );
125
- // register option separately for singular options
126
- if (is_string($settings_field['callback']) && $settings_field['callback'] == 'singular_text_element') {
127
- register_setting( $option_group, $settings_field['args']['option_name'], array( $this->callbacks, 'validate' ) );
128
- }
129
- }
130
- }
131
- // $page, $option_group & $option_name are all the same...
132
- register_setting( $option_group, $option_name, array( $this->callbacks, 'validate' ) );
133
- add_filter( 'option_page_capability_'.$page, array( $this, 'settings_capabilities' ) );
134
-
135
- }
136
-
137
- /**
138
- * Set capability for settings page
139
- */
140
- public function settings_capabilities() {
141
- return 'manage_woocommerce';
142
- }
143
-
144
- public function get_common_document_settings() {
145
- $common_settings = array(
146
- 'paper_size' => isset( $this->general_settings['paper_size'] ) ? $this->general_settings['paper_size'] : '',
147
- 'header_logo' => isset( $this->general_settings['header_logo'] ) ? $this->general_settings['header_logo'] : '',
148
- 'shop_name' => isset( $this->general_settings['shop_name'] ) ? $this->general_settings['shop_name'] : '',
149
- 'shop_address' => isset( $this->general_settings['shop_address'] ) ? $this->general_settings['shop_address'] : '',
150
- 'footer' => isset( $this->general_settings['footer'] ) ? $this->general_settings['footer'] : '',
151
- 'extra_1' => isset( $this->general_settings['extra_1'] ) ? $this->general_settings['extra_1'] : '',
152
- 'extra_2' => isset( $this->general_settings['extra_2'] ) ? $this->general_settings['extra_2'] : '',
153
- 'extra_3' => isset( $this->general_settings['extra_3'] ) ? $this->general_settings['extra_3'] : '',
154
- );
155
- return $common_settings;
156
- }
157
-
158
- public function get_document_settings( $document_type ) {
159
- $documents = WPO_WCPDF()->documents->get_documents('all');
160
- foreach ($documents as $document) {
161
- if ( $document->get_type() == $document_type ) {
162
- return $document->settings;
163
- }
164
- }
165
- return false;
166
- }
167
-
168
- public function get_output_format() {
169
- if ( isset( $this->debug_settings['html_output'] ) ) {
170
- $output_format = 'html';
171
- } else {
172
- $output_format = 'pdf';
173
- }
174
- return $output_format;
175
- }
176
-
177
- public function get_output_mode() {
178
- if ( isset( WPO_WCPDF()->settings->general_settings['download_display'] ) ) {
179
- switch ( WPO_WCPDF()->settings->general_settings['download_display'] ) {
180
- case 'display':
181
- $output_mode = 'inline';
182
- break;
183
- case 'download':
184
- default:
185
- $output_mode = 'download';
186
- break;
187
- }
188
- } else {
189
- $output_mode = 'download';
190
- }
191
- return $output_mode;
192
- }
193
-
194
- public function get_template_path( $document_type = NULL ) {
195
- $template_path = isset( $this->general_settings['template_path'] )?$this->general_settings['template_path']:'';
196
- // forward slash for consistency
197
- $template_path = str_replace('\\','/', $template_path);
198
-
199
- // add abspath, checking if it's not already there
200
- $abspath = str_replace('\\','/', ABSPATH);
201
- if ( strpos( $template_path, $abspath ) === false ) {
202
- // add site base path
203
- $template_path = $abspath . $template_path;
204
- }
205
-
206
- return $template_path;
207
- }
208
-
209
- public function set_number_store() {
210
- check_ajax_referer( "wpo_wcpdf_next_{$_POST['store']}", 'security' );
211
- $number = isset( $_POST['number'] ) ? (int) $_POST['number'] : 0;
212
- $number_store = new Sequential_Number_Store( $_POST['store'] );
213
- $number_store->set_next( $number );
214
- echo "next number ({$_POST['store']}) set to {$number}";
215
- die();
216
- }
217
-
218
- }
219
-
220
- endif; // class_exists
221
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  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' ] ) ? $_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 abspath, checking if it's not already there
213
+ $abspath = str_replace('\\','/', ABSPATH);
214
+ if ( strpos( $template_path, $abspath ) === false ) {
215
+ // add site base path
216
+ $template_path = $abspath . $template_path;
217
+ }
218
+
219
+ return $template_path;
220
+ }
221
+
222
+ public function set_number_store() {
223
+ check_ajax_referer( "wpo_wcpdf_next_{$_POST['store']}", 'security' );
224
+ $number = isset( $_POST['number'] ) ? (int) $_POST['number'] : 0;
225
+ $number_store_method = $this->get_sequential_number_store_method();
226
+ $number_store = new Sequential_Number_Store( $_POST['store'], $number_store_method );
227
+ $number_store->set_next( $number );
228
+ echo "next number ({$_POST['store']}) set to {$number}";
229
+ die();
230
+ }
231
+
232
+ public function get_sequential_number_store_method() {
233
+ global $wpdb;
234
+ $method = isset( $this->debug_settings['calculate_document_numbers'] ) ? 'calculate' : 'auto_increment';
235
+
236
+ // safety first - always use calculate when auto_increment_increment is not 1
237
+ $row = $wpdb->get_row("SHOW VARIABLES LIKE 'auto_increment_increment'");
238
+ if ( !empty($row) && !empty($row->Value) && $row->Value != 1 ) {
239
+ $method = 'calculate';
240
+ }
241
+
242
+ return $method;
243
+ }
244
+
245
+ }
246
+
247
+ endif; // class_exists
248
+
249
  return new Settings();
includes/compatibility/mb-string-compatibility.php CHANGED
@@ -1,121 +1,127 @@
1
- <?php
2
- /**
3
- * mb_string compatibility - something's better than nothing!
4
- * Taken from dompdf 0.6
5
- */
6
- if (!extension_loaded('mbstring')) {
7
- if ( ! defined( 'MB_OVERLOAD_MAIL' ) ) define('MB_OVERLOAD_MAIL', 1);
8
- if ( ! defined( 'MB_OVERLOAD_STRING' ) ) define('MB_OVERLOAD_STRING', 2);
9
- if ( ! defined( 'MB_OVERLOAD_REGEX' ) ) define('MB_OVERLOAD_REGEX', 4);
10
- if ( ! defined( 'MB_CASE_UPPER' ) ) define('MB_CASE_UPPER', 0);
11
- if ( ! defined( 'MB_CASE_LOWER' ) ) define('MB_CASE_LOWER', 1);
12
- if ( ! defined( 'MB_CASE_TITLE' ) ) define('MB_CASE_TITLE', 2);
13
-
14
- if (!function_exists('mb_convert_encoding')) {
15
- function mb_convert_encoding($data, $to_encoding, $from_encoding = 'UTF-8') {
16
- if (str_replace('-', '', strtolower($to_encoding)) === 'utf8') {
17
- return utf8_encode($data);
18
- }
19
-
20
- return utf8_decode($data);
21
- }
22
- }
23
-
24
- if (!function_exists('mb_detect_encoding')) {
25
- function mb_detect_encoding($data, $encoding_list = array('iso-8859-1'), $strict = false) {
26
- return 'iso-8859-1';
27
- }
28
- }
29
-
30
- if (!function_exists('mb_detect_order')) {
31
- function mb_detect_order($encoding_list = array('iso-8859-1')) {
32
- return 'iso-8859-1';
33
- }
34
- }
35
-
36
- if (!function_exists('mb_internal_encoding')) {
37
- function mb_internal_encoding($encoding = null) {
38
- if (isset($encoding)) {
39
- return true;
40
- }
41
-
42
- return 'iso-8859-1';
43
- }
44
- }
45
- if (!function_exists('mb_strlen')) {
46
- function mb_strlen($str, $encoding = 'iso-8859-1') {
47
- switch (str_replace('-', '', strtolower($encoding))) {
48
- case "utf8": return strlen(utf8_encode($str));
49
- case "8bit": return strlen($str);
50
- default: return strlen(utf8_decode($str));
51
- }
52
- }
53
- }
54
-
55
- if (!function_exists('mb_strpos')) {
56
- function mb_strpos($haystack, $needle, $offset = 0) {
57
- return strpos($haystack, $needle, $offset);
58
- }
59
- }
60
-
61
- if (!function_exists('mb_strrpos')) {
62
- function mb_strrpos($haystack, $needle, $offset = 0) {
63
- return strrpos($haystack, $needle, $offset);
64
- }
65
- }
66
-
67
- if (!function_exists('mb_strtolower')) {
68
- function mb_strtolower( $str ) {
69
- return strtolower($str);
70
- }
71
- }
72
-
73
- if (!function_exists('mb_strtoupper')) {
74
- function mb_strtoupper( $str ) {
75
- return strtoupper($str);
76
- }
77
- }
78
-
79
- if (!function_exists('mb_substr')) {
80
- function mb_substr($string, $start, $length = null, $encoding = 'iso-8859-1') {
81
- if ( is_null($length) ) {
82
- return substr($string, $start);
83
- }
84
-
85
- return substr($string, $start, $length);
86
- }
87
- }
88
-
89
- if (!function_exists('mb_substr_count')) {
90
- function mb_substr_count($haystack, $needle, $encoding = 'iso-8859-1') {
91
- return substr_count($haystack, $needle);
92
- }
93
- }
94
-
95
- if (!function_exists('mb_encode_numericentity')) {
96
- function mb_encode_numericentity($str, $convmap, $encoding) {
97
- return htmlspecialchars($str);
98
- }
99
- }
100
-
101
- if (!function_exists('mb_convert_case')) {
102
- function mb_convert_case($str, $mode = MB_CASE_UPPER, $encoding = array()) {
103
- switch($mode) {
104
- case MB_CASE_UPPER: return mb_strtoupper($str);
105
- case MB_CASE_LOWER: return mb_strtolower($str);
106
- case MB_CASE_TITLE: return ucwords(mb_strtolower($str));
107
- default: return $str;
108
- }
109
- }
110
- }
111
-
112
- if (!function_exists('mb_list_encodings')) {
113
- function mb_list_encodings() {
114
- return array(
115
- "ISO-8859-1",
116
- "UTF-8",
117
- "8bit",
118
- );
119
- }
120
- }
121
- }
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * mb_string compatibility - something's better than nothing!
4
+ * Taken from dompdf 0.6
5
+ */
6
+ if (!extension_loaded('mbstring')) {
7
+ if ( ! defined( 'MB_OVERLOAD_MAIL' ) ) define('MB_OVERLOAD_MAIL', 1);
8
+ if ( ! defined( 'MB_OVERLOAD_STRING' ) ) define('MB_OVERLOAD_STRING', 2);
9
+ if ( ! defined( 'MB_OVERLOAD_REGEX' ) ) define('MB_OVERLOAD_REGEX', 4);
10
+ if ( ! defined( 'MB_CASE_UPPER' ) ) define('MB_CASE_UPPER', 0);
11
+ if ( ! defined( 'MB_CASE_LOWER' ) ) define('MB_CASE_LOWER', 1);
12
+ if ( ! defined( 'MB_CASE_TITLE' ) ) define('MB_CASE_TITLE', 2);
13
+
14
+ if (!function_exists('mb_convert_encoding')) {
15
+ function mb_convert_encoding($data, $to_encoding, $from_encoding = 'UTF-8') {
16
+ if (str_replace('-', '', strtolower($to_encoding)) === 'utf8') {
17
+ return utf8_encode($data);
18
+ }
19
+
20
+ return utf8_decode($data);
21
+ }
22
+ }
23
+
24
+ if (!function_exists('mb_detect_encoding')) {
25
+ function mb_detect_encoding($data, $encoding_list = array('iso-8859-1'), $strict = false) {
26
+ return 'iso-8859-1';
27
+ }
28
+ }
29
+
30
+ if (!function_exists('mb_detect_order')) {
31
+ function mb_detect_order($encoding_list = array('iso-8859-1')) {
32
+ return 'iso-8859-1';
33
+ }
34
+ }
35
+
36
+ if (!function_exists('mb_internal_encoding')) {
37
+ function mb_internal_encoding($encoding = null) {
38
+ if (isset($encoding)) {
39
+ return true;
40
+ }
41
+
42
+ return 'iso-8859-1';
43
+ }
44
+ }
45
+ if (!function_exists('mb_strlen')) {
46
+ function mb_strlen($str, $encoding = 'iso-8859-1') {
47
+ switch (str_replace('-', '', strtolower($encoding))) {
48
+ case "utf8": return strlen(utf8_encode($str));
49
+ case "8bit": return strlen($str);
50
+ default: return strlen(utf8_decode($str));
51
+ }
52
+ }
53
+ }
54
+
55
+ if (!function_exists('mb_strpos')) {
56
+ function mb_strpos($haystack, $needle, $offset = 0) {
57
+ return strpos($haystack, $needle, $offset);
58
+ }
59
+ }
60
+
61
+ if (!function_exists('mb_stripos')) {
62
+ function mb_stripos($haystack, $needle, $offset = 0) {
63
+ return stripos($haystack, $needle, $offset);
64
+ }
65
+ }
66
+
67
+ if (!function_exists('mb_strrpos')) {
68
+ function mb_strrpos($haystack, $needle, $offset = 0) {
69
+ return strrpos($haystack, $needle, $offset);
70
+ }
71
+ }
72
+
73
+ if (!function_exists('mb_strtolower')) {
74
+ function mb_strtolower( $str ) {
75
+ return strtolower($str);
76
+ }
77
+ }
78
+
79
+ if (!function_exists('mb_strtoupper')) {
80
+ function mb_strtoupper( $str ) {
81
+ return strtoupper($str);
82
+ }
83
+ }
84
+
85
+ if (!function_exists('mb_substr')) {
86
+ function mb_substr($string, $start, $length = null, $encoding = 'iso-8859-1') {
87
+ if ( is_null($length) ) {
88
+ return substr($string, $start);
89
+ }
90
+
91
+ return substr($string, $start, $length);
92
+ }
93
+ }
94
+
95
+ if (!function_exists('mb_substr_count')) {
96
+ function mb_substr_count($haystack, $needle, $encoding = 'iso-8859-1') {
97
+ return substr_count($haystack, $needle);
98
+ }
99
+ }
100
+
101
+ if (!function_exists('mb_encode_numericentity')) {
102
+ function mb_encode_numericentity($str, $convmap, $encoding) {
103
+ return htmlspecialchars($str);
104
+ }
105
+ }
106
+
107
+ if (!function_exists('mb_convert_case')) {
108
+ function mb_convert_case($str, $mode = MB_CASE_UPPER, $encoding = array()) {
109
+ switch($mode) {
110
+ case MB_CASE_UPPER: return mb_strtoupper($str);
111
+ case MB_CASE_LOWER: return mb_strtolower($str);
112
+ case MB_CASE_TITLE: return ucwords(mb_strtolower($str));
113
+ default: return $str;
114
+ }
115
+ }
116
+ }
117
+
118
+ if (!function_exists('mb_list_encodings')) {
119
+ function mb_list_encodings() {
120
+ return array(
121
+ "ISO-8859-1",
122
+ "UTF-8",
123
+ "8bit",
124
+ );
125
+ }
126
+ }
127
+ }
includes/documents/abstract-wcpdf-order-document.php CHANGED
@@ -1,675 +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
- );
497
- $pdf_maker = wcpdf_get_pdf_maker( $this->get_html(), $pdf_settings );
498
- $pdf = $pdf_maker->output();
499
-
500
- do_action( 'wpo_wcpdf_after_pdf', $this->get_type(), $this );
501
- do_action( 'wpo_wcpdf_pdf_created', $pdf, $this );
502
-
503
- return $pdf;
504
- }
505
-
506
- public function get_html( $args = array() ) {
507
- do_action( 'wpo_wcpdf_before_html', $this->get_type(), $this );
508
- $default_args = array (
509
- 'wrap_html_content' => true,
510
- );
511
- $args = $args + $default_args;
512
-
513
- $html = $this->render_template( $this->locate_template_file( "{$this->type}.php" ) );
514
- if ($args['wrap_html_content']) {
515
- $html = $this->wrap_html_content( $html );
516
- }
517
-
518
- // clean up special characters
519
- if ( function_exists('utf8_decode') && function_exists('mb_convert_encoding') ) {
520
- $html = utf8_decode(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
521
- }
522
-
523
- do_action( 'wpo_wcpdf_after_html', $this->get_type(), $this );
524
-
525
- return apply_filters( 'wpo_wcpdf_get_html', $html, $this );
526
- }
527
-
528
- public function output_pdf( $output_mode = 'download' ) {
529
- $pdf = $this->get_pdf();
530
- wcpdf_pdf_headers( $this->get_filename(), $output_mode, $pdf );
531
- echo $pdf;
532
- die();
533
- }
534
-
535
- public function output_html() {
536
- echo $this->get_html();
537
- die();
538
- }
539
-
540
- public function wrap_html_content( $content ) {
541
- if ( WPO_WCPDF()->legacy_mode_enabled() ) {
542
- $GLOBALS['wpo_wcpdf']->export->output_body = $content;
543
- }
544
-
545
- $html = $this->render_template( $this->locate_template_file( "html-document-wrapper.php" ), array(
546
- 'content' => $content,
547
- )
548
- );
549
- return $html;
550
- }
551
-
552
- public function get_filename( $context = 'download', $args = array() ) {
553
- $order_count = isset($args['order_ids']) ? count($args['order_ids']) : 1;
554
-
555
- $name = $this->get_type();
556
- if ( get_post_type( $this->order_id ) == 'shop_order_refund' ) {
557
- $number = $this->order_id;
558
- } else {
559
- $number = method_exists( $this->order, 'get_order_number' ) ? $this->order->get_order_number() : '';
560
- }
561
-
562
- if ( $order_count == 1 ) {
563
- $suffix = $number;
564
- } else {
565
- $suffix = date('Y-m-d'); // 2020-11-11
566
- }
567
-
568
- $filename = $name . '-' . $suffix . '.pdf';
569
-
570
- // Filter filename
571
- $order_ids = isset($args['order_ids']) ? $args['order_ids'] : array( $this->order_id );
572
- $filename = apply_filters( 'wpo_wcpdf_filename', $filename, $this->get_type(), $order_ids, $context );
573
-
574
- // sanitize filename (after filters to prevent human errors)!
575
- return sanitize_file_name( $filename );
576
- }
577
-
578
- public function get_template_path() {
579
- return WPO_WCPDF()->settings->get_template_path();
580
- }
581
-
582
- public function locate_template_file( $file ) {
583
- if (empty($file)) {
584
- $file = $this->type.'.php';
585
- }
586
- $path = WPO_WCPDF()->settings->get_template_path( $file );
587
- $file_path = "{$path}/{$file}";
588
-
589
- $fallback_file_path = WPO_WCPDF()->plugin_path() . '/templates/Simple/' . $file;
590
- if ( !file_exists( $file_path ) && file_exists( $fallback_file_path ) ) {
591
- $file_path = $fallback_file_path;
592
- }
593
-
594
- $file_path = apply_filters( 'wpo_wcpdf_template_file', $file_path, $this->type, $this->order );
595
-
596
- return $file_path;
597
- }
598
-
599
- public function render_template( $file, $args = array() ) {
600
- do_action( 'wpo_wcpdf_process_template', $this->get_type(), $this );
601
-
602
- if ( ! empty( $args ) && is_array( $args ) ) {
603
- extract( $args );
604
- }
605
- ob_start();
606
- if (file_exists($file)) {
607
- include($file);
608
- }
609
- return ob_get_clean();
610
- }
611
-
612
- /*
613
- |--------------------------------------------------------------------------
614
- | Settings helper functions
615
- |--------------------------------------------------------------------------
616
- */
617
-
618
- /**
619
- * get all emails registered in WooCommerce
620
- * @param boolean $remove_defaults switch to remove default woocommerce emails
621
- * @return array $emails list of all email ids/slugs and names
622
- */
623
- public function get_wc_emails() {
624
- // get emails from WooCommerce
625
- global $woocommerce;
626
- $mailer = $woocommerce->mailer();
627
- $wc_emails = $mailer->get_emails();
628
-
629
- $non_order_emails = array(
630
- 'customer_note',
631
- 'customer_reset_password',
632
- 'customer_new_account'
633
- );
634
-
635
- $emails = array();
636
- foreach ($wc_emails as $class => $email) {
637
- if ( !in_array( $email->id, $non_order_emails ) ) {
638
- switch ($email->id) {
639
- case 'new_order':
640
- $emails[$email->id] = sprintf('%s (%s)', $email->title, __( 'Admin email', 'woocommerce-pdf-invoices-packing-slips' ) );
641
- break;
642
- case 'customer_invoice':
643
- $emails[$email->id] = sprintf('%s (%s)', $email->title, __( 'Manual email', 'woocommerce-pdf-invoices-packing-slips' ) );
644
- break;
645
- default:
646
- $emails[$email->id] = $email->title;
647
- break;
648
- }
649
- }
650
- }
651
-
652
- return apply_filters( 'wpo_wcpdf_wc_emails', $emails );
653
- }
654
-
655
- // get list of WooCommerce statuses
656
- public function get_wc_order_status_list() {
657
- if ( version_compare( WOOCOMMERCE_VERSION, '2.2', '<' ) ) {
658
- $statuses = (array) get_terms( 'shop_order_status', array( 'hide_empty' => 0, 'orderby' => 'id' ) );
659
- foreach ( $statuses as $status ) {
660
- $order_statuses[esc_attr( $status->slug )] = esc_html__( $status->name, 'woocommerce' );
661
- }
662
- } else {
663
- $statuses = wc_get_order_statuses();
664
- foreach ( $statuses as $status_slug => $status ) {
665
- $status_slug = 'wc-' === substr( $status_slug, 0, 3 ) ? substr( $status_slug, 3 ) : $status_slug;
666
- $order_statuses[$status_slug] = $status;
667
- }
668
- }
669
- return $order_statuses;
670
- }
671
-
672
-
673
- }
674
-
675
- 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 $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
includes/documents/class-wcpdf-bulk-document.php CHANGED
@@ -1,118 +1,119 @@
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
-
8
- if ( ! defined( 'ABSPATH' ) ) {
9
- exit; // Exit if accessed directly
10
- }
11
-
12
- if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Documents\\Bulk_Document' ) ) :
13
-
14
- /**
15
- * Bulk Document
16
- *
17
- * Wraps single documents in a bulk document
18
- *
19
- * @class \WPO\WC\PDF_Invoices\Documents\Bulk_Document
20
- * @version 2.0
21
- * @category Class
22
- * @author Ewout Fernhout
23
- */
24
-
25
- class Bulk_Document {
26
- /**
27
- * Document type.
28
- * @var String
29
- */
30
- public $type;
31
-
32
- /**
33
- * Wrapper document - used for filename etc.
34
- * @var String
35
- */
36
- public $wrapper_document;
37
-
38
- /**
39
- * Order IDs.
40
- * @var array
41
- */
42
- public $order_ids;
43
-
44
- public function __construct( $document_type, $order_ids = array() ) {
45
- $this->type = $document_type;
46
- $this->order_ids = $order_ids;
47
- }
48
-
49
- public function get_type() {
50
- return $this->type;
51
- }
52
-
53
- public function get_pdf() {
54
- do_action( 'wpo_wcpdf_before_pdf', $this->get_type(), $this );
55
-
56
- $html = $this->get_html();
57
- $pdf_settings = array(
58
- 'paper_size' => apply_filters( 'wpo_wcpdf_paper_format', $this->wrapper_document->get_setting( 'paper_size', 'A4' ), $this->get_type() ),
59
- 'paper_orientation' => apply_filters( 'wpo_wcpdf_paper_orientation', 'portrait', $this->get_type() ),
60
- );
61
- $pdf_maker = wcpdf_get_pdf_maker( $html, $pdf_settings );
62
- $pdf = $pdf_maker->output();
63
-
64
- do_action( 'wpo_wcpdf_after_pdf', $this->get_type(), $this );
65
-
66
- return $pdf;
67
- }
68
-
69
- public function get_html() {
70
- do_action( 'wpo_wcpdf_before_html', $this->get_type(), $this );
71
- $html_content = array();
72
- foreach ( $this->order_ids as $key => $order_id ) {
73
- do_action( 'wpo_wcpdf_process_template_order', $this->get_type(), $order_id );
74
-
75
- $order = WCX::get_order( $order_id );
76
-
77
- $document = wcpdf_get_document( $this->get_type(), $order, true );
78
- $html_content[ $key ] = $document->get_html( array( 'wrap_html_content' => false ) );
79
- }
80
-
81
- // get wrapper document
82
- $this->wrapper_document = wcpdf_get_document( $this->get_type(), null );
83
-
84
- // insert page breaks and wrap bulk document
85
- $page_break = "\n<div style=\"page-break-before: always;\"></div>\n";
86
- $html = $this->wrapper_document->wrap_html_content( implode( $page_break, $html_content ) );
87
- do_action( 'wpo_wcpdf_after_html', $this->get_type(), $this );
88
-
89
- return $html;
90
- }
91
-
92
- public function output_pdf( $output_mode = 'download' ) {
93
- $pdf = $this->get_pdf();
94
- wcpdf_pdf_headers( $this->get_filename(), $output_mode, $pdf );
95
- echo $pdf;
96
- die();
97
- }
98
-
99
- public function output_html() {
100
- echo $this->get_html();
101
- die();
102
- }
103
-
104
- public function get_filename( $context = 'download', $args = array() ) {
105
- if ( empty( $this->wrapper_document ) ) {
106
- $this->wrapper_document = wcpdf_get_document( $this->get_type(), null );
107
- }
108
- $default_args = array(
109
- 'order_ids' => $this->order_ids,
110
- );
111
- $args = $args + $default_args;
112
- $filename = $this->wrapper_document->get_filename( $context, $args );
113
- return $filename;
114
- }
115
-
116
- }
117
-
118
- 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
+
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit; // Exit if accessed directly
10
+ }
11
+
12
+ if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Documents\\Bulk_Document' ) ) :
13
+
14
+ /**
15
+ * Bulk Document
16
+ *
17
+ * Wraps single documents in a bulk document
18
+ *
19
+ * @class \WPO\WC\PDF_Invoices\Documents\Bulk_Document
20
+ * @version 2.0
21
+ * @category Class
22
+ * @author Ewout Fernhout
23
+ */
24
+
25
+ class Bulk_Document {
26
+ /**
27
+ * Document type.
28
+ * @var String
29
+ */
30
+ public $type;
31
+
32
+ /**
33
+ * Wrapper document - used for filename etc.
34
+ * @var String
35
+ */
36
+ public $wrapper_document;
37
+
38
+ /**
39
+ * Order IDs.
40
+ * @var array
41
+ */
42
+ public $order_ids;
43
+
44
+ public function __construct( $document_type, $order_ids = array() ) {
45
+ $this->type = $document_type;
46
+ $this->order_ids = $order_ids;
47
+ }
48
+
49
+ public function get_type() {
50
+ return $this->type;
51
+ }
52
+
53
+ public function get_pdf() {
54
+ do_action( 'wpo_wcpdf_before_pdf', $this->get_type(), $this );
55
+
56
+ $html = $this->get_html();
57
+ $pdf_settings = array(
58
+ 'paper_size' => apply_filters( 'wpo_wcpdf_paper_format', $this->wrapper_document->get_setting( 'paper_size', 'A4' ), $this->get_type() ),
59
+ 'paper_orientation' => apply_filters( 'wpo_wcpdf_paper_orientation', 'portrait', $this->get_type() ),
60
+ 'font_subsetting' => $this->wrapper_document->get_setting( 'font_subsetting', false ),
61
+ );
62
+ $pdf_maker = wcpdf_get_pdf_maker( $html, $pdf_settings );
63
+ $pdf = $pdf_maker->output();
64
+
65
+ do_action( 'wpo_wcpdf_after_pdf', $this->get_type(), $this );
66
+
67
+ return $pdf;
68
+ }
69
+
70
+ public function get_html() {
71
+ do_action( 'wpo_wcpdf_before_html', $this->get_type(), $this );
72
+ $html_content = array();
73
+ foreach ( $this->order_ids as $key => $order_id ) {
74
+ do_action( 'wpo_wcpdf_process_template_order', $this->get_type(), $order_id );
75
+
76
+ $order = WCX::get_order( $order_id );
77
+
78
+ $document = wcpdf_get_document( $this->get_type(), $order, true );
79
+ $html_content[ $key ] = $document->get_html( array( 'wrap_html_content' => false ) );
80
+ }
81
+
82
+ // get wrapper document
83
+ $this->wrapper_document = wcpdf_get_document( $this->get_type(), null );
84
+
85
+ // insert page breaks and wrap bulk document
86
+ $page_break = "\n<div style=\"page-break-before: always;\"></div>\n";
87
+ $html = $this->wrapper_document->wrap_html_content( implode( $page_break, $html_content ) );
88
+ do_action( 'wpo_wcpdf_after_html', $this->get_type(), $this );
89
+
90
+ return $html;
91
+ }
92
+
93
+ public function output_pdf( $output_mode = 'download' ) {
94
+ $pdf = $this->get_pdf();
95
+ wcpdf_pdf_headers( $this->get_filename(), $output_mode, $pdf );
96
+ echo $pdf;
97
+ die();
98
+ }
99
+
100
+ public function output_html() {
101
+ echo $this->get_html();
102
+ die();
103
+ }
104
+
105
+ public function get_filename( $context = 'download', $args = array() ) {
106
+ if ( empty( $this->wrapper_document ) ) {
107
+ $this->wrapper_document = wcpdf_get_document( $this->get_type(), null );
108
+ }
109
+ $default_args = array(
110
+ 'order_ids' => $this->order_ids,
111
+ );
112
+ $args = $args + $default_args;
113
+ $filename = $this->wrapper_document->get_filename( $context, $args );
114
+ return $filename;
115
+ }
116
+
117
+ }
118
+
119
+ endif; // class_exists
includes/documents/class-wcpdf-invoice.php CHANGED
@@ -1,352 +1,353 @@
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
-
8
- if ( ! defined( 'ABSPATH' ) ) {
9
- exit; // Exit if accessed directly
10
- }
11
-
12
- if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Documents\\Invoice' ) ) :
13
-
14
- /**
15
- * Invoice Document
16
- *
17
- * @class \WPO\WC\PDF_Invoices\Documents\Invoice
18
- * @version 2.0
19
- * @category Class
20
- * @author Ewout Fernhout
21
- */
22
-
23
- class Invoice extends Order_Document_Methods {
24
- /**
25
- * Init/load the order object.
26
- *
27
- * @param int|object|WC_Order $order Order to init.
28
- */
29
- public function __construct( $order = 0 ) {
30
- // set properties
31
- $this->type = 'invoice';
32
- $this->title = __( 'Invoice', 'woocommerce-pdf-invoices-packing-slips' );
33
- $this->icon = WPO_WCPDF()->plugin_url() . "/assets/images/invoice.png";
34
-
35
- // Call parent constructor
36
- parent::__construct( $order );
37
- }
38
-
39
- public function get_title() {
40
- // override/not using $this->title to allow for language switching!
41
- return apply_filters( "wpo_wcpdf_{$this->slug}_title", __( 'Invoice', 'woocommerce-pdf-invoices-packing-slips' ) );
42
- }
43
-
44
- public function init() {
45
- $this->set_date( current_time( 'timestamp', true ) );
46
- $this->init_number();
47
- }
48
-
49
- public function init_number() {
50
- global $wpdb;
51
- // If a third-party plugin claims to generate invoice numbers, trigger this instead
52
- if ( apply_filters( 'woocommerce_invoice_number_by_plugin', false ) || apply_filters( 'wpo_wcpdf_external_invoice_number_enabled', false, $this ) ) {
53
- $invoice_number = apply_filters( 'woocommerce_generate_invoice_number', null, $this->order );
54
- $invoice_number = apply_filters( 'wpo_wcpdf_external_invoice_number', $invoice_number, $this );
55
- if ( is_numeric($invoice_number) || $invoice_number instanceof Document_Number ) {
56
- $this->set_number( $invoice_number );
57
- } else {
58
- // invoice number is not numeric, treat as formatted
59
- // try to extract meaningful number data
60
- $formatted_number = $invoice_number;
61
- $number = (int) preg_replace('/\D/', '', $invoice_number);
62
- $invoice_number = compact( 'number', 'formatted_number' );
63
- $this->set_number( $invoice_number );
64
- }
65
- return $invoice_number;
66
- }
67
-
68
- $number_store = new Sequential_Number_Store( 'invoice_number' );
69
- // reset invoice number yearly
70
- if ( isset( $this->settings['reset_number_yearly'] ) ) {
71
- $current_year = date("Y");
72
- $last_number_year = $number_store->get_last_date('Y');
73
- // check if we need to reset
74
- if ( $current_year != $last_number_year ) {
75
- $number_store->set_next( 1 );
76
- }
77
- }
78
-
79
- $invoice_date = $this->get_date();
80
- $invoice_number = $number_store->increment( $this->order_id, $invoice_date->date_i18n( 'Y-m-d H:i:s' ) );
81
-
82
- $this->set_number( $invoice_number );
83
-
84
- return $invoice_number;
85
- }
86
-
87
- public function get_settings() {
88
- $common_settings = WPO_WCPDF()->settings->get_common_document_settings();
89
- $document_settings = get_option( 'wpo_wcpdf_documents_settings_invoice' );
90
- return (array) $document_settings + (array) $common_settings;
91
- }
92
-
93
- public function get_filename( $context = 'download', $args = array() ) {
94
- $order_count = isset($args['order_ids']) ? count($args['order_ids']) : 1;
95
-
96
- $name = _n( 'invoice', 'invoices', $order_count, 'woocommerce-pdf-invoices-packing-slips' );
97
-
98
- if ( $order_count == 1 ) {
99
- if ( isset( $this->settings['display_number'] ) ) {
100
- $suffix = (string) $this->get_number();
101
- } else {
102
- if ( empty( $this->order ) ) {
103
- $order = WCX::get_order ( $order_ids[0] );
104
- $suffix = method_exists( $order, 'get_order_number' ) ? $order->get_order_number() : '';
105
- } else {
106
- $suffix = method_exists( $this->order, 'get_order_number' ) ? $this->order->get_order_number() : '';
107
- }
108
- }
109
- } else {
110
- $suffix = date('Y-m-d'); // 2020-11-11
111
- }
112
-
113
- $filename = $name . '-' . $suffix . '.pdf';
114
-
115
- // Filter filename
116
- $order_ids = isset($args['order_ids']) ? $args['order_ids'] : array( $this->order_id );
117
- $filename = apply_filters( 'wpo_wcpdf_filename', $filename, $this->get_type(), $order_ids, $context );
118
-
119
- // sanitize filename (after filters to prevent human errors)!
120
- return sanitize_file_name( $filename );
121
- }
122
-
123
-
124
- /**
125
- * Initialise settings
126
- */
127
- public function init_settings() {
128
- // Register settings.
129
- $page = $option_group = $option_name = 'wpo_wcpdf_documents_settings_invoice';
130
-
131
- $settings_fields = array(
132
- array(
133
- 'type' => 'section',
134
- 'id' => 'invoice',
135
- 'title' => '',
136
- 'callback' => 'section',
137
- ),
138
- array(
139
- 'type' => 'setting',
140
- 'id' => 'enabled',
141
- 'title' => __( 'Enable', 'woocommerce-pdf-invoices-packing-slips' ),
142
- 'callback' => 'checkbox',
143
- 'section' => 'invoice',
144
- 'args' => array(
145
- 'option_name' => $option_name,
146
- 'id' => 'enabled',
147
- )
148
- ),
149
- array(
150
- 'type' => 'setting',
151
- 'id' => 'attach_to_email_ids',
152
- 'title' => __( 'Attach to:', 'woocommerce-pdf-invoices-packing-slips' ),
153
- 'callback' => 'multiple_checkboxes',
154
- 'section' => 'invoice',
155
- 'args' => array(
156
- 'option_name' => $option_name,
157
- 'id' => 'attach_to_email_ids',
158
- 'fields' => $this->get_wc_emails(),
159
- 'description' => !is_writable( WPO_WCPDF()->main->get_tmp_path( 'attachments' ) ) ? '<span class="wpo-warning">' . sprintf( __( 'It looks like the temp folder (<code>%s</code>) is not writable, check the permissions for this folder! Without having write access to this folder, the plugin will not be able to email invoices.', 'woocommerce-pdf-invoices-packing-slips' ), WPO_WCPDF()->main->get_tmp_path( 'attachments' ) ).'</span>':'',
160
- )
161
- ),
162
- array(
163
- 'type' => 'setting',
164
- 'id' => 'display_shipping_address',
165
- 'title' => __( 'Display shipping address', 'woocommerce-pdf-invoices-packing-slips' ),
166
- 'callback' => 'checkbox',
167
- 'section' => 'invoice',
168
- 'args' => array(
169
- 'option_name' => $option_name,
170
- 'id' => 'display_shipping_address',
171
- 'description' => __( 'Display shipping address (in addition to the default billing address) if different from billing address', 'woocommerce-pdf-invoices-packing-slips' ),
172
- )
173
- ),
174
- array(
175
- 'type' => 'setting',
176
- 'id' => 'display_email',
177
- 'title' => __( 'Display email address', 'woocommerce-pdf-invoices-packing-slips' ),
178
- 'callback' => 'checkbox',
179
- 'section' => 'invoice',
180
- 'args' => array(
181
- 'option_name' => $option_name,
182
- 'id' => 'display_email',
183
- )
184
- ),
185
- array(
186
- 'type' => 'setting',
187
- 'id' => 'display_phone',
188
- 'title' => __( 'Display phone number', 'woocommerce-pdf-invoices-packing-slips' ),
189
- 'callback' => 'checkbox',
190
- 'section' => 'invoice',
191
- 'args' => array(
192
- 'option_name' => $option_name,
193
- 'id' => 'display_phone',
194
- )
195
- ),
196
- array(
197
- 'type' => 'setting',
198
- 'id' => 'display_date',
199
- 'title' => __( 'Display invoice date', 'woocommerce-pdf-invoices-packing-slips' ),
200
- 'callback' => 'checkbox',
201
- 'section' => 'invoice',
202
- 'args' => array(
203
- 'option_name' => $option_name,
204
- 'id' => 'display_date',
205
- 'value' => 'invoice_date',
206
- )
207
- ),
208
- array(
209
- 'type' => 'setting',
210
- 'id' => 'display_number',
211
- 'title' => __( 'Display invoice number', 'woocommerce-pdf-invoices-packing-slips' ),
212
- 'callback' => 'checkbox',
213
- 'section' => 'invoice',
214
- 'args' => array(
215
- 'option_name' => $option_name,
216
- 'id' => 'display_number',
217
- 'value' => 'invoice_number',
218
- )
219
- ),
220
- array(
221
- 'type' => 'setting',
222
- 'id' => 'next_invoice_number',
223
- 'title' => __( 'Next invoice number (without prefix/suffix etc.)', 'woocommerce-pdf-invoices-packing-slips' ),
224
- 'callback' => 'next_number_edit',
225
- 'section' => 'invoice',
226
- 'args' => array(
227
- 'store' => 'invoice_number',
228
- 'size' => '10',
229
- 'description' => __( 'This is the number that will be used for the next document. By default, numbering starts from 1 and increases for every new document. Note that if you override this and set it lower than the current/highest number, this could create duplicate numbers!', 'woocommerce-pdf-invoices-packing-slips' ),
230
- )
231
- ),
232
- array(
233
- 'type' => 'setting',
234
- 'id' => 'number_format',
235
- 'title' => __( 'Number format', 'woocommerce-pdf-invoices-packing-slips' ),
236
- 'callback' => 'multiple_text_input',
237
- 'section' => 'invoice',
238
- 'args' => array(
239
- 'option_name' => $option_name,
240
- 'id' => 'number_format',
241
- 'fields' => array(
242
- 'prefix' => array(
243
- 'placeholder' => __( 'Prefix' , 'woocommerce-pdf-invoices-packing-slips' ),
244
- 'size' => 20,
245
- 'description' => __( 'to use the invoice year and/or month, use [invoice_year] or [invoice_month] respectively' , 'woocommerce-pdf-invoices-packing-slips' ),
246
- ),
247
- 'suffix' => array(
248
- 'placeholder' => __( 'Suffix' , 'woocommerce-pdf-invoices-packing-slips' ),
249
- 'size' => 20,
250
- 'description' => '',
251
- ),
252
- 'padding' => array(
253
- 'placeholder' => __( 'Padding' , 'woocommerce-pdf-invoices-packing-slips' ),
254
- 'size' => 20,
255
- 'type' => 'number',
256
- 'description' => __( 'enter the number of digits here - enter "6" to display 42 as 000042' , 'woocommerce-pdf-invoices-packing-slips' ),
257
- ),
258
- ),
259
- 'description' => __( 'note: if you have already created a custom invoice number format with a filter, the above settings will be ignored' , 'woocommerce-pdf-invoices-packing-slips' ),
260
- )
261
- ),
262
- array(
263
- 'type' => 'setting',
264
- 'id' => 'reset_number_yearly',
265
- 'title' => __( 'Reset invoice number yearly', 'woocommerce-pdf-invoices-packing-slips' ),
266
- 'callback' => 'checkbox',
267
- 'section' => 'invoice',
268
- 'args' => array(
269
- 'option_name' => $option_name,
270
- 'id' => 'reset_number_yearly',
271
- )
272
- ),
273
- array(
274
- 'type' => 'setting',
275
- 'id' => 'my_account_buttons',
276
- 'title' => __( 'Allow My Account invoice download', 'woocommerce-pdf-invoices-packing-slips' ),
277
- 'callback' => 'select',
278
- 'section' => 'invoice',
279
- 'args' => array(
280
- 'option_name' => $option_name,
281
- 'id' => 'my_account_buttons',
282
- 'options' => array(
283
- 'available' => __( 'Only when an invoice is already created/emailed' , 'woocommerce-pdf-invoices-packing-slips' ),
284
- 'custom' => __( 'Only for specific order statuses (define below)' , 'woocommerce-pdf-invoices-packing-slips' ),
285
- 'always' => __( 'Always' , 'woocommerce-pdf-invoices-packing-slips' ),
286
- 'never' => __( 'Never' , 'woocommerce-pdf-invoices-packing-slips' ),
287
- ),
288
- 'custom' => array(
289
- 'type' => 'multiple_checkboxes',
290
- 'args' => array(
291
- 'option_name' => $option_name,
292
- 'id' => 'my_account_restrict',
293
- 'fields' => $this->get_wc_order_status_list(),
294
- ),
295
- ),
296
- )
297
- ),
298
- array(
299
- 'type' => 'setting',
300
- 'id' => 'invoice_number_column',
301
- 'title' => __( 'Enable invoice number column in the orders list', 'woocommerce-pdf-invoices-packing-slips' ),
302
- 'callback' => 'checkbox',
303
- 'section' => 'invoice',
304
- 'args' => array(
305
- 'option_name' => $option_name,
306
- 'id' => 'invoice_number_column',
307
- )
308
- ),
309
- array(
310
- 'type' => 'setting',
311
- 'id' => 'disable_free',
312
- 'title' => __( 'Disable for free products', 'woocommerce-pdf-invoices-packing-slips' ),
313
- 'callback' => 'checkbox',
314
- 'section' => 'invoice',
315
- 'args' => array(
316
- 'option_name' => $option_name,
317
- 'id' => 'disable_free',
318
- 'description' => __( "Disable automatic creation/attachment when only free products are ordered", 'woocommerce-pdf-invoices-packing-slips' ),
319
- )
320
- ),
321
- );
322
-
323
-
324
- // remove/rename some fields when invoice number is controlled externally
325
- if( apply_filters('woocommerce_invoice_number_by_plugin', false) ) {
326
- $remove_settings = array( 'next_invoice_number', 'number_format', 'reset_number_yearly' );
327
- foreach ($settings_fields as $key => $settings_field) {
328
- if (in_array($settings_field['id'], $remove_settings)) {
329
- unset($settings_fields[$key]);
330
- } elseif ( $settings_field['id'] == 'display_number' ) {
331
- // alternate description for invoice number
332
- $invoice_number_desc = __( 'Invoice numbers are created by a third-party extension.', 'woocommerce-pdf-invoices-packing-slips' );
333
- if ( esc_attr( apply_filters( 'woocommerce_invoice_number_configuration_link', null ) ) ) {
334
- $invoice_number_desc .= ' '.sprintf(__( 'Configure it <a href="%s">here</a>.', 'woocommerce-pdf-invoices-packing-slips' ), $config_link);
335
- }
336
- $settings_fields[$key]['args']['description'] = '<i>'.$invoice_number_desc.'</i>';
337
- }
338
- }
339
- }
340
-
341
- // allow plugins to alter settings fields
342
- $settings_fields = apply_filters( 'wpo_wcpdf_settings_fields_documents_invoice', $settings_fields, $page, $option_group, $option_name );
343
- WPO_WCPDF()->settings->add_settings_fields( $settings_fields, $page, $option_group, $option_name );
344
- return;
345
-
346
- }
347
-
348
- }
349
-
350
- endif; // class_exists
351
-
 
352
  return new Invoice();
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
+
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ exit; // Exit if accessed directly
10
+ }
11
+
12
+ if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Documents\\Invoice' ) ) :
13
+
14
+ /**
15
+ * Invoice Document
16
+ *
17
+ * @class \WPO\WC\PDF_Invoices\Documents\Invoice
18
+ * @version 2.0
19
+ * @category Class
20
+ * @author Ewout Fernhout
21
+ */
22
+
23
+ class Invoice extends Order_Document_Methods {
24
+ /**
25
+ * Init/load the order object.
26
+ *
27
+ * @param int|object|WC_Order $order Order to init.
28
+ */
29
+ public function __construct( $order = 0 ) {
30
+ // set properties
31
+ $this->type = 'invoice';
32
+ $this->title = __( 'Invoice', 'woocommerce-pdf-invoices-packing-slips' );
33
+ $this->icon = WPO_WCPDF()->plugin_url() . "/assets/images/invoice.png";
34
+
35
+ // Call parent constructor
36
+ parent::__construct( $order );
37
+ }
38
+
39
+ public function get_title() {
40
+ // override/not using $this->title to allow for language switching!
41
+ return apply_filters( "wpo_wcpdf_{$this->slug}_title", __( 'Invoice', 'woocommerce-pdf-invoices-packing-slips' ) );
42
+ }
43
+
44
+ public function init() {
45
+ $this->set_date( current_time( 'timestamp', true ) );
46
+ $this->init_number();
47
+ }
48
+
49
+ public function init_number() {
50
+ global $wpdb;
51
+ // If a third-party plugin claims to generate invoice numbers, trigger this instead
52
+ if ( apply_filters( 'woocommerce_invoice_number_by_plugin', false ) || apply_filters( 'wpo_wcpdf_external_invoice_number_enabled', false, $this ) ) {
53
+ $invoice_number = apply_filters( 'woocommerce_generate_invoice_number', null, $this->order );
54
+ $invoice_number = apply_filters( 'wpo_wcpdf_external_invoice_number', $invoice_number, $this );
55
+ if ( is_numeric($invoice_number) || $invoice_number instanceof Document_Number ) {
56
+ $this->set_number( $invoice_number );
57
+ } else {
58
+ // invoice number is not numeric, treat as formatted
59
+ // try to extract meaningful number data
60
+ $formatted_number = $invoice_number;
61
+ $number = (int) preg_replace('/\D/', '', $invoice_number);
62
+ $invoice_number = compact( 'number', 'formatted_number' );
63
+ $this->set_number( $invoice_number );
64
+ }
65
+ return $invoice_number;
66
+ }
67
+
68
+ $number_store_method = WPO_WCPDF()->settings->get_sequential_number_store_method();
69
+ $number_store = new Sequential_Number_Store( 'invoice_number', $number_store_method );
70
+ // reset invoice number yearly
71
+ if ( isset( $this->settings['reset_number_yearly'] ) ) {
72
+ $current_year = date("Y");
73
+ $last_number_year = $number_store->get_last_date('Y');
74
+ // check if we need to reset
75
+ if ( $current_year != $last_number_year ) {
76
+ $number_store->set_next( 1 );
77
+ }
78
+ }
79
+
80
+ $invoice_date = $this->get_date();
81
+ $invoice_number = $number_store->increment( $this->order_id, $invoice_date->date_i18n( 'Y-m-d H:i:s' ) );
82
+
83
+ $this->set_number( $invoice_number );
84
+
85
+ return $invoice_number;
86
+ }
87
+
88
+ public function get_settings() {
89
+ $common_settings = WPO_WCPDF()->settings->get_common_document_settings();
90
+ $document_settings = get_option( 'wpo_wcpdf_documents_settings_invoice' );
91
+ return (array) $document_settings + (array) $common_settings;
92
+ }
93
+
94
+ public function get_filename( $context = 'download', $args = array() ) {
95
+ $order_count = isset($args['order_ids']) ? count($args['order_ids']) : 1;
96
+
97
+ $name = _n( 'invoice', 'invoices', $order_count, 'woocommerce-pdf-invoices-packing-slips' );
98
+
99
+ if ( $order_count == 1 ) {
100
+ if ( isset( $this->settings['display_number'] ) ) {
101
+ $suffix = (string) $this->get_number();
102
+ } else {
103
+ if ( empty( $this->order ) ) {
104
+ $order = WCX::get_order ( $order_ids[0] );
105
+ $suffix = method_exists( $order, 'get_order_number' ) ? $order->get_order_number() : '';
106
+ } else {
107
+ $suffix = method_exists( $this->order, 'get_order_number' ) ? $this->order->get_order_number() : '';
108
+ }
109
+ }
110
+ } else {
111
+ $suffix = date('Y-m-d'); // 2020-11-11
112
+ }
113
+
114
+ $filename = $name . '-' . $suffix . '.pdf';
115
+
116
+ // Filter filename
117
+ $order_ids = isset($args['order_ids']) ? $args['order_ids'] : array( $this->order_id );
118
+ $filename = apply_filters( 'wpo_wcpdf_filename', $filename, $this->get_type(), $order_ids, $context );
119
+
120
+ // sanitize filename (after filters to prevent human errors)!
121
+ return sanitize_file_name( $filename );
122
+ }
123
+
124
+
125
+ /**
126
+ * Initialise settings
127
+ */
128
+ public function init_settings() {
129
+ // Register settings.
130
+ $page = $option_group = $option_name = 'wpo_wcpdf_documents_settings_invoice';
131
+
132
+ $settings_fields = array(
133
+ array(
134
+ 'type' => 'section',
135
+ 'id' => 'invoice',
136
+ 'title' => '',
137
+ 'callback' => 'section',
138
+ ),
139
+ array(
140
+ 'type' => 'setting',
141
+ 'id' => 'enabled',
142
+ 'title' => __( 'Enable', 'woocommerce-pdf-invoices-packing-slips' ),
143
+ 'callback' => 'checkbox',
144
+ 'section' => 'invoice',
145
+ 'args' => array(
146
+ 'option_name' => $option_name,
147
+ 'id' => 'enabled',
148
+ )
149
+ ),
150
+ array(
151
+ 'type' => 'setting',
152
+ 'id' => 'attach_to_email_ids',
153
+ 'title' => __( 'Attach to:', 'woocommerce-pdf-invoices-packing-slips' ),
154
+ 'callback' => 'multiple_checkboxes',
155
+ 'section' => 'invoice',
156
+ 'args' => array(
157
+ 'option_name' => $option_name,
158
+ 'id' => 'attach_to_email_ids',
159
+ 'fields' => $this->get_wc_emails(),
160
+ 'description' => !is_writable( WPO_WCPDF()->main->get_tmp_path( 'attachments' ) ) ? '<span class="wpo-warning">' . sprintf( __( 'It looks like the temp folder (<code>%s</code>) is not writable, check the permissions for this folder! Without having write access to this folder, the plugin will not be able to email invoices.', 'woocommerce-pdf-invoices-packing-slips' ), WPO_WCPDF()->main->get_tmp_path( 'attachments' ) ).'</span>':'',
161
+ )
162
+ ),
163
+ array(
164
+ 'type' => 'setting',
165
+ 'id' => 'display_shipping_address',
166
+ 'title' => __( 'Display shipping address', 'woocommerce-pdf-invoices-packing-slips' ),
167
+ 'callback' => 'checkbox',
168
+ 'section' => 'invoice',
169
+ 'args' => array(
170
+ 'option_name' => $option_name,
171
+ 'id' => 'display_shipping_address',
172
+ 'description' => __( 'Display shipping address (in addition to the default billing address) if different from billing address', 'woocommerce-pdf-invoices-packing-slips' ),
173
+ )
174
+ ),
175
+ array(
176
+ 'type' => 'setting',
177
+ 'id' => 'display_email',
178
+ 'title' => __( 'Display email address', 'woocommerce-pdf-invoices-packing-slips' ),
179
+ 'callback' => 'checkbox',
180
+ 'section' => 'invoice',
181
+ 'args' => array(
182
+ 'option_name' => $option_name,
183
+ 'id' => 'display_email',
184
+ )
185
+ ),
186
+ array(
187
+ 'type' => 'setting',
188
+ 'id' => 'display_phone',
189
+ 'title' => __( 'Display phone number', 'woocommerce-pdf-invoices-packing-slips' ),
190
+ 'callback' => 'checkbox',
191
+ 'section' => 'invoice',
192
+ 'args' => array(
193
+ 'option_name' => $option_name,
194
+ 'id' => 'display_phone',
195
+ )
196
+ ),
197
+ array(
198
+ 'type' => 'setting',
199
+ 'id' => 'display_date',
200
+ 'title' => __( 'Display invoice date', 'woocommerce-pdf-invoices-packing-slips' ),
201
+ 'callback' => 'checkbox',
202
+ 'section' => 'invoice',
203
+ 'args' => array(
204
+ 'option_name' => $option_name,
205
+ 'id' => 'display_date',
206
+ 'value' => 'invoice_date',
207
+ )
208
+ ),
209
+ array(
210
+ 'type' => 'setting',
211
+ 'id' => 'display_number',
212
+ 'title' => __( 'Display invoice number', 'woocommerce-pdf-invoices-packing-slips' ),
213
+ 'callback' => 'checkbox',
214
+ 'section' => 'invoice',
215
+ 'args' => array(
216
+ 'option_name' => $option_name,
217
+ 'id' => 'display_number',
218
+ 'value' => 'invoice_number',
219
+ )
220
+ ),
221
+ array(
222
+ 'type' => 'setting',
223
+ 'id' => 'next_invoice_number',
224
+ 'title' => __( 'Next invoice number (without prefix/suffix etc.)', 'woocommerce-pdf-invoices-packing-slips' ),
225
+ 'callback' => 'next_number_edit',
226
+ 'section' => 'invoice',
227
+ 'args' => array(
228
+ 'store' => 'invoice_number',
229
+ 'size' => '10',
230
+ 'description' => __( 'This is the number that will be used for the next document. By default, numbering starts from 1 and increases for every new document. Note that if you override this and set it lower than the current/highest number, this could create duplicate numbers!', 'woocommerce-pdf-invoices-packing-slips' ),
231
+ )
232
+ ),
233
+ array(
234
+ 'type' => 'setting',
235
+ 'id' => 'number_format',
236
+ 'title' => __( 'Number format', 'woocommerce-pdf-invoices-packing-slips' ),
237
+ 'callback' => 'multiple_text_input',
238
+ 'section' => 'invoice',
239
+ 'args' => array(
240
+ 'option_name' => $option_name,
241
+ 'id' => 'number_format',
242
+ 'fields' => array(
243
+ 'prefix' => array(
244
+ 'placeholder' => __( 'Prefix' , 'woocommerce-pdf-invoices-packing-slips' ),
245
+ 'size' => 20,
246
+ 'description' => __( 'to use the invoice year and/or month, use [invoice_year] or [invoice_month] respectively' , 'woocommerce-pdf-invoices-packing-slips' ),
247
+ ),
248
+ 'suffix' => array(
249
+ 'placeholder' => __( 'Suffix' , 'woocommerce-pdf-invoices-packing-slips' ),
250
+ 'size' => 20,
251
+ 'description' => '',
252
+ ),
253
+ 'padding' => array(
254
+ 'placeholder' => __( 'Padding' , 'woocommerce-pdf-invoices-packing-slips' ),
255
+ 'size' => 20,
256
+ 'type' => 'number',
257
+ 'description' => __( 'enter the number of digits here - enter "6" to display 42 as 000042' , 'woocommerce-pdf-invoices-packing-slips' ),
258
+ ),
259
+ ),
260
+ 'description' => __( 'note: if you have already created a custom invoice number format with a filter, the above settings will be ignored' , 'woocommerce-pdf-invoices-packing-slips' ),
261
+ )
262
+ ),
263
+ array(
264
+ 'type' => 'setting',
265
+ 'id' => 'reset_number_yearly',
266
+ 'title' => __( 'Reset invoice number yearly', 'woocommerce-pdf-invoices-packing-slips' ),
267
+ 'callback' => 'checkbox',
268
+ 'section' => 'invoice',
269
+ 'args' => array(
270
+ 'option_name' => $option_name,
271
+ 'id' => 'reset_number_yearly',
272
+ )
273
+ ),
274
+ array(
275
+ 'type' => 'setting',
276
+ 'id' => 'my_account_buttons',
277
+ 'title' => __( 'Allow My Account invoice download', 'woocommerce-pdf-invoices-packing-slips' ),
278
+ 'callback' => 'select',
279
+ 'section' => 'invoice',
280
+ 'args' => array(
281
+ 'option_name' => $option_name,
282
+ 'id' => 'my_account_buttons',
283
+ 'options' => array(
284
+ 'available' => __( 'Only when an invoice is already created/emailed' , 'woocommerce-pdf-invoices-packing-slips' ),
285
+ 'custom' => __( 'Only for specific order statuses (define below)' , 'woocommerce-pdf-invoices-packing-slips' ),
286
+ 'always' => __( 'Always' , 'woocommerce-pdf-invoices-packing-slips' ),
287
+ 'never' => __( 'Never' , 'woocommerce-pdf-invoices-packing-slips' ),
288
+ ),
289
+ 'custom' => array(
290
+ 'type' => 'multiple_checkboxes',
291
+ 'args' => array(
292
+ 'option_name' => $option_name,
293
+ 'id' => 'my_account_restrict',
294
+ 'fields' => $this->get_wc_order_status_list(),
295
+ ),
296
+ ),
297
+ )
298
+ ),
299
+ array(
300
+ 'type' => 'setting',
301
+ 'id' => 'invoice_number_column',
302
+ 'title' => __( 'Enable invoice number column in the orders list', 'woocommerce-pdf-invoices-packing-slips' ),
303
+ 'callback' => 'checkbox',
304
+ 'section' => 'invoice',
305
+ 'args' => array(
306
+ 'option_name' => $option_name,
307
+ 'id' => 'invoice_number_column',
308
+ )
309
+ ),
310
+ array(
311
+ 'type' => 'setting',
312
+ 'id' => 'disable_free',
313
+ 'title' => __( 'Disable for free products', 'woocommerce-pdf-invoices-packing-slips' ),
314
+ 'callback' => 'checkbox',
315
+ 'section' => 'invoice',
316
+ 'args' => array(
317
+ 'option_name' => $option_name,
318
+ 'id' => 'disable_free',
319
+ 'description' => __( "Disable automatic creation/attachment when only free products are ordered", 'woocommerce-pdf-invoices-packing-slips' ),
320
+ )
321
+ ),
322
+ );
323
+
324
+
325
+ // remove/rename some fields when invoice number is controlled externally
326
+ if( apply_filters('woocommerce_invoice_number_by_plugin', false) ) {
327
+ $remove_settings = array( 'next_invoice_number', 'number_format', 'reset_number_yearly' );
328
+ foreach ($settings_fields as $key => $settings_field) {
329
+ if (in_array($settings_field['id'], $remove_settings)) {
330
+ unset($settings_fields[$key]);
331
+ } elseif ( $settings_field['id'] == 'display_number' ) {
332
+ // alternate description for invoice number
333
+ $invoice_number_desc = __( 'Invoice numbers are created by a third-party extension.', 'woocommerce-pdf-invoices-packing-slips' );
334
+ if ( esc_attr( apply_filters( 'woocommerce_invoice_number_configuration_link', null ) ) ) {
335
+ $invoice_number_desc .= ' '.sprintf(__( 'Configure it <a href="%s">here</a>.', 'woocommerce-pdf-invoices-packing-slips' ), $config_link);
336
+ }
337
+ $settings_fields[$key]['args']['description'] = '<i>'.$invoice_number_desc.'</i>';
338
+ }
339
+ }
340
+ }
341
+
342
+ // allow plugins to alter settings fields
343
+ $settings_fields = apply_filters( 'wpo_wcpdf_settings_fields_documents_invoice', $settings_fields, $page, $option_group, $option_name );
344
+ WPO_WCPDF()->settings->add_settings_fields( $settings_fields, $page, $option_group, $option_name );
345
+ return;
346
+
347
+ }
348
+
349
+ }
350
+
351
+ endif; // class_exists
352
+
353
  return new Invoice();
includes/documents/class-wcpdf-sequential-number-store.php CHANGED
@@ -1,127 +1,165 @@
1
- <?php
2
- namespace WPO\WC\PDF_Invoices\Documents;
3
-
4
- if ( ! defined( 'ABSPATH' ) ) {
5
- exit; // Exit if accessed directly
6
- }
7
-
8
- if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Documents\\Sequential_Number_Store' ) ) :
9
-
10
- /**
11
- * Class handling database interaction for sequential numbers
12
- *
13
- * @class \WPO\WC\PDF_Invoices\Documents\Sequential_Number_Store
14
- * @version 2.0
15
- * @category Class
16
- * @author Ewout Fernhout
17
- */
18
-
19
- class Sequential_Number_Store {
20
- /**
21
- * Name of the table that stores the number sequence (without the wp_wcpdf_ table prefix)
22
- * @var String
23
- */
24
- public $table_name;
25
-
26
- public function __construct( $table_name ) {
27
- global $wpdb;
28
- $this->table_name = "{$wpdb->prefix}wcpdf_{$table_name}"; // i.e. wp_wcpdf_invoice_number
29
-
30
- $this->init();
31
- }
32
-
33
- public function init() {
34
- global $wpdb;
35
- // check if table exists
36
- if( $wpdb->get_var("SHOW TABLES LIKE '{$this->table_name}'") == $this->table_name) {
37
- return; // no further business
38
- }
39
-
40
- // create table (in case of concurrent requests, this does no harm if it already exists)
41
- $charset_collate = $wpdb->get_charset_collate();
42
- // dbDelta is a sensitive kid, so we omit indentation
43
- $sql = "CREATE TABLE {$this->table_name} (
44
- id int(16) NOT NULL AUTO_INCREMENT,
45
- order_id int(16),
46
- date datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
47
- PRIMARY KEY (id)
48
- ) $charset_collate;";
49
-
50
- require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
51
- $result = dbDelta( $sql );
52
-
53
- return $result;
54
- }
55
-
56
- /**
57
- * Consume/create the next number and return it
58
- * @param integer $order_id WooCommerce Order ID
59
- * @param string $date Local date, formatted as Y-m-d H:i:s
60
- * @return int Number that was consumed/created
61
- */
62
- public function increment( $order_id = 0, $date = null ) {
63
- global $wpdb;
64
- if ( empty( $date ) ) {
65
- $date = get_date_from_gmt( date( 'Y-m-d H:i:s' ) );
66
- }
67
-
68
- $data = array(
69
- 'order_id' => (int) $order_id,
70
- 'date' => $date,
71
- );
72
- $wpdb->insert( $this->table_name, $data );
73
-
74
- // return generated number
75
- return $wpdb->insert_id;
76
- }
77
-
78
- /**
79
- * Get the number that will be used on the next increment
80
- * @return int next number
81
- */
82
- public function get_next() {
83
- global $wpdb;
84
- $table_status = $wpdb->get_row("SHOW TABLE STATUS LIKE '{$this->table_name}'");
85
-
86
- // return next auto_increment value
87
- return $table_status->Auto_increment;
88
- }
89
-
90
- /**
91
- * Set the number that will be used on the next increment
92
- */
93
- public function set_next( $number = 1 ) {
94
- global $wpdb;
95
-
96
- // delete all rows
97
- $delete = $wpdb->query("TRUNCATE TABLE {$this->table_name}");
98
-
99
- // set auto_increment
100
- if ( $number > 1 ) {
101
- // if AUTO_INCREMENT is not 1, we need to make sure we have a 'highest value' in case of server restarts
102
- // https://serverfault.com/questions/228690/mysql-auto-increment-fields-resets-by-itself
103
- $highest_number = (int) $number - 1;
104
- $wpdb->query("ALTER TABLE {$this->table_name} AUTO_INCREMENT={$highest_number};");
105
- $data = array(
106
- 'order_id' => 0,
107
- 'date' => get_date_from_gmt( date( 'Y-m-d H:i:s' ) ),
108
- );
109
- // after this insert, AUTO_INCREMENT will be equal to $number
110
- $wpdb->insert( $this->table_name, $data );
111
- } else {
112
- // simple scenario, no need to insert any rows
113
- $wpdb->query("ALTER TABLE {$this->table_name} AUTO_INCREMENT={$number};");
114
- }
115
- }
116
-
117
- public function get_last_date( $format = 'Y-m-d H:i:s' ) {
118
- global $wpdb;
119
- $row = $wpdb->get_row( "SELECT * FROM {$this->table_name} WHERE id = ( SELECT MAX(id) from {$this->table_name} )" );
120
- $date = isset( $row->date ) ? $row->date : 'now';
121
- $formatted_date = date( $format, strtotime( $date ) );
122
-
123
- return $formatted_date;
124
- }
125
- }
126
-
127
- endif; // class_exists
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WPO\WC\PDF_Invoices\Documents;
3
+
4
+ if ( ! defined( 'ABSPATH' ) ) {
5
+ exit; // Exit if accessed directly
6
+ }
7
+
8
+ if ( !class_exists( '\\WPO\\WC\\PDF_Invoices\\Documents\\Sequential_Number_Store' ) ) :
9
+
10
+ /**
11
+ * Class handling database interaction for sequential numbers
12
+ *
13
+ * @class \WPO\WC\PDF_Invoices\Documents\Sequential_Number_Store
14
+ * @version 2.0
15
+ * @category Class
16
+ * @author Ewout Fernhout
17
+ */
18
+
19
+ class Sequential_Number_Store {
20
+ /**
21
+ * Name of the table that stores the number sequence (without the wp_wcpdf_ table prefix)
22
+ * @var String
23
+ */
24
+ public $table_name;
25
+
26
+ /**
27
+ * Number store method, either 'auto_increment' or 'calculate'
28
+ * @var String
29
+ */
30
+ public $method;
31
+
32
+ public function __construct( $table_name, $method = 'auto_increment' ) {
33
+ global $wpdb;
34
+ $this->table_name = "{$wpdb->prefix}wcpdf_{$table_name}"; // i.e. wp_wcpdf_invoice_number
35
+ $this->method = $method;
36
+
37
+ $this->init();
38
+ }
39
+
40
+ public function init() {
41
+ global $wpdb;
42
+ // check if table exists
43
+ if( $wpdb->get_var("SHOW TABLES LIKE '{$this->table_name}'") == $this->table_name) {
44
+ // check calculated_number column if using 'calculate' method
45
+ if ( $this->method == 'calculate' ) {
46
+ $column_exists = $wpdb->get_var("SHOW COLUMNS FROM `{$this->table_name}` LIKE 'calculated_number'");
47
+ if (empty($column_exists)) {
48
+ $wpdb->query("ALTER TABLE {$this->table_name} ADD calculated_number int (16)");
49
+ }
50
+ }
51
+ return; // no further business
52
+ }
53
+
54
+ // create table (in case of concurrent requests, this does no harm if it already exists)
55
+ $charset_collate = $wpdb->get_charset_collate();
56
+ // dbDelta is a sensitive kid, so we omit indentation
57
+ $sql = "CREATE TABLE {$this->table_name} (
58
+ id int(16) NOT NULL AUTO_INCREMENT,
59
+ order_id int(16),
60
+ date datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
61
+ calculated_number int (16),
62
+ PRIMARY KEY (id)
63
+ ) $charset_collate;";
64
+
65
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
66
+ $result = dbDelta( $sql );
67
+
68
+ return $result;
69
+ }
70
+
71
+ /**
72
+ * Consume/create the next number and return it
73
+ * @param integer $order_id WooCommerce Order ID
74
+ * @param string $date Local date, formatted as Y-m-d H:i:s
75
+ * @return int Number that was consumed/created
76
+ */
77
+ public function increment( $order_id = 0, $date = null ) {
78
+ global $wpdb;
79
+ if ( empty( $date ) ) {
80
+ $date = get_date_from_gmt( date( 'Y-m-d H:i:s' ) );
81
+ }
82
+
83
+ $data = array(
84
+ 'order_id' => (int) $order_id,
85
+ 'date' => $date,
86
+ );
87
+
88
+ if ( $this->method == 'auto_increment' ) {
89
+ $wpdb->insert( $this->table_name, $data );
90
+ $number = $wpdb->insert_id;
91
+ } elseif ( $this->method == 'calculate' ) {
92
+ $number = $data['calculated_number'] = $this->get_next();
93
+ $wpdb->insert( $this->table_name, $data );
94
+ }
95
+
96
+ // return generated number
97
+ return $number;
98
+ }
99
+
100
+ /**
101
+ * Get the number that will be used on the next increment
102
+ * @return int next number
103
+ */
104
+ public function get_next() {
105
+ global $wpdb;
106
+ if ( $this->method == 'auto_increment' ) {
107
+ // get next auto_increment value
108
+ $table_status = $wpdb->get_row("SHOW TABLE STATUS LIKE '{$this->table_name}'");
109
+ $next = $table_status->Auto_increment;
110
+ } elseif ( $this->method == 'calculate' ) {
111
+ $last_row = $wpdb->get_row( "SELECT * FROM {$this->table_name} WHERE id = ( SELECT MAX(id) from {$this->table_name} )" );
112
+ if ( empty( $last_row ) ) {
113
+ $next = 1;
114
+ } elseif ( !empty( $last_row->calculated_number ) ) {
115
+ $next = (int) $last_row->calculated_number + 1;
116
+ } else {
117
+ $next = (int) $last_row->id + 1;
118
+ }
119
+ }
120
+ return $next;
121
+ }
122
+
123
+ /**
124
+ * Set the number that will be used on the next increment
125
+ */
126
+ public function set_next( $number = 1 ) {
127
+ global $wpdb;
128
+
129
+ // delete all rows
130
+ $delete = $wpdb->query("TRUNCATE TABLE {$this->table_name}");
131
+
132
+ // set auto_increment
133
+ if ( $number > 1 ) {
134
+ // if AUTO_INCREMENT is not 1, we need to make sure we have a 'highest value' in case of server restarts
135
+ // https://serverfault.com/questions/228690/mysql-auto-increment-fields-resets-by-itself
136
+ $highest_number = (int) $number - 1;
137
+ $wpdb->query("ALTER TABLE {$this->table_name} AUTO_INCREMENT={$highest_number};");
138
+ $data = array(
139
+ 'order_id' => 0,
140
+ 'date' => get_date_from_gmt( date( 'Y-m-d H:i:s' ) ),
141
+ );
142
+
143
+ if ( $this->method == 'calculate' ) {
144
+ $data['calculated_number'] = $highest_number;
145
+ }
146
+
147
+ // after this insert, AUTO_INCREMENT will be equal to $number
148
+ $wpdb->insert( $this->table_name, $data );
149
+ } else {
150
+ // simple scenario, no need to insert any rows
151
+ $wpdb->query("ALTER TABLE {$this->table_name} AUTO_INCREMENT={$number};");
152
+ }
153
+ }
154
+
155
+ public function get_last_date( $format = 'Y-m-d H:i:s' ) {
156
+ global $wpdb;
157
+ $row = $wpdb->get_row( "SELECT * FROM {$this->table_name} WHERE id = ( SELECT MAX(id) from {$this->table_name} )" );
158
+ $date = isset( $row->date ) ? $row->date : 'now';
159
+ $formatted_date = date( $format, strtotime( $date ) );
160
+
161
+ return $formatted_date;
162
+ }
163
+ }
164
+
165
+ endif; // class_exists
readme.txt CHANGED
@@ -1,603 +1,609 @@
1
- === WooCommerce PDF Invoices & Packing Slips ===
2
- Contributors: pomegranate
3
- Donate link: https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-bundle/
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.6
8
- License: GPLv2 or later
9
- License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
-
11
- Create, print & automatically email PDF invoices & packing slips for WooCommerce orders.
12
-
13
- == Description ==
14
-
15
- This WooCommerce extension automatically adds a PDF invoice to the order confirmation emails sent out to your customers. Includes a basic template (additional templates are available from [WP Overnight](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/)) as well as the possibility to modify/create your own templates. In addition, you can choose to download or print invoices and packing slips from the WooCommerce order admin.
16
-
17
- = Main features =
18
- * Automatically attach invoice PDF to WooCommerce emails of your choice
19
- * Download the PDF invoice / packing slip from the order admin page
20
- * Generate PDF invoices / packings slips in bulk
21
- * **Fully customizable** HTML/CSS invoice templates
22
- * Download invoices from the My Account page
23
- * Sequential invoice numbers - with custom formatting
24
- * **Available in: Czech, Dutch, English, Finnish, French, German, Hungarian, Italian, Japanese (see FAQ for adding custom fonts!), Norwegian, Polish, Romanian, Russian, Slovak, Slovenian, Spanish, Swedish & Ukrainian**
25
-
26
- In addition to this, we offer several premium extensions:
27
-
28
- * Create/email PDF Proforma Invoices, Credit Notes (for Refunds), email Packing Slips & more with [WooCommerce PDF Invoices & Packing Slips Professional](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-professional/)
29
- * Upload all invoices automatically to Dropbox with [WooCommerce PDF Invoices & Packing Slips to Dropbox](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-dropbox/)
30
- * Automatically send new orders or packing slips to your printer, as soon as the customer orders! [WooCommerce Automatic Order Printing](https://www.simbahosting.co.uk/s3/product/woocommerce-automatic-order-printing/?affiliates=2) (from our partners at Simba Hosting)
31
- * More advanced & stylish templates with [WooCommerce PDF Invoices & Packing Slips Premium Templates](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/)
32
-
33
- = Fully customizable =
34
- In addition to a number of default settings (including a custom header/logo) and several layout fields that you can use out of the box, the plugin contains HTML/CSS based templates that allow for customization & full control over the PDF output. Copy the templates to your theme folder and you don't have to worry that your customizations will be overwritten when you update the plugin.
35
-
36
- * Insert customer header image/logo
37
- * Modify shop data / footer / disclaimer etc. on the invoices & packing slips
38
- * Select paper size (Letter or A4)
39
- * Translation ready
40
-
41
- == Installation ==
42
-
43
- = Minimum Requirements =
44
-
45
- * WooCommerce 2.0 or later
46
- * WordPress 3.5 or later
47
-
48
- = Automatic installation =
49
- Automatic installation is the easiest option as WordPress handles the file transfers itself and you don't even need to leave your web browser. To do an automatic install of WooCommerce PDF Invoices & Packing Slips, log in to your WordPress admin panel, navigate to the Plugins menu and click Add New.
50
-
51
- In the search field type "WooCommerce PDF Invoices & Packing Slips" and click Search Plugins. You can install it by simply clicking Install Now. After clicking that link you will be asked if you're sure you want to install the plugin. Click yes and WordPress will automatically complete the installation. After installation has finished, click the 'activate plugin' link.
52
-
53
- = Manual installation via the WordPress interface =
54
- 1. Download the plugin zip file to your computer
55
- 2. Go to the WordPress admin panel menu Plugins > Add New
56
- 3. Choose upload
57
- 4. Upload the plugin zip file, the plugin will now be installed
58
- 5. After installation has finished, click the 'activate plugin' link
59
-
60
- = Manual installation via FTP =
61
- 1. Download the plugin file to your computer and unzip it
62
- 2. Using an FTP program, or your hosting control panel, upload the unzipped plugin folder to your WordPress installation's wp-content/plugins/ directory.
63
- 3. Activate the plugin from the Plugins menu within the WordPress admin.
64
-
65
- == Frequently Asked Questions ==
66
-
67
- = Where can I find the documentation? =
68
-
69
- [WooCommerce PDF Invoices & Packing Slips documentation](http://docs.wpovernight.com/woocommerce-pdf-invoices-packing-slips/)
70
-
71
- = It's not working! =
72
-
73
- Check out our step by step diagnostic instructions here: https://wordpress.org/support/topic/read-this-first-9/
74
-
75
-
76
-
77
-
78
-
79
- = Where can I find more templates? =
80
-
81
- Go to [wpovernight.com](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/) to checkout more templates! These include templates with more tax details and product thumbnails. Need a custom templates? Contact us at support@wpovernight.com for more information.
82
-
83
- = Can I create/send a proforma invoice or a credit note? =
84
- This is a feature of our Professional extension, which can be found at [wpovernight.com](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-professional/)
85
-
86
- = Can I contribute to the code? =
87
- You're more than welcome! This plugin is hosted on github, where you can post issues or make pull requests.
88
- https://github.com/wpovernight/woocommerce-pdf-invoices-packing-slips
89
-
90
- = How can I display the HTML/CSS source for debugging/developing templates? =
91
- There's a setting on the Status tab of the settings page that allows you to toggle HTML output. Don't forget to turn if off after you're done testing!
92
-
93
-
94
- == Screenshots ==
95
-
96
- 1. General settings page
97
- 2. Template settings page
98
- 3. Simple invoice PDF
99
- 4. Simple packing slip PDF
100
-
101
- == Changelog ==
102
-
103
- **2.0 is a BIG update! Make a full site backup before upgrading**
104
-
105
- = 2.0.6 =
106
- * Feature: Improved third party invoice number filters (`wpo_wcpdf_external_invoice_number_enabled` & `wpo_wcpdf_external_invoice_number`)
107
- * Fix: Underline position for Open Sans font
108
- * Fix: Invoice number auto_increment for servers that restarted frequently
109
- * Fix: Dompdf log file location (preventing open base_dir notices breaking PDF header)
110
- * Fix: 1.6.6 Settings migration duplicates merging
111
- * Tweak: Clear fonts folder when manually reinstalling fonts
112
-
113
- = 2.0.5 =
114
- * Feature: Remove temporary files (Status tab)
115
- * Fix: Page number replacement
116
- * Tweak: Fallback functions for MB String extension
117
- * Tweak: Improved wpo_wcpdf_check_privs usability for my account privileges
118
- * Legacy support: added wc_price alias for format_price method in document
119
-
120
- = 2.0.4 =
121
- * Fix: Apply filters for custom invoice number formatting in document too
122
- * Fix: Parent fallback for missing dates from refunds
123
-
124
- = 2.0.3 =
125
- * Fix: Better support for legacy invoice number filter (`wpo_wcpdf_invoice_number` - replaced by `wpo_wcpdf_formatted_document_number`)
126
- * Fix: Document number formatting fallback to order date if no document date available
127
- * Fix: Updated classmap: PSR loading didn't work on some installations
128
- * Fix: Prevent order notes from all orders showing when document is not loaded properly in filter
129
- * Tweak: Disable deprecation notices during email sending
130
- * Tweak: ignore outdated language packs
131
-
132
- = 2.0.2 =
133
- * Fix: order notes using correct order_id
134
- * Fix: WC3.0 deprecation notice for currency
135
- * Fix: Avoid crashing on PHP5.2 and older
136
- * Fix: Only use PHP MB String when present
137
- * Fix: Remote images
138
- * Fix: Download option
139
-
140
- = 2.0.1 =
141
- * Fix: PHP 5.4 issue
142
-
143
- = 2.0.0 =
144
- * New: Better structured & more advanced settings for documents
145
- * New: Option to enable & disable Packing Slips or Invoices
146
- * New: Invoice number sequence stored separately for improved speed & performance
147
- * New: Completely rewritten codebase for more flexibility & better reliability
148
- * New: Updated PDF library to DOMPDF 0.8
149
- * New: PDF Library made pluggable (by using the `wpo_wcpdf_pdf_maker` filter)
150
- * New: lots of new functions & filters to allow developers to hook into the plugin
151
- * Changed: **$wpo_wcpdf variable is now deprecated** (legacy mode available & automatically enabled on update)
152
- * Fix: Improved PHP 7 & 7.1 support
153
- * Fix: Positive prices for refunds
154
- * Fix: Use parent for attributes retrieved for product variations
155
- * Fix: Set content type to PDF for download
156
-
157
- = 1.6.6 =
158
- * Feature: Facilitate downgrading from 2.0 (re-installing fonts & resetting version)
159
- * Fix: Update currencies font (added Georgian Lari)
160
- * Translations: Added Indonesian
161
-
162
- = 1.6.5 =
163
- * Fix: Duplicate invoice numbers when bulk completing orders (WC3.0)
164
- * Fix: Hidden Invoice date when order refunded
165
-
166
- = 1.6.4 =
167
- * Fix: My account invoice button visibility
168
-
169
- = 1.6.3 =
170
- * Fix: Empty date handling
171
- * Fix: Shipping notes on refunds (reason for refund)
172
-
173
- = 1.6.2 =
174
- * Fix: TM Extra Product Options compatibility (in WC3.0)
175
- * Fix: Tax display in WC3.0
176
-
177
- = 1.6.1 =
178
- * Fix: Error with totals in credit notes
179
- * Fix: Always set invoice date when invoice is create (even display is disabled in the settings)
180
-
181
- = 1.6.0.2 =
182
- * Fix: Don't crash with PHP 5.2 or older (5.3 or higher required, 5.6 or higher recommended)
183
-
184
- = 1.6.0 =
185
- * WooCommerce 3.0 Compatible
186
- * **Requires PHP version 5.3 or higher**
187
- * Fix: Invoice number display in mobile view
188
- * Fix: Update formatted invoice number in order meta when number is altered
189
- * Fix: global plugin object loading in wrapped cron methods
190
- * Tweak: Avoid PHP7 scan false positives in DomPDF
191
-
192
- = 1.5.39 =
193
- * Feature: new template action hooks `wpo_wcpdf_before_document` & `wpo_wcpdf_after_document`
194
- * Tweak: In totals, emphasize order total rather than last item
195
- * Fix: User deprecation notices
196
- * Translations: Updated Slovenian
197
-
198
- = 1.5.38 =
199
- * Fix: Thumbnail path fallback
200
- * Fix: Edge/IE hour & minute pattern
201
- * Fix: Skip over non-order objects
202
- * Tweak: Let shop manager view My Account links
203
- * Dev: added `wpo_wcpdf_before_attachment_creation` action
204
- * Translations: Updated POT, Swedish, Dutch & Norwegian
205
-
206
- = 1.5.37 =
207
- * Feature: Added support for third party invoice numbers
208
- * Feature: Enable pre-invoice number click-to-edit
209
- * Fix: Review link for custom admins
210
- * Fix: PHP7 compatibility
211
- * Fix: Invoice date hour/minute pattern
212
- * Tweak: Multisite WooCommerce check optimization
213
-
214
- = 1.5.36 =
215
- * Translations: Fixed Romanian (incorrect "Factură Proforma" translation for "Invoice")
216
-
217
- = 1.5.35 =
218
- * Translations: Fixed "Includes %s" string for WC2.6+
219
-
220
- = 1.5.34 =
221
- * Fix: Document check that was introduced in 1.5.33 for disable free setting
222
-
223
- = 1.5.33 =
224
- * Tweak: Don't apply 'disable free' setting to packing slip attachment
225
- * Translations: Updated Romanian
226
-
227
- = 1.5.32 =
228
- * Fix: Updated currency font with Indian Rupee symbol
229
- * Translations: added Formal German (currently a copy of informal German)
230
-
231
- = 1.5.31 =
232
- * Feature: [invoice_day] or [order_day] in invoice number format
233
- * Fix: Link to hide all ads when premium extensions active
234
-
235
- = 1.5.30 =
236
- * Feature: Enable currency font for extended currency support
237
- * Fix: Font sync on plugin update
238
-
239
- = 1.5.29 =
240
- * Translations: Added Croation (Thanks Neven/Spine ICT!), updated French (Thanks Sabra!)
241
- * Tweak: filter shop address before checking if it's empty
242
- * Dev: added $order to `wpo_wcpdf_template_file` filter
243
-
244
- = 1.5.28 =
245
- * Tweak: the 'Next invoice number' is now stored separately in the database for faster and more reliable retrieval. Circumventing any caching, this should prevent duplicate invoice numbers.
246
- * Fix: Bulk actions plugin conflicts
247
- * Experimental: page numbers (use {{PAGE_NUM}} / {{PAGE_COUNT}} in your template)
248
-
249
- = 1.5.27 =
250
- * Feature: Use [invoice_year] and [invoice_month] placeholders in invoice number prefix/suffix
251
- * Feature: WooCommerce Order Status & Actions Manager emails compatibility
252
- * Feature: Add invoice number to WC REST API
253
- * Fix: Allow positive 'discounts' (price corrections)
254
- * Fix: Discounts rounding
255
- * Translations: Updated Finnish & Portugese & POT
256
-
257
- = 1.5.26 =
258
- * Feature: Automatically list all emails registered in WooCommerce
259
- * Feature: Reset invoice number yearly
260
- * Feature: WooCommerce Chained Products compatibility
261
- * Feature: WooCommerce Product Bundles visibility settings taken into account in invoice
262
- * Fix: Disable PDF creation from trashed order_ids
263
- * Tweak: Alert when no orders selected for bulk export (Props to Dartui!)
264
- * Tweak: PDF invoice settings always under WooCommerce menu (also for premium users)
265
- * Tweak: extra $item_id passed in row class filter
266
- * Translations: Updated Slovenian, Spanish, Dutch & POT file
267
-
268
- = 1.5.24 =
269
- * Hotfix: Subscriptions renewal filter arguments
270
-
271
- = 1.5.23 =
272
- * Fix: WooCommerce Subscriptons 2.0 deprecation notice.
273
- * Tweak: better qTranslate-X support
274
- * Tweak: filter for user privileges check (wpo_wcpdf_check_privs)
275
- * Translations: French translations fix
276
-
277
- = 1.5.22 =
278
- * Fix: Workaround for bug in WPML (which cleared all settings)
279
- * Translation: fixed Polish translation for invoice
280
-
281
- = 1.5.21 =
282
- * Translations: Added Estionan (thanks Tanel!)
283
- * Tweak: WC2.4 compatibility
284
-
285
- = 1.5.20 =
286
- * Feature: Option to 'never' display My Account invoice link
287
- * Fix: Order total for refunds in WC2.4
288
- * Fix: notice when no custom statuses selected for My Account display
289
- * Tweak: Product bundles styles
290
-
291
- = 1.5.19 =
292
- * Fix: Invoice number search (broke other custom searches)
293
-
294
- = 1.5.18 =
295
- * Fix: wpo_wcpdf_item_row_class packing slip filter arguments
296
-
297
- = 1.5.17 =
298
- * Feature: WooCommerce Product Bundles compatibility styles
299
- * Tweak: wpo_wcpdf_item_row_class as filter instead of action
300
-
301
- = 1.5.16 =
302
- * Feature: Search orders by invoice number (note: search on formatted invoice number only works for new orders)
303
- * Feature: Formatted invoice number stored in order
304
- * Tweak: Function parameters added to some of the filters
305
- * Tweak: WooCommerce 2.4 compatibility
306
- * Dev feature: action to add class to items table row (wpo_wcpdf_item_row_class)
307
- * Translations: Swedish updated (thanks Conney!)
308
- * Translations: Norwegian updated
309
-
310
- = 1.5.15 =
311
- * Fix: invoice number padding didn't work for values lower than 3
312
- * Tweak: WPML compatibility filter
313
- * Translations: Updated French (Thanks Nicolas!)
314
-
315
- = 1.5.14 =
316
- * Tweak: Invoice number & date edit fields moved to separate box on order edit page
317
- * Translations: Updated POT & Dutch
318
-
319
- = 1.5.13 =
320
- * Fix: Better address comparison to determine when to display alternate address
321
- * Tweak: Filter N/A addresses
322
- * Tweak: Use WooCommerce function for 2.3 discounts
323
- * Translations: Czech Updated (Thanks Ivo!)
324
- * Translations: French (minor fixes)
325
-
326
- = 1.5.12 =
327
- * Translations: added Danish, Updated POT & Italian
328
-
329
- = 1.5.11 =
330
- * Fix: Product text attributes (now checks key too)
331
- * Fix: Status page upload explanation typos
332
-
333
- = 1.5.10 =
334
- * Fix: Double check to make sure plugin doesn't attach to user emails
335
-
336
- = 1.5.9 =
337
- * Feature: Shorthand function to display product attributes: `$wpo_wcpdf->get_product_attribute( $attribute_name, $product )`
338
-
339
- = 1.5.8 =
340
- * Feature: disable invoice for free orders
341
- * Feature: action to insert data before & after item meta
342
- * Tweak: Added classes to sku & weight
343
- * Tweak: Hide payment method from totals (already shown in template)
344
- * Translations: Updated POT & Dutch
345
-
346
- = 1.5.7 =
347
- * Feature: Setting to show email address & phone number on invoice or packing slip (does not work on custom templates based on previous versions!)
348
-
349
- = 1.5.6 =
350
- * Feature: Setting to show shipping address on invoice (does not work on custom templates based on previous versions!)
351
- * Feature: My Account invoice download setting
352
- * Feature: several new template actions
353
- * Tweak: WooCommerce Bookings compatibility
354
- * Tweak: Gerenal stylesheet cleanup
355
- * Fix: temp path check/error on settings page
356
- * Fix: Document titles for credit notes and proforma (Pro)
357
- * Fix: Discount including tax
358
- * Fix: Special characters on item meta (requires WooCommerce 2.3.6)
359
- * Translations: Missing text domain on several strings
360
- * Translations: Updated POT & Dutch
361
-
362
- = 1.5.5 =
363
- * Fix: Check for incomplete line tax data (Subscriptions compatibility)
364
- * Fix: More precise template path instructions
365
- * Fix: duplicate stylesheet filter
366
- * Fix: Always prefer original order's billing address for refunds (WooCommerce EU VAT Number compatibility)
367
- * Translations: Updated German (MwSt. instead of formal Ust.)
368
- * Translations: Updated Dutch
369
-
370
- = 1.5.4 =
371
- * Tweak: include plugin version in style/script includes
372
- * Tweak: upload code cleanup
373
- * Fix: Parent invoice number (for Credit Notes in professional extension)
374
-
375
- = 1.5.3 =
376
- * Feature: add original order date value to order date filter
377
- * Feature: Work with line_tax_data when available
378
- * Feature: pass item_id to items
379
- * Tweak: later check for woocommerce active
380
- * Fix: do not try to validate empty settings (Status page settings)
381
- * Translations: Fixed Dutch typo
382
-
383
- = 1.5.2 =
384
- * Fix: fatal error when trying to activate with WooCommerce not installed yet.
385
- * Tweak: indentation fix on the Simple template
386
-
387
- = 1.5.1 =
388
- * Fix: prevent errors when upgrading
389
-
390
- = 1.5.0 =
391
- * Feature: All temporary files are now stored centrally in the WP uploads folder.
392
- * Feature: Debug settings in status panel (output errors & output to HTML)
393
- * Feature: Compatibility filter for WooCommerce Subscriptions (prevents duplicate invoice numbers)
394
- * Tweak: Pass order to totals filters
395
- * Translations: Updated POT
396
- * Translations: Updated Italian (Thanks Astrid!)
397
- * Translations: Updated Dutch
398
- * FAQ: instructions for placing a link on the thank you page
399
-
400
- = 1.4.14 =
401
- * Fix: fatal error when user registers at checkout (applies to credit notes only)
402
- * Translations: Updated German (Thanks Dietmar!)
403
- * Translations: Place your custom translations in wp-content/languages/woocommerce-pdf-invoices-packing-slips/wpo_wcpdf-LOCALE.mo to protect them from being overwritten by plugin updates.
404
-
405
- = 1.4.13 =
406
- * Feature: use separate file for all your template specific functions (template-functions.php)
407
- * Translations: Added Slovenian (thanks gregy1403!)
408
- * Translations: Updated Norwegian & Dutch.
409
- * Translations: Added Japanese - needs custom font!
410
- * FAQ: instructions on how to use custom fonts
411
-
412
- = 1.4.12 =
413
- * Fix: issues with post parent objects (WC2.1 and older)
414
-
415
- = 1.4.11 =
416
- * Small fix: bulk actions for specific i18n configurations
417
- * Tweak: total row key used as class in Simple template
418
-
419
- = 1.4.10 =
420
- * Fix: Invoice not attaching
421
- * Translations: Updated POT file
422
- * Translations: Updated Dutch, Norwegian, Polish, Brazilian Portugese, Romanian, Russian, Slovak, Spanish & Ukrainian (Many thanks to all the translators!)
423
- * Templates: added action hooks for easier customizations (`wpo_wcpdf_before_order_details`, `wpo_wcpdf_after_order_details`, `wpo_wcpdf_invoice_title` & `wpo_wcpdf_packing_slip_title`)
424
-
425
- = 1.4.9 =
426
- * Feature: Order number and date are now displayed by default in the Simple template (invoice number and date still optional)
427
- * Feature: Display Customer/Order notes with a simple shorthand (see FAQ)
428
- * Translations: Added Brazilian Portugese (thanks Victor Debone!)
429
- * Tweak: Fail more gracefully when there are errors during PDF generation
430
- * Tweak: added template type class to template output body
431
- * Tweak: cleaned up Simple template style.css
432
-
433
- = 1.4.8 =
434
- * Translations: Added Finnish (Thanks Sami Mäkelä/Contrast.fi!)
435
-
436
- = 1.4.7 =
437
- * Fix: check if image file exists locally, fallback to url if not (CDN compatibility)
438
- * Fix: make deleting invoice date possible
439
- * Fix: correct tmp folder check on status page
440
- * Translations: updated po/mo files
441
- * Tweak: changed settings capability requirement to `manage_woocommerce` (was: `manage_options`)
442
- * Tweak: better email attachment function
443
- * Tweak: prevent footer overlap (Simple template)
444
- * Tweak: fallback if `glob()` is not allowed on the server
445
- * Tweak: better custom template instructions (reflects path to actual (child-)theme)
446
-
447
- = 1.4.6 =
448
- * HOTFIX: WooCommerce 2.2 compatibility fix
449
- * Filter for PDF temp folder (wpo_wcpdf_tmp_path)
450
-
451
- = 1.4.5 =
452
- * Fix: Double date conversion for order date on invoice number filter (to avoid i18n date issues)
453
- * Fix: Template selector reset after update
454
- * Translations: added Norwegian (Thanks Aleksander!)
455
-
456
- = 1.4.4 =
457
- * Feature: Editable invoice date per order/invoice.
458
- * Feature: HTML is now allowed in footer and other settings fields.
459
- * Translations: Updated German (Thanks Nadine!)
460
- * Fix: template paths are now saved relative to the site base path (ABSPATH) to prevent errors when moving to another server
461
- * Tweak: Changed bulk action hook for better theme compatibility
462
- * Tweak: Newlines in custom checkout fields
463
-
464
- = 1.4.3 =
465
- * Feature: Added function to call custom fields more easily (see FAQ)
466
- * Feature: Change the my account button text via a filter (wpo_wcpdf_myaccount_button_text)
467
- * Translations: Added Danish (Thanks Mads!)
468
- * Tweak: only load PDF engine if it's not already loaded by another plugin
469
-
470
- = 1.4.2 =
471
- * Fix: Don't create invoice number when exporting packing slips
472
- * Fix: Division by zero for 0 quantity items
473
-
474
- = 1.4.1 =
475
- * Translations: Added Polish (Thanks Mike!)
476
- * Fix: Invoice number formatting notices in debug mode
477
-
478
- = 1.4.0 =
479
- * Feature: Invoice numbers can now be given a prefix, suffix or padding on the settings page!
480
- * Filter: `wpo_wcpdf_email_allowed_statuses` to attach pdf to custom order status emails
481
- * Tweak: Sequential Order Numbers Pro compatibility
482
- * Tweak: Filenames are now automatically sanitized to prevent issues with illegal characters
483
-
484
- = 1.3.2 =
485
- * Fix: error on wpo_wcpdf_email_attachment filter when $pdf_path not set
486
-
487
- = 1.3.1 =
488
- * Fix: invoice number was cleared when Order Actions were being used when an invoice number was not set
489
- * Translations: Updated Slovak (Thanks Jozef!)
490
- * Translations: Added Czech (Thanks CubiQ!)
491
-
492
- = 1.3.0 =
493
- * Feature: Added 'status' panel for better problem diagnosis
494
- * Tweak: split create & get invoice number calls to prevent slow database calls from causing number skipping
495
- * Translations: Added Romanian (Thanks Leonardo!)
496
- * Translations: Added Slovak (Thanks Oleg!)
497
-
498
- = 1.2.13 =
499
- * Feature: added filter for custom email attachment condition (wpo_wcpdf_custom_email_condition)
500
- * Fix: warning for tax implode
501
-
502
- = 1.2.12 =
503
- * Fix: hyperlink underline (was more like a strikethrough)
504
-
505
- = 1.2.11 =
506
- * Translations: Fixed German spelling error
507
- * Translations: Updated Swedish (Thanks Fredrik!)
508
-
509
- = 1.2.10 =
510
- * Translations: Added Swedish (Thanks Jonathan!)
511
- * Fix: Line-height issue (on some systems, the space between lines was very high)
512
-
513
- = 1.2.9 =
514
- * Fix: bug where 'standard' tax class would not display in some cases
515
- * Fix: bug that caused the totals to jump for some font sizes
516
- * Fix: WC2.1 deprecated totals function
517
- * Fix: If multiple taxes were set up with the same name, only one would display (Simple template was not affected)
518
-
519
- = 1.2.8 =
520
- * Template: Body line-height defined to prevent character jumping with italic texts
521
- * Fix: Open Sans now included in plugin package (fixes font issues for servers with allow_url_fopen disabled)
522
-
523
- = 1.2.7 =
524
- * Translations: POT, DE & NL updated
525
- * Fix: Removed stray span tag in totals table
526
-
527
- = 1.2.6 =
528
- * Translations: Spanish update (thanks prepu!)
529
- * Fix: More advanced checks to determine if a customer can download the invoice (including a status filter)
530
-
531
- = 1.2.5 =
532
- * Feature: Optional Invoice Number column for the orders listing
533
- * Feature: Better support for international characters
534
- * Translations: Added Russian & Ukrainian translation (thanks Oleg!)
535
- * Translations: Updated Spanish (Thanks Manuel!) and Dutch translations & POT file
536
- * Tweak: Memory limit notice
537
- * Tweak: Filename name now includes invoice number (when configured in the settings)
538
-
539
- = 1.2.4 =
540
- * Feature: Set which type of emails you want to attach the invoice to
541
-
542
- = 1.2.3 =
543
- * Feature: Manually edit invoice number on the edit order screen
544
- * Feature: Set the first (/next) invoice number on the settings screen
545
- * Fix: Bug where invoice number would be generated twice due to slow database calls
546
- * Fix: php strict warnings
547
-
548
- = 1.2.2 =
549
- * Feature: Simple template now uses Open Sans to include more international special characters
550
- * Feature: Implemented filters for paper size & orientation ([read here](http://wordpress.org/support/topic/select-a5-paper-size-for-packing-slips?replies=5#post-5211129))
551
- * Tweak: PDF engine updated (dompdf 0.6.0)
552
- * Tweak: Download PDF link on the my account page is now only shown when an invoice is already created by the admin or automatically, to prevent unwanted invoice created (creating problems with european laws).
553
-
554
- = 1.2.1 =
555
- * Fix: shipping & fees functions didn't output correctly with the tax set to 'incl'
556
-
557
- = 1.2.0 =
558
- * Feature: Sequential invoice numbers (set upon invoice creation).
559
- * Feature: Invoice date (set upon invoice creation).
560
-
561
- = 1.1.6 =
562
- * Feature: Hungarian translations added - thanks Joseph!
563
- * Tweak: Better debug code.
564
- * Tweak: Error reporting when templates not found.
565
- * Fix: tax rate calculation for free items.
566
-
567
- = 1.1.5 =
568
- * Feature: German translations added - thanks Christian!
569
- * Fix: dompdf 0.6.0 proved to be less stable, so switching back to beta3 for now.
570
-
571
- = 1.1.4 =
572
- * Fix: Template paths on windows servers were not properly saved (stripslashes), resulting in an empty output.
573
-
574
- = 1.1.3 =
575
- * Feature: PDF engine (dompdf) updated to 0.6.0 for better stability and utf-8 support.
576
- * Tweak: Local server path is used for header image for better compatibility with server settings.
577
- * Fix: several small bugs.
578
-
579
- = 1.1.2 =
580
- * Feature: Totals can now be called with simpler template functions
581
- * Feature: Italian translations - thanks max66max!
582
- * Tweak: improved memory performance
583
-
584
- = 1.1.1 =
585
- * Feature: French translations - thanks Guillaume!
586
-
587
- = 1.1.0 =
588
- * Feature: Fees can now also be called ex. VAT
589
- * Feature: Invoices can now be downloaded from the My Account page
590
- * Feature: Spanish translation & POT file included
591
- * Fix: ternary statements that caused an error
592
-
593
- = 1.0.1 =
594
- * Tweak: Packing slip now displays shipping address instead of billing address
595
- * Tweak: Variation data is now displayed by default
596
-
597
- = 1.0.0 =
598
- * First release
599
-
600
- == Upgrade Notice ==
601
-
602
- = 2.0.6 =
 
 
 
 
 
 
603
  **2.0 is a BIG update! Make a full site backup before upgrading!**
1
+ === WooCommerce PDF Invoices & Packing Slips ===
2
+ Contributors: pomegranate
3
+ Donate link: https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-bundle/
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.7
8
+ License: GPLv2 or later
9
+ License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
+
11
+ Create, print & automatically email PDF invoices & packing slips for WooCommerce orders.
12
+
13
+ == Description ==
14
+
15
+ This WooCommerce extension automatically adds a PDF invoice to the order confirmation emails sent out to your customers. Includes a basic template (additional templates are available from [WP Overnight](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/)) as well as the possibility to modify/create your own templates. In addition, you can choose to download or print invoices and packing slips from the WooCommerce order admin.
16
+
17
+ = Main features =
18
+ * Automatically attach invoice PDF to WooCommerce emails of your choice
19
+ * Download the PDF invoice / packing slip from the order admin page
20
+ * Generate PDF invoices / packings slips in bulk
21
+ * **Fully customizable** HTML/CSS invoice templates
22
+ * Download invoices from the My Account page
23
+ * Sequential invoice numbers - with custom formatting
24
+ * **Available in: Czech, Dutch, English, Finnish, French, German, Hungarian, Italian, Japanese (see FAQ for adding custom fonts!), Norwegian, Polish, Romanian, Russian, Slovak, Slovenian, Spanish, Swedish & Ukrainian**
25
+
26
+ In addition to this, we offer several premium extensions:
27
+
28
+ * Create/email PDF Proforma Invoices, Credit Notes (for Refunds), email Packing Slips & more with [WooCommerce PDF Invoices & Packing Slips Professional](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-professional/)
29
+ * Upload all invoices automatically to Dropbox with [WooCommerce PDF Invoices & Packing Slips to Dropbox](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-dropbox/)
30
+ * Automatically send new orders or packing slips to your printer, as soon as the customer orders! [WooCommerce Automatic Order Printing](https://www.simbahosting.co.uk/s3/product/woocommerce-automatic-order-printing/?affiliates=2) (from our partners at Simba Hosting)
31
+ * More advanced & stylish templates with [WooCommerce PDF Invoices & Packing Slips Premium Templates](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/)
32
+
33
+ = Fully customizable =
34
+ In addition to a number of default settings (including a custom header/logo) and several layout fields that you can use out of the box, the plugin contains HTML/CSS based templates that allow for customization & full control over the PDF output. Copy the templates to your theme folder and you don't have to worry that your customizations will be overwritten when you update the plugin.
35
+
36
+ * Insert customer header image/logo
37
+ * Modify shop data / footer / disclaimer etc. on the invoices & packing slips
38
+ * Select paper size (Letter or A4)
39
+ * Translation ready
40
+
41
+ == Installation ==
42
+
43
+ = Minimum Requirements =
44
+
45
+ * WooCommerce 2.0 or later
46
+ * WordPress 3.5 or later
47
+
48
+ = Automatic installation =
49
+ Automatic installation is the easiest option as WordPress handles the file transfers itself and you don't even need to leave your web browser. To do an automatic install of WooCommerce PDF Invoices & Packing Slips, log in to your WordPress admin panel, navigate to the Plugins menu and click Add New.
50
+
51
+ In the search field type "WooCommerce PDF Invoices & Packing Slips" and click Search Plugins. You can install it by simply clicking Install Now. After clicking that link you will be asked if you're sure you want to install the plugin. Click yes and WordPress will automatically complete the installation. After installation has finished, click the 'activate plugin' link.
52
+
53
+ = Manual installation via the WordPress interface =
54
+ 1. Download the plugin zip file to your computer
55
+ 2. Go to the WordPress admin panel menu Plugins > Add New
56
+ 3. Choose upload
57
+ 4. Upload the plugin zip file, the plugin will now be installed
58
+ 5. After installation has finished, click the 'activate plugin' link
59
+
60
+ = Manual installation via FTP =
61
+ 1. Download the plugin file to your computer and unzip it
62
+ 2. Using an FTP program, or your hosting control panel, upload the unzipped plugin folder to your WordPress installation's wp-content/plugins/ directory.
63
+ 3. Activate the plugin from the Plugins menu within the WordPress admin.
64
+
65
+ == Frequently Asked Questions ==
66
+
67
+ = Where can I find the documentation? =
68
+
69
+ [WooCommerce PDF Invoices & Packing Slips documentation](http://docs.wpovernight.com/woocommerce-pdf-invoices-packing-slips/)
70
+
71
+ = It's not working! =
72
+
73
+ Check out our step by step diagnostic instructions here: https://wordpress.org/support/topic/read-this-first-9/
74
+
75
+
76
+
77
+
78
+
79
+ = Where can I find more templates? =
80
+
81
+ Go to [wpovernight.com](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-premium-templates/) to checkout more templates! These include templates with more tax details and product thumbnails. Need a custom templates? Contact us at support@wpovernight.com for more information.
82
+
83
+ = Can I create/send a proforma invoice or a credit note? =
84
+ This is a feature of our Professional extension, which can be found at [wpovernight.com](https://wpovernight.com/downloads/woocommerce-pdf-invoices-packing-slips-professional/)
85
+
86
+ = Can I contribute to the code? =
87
+ You're more than welcome! This plugin is hosted on github, where you can post issues or make pull requests.
88
+ https://github.com/wpovernight/woocommerce-pdf-invoices-packing-slips
89
+
90
+ = How can I display the HTML/CSS source for debugging/developing templates? =
91
+ There's a setting on the Status tab of the settings page that allows you to toggle HTML output. Don't forget to turn if off after you're done testing!
92
+
93
+
94
+ == Screenshots ==
95
+
96
+ 1. General settings page
97
+ 2. Template settings page
98
+ 3. Simple invoice PDF
99
+ 4. Simple packing slip PDF
100
+
101
+ == Changelog ==
102
+
103
+ **2.0 is a BIG update! Make a full site backup before upgrading**
104
+
105
+ = 2.0.7 =
106
+ * Feature: Added button to delete legacy settings
107
+ * Feature: Option to enable font subsetting
108
+ * Fix: Invoice number sequence for databases with alternative auto_increment_increment settings
109
+ * Fix: Fallback function for MB String (mb_stripos)
110
+
111
+ = 2.0.6 =
112
+ * Feature: Improved third party invoice number filters (`wpo_wcpdf_external_invoice_number_enabled` & `wpo_wcpdf_external_invoice_number`)
113
+ * Fix: Underline position for Open Sans font
114
+ * Fix: Invoice number auto_increment for servers that restarted frequently
115
+ * Fix: Dompdf log file location (preventing open base_dir notices breaking PDF header)
116
+ * Fix: 1.6.6 Settings migration duplicates merging
117
+ * Tweak: Clear fonts folder when manually reinstalling fonts
118
+
119
+ = 2.0.5 =
120
+ * Feature: Remove temporary files (Status tab)
121
+ * Fix: Page number replacement
122
+ * Tweak: Fallback functions for MB String extension
123
+ * Tweak: Improved wpo_wcpdf_check_privs usability for my account privileges
124
+ * Legacy support: added wc_price alias for format_price method in document
125
+
126
+ = 2.0.4 =
127
+ * Fix: Apply filters for custom invoice number formatting in document too
128
+ * Fix: Parent fallback for missing dates from refunds
129
+
130
+ = 2.0.3 =
131
+ * Fix: Better support for legacy invoice number filter (`wpo_wcpdf_invoice_number` - replaced by `wpo_wcpdf_formatted_document_number`)
132
+ * Fix: Document number formatting fallback to order date if no document date available
133
+ * Fix: Updated classmap: PSR loading didn't work on some installations
134
+ * Fix: Prevent order notes from all orders showing when document is not loaded properly in filter
135
+ * Tweak: Disable deprecation notices during email sending
136
+ * Tweak: ignore outdated language packs
137
+
138
+ = 2.0.2 =
139
+ * Fix: order notes using correct order_id
140
+ * Fix: WC3.0 deprecation notice for currency
141
+ * Fix: Avoid crashing on PHP5.2 and older
142
+ * Fix: Only use PHP MB String when present
143
+ * Fix: Remote images
144
+ * Fix: Download option
145
+
146
+ = 2.0.1 =
147
+ * Fix: PHP 5.4 issue
148
+
149
+ = 2.0.0 =
150
+ * New: Better structured & more advanced settings for documents
151
+ * New: Option to enable & disable Packing Slips or Invoices
152
+ * New: Invoice number sequence stored separately for improved speed & performance
153
+ * New: Completely rewritten codebase for more flexibility & better reliability
154
+ * New: Updated PDF library to DOMPDF 0.8
155
+ * New: PDF Library made pluggable (by using the `wpo_wcpdf_pdf_maker` filter)
156
+ * New: lots of new functions & filters to allow developers to hook into the plugin
157
+ * Changed: **$wpo_wcpdf variable is now deprecated** (legacy mode available & automatically enabled on update)
158
+ * Fix: Improved PHP 7 & 7.1 support
159
+ * Fix: Positive prices for refunds
160
+ * Fix: Use parent for attributes retrieved for product variations
161
+ * Fix: Set content type to PDF for download
162
+
163
+ = 1.6.6 =
164
+ * Feature: Facilitate downgrading from 2.0 (re-installing fonts & resetting version)
165
+ * Fix: Update currencies font (added Georgian Lari)
166
+ * Translations: Added Indonesian
167
+
168
+ = 1.6.5 =
169
+ * Fix: Duplicate invoice numbers when bulk completing orders (WC3.0)
170
+ * Fix: Hidden Invoice date when order refunded
171
+
172
+ = 1.6.4 =
173
+ * Fix: My account invoice button visibility
174
+
175
+ = 1.6.3 =
176
+ * Fix: Empty date handling
177
+ * Fix: Shipping notes on refunds (reason for refund)
178
+
179
+ = 1.6.2 =
180
+ * Fix: TM Extra Product Options compatibility (in WC3.0)
181
+ * Fix: Tax display in WC3.0
182
+
183
+ = 1.6.1 =
184
+ * Fix: Error with totals in credit notes
185
+ * Fix: Always set invoice date when invoice is create (even display is disabled in the settings)
186
+
187
+ = 1.6.0.2 =
188
+ * Fix: Don't crash with PHP 5.2 or older (5.3 or higher required, 5.6 or higher recommended)
189
+
190
+ = 1.6.0 =
191
+ * WooCommerce 3.0 Compatible
192
+ * **Requires PHP version 5.3 or higher**
193
+ * Fix: Invoice number display in mobile view
194
+ * Fix: Update formatted invoice number in order meta when number is altered
195
+ * Fix: global plugin object loading in wrapped cron methods
196
+ * Tweak: Avoid PHP7 scan false positives in DomPDF
197
+
198
+ = 1.5.39 =
199
+ * Feature: new template action hooks `wpo_wcpdf_before_document` & `wpo_wcpdf_after_document`
200
+ * Tweak: In totals, emphasize order total rather than last item
201
+ * Fix: User deprecation notices
202
+ * Translations: Updated Slovenian
203
+
204
+ = 1.5.38 =
205
+ * Fix: Thumbnail path fallback
206
+ * Fix: Edge/IE hour & minute pattern
207
+ * Fix: Skip over non-order objects
208
+ * Tweak: Let shop manager view My Account links
209
+ * Dev: added `wpo_wcpdf_before_attachment_creation` action
210
+ * Translations: Updated POT, Swedish, Dutch & Norwegian
211
+
212
+ = 1.5.37 =
213
+ * Feature: Added support for third party invoice numbers
214
+ * Feature: Enable pre-invoice number click-to-edit
215
+ * Fix: Review link for custom admins
216
+ * Fix: PHP7 compatibility
217
+ * Fix: Invoice date hour/minute pattern
218
+ * Tweak: Multisite WooCommerce check optimization
219
+
220
+ = 1.5.36 =
221
+ * Translations: Fixed Romanian (incorrect "Factură Proforma" translation for "Invoice")
222
+
223
+ = 1.5.35 =
224
+ * Translations: Fixed "Includes %s" string for WC2.6+
225
+
226
+ = 1.5.34 =
227
+ * Fix: Document check that was introduced in 1.5.33 for disable free setting
228
+
229
+ = 1.5.33 =
230
+ * Tweak: Don't apply 'disable free' setting to packing slip attachment
231
+ * Translations: Updated Romanian
232
+
233
+ = 1.5.32 =
234
+ * Fix: Updated currency font with Indian Rupee symbol
235
+ * Translations: added Formal German (currently a copy of informal German)
236
+
237
+ = 1.5.31 =
238
+ * Feature: [invoice_day] or [order_day] in invoice number format
239
+ * Fix: Link to hide all ads when premium extensions active
240
+
241
+ = 1.5.30 =
242
+ * Feature: Enable currency font for extended currency support
243
+ * Fix: Font sync on plugin update
244
+
245
+ = 1.5.29 =
246
+ * Translations: Added Croation (Thanks Neven/Spine ICT!), updated French (Thanks Sabra!)
247
+ * Tweak: filter shop address before checking if it's empty
248
+ * Dev: added $order to `wpo_wcpdf_template_file` filter
249
+
250
+ = 1.5.28 =
251
+ * Tweak: the 'Next invoice number' is now stored separately in the database for faster and more reliable retrieval. Circumventing any caching, this should prevent duplicate invoice numbers.
252
+ * Fix: Bulk actions plugin conflicts
253
+ * Experimental: page numbers (use {{PAGE_NUM}} / {{PAGE_COUNT}} in your template)
254
+
255
+ = 1.5.27 =
256
+ * Feature: Use [invoice_year] and [invoice_month] placeholders in invoice number prefix/suffix
257
+ * Feature: WooCommerce Order Status & Actions Manager emails compatibility
258
+ * Feature: Add invoice number to WC REST API
259
+ * Fix: Allow positive 'discounts' (price corrections)
260
+ * Fix: Discounts rounding
261
+ * Translations: Updated Finnish & Portugese & POT
262
+
263
+ = 1.5.26 =
264
+ * Feature: Automatically list all emails registered in WooCommerce
265
+ * Feature: Reset invoice number yearly
266
+ * Feature: WooCommerce Chained Products compatibility
267
+ * Feature: WooCommerce Product Bundles visibility settings taken into account in invoice
268
+ * Fix: Disable PDF creation from trashed order_ids
269
+ * Tweak: Alert when no orders selected for bulk export (Props to Dartui!)
270
+ * Tweak: PDF invoice settings always under WooCommerce menu (also for premium users)
271
+ * Tweak: extra $item_id passed in row class filter
272
+ * Translations: Updated Slovenian, Spanish, Dutch & POT file
273
+
274
+ = 1.5.24 =
275
+ * Hotfix: Subscriptions renewal filter arguments
276
+
277
+ = 1.5.23 =
278
+ * Fix: WooCommerce Subscriptons 2.0 deprecation notice.
279
+ * Tweak: better qTranslate-X support
280
+ * Tweak: filter for user privileges check (wpo_wcpdf_check_privs)
281
+ * Translations: French translations fix
282
+
283
+ = 1.5.22 =
284
+ * Fix: Workaround for bug in WPML (which cleared all settings)
285
+ * Translation: fixed Polish translation for invoice
286
+
287
+ = 1.5.21 =
288
+ * Translations: Added Estionan (thanks Tanel!)
289
+ * Tweak: WC2.4 compatibility
290
+
291
+ = 1.5.20 =
292
+ * Feature: Option to 'never' display My Account invoice link
293
+ * Fix: Order total for refunds in WC2.4
294
+ * Fix: notice when no custom statuses selected for My Account display
295
+ * Tweak: Product bundles styles
296
+
297
+ = 1.5.19 =
298
+ * Fix: Invoice number search (broke other custom searches)
299
+
300
+ = 1.5.18 =
301
+ * Fix: wpo_wcpdf_item_row_class packing slip filter arguments
302
+
303
+ = 1.5.17 =
304
+ * Feature: WooCommerce Product Bundles compatibility styles
305
+ * Tweak: wpo_wcpdf_item_row_class as filter instead of action
306
+
307
+ = 1.5.16 =
308
+ * Feature: Search orders by invoice number (note: search on formatted invoice number only works for new orders)
309
+ * Feature: Formatted invoice number stored in order
310
+ * Tweak: Function parameters added to some of the filters
311
+ * Tweak: WooCommerce 2.4 compatibility
312
+ * Dev feature: action to add class to items table row (wpo_wcpdf_item_row_class)
313
+ * Translations: Swedish updated (thanks Conney!)
314
+ * Translations: Norwegian updated
315
+
316
+ = 1.5.15 =
317
+ * Fix: invoice number padding didn't work for values lower than 3
318
+ * Tweak: WPML compatibility filter
319
+ * Translations: Updated French (Thanks Nicolas!)
320
+
321
+ = 1.5.14 =
322
+ * Tweak: Invoice number & date edit fields moved to separate box on order edit page
323
+ * Translations: Updated POT & Dutch
324
+
325
+ = 1.5.13 =
326
+ * Fix: Better address comparison to determine when to display alternate address
327
+ * Tweak: Filter N/A addresses
328
+ * Tweak: Use WooCommerce function for 2.3 discounts
329
+ * Translations: Czech Updated (Thanks Ivo!)
330
+ * Translations: French (minor fixes)
331
+
332
+ = 1.5.12 =
333
+ * Translations: added Danish, Updated POT & Italian
334
+
335
+ = 1.5.11 =
336
+ * Fix: Product text attributes (now checks key too)
337
+ * Fix: Status page upload explanation typos
338
+
339
+ = 1.5.10 =
340
+ * Fix: Double check to make sure plugin doesn't attach to user emails
341
+
342
+ = 1.5.9 =
343
+ * Feature: Shorthand function to display product attributes: `$wpo_wcpdf->get_product_attribute( $attribute_name, $product )`
344
+
345
+ = 1.5.8 =
346
+ * Feature: disable invoice for free orders
347
+ * Feature: action to insert data before & after item meta
348
+ * Tweak: Added classes to sku & weight
349
+ * Tweak: Hide payment method from totals (already shown in template)
350
+ * Translations: Updated POT & Dutch
351
+
352
+ = 1.5.7 =
353
+ * Feature: Setting to show email address & phone number on invoice or packing slip (does not work on custom templates based on previous versions!)
354
+
355
+ = 1.5.6 =
356
+ * Feature: Setting to show shipping address on invoice (does not work on custom templates based on previous versions!)
357
+ * Feature: My Account invoice download setting
358
+ * Feature: several new template actions
359
+ * Tweak: WooCommerce Bookings compatibility
360
+ * Tweak: Gerenal stylesheet cleanup
361
+ * Fix: temp path check/error on settings page
362
+ * Fix: Document titles for credit notes and proforma (Pro)
363
+ * Fix: Discount including tax
364
+ * Fix: Special characters on item meta (requires WooCommerce 2.3.6)
365
+ * Translations: Missing text domain on several strings
366
+ * Translations: Updated POT & Dutch
367
+
368
+ = 1.5.5 =
369
+ * Fix: Check for incomplete line tax data (Subscriptions compatibility)
370
+ * Fix: More precise template path instructions
371
+ * Fix: duplicate stylesheet filter
372
+ * Fix: Always prefer original order's billing address for refunds (WooCommerce EU VAT Number compatibility)
373
+ * Translations: Updated German (MwSt. instead of formal Ust.)
374
+ * Translations: Updated Dutch
375
+
376
+ = 1.5.4 =
377
+ * Tweak: include plugin version in style/script includes
378
+ * Tweak: upload code cleanup
379
+ * Fix: Parent invoice number (for Credit Notes in professional extension)
380
+
381
+ = 1.5.3 =
382
+ * Feature: add original order date value to order date filter
383
+ * Feature: Work with line_tax_data when available
384
+ * Feature: pass item_id to items
385
+ * Tweak: later check for woocommerce active
386
+ * Fix: do not try to validate empty settings (Status page settings)
387
+ * Translations: Fixed Dutch typo
388
+
389
+ = 1.5.2 =
390
+ * Fix: fatal error when trying to activate with WooCommerce not installed yet.
391
+ * Tweak: indentation fix on the Simple template
392
+
393
+ = 1.5.1 =
394
+ * Fix: prevent errors when upgrading
395
+
396
+ = 1.5.0 =
397
+ * Feature: All temporary files are now stored centrally in the WP uploads folder.
398
+ * Feature: Debug settings in status panel (output errors & output to HTML)
399
+ * Feature: Compatibility filter for WooCommerce Subscriptions (prevents duplicate invoice numbers)
400
+ * Tweak: Pass order to totals filters
401
+ * Translations: Updated POT
402
+ * Translations: Updated Italian (Thanks Astrid!)
403
+ * Translations: Updated Dutch
404
+ * FAQ: instructions for placing a link on the thank you page
405
+
406
+ = 1.4.14 =
407
+ * Fix: fatal error when user registers at checkout (applies to credit notes only)
408
+ * Translations: Updated German (Thanks Dietmar!)
409
+ * Translations: Place your custom translations in wp-content/languages/woocommerce-pdf-invoices-packing-slips/wpo_wcpdf-LOCALE.mo to protect them from being overwritten by plugin updates.
410
+
411
+ = 1.4.13 =
412
+ * Feature: use separate file for all your template specific functions (template-functions.php)
413
+ * Translations: Added Slovenian (thanks gregy1403!)
414
+ * Translations: Updated Norwegian & Dutch.
415
+ * Translations: Added Japanese - needs custom font!
416
+ * FAQ: instructions on how to use custom fonts
417
+
418
+ = 1.4.12 =
419
+ * Fix: issues with post parent objects (WC2.1 and older)
420
+
421
+ = 1.4.11 =
422
+ * Small fix: bulk actions for specific i18n configurations
423
+ * Tweak: total row key used as class in Simple template
424
+
425
+ = 1.4.10 =
426
+ * Fix: Invoice not attaching
427
+ * Translations: Updated POT file
428
+ * Translations: Updated Dutch, Norwegian, Polish, Brazilian Portugese, Romanian, Russian, Slovak, Spanish & Ukrainian (Many thanks to all the translators!)
429
+ * Templates: added action hooks for easier customizations (`wpo_wcpdf_before_order_details`, `wpo_wcpdf_after_order_details`, `wpo_wcpdf_invoice_title` & `wpo_wcpdf_packing_slip_title`)
430
+
431
+ = 1.4.9 =
432
+ * Feature: Order number and date are now displayed by default in the Simple template (invoice number and date still optional)
433
+ * Feature: Display Customer/Order notes with a simple shorthand (see FAQ)
434
+ * Translations: Added Brazilian Portugese (thanks Victor Debone!)
435
+ * Tweak: Fail more gracefully when there are errors during PDF generation
436
+ * Tweak: added template type class to template output body
437
+ * Tweak: cleaned up Simple template style.css
438
+
439
+ = 1.4.8 =
440
+ * Translations: Added Finnish (Thanks Sami Mäkelä/Contrast.fi!)
441
+
442
+ = 1.4.7 =
443
+ * Fix: check if image file exists locally, fallback to url if not (CDN compatibility)
444
+ * Fix: make deleting invoice date possible
445
+ * Fix: correct tmp folder check on status page
446
+ * Translations: updated po/mo files
447
+ * Tweak: changed settings capability requirement to `manage_woocommerce` (was: `manage_options`)
448
+ * Tweak: better email attachment function
449
+ * Tweak: prevent footer overlap (Simple template)
450
+ * Tweak: fallback if `glob()` is not allowed on the server
451
+ * Tweak: better custom template instructions (reflects path to actual (child-)theme)
452
+
453
+ = 1.4.6 =
454
+ * HOTFIX: WooCommerce 2.2 compatibility fix
455
+ * Filter for PDF temp folder (wpo_wcpdf_tmp_path)
456
+
457
+ = 1.4.5 =
458
+ * Fix: Double date conversion for order date on invoice number filter (to avoid i18n date issues)
459
+ * Fix: Template selector reset after update
460
+ * Translations: added Norwegian (Thanks Aleksander!)
461
+
462
+ = 1.4.4 =
463
+ * Feature: Editable invoice date per order/invoice.
464
+ * Feature: HTML is now allowed in footer and other settings fields.
465
+ * Translations: Updated German (Thanks Nadine!)
466
+ * Fix: template paths are now saved relative to the site base path (ABSPATH) to prevent errors when moving to another server
467
+ * Tweak: Changed bulk action hook for better theme compatibility
468
+ * Tweak: Newlines in custom checkout fields
469
+
470
+ = 1.4.3 =
471
+ * Feature: Added function to call custom fields more easily (see FAQ)
472
+ * Feature: Change the my account button text via a filter (wpo_wcpdf_myaccount_button_text)
473
+ * Translations: Added Danish (Thanks Mads!)
474
+ * Tweak: only load PDF engine if it's not already loaded by another plugin
475
+
476
+ = 1.4.2 =
477
+ * Fix: Don't create invoice number when exporting packing slips
478
+ * Fix: Division by zero for 0 quantity items
479
+
480
+ = 1.4.1 =
481
+ * Translations: Added Polish (Thanks Mike!)
482
+ * Fix: Invoice number formatting notices in debug mode
483
+
484
+ = 1.4.0 =
485
+ * Feature: Invoice numbers can now be given a prefix, suffix or padding on the settings page!
486
+ * Filter: `wpo_wcpdf_email_allowed_statuses` to attach pdf to custom order status emails
487
+ * Tweak: Sequential Order Numbers Pro compatibility
488
+ * Tweak: Filenames are now automatically sanitized to prevent issues with illegal characters
489
+
490
+ = 1.3.2 =
491
+ * Fix: error on wpo_wcpdf_email_attachment filter when $pdf_path not set
492
+
493
+ = 1.3.1 =
494
+ * Fix: invoice number was cleared when Order Actions were being used when an invoice number was not set
495
+ * Translations: Updated Slovak (Thanks Jozef!)
496
+ * Translations: Added Czech (Thanks CubiQ!)
497
+
498
+ = 1.3.0 =
499
+ * Feature: Added 'status' panel for better problem diagnosis
500
+ * Tweak: split create & get invoice number calls to prevent slow database calls from causing number skipping
501
+ * Translations: Added Romanian (Thanks Leonardo!)
502
+ * Translations: Added Slovak (Thanks Oleg!)
503
+
504
+ = 1.2.13 =
505
+ * Feature: added filter for custom email attachment condition (wpo_wcpdf_custom_email_condition)
506
+ * Fix: warning for tax implode
507
+
508
+ = 1.2.12 =
509
+ * Fix: hyperlink underline (was more like a strikethrough)
510
+
511
+ = 1.2.11 =
512
+ * Translations: Fixed German spelling error
513
+ * Translations: Updated Swedish (Thanks Fredrik!)
514
+
515
+ = 1.2.10 =
516
+ * Translations: Added Swedish (Thanks Jonathan!)
517
+ * Fix: Line-height issue (on some systems, the space between lines was very high)
518
+
519
+ = 1.2.9 =
520
+ * Fix: bug where 'standard' tax class would not display in some cases
521
+ * Fix: bug that caused the totals to jump for some font sizes
522
+ * Fix: WC2.1 deprecated totals function
523
+ * Fix: If multiple taxes were set up with the same name, only one would display (Simple template was not affected)
524
+
525
+ = 1.2.8 =
526
+ * Template: Body line-height defined to prevent character jumping with italic texts
527
+ * Fix: Open Sans now included in plugin package (fixes font issues for servers with allow_url_fopen disabled)
528
+
529
+ = 1.2.7 =
530
+ * Translations: POT, DE & NL updated
531
+ * Fix: Removed stray span tag in totals table
532
+
533
+ = 1.2.6 =
534
+ * Translations: Spanish update (thanks prepu!)
535
+ * Fix: More advanced checks to determine if a customer can download the invoice (including a status filter)
536
+
537
+ = 1.2.5 =
538
+ * Feature: Optional Invoice Number column for the orders listing
539
+ * Feature: Better support for international characters
540
+ * Translations: Added Russian & Ukrainian translation (thanks Oleg!)
541
+ * Translations: Updated Spanish (Thanks Manuel!) and Dutch translations & POT file
542
+ * Tweak: Memory limit notice
543
+ * Tweak: Filename name now includes invoice number (when configured in the settings)
544
+
545
+ = 1.2.4 =
546
+ * Feature: Set which type of emails you want to attach the invoice to
547
+
548
+ = 1.2.3 =
549
+ * Feature: Manually edit invoice number on the edit order screen
550
+ * Feature: Set the first (/next) invoice number on the settings screen
551
+ * Fix: Bug where invoice number would be generated twice due to slow database calls
552
+ * Fix: php strict warnings
553
+
554
+ = 1.2.2 =
555
+ * Feature: Simple template now uses Open Sans to include more international special characters
556
+ * Feature: Implemented filters for paper size & orientation ([read here](http://wordpress.org/support/topic/select-a5-paper-size-for-packing-slips?replies=5#post-5211129))
557
+ * Tweak: PDF engine updated (dompdf 0.6.0)
558
+ * Tweak: Download PDF link on the my account page is now only shown when an invoice is already created by the admin or automatically, to prevent unwanted invoice created (creating problems with european laws).
559
+
560
+ = 1.2.1 =
561
+ * Fix: shipping & fees functions didn't output correctly with the tax set to 'incl'
562
+
563
+ = 1.2.0 =
564
+ * Feature: Sequential invoice numbers (set upon invoice creation).
565
+ * Feature: Invoice date (set upon invoice creation).
566
+
567
+ = 1.1.6 =
568
+ * Feature: Hungarian translations added - thanks Joseph!
569
+ * Tweak: Better debug code.
570
+ * Tweak: Error reporting when templates not found.
571
+ * Fix: tax rate calculation for free items.
572
+
573
+ = 1.1.5 =
574
+ * Feature: German translations added - thanks Christian!
575
+ * Fix: dompdf 0.6.0 proved to be less stable, so switching back to beta3 for now.
576
+
577
+ = 1.1.4 =
578
+ * Fix: Template paths on windows servers were not properly saved (stripslashes), resulting in an empty output.
579
+
580
+ = 1.1.3 =
581
+ * Feature: PDF engine (dompdf) updated to 0.6.0 for better stability and utf-8 support.
582
+ * Tweak: Local server path is used for header image for better compatibility with server settings.
583
+ * Fix: several small bugs.
584
+
585
+ = 1.1.2 =
586
+ * Feature: Totals can now be called with simpler template functions
587
+ * Feature: Italian translations - thanks max66max!
588
+ * Tweak: improved memory performance
589
+
590
+ = 1.1.1 =
591
+ * Feature: French translations - thanks Guillaume!
592
+
593
+ = 1.1.0 =
594
+ * Feature: Fees can now also be called ex. VAT
595
+ * Feature: Invoices can now be downloaded from the My Account page
596
+ * Feature: Spanish translation & POT file included
597
+ * Fix: ternary statements that caused an error
598
+
599
+ = 1.0.1 =
600
+ * Tweak: Packing slip now displays shipping address instead of billing address
601
+ * Tweak: Variation data is now displayed by default
602
+
603
+ = 1.0.0 =
604
+ * First release
605
+
606
+ == Upgrade Notice ==
607
+
608
+ = 2.0.7 =
609
  **2.0 is a BIG update! Make a full site backup before upgrading!**
woocommerce-pdf-invoices-packingslips.php CHANGED
@@ -1,355 +1,355 @@
1
- <?php
2
- /**
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.6
7
- * Author: Ewout Fernhout
8
- * Author URI: http://www.wpovernight.com
9
- * License: GPLv2 or later
10
- * License URI: http://www.opensource.org/licenses/gpl-license.php
11
- * Text Domain: woocommerce-pdf-invoices-packing-slips
12
- */
13
-
14
- if ( ! defined( 'ABSPATH' ) ) {
15
- exit; // Exit if accessed directly
16
- }
17
-
18
- if ( !class_exists( 'WPO_WCPDF' ) ) :
19
-
20
- class WPO_WCPDF {
21
-
22
- public $version = '2.0.6';
23
- public $plugin_basename;
24
- public $legacy_mode;
25
-
26
- protected static $_instance = null;
27
-
28
- /**
29
- * Main Plugin Instance
30
- *
31
- * Ensures only one instance of plugin is loaded or can be loaded.
32
- */
33
- public static function instance() {
34
- if ( is_null( self::$_instance ) ) {
35
- self::$_instance = new self();
36
- }
37
- return self::$_instance;
38
- }
39
-
40
- /**
41
- * Constructor
42
- */
43
- public function __construct() {
44
- $this->plugin_basename = plugin_basename(__FILE__);
45
-
46
- $this->define( 'WPO_WCPDF_VERSION', $this->version );
47
-
48
- // load the localisation & classes
49
- add_action( 'plugins_loaded', array( $this, 'translations' ) );
50
- add_filter( 'load_textdomain_mofile', array( $this, 'textdomain_fallback' ), 10, 2 );
51
- add_action( 'plugins_loaded', array( $this, 'load_classes' ), 9 );
52
- add_action( 'in_plugin_update_message-'.$this->plugin_basename, array( $this, 'in_plugin_update_message' ) );
53
- }
54
-
55
- /**
56
- * Define constant if not already set
57
- * @param string $name
58
- * @param string|bool $value
59
- */
60
- private function define( $name, $value ) {
61
- if ( ! defined( $name ) ) {
62
- define( $name, $value );
63
- }
64
- }
65
-
66
-
67
- /**
68
- * Load the translation / textdomain files
69
- *
70
- * Note: the first-loaded translation file overrides any following ones if the same translation is present
71
- */
72
- public function translations() {
73
- $locale = apply_filters( 'plugin_locale', get_locale(), 'woocommerce-pdf-invoices-packing-slips' );
74
- $dir = trailingslashit( WP_LANG_DIR );
75
-
76
- $textdomains = array( 'woocommerce-pdf-invoices-packing-slips' );
77
- if ( $this->legacy_mode_enabled() === true ) {
78
- $textdomains[] = 'wpo_wcpdf';
79
- }
80
-
81
- /**
82
- * Frontend/global Locale. Looks in:
83
- *
84
- * - WP_LANG_DIR/woocommerce-pdf-invoices-packing-slips/woocommerce-pdf-invoices-packing-slips-LOCALE.mo
85
- * - WP_LANG_DIR/plugins/woocommerce-pdf-invoices-packing-slips-LOCALE.mo
86
- * - woocommerce-pdf-invoices-packing-slips-pro/languages/woocommerce-pdf-invoices-packing-slips-LOCALE.mo (which if not found falls back to:)
87
- * - WP_LANG_DIR/plugins/woocommerce-pdf-invoices-packing-slips-LOCALE.mo
88
- */
89
- foreach ($textdomains as $textdomain) {
90
- load_textdomain( $textdomain, $dir . 'woocommerce-pdf-invoices-packing-slips/woocommerce-pdf-invoices-packing-slips-' . $locale . '.mo' );
91
- load_textdomain( $textdomain, $dir . 'plugins/woocommerce-pdf-invoices-packing-slips-' . $locale . '.mo' );
92
- load_plugin_textdomain( $textdomain, false, dirname( plugin_basename(__FILE__) ) . '/languages' );
93
- }
94
- }
95
-
96
- /**
97
- * Maintain backwards compatibility with old translation files
98
- * Uses old .mo file if it exists in any of the override locations
99
- */
100
- public function textdomain_fallback( $mofile, $textdomain ) {
101
- $plugin_domain = 'woocommerce-pdf-invoices-packing-slips';
102
- $old_domain = 'wpo_wcpdf';
103
-
104
- if ($textdomain == $old_domain) {
105
- $textdomain = $plugin_domain;
106
- $mofile = str_replace( "{$old_domain}-", "{$textdomain}-", $mofile ); // with trailing dash to target file and not folder
107
- }
108
-
109
- if ( $textdomain === $plugin_domain ) {
110
- $old_mofile = str_replace( "{$textdomain}-", "{$old_domain}-", $mofile ); // with trailing dash to target file and not folder
111
- if ( file_exists( $old_mofile ) ) {
112
- // we have an old override - use it
113
- return $old_mofile;
114
- }
115
-
116
- // prevent loading outdated language packs
117
- $pofile = str_replace('.mo', '.po', $mofile);
118
- if ( file_exists( $pofile ) ) {
119
- // load po file
120
- $podata = file_get_contents($pofile);
121
- // set revision date threshold
122
- $block_before = strtotime( '2017-05-15' );
123
- // read revision date
124
- preg_match('~PO-Revision-Date: (.*?)\\\n~s',$podata,$matches);
125
- if (isset($matches[1])) {
126
- $revision_date = $matches[1];
127
- if ( $revision_timestamp = strtotime($revision_date) ) {
128
- // check if revision is before threshold date
129
- if ( $revision_timestamp < $block_before ) {
130
- // try bundled
131
- $bundled_file = $this->plugin_path() . '/languages/'. basename( $mofile );
132
- if (file_exists($bundled_file)) {
133
- return $bundled_file;
134
- } else {
135
- return '';
136
- }
137
- // delete po & mo file if possible
138
- // @unlink($pofile);
139
- // @unlink($mofile);
140
- }
141
- }
142
- }
143
- }
144
- }
145
-
146
- return $mofile;
147
- }
148
-
149
- /**
150
- * Load the main plugin classes and functions
151
- */
152
- public function includes() {
153
- // WooCommerce compatibility classes
154
- include_once( $this->plugin_path() . '/includes/compatibility/abstract-wc-data-compatibility.php' );
155
- include_once( $this->plugin_path() . '/includes/compatibility/class-wc-date-compatibility.php' );
156
- include_once( $this->plugin_path() . '/includes/compatibility/class-wc-core-compatibility.php' );
157
- include_once( $this->plugin_path() . '/includes/compatibility/class-wc-order-compatibility.php' );
158
- include_once( $this->plugin_path() . '/includes/compatibility/class-wc-product-compatibility.php' );
159
- include_once( $this->plugin_path() . '/includes/compatibility/wc-datetime-functions-compatibility.php' );
160
-
161
- // Third party compatibility
162
- include_once( $this->plugin_path() . '/includes/compatibility/class-wcpdf-compatibility-third-party-plugins.php' );
163
-
164
- // Plugin classes
165
- include_once( $this->plugin_path() . '/includes/wcpdf-functions.php' );
166
- $this->settings = include_once( $this->plugin_path() . '/includes/class-wcpdf-settings.php' );
167
- $this->documents = include_once( $this->plugin_path() . '/includes/class-wcpdf-documents.php' );
168
- $this->main = include_once( $this->plugin_path() . '/includes/class-wcpdf-main.php' );
169
- include_once( $this->plugin_path() . '/includes/class-wcpdf-assets.php' );
170
- include_once( $this->plugin_path() . '/includes/class-wcpdf-admin.php' );
171
- include_once( $this->plugin_path() . '/includes/class-wcpdf-frontend.php' );
172
- include_once( $this->plugin_path() . '/includes/class-wcpdf-install.php' );
173
-
174
- // Backwards compatibility with self
175
- include_once( $this->plugin_path() . '/includes/legacy/class-wcpdf-legacy.php' );
176
- include_once( $this->plugin_path() . '/includes/legacy/class-wcpdf-legacy-deprecated-hooks.php' );
177
-
178
- // PHP MB String fallback functions
179
- include_once( $this->plugin_path() . '/includes/compatibility/mb-string-compatibility.php' );
180
- }
181
-
182
-
183
- /**
184
- * Instantiate classes when woocommerce is activated
185
- */
186
- public function load_classes() {
187
- if ( $this->is_woocommerce_activated() === false ) {
188
- add_action( 'admin_notices', array ( $this, 'need_woocommerce' ) );
189
- return;
190
- }
191
-
192
- if ( version_compare( PHP_VERSION, '5.3', '<' ) ) {
193
- add_action( 'admin_notices', array ( $this, 'required_php_version' ) );
194
- return;
195
- }
196
-
197
- // all systems ready - GO!
198
- $this->includes();
199
- }
200
-
201
- /**
202
- * Check if legacy mode is enabled
203
- */
204
- public function legacy_mode_enabled() {
205
- if (!isset($this->legacy_mode)) {
206
- $debug_settings = get_option( 'wpo_wcpdf_settings_debug' );
207
- $this->legacy_mode = isset($debug_settings['legacy_mode']);
208
- }
209
- return $this->legacy_mode;
210
- }
211
-
212
-
213
- /**
214
- * Check if woocommerce is activated
215
- */
216
- public function is_woocommerce_activated() {
217
- $blog_plugins = get_option( 'active_plugins', array() );
218
- $site_plugins = is_multisite() ? (array) maybe_unserialize( get_site_option('active_sitewide_plugins' ) ) : array();
219
-
220
- if ( in_array( 'woocommerce/woocommerce.php', $blog_plugins ) || isset( $site_plugins['woocommerce/woocommerce.php'] ) ) {
221
- return true;
222
- } else {
223
- return false;
224
- }
225
- }
226
-
227
- /**
228
- * WooCommerce not active notice.
229
- *
230
- * @return string Fallack notice.
231
- */
232
-
233
- public function need_woocommerce() {
234
- $error = sprintf( __( 'WooCommerce PDF Invoices & Packing Slips requires %sWooCommerce%s to be installed & activated!' , 'woocommerce-pdf-invoices-packing-slips' ), '<a href="http://wordpress.org/extend/plugins/woocommerce/">', '</a>' );
235
-
236
- $message = '<div class="error"><p>' . $error . '</p></div>';
237
-
238
- echo $message;
239
- }
240
-
241
- /**
242
- * PHP version requirement notice
243
- */
244
-
245
- public function required_php_version() {
246
- $error = __( 'WooCommerce PDF Invoices & Packing Slips requires PHP 5.3 or higher (5.6 or higher recommended).', 'woocommerce-pdf-invoices-packing-slips' );
247
- $how_to_update = __( 'How to update your PHP version', 'woocommerce-pdf-invoices-packing-slips' );
248
- $message = sprintf('<div class="error"><p>%s</p><p><a href="%s">%s</a></p></div>', $error, 'http://docs.wpovernight.com/general/how-to-update-your-php-version/', $how_to_update);
249
-
250
- echo $message;
251
- }
252
-
253
- /**
254
- * Show plugin changes. Code adapted from W3 Total Cache.
255
- */
256
- public function in_plugin_update_message( $args ) {
257
- $transient_name = 'wpo_wcpdf_upgrade_notice_' . $args['Version'];
258
-
259
- if ( false === ( $upgrade_notice = get_transient( $transient_name ) ) ) {
260
- $response = wp_safe_remote_get( 'https://plugins.svn.wordpress.org/woocommerce-pdf-invoices-packing-slips/trunk/readme.txt' );
261
-
262
- if ( ! is_wp_error( $response ) && ! empty( $response['body'] ) ) {
263
- $upgrade_notice = self::parse_update_notice( $response['body'], $args['new_version'] );
264
- set_transient( $transient_name, $upgrade_notice, DAY_IN_SECONDS );
265
- }
266
- }
267
-
268
- echo wp_kses_post( $upgrade_notice );
269
- }
270
-
271
- /**
272
- * Parse update notice from readme file.
273
- *
274
- * @param string $content
275
- * @param string $new_version
276
- * @return string
277
- */
278
- private function parse_update_notice( $content, $new_version ) {
279
- // Output Upgrade Notice.
280
- $matches = null;
281
- $regexp = '~==\s*Upgrade Notice\s*==\s*=\s*(.*)\s*=(.*)(=\s*' . preg_quote( $new_version ) . '\s*=|$)~Uis';
282
- $upgrade_notice = '';
283
-
284
-
285
- if ( preg_match( $regexp, $content, $matches ) ) {
286
- $notices = (array) preg_split( '~[\r\n]+~', trim( $matches[2] ) );
287
-
288
- // Convert the full version strings to minor versions.
289
- $notice_version_parts = explode( '.', trim( $matches[1] ) );
290
- $current_version_parts = explode( '.', $this->version );
291
-
292
- if ( 3 !== sizeof( $notice_version_parts ) ) {
293
- return;
294
- }
295
-
296
- $notice_version = $notice_version_parts[0] . '.' . $notice_version_parts[1];
297
- $current_version = $current_version_parts[0] . '.' . $current_version_parts[1];
298
-
299
- // Check the latest stable version and ignore trunk.
300
- if ( version_compare( $current_version, $notice_version, '<' ) ) {
301
-
302
- $upgrade_notice .= '</p><p class="wpo_wcpdf_upgrade_notice">';
303
-
304
- foreach ( $notices as $index => $line ) {
305
- $upgrade_notice .= preg_replace( '~\[([^\]]*)\]\(([^\)]*)\)~', '<a href="${2}">${1}</a>', $line );
306
- }
307
- }
308
- }
309
-
310
- return wp_kses_post( $upgrade_notice );
311
- }
312
-
313
- /**
314
- * Get the plugin url.
315
- * @return string
316
- */
317
- public function plugin_url() {
318
- return untrailingslashit( plugins_url( '/', __FILE__ ) );
319
- }
320
-
321
- /**
322
- * Get the plugin path.
323
- * @return string
324
- */
325
- public function plugin_path() {
326
- return untrailingslashit( plugin_dir_path( __FILE__ ) );
327
- }
328
-
329
- } // class WPO_WCPDF
330
-
331
- endif; // class_exists
332
-
333
- /**
334
- * Returns the main instance of WooCommerce PDF Invoices & Packing Slips to prevent the need to use globals.
335
- *
336
- * @since 1.6
337
- * @return WPO_WCPDF
338
- */
339
- function WPO_WCPDF() {
340
- return WPO_WCPDF::instance();
341
- }
342
-
343
- WPO_WCPDF(); // load plugin
344
-
345
- // legacy class for plugin detecting
346
- if ( !class_exists( 'WooCommerce_PDF_Invoices' ) ) {
347
- class WooCommerce_PDF_Invoices{
348
- public static $version;
349
-
350
- public function __construct() {
351
- self::$version = WPO_WCPDF()->version;
352
- }
353
- }
354
- new WooCommerce_PDF_Invoices();
355
- }
1
+ <?php
2
+ /**
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.7
7
+ * Author: Ewout Fernhout
8
+ * Author URI: http://www.wpovernight.com
9
+ * License: GPLv2 or later
10
+ * License URI: http://www.opensource.org/licenses/gpl-license.php
11
+ * Text Domain: woocommerce-pdf-invoices-packing-slips
12
+ */
13
+
14
+ if ( ! defined( 'ABSPATH' ) ) {
15
+ exit; // Exit if accessed directly
16
+ }
17
+
18
+ if ( !class_exists( 'WPO_WCPDF' ) ) :
19
+
20
+ class WPO_WCPDF {
21
+
22
+ public $version = '2.0.7';
23
+ public $plugin_basename;
24
+ public $legacy_mode;
25
+
26
+ protected static $_instance = null;
27
+
28
+ /**
29
+ * Main Plugin Instance
30
+ *
31
+ * Ensures only one instance of plugin is loaded or can be loaded.
32
+ */
33
+ public static function instance() {
34
+ if ( is_null( self::$_instance ) ) {
35
+ self::$_instance = new self();
36
+ }
37
+ return self::$_instance;
38
+ }
39
+
40
+ /**
41
+ * Constructor
42
+ */
43
+ public function __construct() {
44
+ $this->plugin_basename = plugin_basename(__FILE__);
45
+
46
+ $this->define( 'WPO_WCPDF_VERSION', $this->version );
47
+
48
+ // load the localisation & classes
49
+ add_action( 'plugins_loaded', array( $this, 'translations' ) );
50
+ add_filter( 'load_textdomain_mofile', array( $this, 'textdomain_fallback' ), 10, 2 );
51
+ add_action( 'plugins_loaded', array( $this, 'load_classes' ), 9 );
52
+ add_action( 'in_plugin_update_message-'.$this->plugin_basename, array( $this, 'in_plugin_update_message' ) );
53
+ }
54
+
55
+ /**
56
+ * Define constant if not already set
57
+ * @param string $name
58
+ * @param string|bool $value
59
+ */
60
+ private function define( $name, $value ) {
61
+ if ( ! defined( $name ) ) {
62
+ define( $name, $value );
63
+ }
64
+ }
65
+
66
+
67
+ /**
68
+ * Load the translation / textdomain files
69
+ *
70
+ * Note: the first-loaded translation file overrides any following ones if the same translation is present
71
+ */
72
+ public function translations() {
73
+ $locale = apply_filters( 'plugin_locale', get_locale(), 'woocommerce-pdf-invoices-packing-slips' );
74
+ $dir = trailingslashit( WP_LANG_DIR );
75
+
76
+ $textdomains = array( 'woocommerce-pdf-invoices-packing-slips' );
77
+ if ( $this->legacy_mode_enabled() === true ) {
78
+ $textdomains[] = 'wpo_wcpdf';
79
+ }
80
+
81
+ /**
82
+ * Frontend/global Locale. Looks in:
83
+ *
84
+ * - WP_LANG_DIR/woocommerce-pdf-invoices-packing-slips/woocommerce-pdf-invoices-packing-slips-LOCALE.mo
85
+ * - WP_LANG_DIR/plugins/woocommerce-pdf-invoices-packing-slips-LOCALE.mo
86
+ * - woocommerce-pdf-invoices-packing-slips-pro/languages/woocommerce-pdf-invoices-packing-slips-LOCALE.mo (which if not found falls back to:)
87
+ * - WP_LANG_DIR/plugins/woocommerce-pdf-invoices-packing-slips-LOCALE.mo
88
+ */
89
+ foreach ($textdomains as $textdomain) {
90
+ load_textdomain( $textdomain, $dir . 'woocommerce-pdf-invoices-packing-slips/woocommerce-pdf-invoices-packing-slips-' . $locale . '.mo' );
91
+ load_textdomain( $textdomain, $dir . 'plugins/woocommerce-pdf-invoices-packing-slips-' . $locale . '.mo' );
92
+ load_plugin_textdomain( $textdomain, false, dirname( plugin_basename(__FILE__) ) . '/languages' );
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Maintain backwards compatibility with old translation files
98
+ * Uses old .mo file if it exists in any of the override locations
99
+ */
100
+ public function textdomain_fallback( $mofile, $textdomain ) {
101
+ $plugin_domain = 'woocommerce-pdf-invoices-packing-slips';
102
+ $old_domain = 'wpo_wcpdf';
103
+
104
+ if ($textdomain == $old_domain) {
105
+ $textdomain = $plugin_domain;
106
+ $mofile = str_replace( "{$old_domain}-", "{$textdomain}-", $mofile ); // with trailing dash to target file and not folder
107
+ }
108
+
109
+ if ( $textdomain === $plugin_domain ) {
110
+ $old_mofile = str_replace( "{$textdomain}-", "{$old_domain}-", $mofile ); // with trailing dash to target file and not folder
111
+ if ( file_exists( $old_mofile ) ) {
112
+ // we have an old override - use it
113
+ return $old_mofile;
114
+ }
115
+
116
+ // prevent loading outdated language packs
117
+ $pofile = str_replace('.mo', '.po', $mofile);
118
+ if ( file_exists( $pofile ) ) {
119
+ // load po file
120
+ $podata = file_get_contents($pofile);
121
+ // set revision date threshold
122
+ $block_before = strtotime( '2017-05-15' );
123
+ // read revision date
124
+ preg_match('~PO-Revision-Date: (.*?)\\\n~s',$podata,$matches);
125
+ if (isset($matches[1])) {
126
+ $revision_date = $matches[1];
127
+ if ( $revision_timestamp = strtotime($revision_date) ) {
128
+ // check if revision is before threshold date
129
+ if ( $revision_timestamp < $block_before ) {
130
+ // try bundled
131
+ $bundled_file = $this->plugin_path() . '/languages/'. basename( $mofile );
132
+ if (file_exists($bundled_file)) {
133
+ return $bundled_file;
134
+ } else {
135
+ return '';
136
+ }
137
+ // delete po & mo file if possible
138
+ // @unlink($pofile);
139
+ // @unlink($mofile);
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+
146
+ return $mofile;
147
+ }
148
+
149
+ /**
150
+ * Load the main plugin classes and functions
151
+ */
152
+ public function includes() {
153
+ // WooCommerce compatibility classes
154
+ include_once( $this->plugin_path() . '/includes/compatibility/abstract-wc-data-compatibility.php' );
155
+ include_once( $this->plugin_path() . '/includes/compatibility/class-wc-date-compatibility.php' );
156
+ include_once( $this->plugin_path() . '/includes/compatibility/class-wc-core-compatibility.php' );
157
+ include_once( $this->plugin_path() . '/includes/compatibility/class-wc-order-compatibility.php' );
158
+ include_once( $this->plugin_path() . '/includes/compatibility/class-wc-product-compatibility.php' );
159
+ include_once( $this->plugin_path() . '/includes/compatibility/wc-datetime-functions-compatibility.php' );
160
+
161
+ // Third party compatibility
162
+ include_once( $this->plugin_path() . '/includes/compatibility/class-wcpdf-compatibility-third-party-plugins.php' );
163
+
164
+ // Plugin classes
165
+ include_once( $this->plugin_path() . '/includes/wcpdf-functions.php' );
166
+ $this->settings = include_once( $this->plugin_path() . '/includes/class-wcpdf-settings.php' );
167
+ $this->documents = include_once( $this->plugin_path() . '/includes/class-wcpdf-documents.php' );
168
+ $this->main = include_once( $this->plugin_path() . '/includes/class-wcpdf-main.php' );
169
+ include_once( $this->plugin_path() . '/includes/class-wcpdf-assets.php' );
170
+ include_once( $this->plugin_path() . '/includes/class-wcpdf-admin.php' );
171
+ include_once( $this->plugin_path() . '/includes/class-wcpdf-frontend.php' );
172
+ include_once( $this->plugin_path() . '/includes/class-wcpdf-install.php' );
173
+
174
+ // Backwards compatibility with self
175
+ include_once( $this->plugin_path() . '/includes/legacy/class-wcpdf-legacy.php' );
176
+ include_once( $this->plugin_path() . '/includes/legacy/class-wcpdf-legacy-deprecated-hooks.php' );
177
+
178
+ // PHP MB String fallback functions
179
+ include_once( $this->plugin_path() . '/includes/compatibility/mb-string-compatibility.php' );
180
+ }
181
+
182
+
183
+ /**
184
+ * Instantiate classes when woocommerce is activated
185
+ */
186
+ public function load_classes() {
187
+ if ( $this->is_woocommerce_activated() === false ) {
188
+ add_action( 'admin_notices', array ( $this, 'need_woocommerce' ) );
189
+ return;
190
+ }
191
+
192
+ if ( version_compare( PHP_VERSION, '5.3', '<' ) ) {
193
+ add_action( 'admin_notices', array ( $this, 'required_php_version' ) );
194
+ return;
195
+ }
196
+
197
+ // all systems ready - GO!
198
+ $this->includes();
199
+ }
200
+
201
+ /**
202
+ * Check if legacy mode is enabled
203
+ */
204
+ public function legacy_mode_enabled() {
205
+ if (!isset($this->legacy_mode)) {
206
+ $debug_settings = get_option( 'wpo_wcpdf_settings_debug' );
207
+ $this->legacy_mode = isset($debug_settings['legacy_mode']);
208
+ }
209
+ return $this->legacy_mode;
210
+ }
211
+
212
+
213
+ /**
214
+ * Check if woocommerce is activated
215
+ */
216
+ public function is_woocommerce_activated() {
217
+ $blog_plugins = get_option( 'active_plugins', array() );
218
+ $site_plugins = is_multisite() ? (array) maybe_unserialize( get_site_option('active_sitewide_plugins' ) ) : array();
219
+
220
+ if ( in_array( 'woocommerce/woocommerce.php', $blog_plugins ) || isset( $site_plugins['woocommerce/woocommerce.php'] ) ) {
221
+ return true;
222
+ } else {
223
+ return false;
224
+ }
225
+ }
226
+
227
+ /**
228
+ * WooCommerce not active notice.
229
+ *
230
+ * @return string Fallack notice.
231
+ */
232
+
233
+ public function need_woocommerce() {
234
+ $error = sprintf( __( 'WooCommerce PDF Invoices & Packing Slips requires %sWooCommerce%s to be installed & activated!' , 'woocommerce-pdf-invoices-packing-slips' ), '<a href="http://wordpress.org/extend/plugins/woocommerce/">', '</a>' );
235
+
236
+ $message = '<div class="error"><p>' . $error . '</p></div>';
237
+
238
+ echo $message;
239
+ }
240
+
241
+ /**
242
+ * PHP version requirement notice
243
+ */
244
+
245
+ public function required_php_version() {
246
+ $error = __( 'WooCommerce PDF Invoices & Packing Slips requires PHP 5.3 or higher (5.6 or higher recommended).', 'woocommerce-pdf-invoices-packing-slips' );
247
+ $how_to_update = __( 'How to update your PHP version', 'woocommerce-pdf-invoices-packing-slips' );
248
+ $message = sprintf('<div class="error"><p>%s</p><p><a href="%s">%s</a></p></div>', $error, 'http://docs.wpovernight.com/general/how-to-update-your-php-version/', $how_to_update);
249
+
250
+ echo $message;
251
+ }
252
+
253
+ /**
254
+ * Show plugin changes. Code adapted from W3 Total Cache.
255
+ */
256
+ public function in_plugin_update_message( $args ) {
257
+ $transient_name = 'wpo_wcpdf_upgrade_notice_' . $args['Version'];
258
+
259
+ if ( false === ( $upgrade_notice = get_transient( $transient_name ) ) ) {
260
+ $response = wp_safe_remote_get( 'https://plugins.svn.wordpress.org/woocommerce-pdf-invoices-packing-slips/trunk/readme.txt' );
261
+
262
+ if ( ! is_wp_error( $response ) && ! empty( $response['body'] ) ) {
263
+ $upgrade_notice = self::parse_update_notice( $response['body'], $args['new_version'] );
264
+ set_transient( $transient_name, $upgrade_notice, DAY_IN_SECONDS );
265
+ }
266
+ }
267
+
268
+ echo wp_kses_post( $upgrade_notice );
269
+ }
270
+
271
+ /**
272
+ * Parse update notice from readme file.
273
+ *
274
+ * @param string $content
275
+ * @param string $new_version
276
+ * @return string
277
+ */
278
+ private function parse_update_notice( $content, $new_version ) {
279
+ // Output Upgrade Notice.
280
+ $matches = null;
281
+ $regexp = '~==\s*Upgrade Notice\s*==\s*=\s*(.*)\s*=(.*)(=\s*' . preg_quote( $new_version ) . '\s*=|$)~Uis';
282
+ $upgrade_notice = '';
283
+
284
+
285
+ if ( preg_match( $regexp, $content, $matches ) ) {
286
+ $notices = (array) preg_split( '~[\r\n]+~', trim( $matches[2] ) );
287
+
288
+ // Convert the full version strings to minor versions.
289
+ $notice_version_parts = explode( '.', trim( $matches[1] ) );
290
+ $current_version_parts = explode( '.', $this->version );
291
+
292
+ if ( 3 !== sizeof( $notice_version_parts ) ) {
293
+ return;
294
+ }
295
+
296
+ $notice_version = $notice_version_parts[0] . '.' . $notice_version_parts[1];
297
+ $current_version = $current_version_parts[0] . '.' . $current_version_parts[1];
298
+
299
+ // Check the latest stable version and ignore trunk.
300
+ if ( version_compare( $current_version, $notice_version, '<' ) ) {
301
+
302
+ $upgrade_notice .= '</p><p class="wpo_wcpdf_upgrade_notice">';
303
+
304
+ foreach ( $notices as $index => $line ) {
305
+ $upgrade_notice .= preg_replace( '~\[([^\]]*)\]\(([^\)]*)\)~', '<a href="${2}">${1}</a>', $line );
306
+ }
307
+ }
308
+ }
309
+
310
+ return wp_kses_post( $upgrade_notice );
311
+ }
312
+
313
+ /**
314
+ * Get the plugin url.
315
+ * @return string
316
+ */
317
+ public function plugin_url() {
318
+ return untrailingslashit( plugins_url( '/', __FILE__ ) );
319
+ }
320
+
321
+ /**
322
+ * Get the plugin path.
323
+ * @return string
324
+ */
325
+ public function plugin_path() {
326
+ return untrailingslashit( plugin_dir_path( __FILE__ ) );
327
+ }
328
+
329
+ } // class WPO_WCPDF
330
+
331
+ endif; // class_exists
332
+
333
+ /**
334
+ * Returns the main instance of WooCommerce PDF Invoices & Packing Slips to prevent the need to use globals.
335
+ *
336
+ * @since 1.6
337
+ * @return WPO_WCPDF
338
+ */
339
+ function WPO_WCPDF() {
340
+ return WPO_WCPDF::instance();
341
+ }
342
+
343
+ WPO_WCPDF(); // load plugin
344
+
345
+ // legacy class for plugin detecting
346
+ if ( !class_exists( 'WooCommerce_PDF_Invoices' ) ) {
347
+ class WooCommerce_PDF_Invoices{
348
+ public static $version;
349
+
350
+ public function __construct() {
351
+ self::$version = WPO_WCPDF()->version;
352
+ }
353
+ }
354
+ new WooCommerce_PDF_Invoices();
355
+ }