Printful Integration for WooCommerce - Version 2.1.14

Version Description

Advanced size guide feature

Download this release

Release Info

Developer printful
Plugin Icon 128x128 Printful Integration for WooCommerce
Version 2.1.14
Comparing to
See all releases

Code changes from version 2.1.13 to 2.1.14

Files changed (38) hide show
  1. assets/css/dashboard.css +180 -180
  2. assets/css/global.css +18 -18
  3. assets/css/settings.css +45 -45
  4. assets/css/size-guide.css +171 -0
  5. assets/css/status.css +20 -20
  6. assets/css/support.css +61 -61
  7. assets/images/connect.svg +74 -74
  8. assets/js/block-loader.js +21 -21
  9. assets/js/connect.js +59 -59
  10. assets/js/intercom.min.js +11 -11
  11. assets/js/product-size-guide.js +439 -0
  12. assets/js/settings.js +43 -43
  13. i18n/languages/printful.pot +113 -28
  14. includes/class-printful-admin-settings.php +129 -32
  15. includes/class-printful-admin.php +171 -171
  16. includes/class-printful-client.php +191 -191
  17. includes/class-printful-integration.php +164 -164
  18. includes/class-printful-request-log.php +150 -150
  19. includes/class-printful-rest-api-controller.php +30 -1
  20. includes/class-printful-size-chart-tab.php +128 -123
  21. includes/class-printful-size-guide.php +211 -0
  22. includes/class-printful-taxes.php +188 -188
  23. includes/class-printful-template.php +26 -1
  24. includes/templates/ajax-loader.php +10 -10
  25. includes/templates/connect.php +39 -39
  26. includes/templates/header.php +19 -19
  27. includes/templates/inline-script.php +2 -2
  28. includes/templates/order-table.php +68 -68
  29. includes/templates/personalize-button.php +11 -11
  30. includes/templates/setting-group.php +13 -2
  31. includes/templates/setting-submit.php +23 -23
  32. includes/templates/shipping-notification.php +3 -3
  33. includes/templates/size-guide-button.php +10 -0
  34. includes/templates/stats.php +43 -43
  35. includes/templates/status-report.php +22 -22
  36. includes/templates/support-info.php +26 -26
  37. printful-shipping.php +5 -3
  38. readme.txt +8 -2
assets/css/dashboard.css CHANGED
@@ -1,181 +1,181 @@
1
- .printful-connect {
2
- position: relative;
3
- height: 100%;
4
- }
5
-
6
- .printful-connect-inner {
7
- max-width: 600px;
8
- margin: 100px auto 0 auto;
9
- text-align: center;
10
- position: relative;
11
- }
12
- .printful-connect .connect-image {
13
- max-width: 100%;
14
- }
15
- .printful-connect h1 {
16
- margin-bottom: 30px;
17
- font-size: 34px;
18
- }
19
- .printful-connect .connect-description {
20
- margin: 20px 0 30px 0;
21
- }
22
-
23
- /*noinspection CssUnusedSymbol*/
24
- .printful-connect .printful-connect-button {
25
- height: 53px;
26
- line-height: 53px;
27
- padding: 0 40px;
28
- font-size: 18px;
29
- }
30
-
31
- .printful-stats {
32
- position: relative;
33
- margin: 20px 0 50px 0;
34
- background-color: #fff;
35
- padding: 30px 20px;
36
- box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
37
- border-left: #0073aa 4px solid;
38
- }
39
- .printful-stats .printful-stats-item {
40
- width: 49%;
41
- display: inline-block;
42
- margin-bottom: 25px;
43
- box-sizing: border-box;
44
- vertical-align: top;
45
- padding: 0 8px;
46
- }
47
-
48
- .printful-stats .printful-stats-item h4 {
49
- margin: 0 0 10px 0;
50
- font-size: 24px;
51
- line-height: 26px;
52
- }
53
- .printful-stats .printful-stats-item h4 .dashicons {
54
- font-size: 26px;
55
- width: 26px;
56
- height: 26px;
57
- }
58
-
59
- /*noinspection ALL*/
60
- .printful-stats .printful-stats-item h4 .dashicons-arrow-up-alt {
61
- color: green;
62
- }
63
- .printful-stats .printful-stats-item h4 .dashicons-arrow-down-alt {
64
- color: #d54e21;
65
- }
66
-
67
- @media (min-width: 768px) {
68
- .printful-stats .printful-stats-item {
69
- width: 24%;
70
- margin-bottom: 0;
71
- padding: 0 25px;
72
- }
73
- .printful-stats .printful-stats-item h4 {
74
- font-size: 32px;
75
- line-height: 36px;
76
- }
77
- .printful-stats .printful-stats-item h4 .dashicons {
78
- padding-top: 4px;
79
- font-size: 30px;
80
- width: 30px;
81
- height: 30px;
82
- }
83
- }
84
-
85
- .printful-latest-orders {
86
- display: table;
87
- width: 100%;
88
- margin-bottom: 50px;
89
- }
90
- .printful-latest-orders .row {
91
- display: table-row;
92
- }
93
- .printful-latest-orders .row .cell {
94
- display: table-cell;
95
- }
96
-
97
- .wrap h2.nav-tab-wrapper.printful-tabs {
98
- margin-bottom: 15px;
99
- }
100
-
101
-
102
- .printful-quick-links {
103
- position: relative;
104
- margin-bottom: 30px;
105
- }
106
- .printful-quick-links .printful-quick-links-item {
107
- width: 120px;
108
- background: #fff;
109
- border: solid 1px #e1e1e1;
110
- float: left;
111
- padding: 20px;
112
- margin-right: 25px;
113
- margin-bottom: 25px;
114
- text-align: center;
115
- text-decoration: none;
116
- }
117
- .printful-quick-links .printful-quick-links-item .dashicons {
118
- font-size: 40px;
119
- width: 40px;
120
- height: 40px;
121
- text-decoration: none;
122
- color: #444;
123
- }
124
- .printful-quick-links .printful-quick-links-item h4 {
125
- text-decoration: none;
126
- color: #444;
127
- margin: 5px 0 0 0;
128
- }
129
- .printful-quick-links .printful-quick-links-item:hover {
130
- cursor: pointer;
131
- }
132
- .printful-quick-links .printful-quick-links-item:hover .dashicons,
133
- .printful-quick-links .printful-quick-links-item:hover h4 {
134
- color: #0D72B2;
135
- }
136
-
137
- .printful-error {
138
- margin-top: 20px;
139
- }
140
-
141
- .printful-notice {
142
- background: #fff;
143
- border-left: 4px solid #dc3232;
144
- padding: 1px 12px;
145
- margin: 10px 0 20px 0;
146
- box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
147
- text-align: left;
148
- }
149
- .printful-notice li {
150
- font-weight: bold;
151
- margin-left: 20px;
152
- list-style: disc;
153
- }
154
-
155
- .block-loader {
156
- text-align: center;
157
- padding: 20px 0;
158
- line-height: 20px;
159
- }
160
- .block-loader .message {
161
- padding-left: 10px;
162
- line-height: 20px;
163
- display: inline-block;
164
- font-size: 16px;
165
- }
166
- .block-loader .loader {
167
- vertical-align: top;
168
- }
169
-
170
- #wpfooter::after {
171
- box-sizing: border-box;
172
- content: "";
173
- width: 64px;
174
- left: 50%;
175
- margin-left: -32px;
176
- height: 28px;
177
- position: absolute;
178
- bottom: 0;
179
- background: url("../images/printful-logo-footer.png") no-repeat bottom center;
180
- background-size: 64px;
181
  }
1
+ .printful-connect {
2
+ position: relative;
3
+ height: 100%;
4
+ }
5
+
6
+ .printful-connect-inner {
7
+ max-width: 600px;
8
+ margin: 100px auto 0 auto;
9
+ text-align: center;
10
+ position: relative;
11
+ }
12
+ .printful-connect .connect-image {
13
+ max-width: 100%;
14
+ }
15
+ .printful-connect h1 {
16
+ margin-bottom: 30px;
17
+ font-size: 34px;
18
+ }
19
+ .printful-connect .connect-description {
20
+ margin: 20px 0 30px 0;
21
+ }
22
+
23
+ /*noinspection CssUnusedSymbol*/
24
+ .printful-connect .printful-connect-button {
25
+ height: 53px;
26
+ line-height: 53px;
27
+ padding: 0 40px;
28
+ font-size: 18px;
29
+ }
30
+
31
+ .printful-stats {
32
+ position: relative;
33
+ margin: 20px 0 50px 0;
34
+ background-color: #fff;
35
+ padding: 30px 20px;
36
+ box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
37
+ border-left: #0073aa 4px solid;
38
+ }
39
+ .printful-stats .printful-stats-item {
40
+ width: 49%;
41
+ display: inline-block;
42
+ margin-bottom: 25px;
43
+ box-sizing: border-box;
44
+ vertical-align: top;
45
+ padding: 0 8px;
46
+ }
47
+
48
+ .printful-stats .printful-stats-item h4 {
49
+ margin: 0 0 10px 0;
50
+ font-size: 24px;
51
+ line-height: 26px;
52
+ }
53
+ .printful-stats .printful-stats-item h4 .dashicons {
54
+ font-size: 26px;
55
+ width: 26px;
56
+ height: 26px;
57
+ }
58
+
59
+ /*noinspection ALL*/
60
+ .printful-stats .printful-stats-item h4 .dashicons-arrow-up-alt {
61
+ color: green;
62
+ }
63
+ .printful-stats .printful-stats-item h4 .dashicons-arrow-down-alt {
64
+ color: #d54e21;
65
+ }
66
+
67
+ @media (min-width: 768px) {
68
+ .printful-stats .printful-stats-item {
69
+ width: 24%;
70
+ margin-bottom: 0;
71
+ padding: 0 25px;
72
+ }
73
+ .printful-stats .printful-stats-item h4 {
74
+ font-size: 32px;
75
+ line-height: 36px;
76
+ }
77
+ .printful-stats .printful-stats-item h4 .dashicons {
78
+ padding-top: 4px;
79
+ font-size: 30px;
80
+ width: 30px;
81
+ height: 30px;
82
+ }
83
+ }
84
+
85
+ .printful-latest-orders {
86
+ display: table;
87
+ width: 100%;
88
+ margin-bottom: 50px;
89
+ }
90
+ .printful-latest-orders .row {
91
+ display: table-row;
92
+ }
93
+ .printful-latest-orders .row .cell {
94
+ display: table-cell;
95
+ }
96
+
97
+ .wrap h2.nav-tab-wrapper.printful-tabs {
98
+ margin-bottom: 15px;
99
+ }
100
+
101
+
102
+ .printful-quick-links {
103
+ position: relative;
104
+ margin-bottom: 30px;
105
+ }
106
+ .printful-quick-links .printful-quick-links-item {
107
+ width: 120px;
108
+ background: #fff;
109
+ border: solid 1px #e1e1e1;
110
+ float: left;
111
+ padding: 20px;
112
+ margin-right: 25px;
113
+ margin-bottom: 25px;
114
+ text-align: center;
115
+ text-decoration: none;
116
+ }
117
+ .printful-quick-links .printful-quick-links-item .dashicons {
118
+ font-size: 40px;
119
+ width: 40px;
120
+ height: 40px;
121
+ text-decoration: none;
122
+ color: #444;
123
+ }
124
+ .printful-quick-links .printful-quick-links-item h4 {
125
+ text-decoration: none;
126
+ color: #444;
127
+ margin: 5px 0 0 0;
128
+ }
129
+ .printful-quick-links .printful-quick-links-item:hover {
130
+ cursor: pointer;
131
+ }
132
+ .printful-quick-links .printful-quick-links-item:hover .dashicons,
133
+ .printful-quick-links .printful-quick-links-item:hover h4 {
134
+ color: #0D72B2;
135
+ }
136
+
137
+ .printful-error {
138
+ margin-top: 20px;
139
+ }
140
+
141
+ .printful-notice {
142
+ background: #fff;
143
+ border-left: 4px solid #dc3232;
144
+ padding: 1px 12px;
145
+ margin: 10px 0 20px 0;
146
+ box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
147
+ text-align: left;
148
+ }
149
+ .printful-notice li {
150
+ font-weight: bold;
151
+ margin-left: 20px;
152
+ list-style: disc;
153
+ }
154
+
155
+ .block-loader {
156
+ text-align: center;
157
+ padding: 20px 0;
158
+ line-height: 20px;
159
+ }
160
+ .block-loader .message {
161
+ padding-left: 10px;
162
+ line-height: 20px;
163
+ display: inline-block;
164
+ font-size: 16px;
165
+ }
166
+ .block-loader .loader {
167
+ vertical-align: top;
168
+ }
169
+
170
+ #wpfooter::after {
171
+ box-sizing: border-box;
172
+ content: "";
173
+ width: 64px;
174
+ left: 50%;
175
+ margin-left: -32px;
176
+ height: 28px;
177
+ position: absolute;
178
+ bottom: 0;
179
+ background: url("../images/printful-logo-footer.png") no-repeat bottom center;
180
+ background-size: 64px;
181
  }
assets/css/global.css CHANGED
@@ -1,19 +1,19 @@
1
- .printful-toolbar .printful-toolbar-issues {
2
- background-color: #d54e21;
3
- display: inline-block;
4
- padding: 0 5px !important;
5
- border-radius: 10px !important;
6
- line-height: 16px !important;
7
- font-size: 11px !important;
8
- color: #fff !important;
9
- }
10
- .printful-toolbar:hover .printful-toolbar-issues,
11
- .printful-toolbar:active .printful-toolbar-issues,
12
- .printful-toolbar.hover .printful-toolbar-issues
13
- {
14
- color: #fff !important;
15
- }
16
-
17
- #adminmenu .toplevel_page_printful-dashboard img {
18
- width: 28px;
19
  }
1
+ .printful-toolbar .printful-toolbar-issues {
2
+ background-color: #d54e21;
3
+ display: inline-block;
4
+ padding: 0 5px !important;
5
+ border-radius: 10px !important;
6
+ line-height: 16px !important;
7
+ font-size: 11px !important;
8
+ color: #fff !important;
9
+ }
10
+ .printful-toolbar:hover .printful-toolbar-issues,
11
+ .printful-toolbar:active .printful-toolbar-issues,
12
+ .printful-toolbar.hover .printful-toolbar-issues
13
+ {
14
+ color: #fff !important;
15
+ }
16
+
17
+ #adminmenu .toplevel_page_printful-dashboard img {
18
+ width: 28px;
19
  }
assets/css/settings.css CHANGED
@@ -1,46 +1,46 @@
1
- .printful-setting-group {
2
- overflow: hidden;
3
- margin-bottom: 20px;
4
- }
5
-
6
- .printful-setting-group .input-text {
7
- width: 400px;
8
- }
9
-
10
- .printful-setting-group .checkbox-group-item i {
11
- color: #777;
12
- }
13
-
14
- .printful-submit .woocommerce-save-button {
15
- float: left;
16
- }
17
-
18
- .printful-submit .loader-wrap {
19
- display: inline-block;
20
- padding: 4px 10px;
21
- float: left;
22
- }
23
-
24
- .printful-submit .loader-wrap .loader {
25
- display: none;
26
- float: left;
27
- }
28
-
29
- .printful-submit .loader-wrap .dashicons {
30
- font-size: 24px;
31
- width: 24px;
32
- height: 24px;
33
- }
34
-
35
- .printful-submit .loader-wrap .pass,
36
- .printful-submit .loader-wrap .fail {
37
- display: none;
38
- float: left;
39
- line-height: 24px;
40
- }
41
-
42
- .printful-setting-group .carrier-type {
43
- font-weight: bold;
44
- padding: 20px 0;
45
- display: block;
46
  }
1
+ .printful-setting-group {
2
+ overflow: hidden;
3
+ margin-bottom: 20px;
4
+ }
5
+
6
+ .printful-setting-group .input-text {
7
+ width: 400px;
8
+ }
9
+
10
+ .printful-setting-group .checkbox-group-item i {
11
+ color: #777;
12
+ }
13
+
14
+ .printful-submit .woocommerce-save-button {
15
+ float: left;
16
+ }
17
+
18
+ .printful-submit .loader-wrap {
19
+ display: inline-block;
20
+ padding: 4px 10px;
21
+ float: left;
22
+ }
23
+
24
+ .printful-submit .loader-wrap .loader {
25
+ display: none;
26
+ float: left;
27
+ }
28
+
29
+ .printful-submit .loader-wrap .dashicons {
30
+ font-size: 24px;
31
+ width: 24px;
32
+ height: 24px;
33
+ }
34
+
35
+ .printful-submit .loader-wrap .pass,
36
+ .printful-submit .loader-wrap .fail {
37
+ display: none;
38
+ float: left;
39
+ line-height: 24px;
40
+ }
41
+
42
+ .printful-setting-group .carrier-type {
43
+ font-weight: bold;
44
+ padding: 20px 0;
45
+ display: block;
46
  }
assets/css/size-guide.css ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .pf-size-guide-modal-wrapper {
2
+ position: fixed;
3
+ z-index: 2147483648;
4
+ padding: 20px;
5
+ top: 0;
6
+ width: 100%;
7
+ height: 100%;
8
+ left: 0;
9
+ background: rgba(0, 0, 0, 0.6);
10
+ box-sizing: border-box;
11
+ }
12
+
13
+ .pf-size-guide-modal {
14
+ background-color: #fff;
15
+ width: 100%;
16
+ height: 100%;
17
+ max-width: 900px;
18
+ overflow: hidden;
19
+ overflow-y: auto;
20
+ position: relative;
21
+ margin: auto;
22
+ }
23
+
24
+ .pf-size-guide-modal__close {
25
+ height: 30px;
26
+ width: 30px;
27
+ cursor: pointer;
28
+ border: 0;
29
+ background: 0 0;
30
+ padding: 0;
31
+ -webkit-appearance: none;
32
+ float: right;
33
+ background: none !important;
34
+ }
35
+
36
+ .pf-size-guide-modal__header {
37
+ width: 100%;
38
+ box-sizing: border-box;
39
+ padding: 15px;
40
+ }
41
+
42
+ .pf-size-guide-modal__title {
43
+ float: left;
44
+ font-weight: bold;
45
+ margin: 0;
46
+ clear: none;
47
+ }
48
+
49
+ .pf-size-guide-modal__close img {
50
+ height: 30px;
51
+ width: 30px;
52
+ }
53
+
54
+ .pf-size-guide-modal__content {
55
+ padding: 15px;
56
+ }
57
+
58
+ .pf-size-guide-modal__content h4 {
59
+ margin: 24px 0 16px;
60
+ }
61
+ .pf-size-guide-modal__content p {
62
+ margin: 0 0 10px;
63
+ }
64
+ .pf-size-guide-modal-size-chart {
65
+ overflow-x: auto;
66
+ }
67
+
68
+ .pf-product-size-guide__tabs {
69
+ white-space: nowrap;
70
+ }
71
+
72
+ .pf-product-size-guide__tabs,
73
+ .pf-size-guide-modal-size-chart__tabs {
74
+ list-style: none;
75
+ padding: 0;
76
+ margin: 0;
77
+ box-shadow: inset 0 -1px 0 0 #E5E5E5;
78
+ }
79
+
80
+ .pf-product-size-guide__tab {
81
+ cursor: pointer;
82
+ vertical-align: bottom;
83
+ margin-right: 8px;
84
+ display: inline-block;
85
+ padding: 12px 16px;
86
+ border: 1px solid #F8F8F8;
87
+ border-bottom: 1px solid #E5E5E5;
88
+ }
89
+
90
+ .pf-product-size-guide__tab.pf-product-size-guide__tab--active {
91
+ border: solid 1px #E5E5E5;
92
+ border-bottom: none;
93
+ }
94
+
95
+ .pf-size-guide-modal-size-chart__tab {
96
+ font-size: 15px;
97
+ font-weight: bold;
98
+ border: solid 1px transparent;
99
+ cursor: pointer;
100
+ vertical-align: bottom;
101
+ margin-right: 8px;
102
+ display: inline-block;
103
+ padding: 12px 8px;
104
+ text-transform: capitalize;
105
+ }
106
+ .pf-size-guide-modal-size-chart__tab.pf-size-guide-modal-size-chart__tab--active {
107
+ box-shadow: inset 0 -3px #222;
108
+ font-weight: bold;
109
+ }
110
+
111
+ .pf-size-guide-modal-measurements {
112
+ padding: 15px 0;
113
+ }
114
+
115
+ .pf-size-guide-modal-measurements__description {
116
+ float: left;
117
+ width: 65%;
118
+ position: relative;
119
+ }
120
+
121
+ .pf-size-guide-modal-measurements__image {
122
+ position: relative;
123
+ width: 35%;
124
+ padding: 5px;
125
+ text-align: center;
126
+ float: left;
127
+ }
128
+
129
+ @media only screen and (max-width: 960px) {
130
+ .pf-size-guide-modal-measurements__image,
131
+ .pf-size-guide-modal-measurements__description {
132
+ float: none;
133
+ display: block;
134
+ width: 100%;
135
+ }
136
+
137
+ .pf-product-size-guide__tabs {
138
+ overflow-x: scroll;
139
+ }
140
+ }
141
+
142
+ .pf-size-guide-modal-size-chart__table {
143
+ border-collapse: collapse;
144
+ table-layout: fixed;
145
+ width: 100%;
146
+ overflow-x: auto;
147
+ white-space: nowrap;
148
+ }
149
+
150
+ .pf-size-guide-modal-size-chart__table thead {
151
+ text-transform: uppercase;
152
+ font-weight: bold;
153
+ }
154
+
155
+ .pf-size-guide-modal-size-chart__table tr {
156
+ border: none;
157
+ border-collapse: collapse;
158
+ }
159
+
160
+ .pf-size-guide-modal-size-chart__table td {
161
+ border: none;
162
+ border-bottom: 1px solid #E5E5E5;
163
+ font-size: 16px;
164
+ text-transform: uppercase;
165
+ padding: 12px 8px;
166
+ }
167
+
168
+ .pf-size-guide-modal-clear {
169
+ clear: both;
170
+ }
171
+
assets/css/status.css CHANGED
@@ -1,21 +1,21 @@
1
- .pass {
2
- font-weight: bold;
3
- color: green;
4
- }
5
- .fail {
6
- font-weight: bold;
7
- color: red;
8
- }
9
- .warning {
10
- font-weight: bold;
11
- color: #cab827;
12
- }
13
- .printful-status .col-status {
14
- width: 10%;
15
- }
16
- .printful-status .col-desc {
17
- width: 57%;
18
- }
19
- .printful-status tbody tr:first-child td {
20
- font-weight: bold;
21
  }
1
+ .pass {
2
+ font-weight: bold;
3
+ color: green;
4
+ }
5
+ .fail {
6
+ font-weight: bold;
7
+ color: red;
8
+ }
9
+ .warning {
10
+ font-weight: bold;
11
+ color: #cab827;
12
+ }
13
+ .printful-status .col-status {
14
+ width: 10%;
15
+ }
16
+ .printful-status .col-desc {
17
+ width: 57%;
18
+ }
19
+ .printful-status tbody tr:first-child td {
20
+ font-weight: bold;
21
  }
assets/css/support.css CHANGED
@@ -1,62 +1,62 @@
1
- .support-report-wrap {
2
- background-color: #fff;
3
- padding: 20px;
4
- margin: 20px 0;
5
- box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
6
- border-left: #0073aa 4px solid;
7
- }
8
-
9
- .support-report-wrap p {
10
- margin-top: 0;
11
- }
12
-
13
- .support-report-wrap .support-report {
14
- width: 100%;
15
- height: 100px;
16
- font-family: monospace;
17
- margin-bottom: 10px;
18
- background: #efefef;
19
- }
20
-
21
- .support-info-wrap {
22
- margin-top: 40px;
23
- box-sizing: border-box;
24
-
25
- }
26
-
27
- .support-info-wrap .support-info-block {
28
- box-sizing: border-box;
29
- padding: 20px;
30
- background: #fff;
31
- box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
32
- margin-bottom: 20px;
33
- }
34
-
35
- .support-info-wrap .support-info-block h3 {
36
- text-align: center;
37
- margin-top: 10px;
38
- }
39
-
40
- .support-info-wrap .support-info-block .btn-wrap {
41
- margin-top: 20px;
42
- text-align: center;
43
- }
44
-
45
- @media (min-width: 768px) {
46
-
47
- .support-info-wrap {
48
- justify-content: space-between;
49
- display: flex;
50
- }
51
-
52
- .support-info-wrap .support-info-block {
53
- width: 32%;
54
- }
55
-
56
- .support-info-wrap .support-info-block:first-child {
57
-
58
- }
59
- .support-info-wrap .support-info-block:last-child {
60
-
61
- }
62
  }
1
+ .support-report-wrap {
2
+ background-color: #fff;
3
+ padding: 20px;
4
+ margin: 20px 0;
5
+ box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
6
+ border-left: #0073aa 4px solid;
7
+ }
8
+
9
+ .support-report-wrap p {
10
+ margin-top: 0;
11
+ }
12
+
13
+ .support-report-wrap .support-report {
14
+ width: 100%;
15
+ height: 100px;
16
+ font-family: monospace;
17
+ margin-bottom: 10px;
18
+ background: #efefef;
19
+ }
20
+
21
+ .support-info-wrap {
22
+ margin-top: 40px;
23
+ box-sizing: border-box;
24
+
25
+ }
26
+
27
+ .support-info-wrap .support-info-block {
28
+ box-sizing: border-box;
29
+ padding: 20px;
30
+ background: #fff;
31
+ box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
32
+ margin-bottom: 20px;
33
+ }
34
+
35
+ .support-info-wrap .support-info-block h3 {
36
+ text-align: center;
37
+ margin-top: 10px;
38
+ }
39
+
40
+ .support-info-wrap .support-info-block .btn-wrap {
41
+ margin-top: 20px;
42
+ text-align: center;
43
+ }
44
+
45
+ @media (min-width: 768px) {
46
+
47
+ .support-info-wrap {
48
+ justify-content: space-between;
49
+ display: flex;
50
+ }
51
+
52
+ .support-info-wrap .support-info-block {
53
+ width: 32%;
54
+ }
55
+
56
+ .support-info-wrap .support-info-block:first-child {
57
+
58
+ }
59
+ .support-info-wrap .support-info-block:last-child {
60
+
61
+ }
62
  }
assets/images/connect.svg CHANGED
@@ -1,74 +1,74 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="408" height="177" viewBox="0 0 408 177">
2
- <g fill="none" fill-rule="evenodd" transform="translate(1 1)">
3
- <path fill="#FFF" fill-rule="nonzero" stroke="#231F20" stroke-width="2" d="M299 120h107v8H299zM0 120h107v8H0z"/>
4
- <path stroke="#D1D3D4" d="M197.5 21V10.2c0-5.5-4.1-9.8-9.6-9.8h-124c-5.5 0-10.4 4.2-10.4 9.8V87M208.5 21V10.2c0-5.5 4.9-9.8 10.4-9.8h124c5.5 0 9.6 4.2 9.6 9.8V87"/>
5
- <path fill="#FFF" fill-rule="nonzero" stroke="#231F20" stroke-width="2" d="M5 48h97v72H5z"/>
6
- <g fill-rule="nonzero">
7
- <path fill="#9B5C8F" d="M27.104 70h44.77C74.706 70 77 72.312 77 75.168v17.225c0 2.856-2.293 5.168-5.127 5.168H55.82L58.022 103l-9.691-5.44H27.127C24.294 97.56 22 95.25 22 92.394V75.168C21.978 72.334 24.27 70 27.104 70z"/>
8
- <path fill="#FFF" d="M25.389 74.664c.316-.431.79-.658 1.422-.703 1.152-.091 1.807.453 1.965 1.633.7 4.743 1.468 8.76 2.28 12.05l4.946-9.463c.451-.862 1.016-1.316 1.693-1.361.994-.068 1.603.567 1.852 1.906.565 3.017 1.287 5.582 2.145 7.76.587-5.763 1.58-9.916 2.98-12.48.34-.635.836-.953 1.491-.998.52-.046.994.113 1.423.453.429.34.655.772.7 1.294.022.408-.045.749-.226 1.09-.88 1.633-1.603 4.378-2.19 8.19-.565 3.699-.768 6.58-.633 8.646.046.567-.045 1.066-.27 1.497-.272.5-.678.772-1.197.817-.588.046-1.197-.227-1.784-.84-2.1-2.155-3.771-5.377-4.99-9.666a425.409 425.409 0 0 0-3.252 6.535c-1.333 2.564-2.462 3.88-3.41 3.948-.61.046-1.13-.476-1.58-1.565-1.152-2.973-2.394-8.714-3.726-17.223-.09-.59.044-1.112.36-1.52zm48.526 3.563c-.813-1.43-2.01-2.292-3.613-2.633a5.904 5.904 0 0 0-1.22-.136c-2.167 0-3.928 1.135-5.306 3.404-1.174 1.929-1.76 4.062-1.76 6.399 0 1.747.36 3.244 1.083 4.493.813 1.43 2.01 2.291 3.613 2.632.429.09.835.136 1.22.136 2.19 0 3.95-1.135 5.306-3.404 1.174-1.951 1.761-4.084 1.761-6.421.023-1.77-.361-3.245-1.084-4.47zm-2.845 6.285c-.316 1.498-.88 2.61-1.716 3.358-.655.59-1.265.84-1.83.726-.541-.113-.993-.59-1.331-1.475a5.778 5.778 0 0 1-.407-2.065c0-.567.045-1.134.158-1.656a7.357 7.357 0 0 1 1.197-2.7c.745-1.112 1.535-1.566 2.348-1.407.542.113.994.59 1.333 1.475.27.703.406 1.407.406 2.065 0 .59-.045 1.157-.158 1.679zm-11.29-6.285c-.813-1.43-2.033-2.292-3.613-2.633a5.902 5.902 0 0 0-1.22-.136c-2.168 0-3.929 1.135-5.306 3.404-1.174 1.929-1.762 4.062-1.762 6.399 0 1.747.362 3.244 1.084 4.493.813 1.43 2.01 2.291 3.613 2.632.43.09.835.136 1.22.136 2.19 0 3.951-1.135 5.306-3.404 1.174-1.951 1.761-4.084 1.761-6.421 0-1.77-.361-3.245-1.084-4.47zm-2.868 6.285c-.316 1.498-.881 2.61-1.716 3.358-.655.59-1.265.84-1.83.726-.541-.113-.993-.59-1.332-1.475a5.787 5.787 0 0 1-.406-2.065c0-.567.045-1.134.158-1.656a7.354 7.354 0 0 1 1.197-2.7c.745-1.112 1.535-1.566 2.348-1.407.542.113.994.59 1.332 1.475.272.703.407 1.407.407 2.065a6.726 6.726 0 0 1-.158 1.679z"/>
9
- </g>
10
- <path fill="#FFF" fill-rule="nonzero" stroke="#231F20" stroke-width="2" d="M304 48h97v72h-97z"/>
11
- <g fill-rule="nonzero">
12
- <path fill="#F2C994" d="M320 98l17.5-30L355 98z"/>
13
- <path fill="#ED4642" d="M336 98l17.5-30L371 98z"/>
14
- <path fill="#17BCB5" d="M352 98l17.5-30L387 98z"/>
15
- <path fill="#16342F" d="M352 98h19l-9.5-16z"/>
16
- <path fill="#DF392F" d="M336 98h19l-9.5-16z"/>
17
- <path fill="#15291B" d="M352 98h3l-1.5-3z"/>
18
- </g>
19
- <path fill="#FFF" fill-rule="nonzero" stroke="#231F20" stroke-width="2" d="M155 21h97v124h-97z"/>
20
- <path fill="#FFF" fill-rule="nonzero" stroke="#231F20" stroke-width="2" d="M150 142h107v33H150z"/>
21
- <path stroke="#D1D3D4" d="M162.5 161.8l1.3-3.4 2 3.6 5.3-9.2 4 9.2 2.7-4.6 3.3 2.6"/>
22
- <path stroke="#231F20" d="M161.5 148.5h20v20h-20zM187.5 148.5h61v20h-61z"/>
23
- <circle cx="191.2" cy="33.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
24
- <path stroke="#FFF" d="M191.2 33.8l.7-3.8"/>
25
- <circle cx="191.2" cy="44.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
26
- <path stroke="#FFF" d="M191.2 44.8l2.3 3"/>
27
- <circle cx="191.2" cy="55.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
28
- <path stroke="#FFF" d="M191.2 55.8l-3 2.3"/>
29
- <circle cx="191.2" cy="66.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
30
- <path stroke="#FFF" d="M191.2 66.8l2.3 3"/>
31
- <circle cx="191.2" cy="77.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
32
- <path stroke="#FFF" d="M191.2 77.8l3.6 1.2"/>
33
- <path fill="#F1F2F2" fill-rule="nonzero" stroke="#231F20" d="M183.5 87.5h7v3h-7zM193.5 87.5h7v3h-7zM203.5 87.5h7v3h-7zM213.5 87.5h7v3h-7zM223.5 87.5h7v3h-7z"/>
34
- <path fill="#F2C994" fill-rule="nonzero" stroke="#231F20" d="M233.5 87.5h7v3h-7z"/>
35
- <path stroke="#231F20" stroke-width="2" d="M184 30v52"/>
36
- <circle cx="214.2" cy="33.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
37
- <path stroke="#FFF" d="M214.2 33.8l2.3 3"/>
38
- <circle cx="214.2" cy="44.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
39
- <path stroke="#FFF" d="M214.2 44.8l3-2.4"/>
40
- <circle cx="214.2" cy="55.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
41
- <path stroke="#FFF" d="M214.2 55.8l2.3 3"/>
42
- <circle cx="214.2" cy="66.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
43
- <path stroke="#FFF" d="M214.2 66.8l-3 2.3"/>
44
- <circle cx="214.2" cy="77.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
45
- <path stroke="#FFF" d="M214.2 77.8l2.3 3"/>
46
- <path stroke="#231F20" stroke-width="2" d="M207 30v52"/>
47
- <circle cx="237.2" cy="33.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
48
- <path stroke="#FFF" d="M237.2 33.8l3.7.8"/>
49
- <circle cx="237.2" cy="44.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
50
- <path stroke="#FFF" d="M237.2 44.8l2.3 3"/>
51
- <circle cx="237.2" cy="55.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
52
- <path stroke="#FFF" d="M237.2 55.8l.3 3.8"/>
53
- <circle cx="237.2" cy="66.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
54
- <path stroke="#FFF" d="M237.2 66.8l3.4-1.7"/>
55
- <circle cx="237.2" cy="77.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
56
- <path stroke="#FFF" d="M237.2 77.8l2.3 3"/>
57
- <path stroke="#231F20" stroke-width="2" d="M230 30v52M184 96v34M207 96v34"/>
58
- <path fill="#F1F2F2" fill-rule="nonzero" stroke="#231F20" d="M179.5 109.5h9v3h-9zM202.5 125.5h9v4h-9z"/>
59
- <path stroke="#231F20" stroke-width="2" d="M230 96v34"/>
60
- <path fill="#F1F2F2" fill-rule="nonzero" stroke="#231F20" d="M225.5 114.5h9v4h-9z"/>
61
- <circle cx="169.5" cy="35.8" r="5" fill="#F2C994" fill-rule="nonzero" stroke="#231F20" stroke-width="2"/>
62
- <circle cx="169.5" cy="48.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
63
- <circle cx="169.5" cy="59.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
64
- <circle cx="169.5" cy="70.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
65
- <circle cx="169.5" cy="81.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
66
- <circle cx="169.5" cy="99.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
67
- <circle cx="169.5" cy="113.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
68
- <circle cx="169.5" cy="127.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
69
- <circle cx="194.2" cy="152.8" r="2" fill="#F2C994" fill-rule="nonzero"/>
70
- <circle cx="210.2" cy="162.8" r="2" fill="#F2C994" fill-rule="nonzero"/>
71
- <path stroke="#231F20" d="M187 156.5h61"/>
72
- <circle cx="239.2" cy="159.8" r="2" fill="#F2C994" fill-rule="nonzero"/>
73
- </g>
74
- </svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="408" height="177" viewBox="0 0 408 177">
2
+ <g fill="none" fill-rule="evenodd" transform="translate(1 1)">
3
+ <path fill="#FFF" fill-rule="nonzero" stroke="#231F20" stroke-width="2" d="M299 120h107v8H299zM0 120h107v8H0z"/>
4
+ <path stroke="#D1D3D4" d="M197.5 21V10.2c0-5.5-4.1-9.8-9.6-9.8h-124c-5.5 0-10.4 4.2-10.4 9.8V87M208.5 21V10.2c0-5.5 4.9-9.8 10.4-9.8h124c5.5 0 9.6 4.2 9.6 9.8V87"/>
5
+ <path fill="#FFF" fill-rule="nonzero" stroke="#231F20" stroke-width="2" d="M5 48h97v72H5z"/>
6
+ <g fill-rule="nonzero">
7
+ <path fill="#9B5C8F" d="M27.104 70h44.77C74.706 70 77 72.312 77 75.168v17.225c0 2.856-2.293 5.168-5.127 5.168H55.82L58.022 103l-9.691-5.44H27.127C24.294 97.56 22 95.25 22 92.394V75.168C21.978 72.334 24.27 70 27.104 70z"/>
8
+ <path fill="#FFF" d="M25.389 74.664c.316-.431.79-.658 1.422-.703 1.152-.091 1.807.453 1.965 1.633.7 4.743 1.468 8.76 2.28 12.05l4.946-9.463c.451-.862 1.016-1.316 1.693-1.361.994-.068 1.603.567 1.852 1.906.565 3.017 1.287 5.582 2.145 7.76.587-5.763 1.58-9.916 2.98-12.48.34-.635.836-.953 1.491-.998.52-.046.994.113 1.423.453.429.34.655.772.7 1.294.022.408-.045.749-.226 1.09-.88 1.633-1.603 4.378-2.19 8.19-.565 3.699-.768 6.58-.633 8.646.046.567-.045 1.066-.27 1.497-.272.5-.678.772-1.197.817-.588.046-1.197-.227-1.784-.84-2.1-2.155-3.771-5.377-4.99-9.666a425.409 425.409 0 0 0-3.252 6.535c-1.333 2.564-2.462 3.88-3.41 3.948-.61.046-1.13-.476-1.58-1.565-1.152-2.973-2.394-8.714-3.726-17.223-.09-.59.044-1.112.36-1.52zm48.526 3.563c-.813-1.43-2.01-2.292-3.613-2.633a5.904 5.904 0 0 0-1.22-.136c-2.167 0-3.928 1.135-5.306 3.404-1.174 1.929-1.76 4.062-1.76 6.399 0 1.747.36 3.244 1.083 4.493.813 1.43 2.01 2.291 3.613 2.632.429.09.835.136 1.22.136 2.19 0 3.95-1.135 5.306-3.404 1.174-1.951 1.761-4.084 1.761-6.421.023-1.77-.361-3.245-1.084-4.47zm-2.845 6.285c-.316 1.498-.88 2.61-1.716 3.358-.655.59-1.265.84-1.83.726-.541-.113-.993-.59-1.331-1.475a5.778 5.778 0 0 1-.407-2.065c0-.567.045-1.134.158-1.656a7.357 7.357 0 0 1 1.197-2.7c.745-1.112 1.535-1.566 2.348-1.407.542.113.994.59 1.333 1.475.27.703.406 1.407.406 2.065 0 .59-.045 1.157-.158 1.679zm-11.29-6.285c-.813-1.43-2.033-2.292-3.613-2.633a5.902 5.902 0 0 0-1.22-.136c-2.168 0-3.929 1.135-5.306 3.404-1.174 1.929-1.762 4.062-1.762 6.399 0 1.747.362 3.244 1.084 4.493.813 1.43 2.01 2.291 3.613 2.632.43.09.835.136 1.22.136 2.19 0 3.951-1.135 5.306-3.404 1.174-1.951 1.761-4.084 1.761-6.421 0-1.77-.361-3.245-1.084-4.47zm-2.868 6.285c-.316 1.498-.881 2.61-1.716 3.358-.655.59-1.265.84-1.83.726-.541-.113-.993-.59-1.332-1.475a5.787 5.787 0 0 1-.406-2.065c0-.567.045-1.134.158-1.656a7.354 7.354 0 0 1 1.197-2.7c.745-1.112 1.535-1.566 2.348-1.407.542.113.994.59 1.332 1.475.272.703.407 1.407.407 2.065a6.726 6.726 0 0 1-.158 1.679z"/>
9
+ </g>
10
+ <path fill="#FFF" fill-rule="nonzero" stroke="#231F20" stroke-width="2" d="M304 48h97v72h-97z"/>
11
+ <g fill-rule="nonzero">
12
+ <path fill="#F2C994" d="M320 98l17.5-30L355 98z"/>
13
+ <path fill="#ED4642" d="M336 98l17.5-30L371 98z"/>
14
+ <path fill="#17BCB5" d="M352 98l17.5-30L387 98z"/>
15
+ <path fill="#16342F" d="M352 98h19l-9.5-16z"/>
16
+ <path fill="#DF392F" d="M336 98h19l-9.5-16z"/>
17
+ <path fill="#15291B" d="M352 98h3l-1.5-3z"/>
18
+ </g>
19
+ <path fill="#FFF" fill-rule="nonzero" stroke="#231F20" stroke-width="2" d="M155 21h97v124h-97z"/>
20
+ <path fill="#FFF" fill-rule="nonzero" stroke="#231F20" stroke-width="2" d="M150 142h107v33H150z"/>
21
+ <path stroke="#D1D3D4" d="M162.5 161.8l1.3-3.4 2 3.6 5.3-9.2 4 9.2 2.7-4.6 3.3 2.6"/>
22
+ <path stroke="#231F20" d="M161.5 148.5h20v20h-20zM187.5 148.5h61v20h-61z"/>
23
+ <circle cx="191.2" cy="33.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
24
+ <path stroke="#FFF" d="M191.2 33.8l.7-3.8"/>
25
+ <circle cx="191.2" cy="44.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
26
+ <path stroke="#FFF" d="M191.2 44.8l2.3 3"/>
27
+ <circle cx="191.2" cy="55.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
28
+ <path stroke="#FFF" d="M191.2 55.8l-3 2.3"/>
29
+ <circle cx="191.2" cy="66.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
30
+ <path stroke="#FFF" d="M191.2 66.8l2.3 3"/>
31
+ <circle cx="191.2" cy="77.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
32
+ <path stroke="#FFF" d="M191.2 77.8l3.6 1.2"/>
33
+ <path fill="#F1F2F2" fill-rule="nonzero" stroke="#231F20" d="M183.5 87.5h7v3h-7zM193.5 87.5h7v3h-7zM203.5 87.5h7v3h-7zM213.5 87.5h7v3h-7zM223.5 87.5h7v3h-7z"/>
34
+ <path fill="#F2C994" fill-rule="nonzero" stroke="#231F20" d="M233.5 87.5h7v3h-7z"/>
35
+ <path stroke="#231F20" stroke-width="2" d="M184 30v52"/>
36
+ <circle cx="214.2" cy="33.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
37
+ <path stroke="#FFF" d="M214.2 33.8l2.3 3"/>
38
+ <circle cx="214.2" cy="44.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
39
+ <path stroke="#FFF" d="M214.2 44.8l3-2.4"/>
40
+ <circle cx="214.2" cy="55.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
41
+ <path stroke="#FFF" d="M214.2 55.8l2.3 3"/>
42
+ <circle cx="214.2" cy="66.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
43
+ <path stroke="#FFF" d="M214.2 66.8l-3 2.3"/>
44
+ <circle cx="214.2" cy="77.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
45
+ <path stroke="#FFF" d="M214.2 77.8l2.3 3"/>
46
+ <path stroke="#231F20" stroke-width="2" d="M207 30v52"/>
47
+ <circle cx="237.2" cy="33.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
48
+ <path stroke="#FFF" d="M237.2 33.8l3.7.8"/>
49
+ <circle cx="237.2" cy="44.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
50
+ <path stroke="#FFF" d="M237.2 44.8l2.3 3"/>
51
+ <circle cx="237.2" cy="55.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
52
+ <path stroke="#FFF" d="M237.2 55.8l.3 3.8"/>
53
+ <circle cx="237.2" cy="66.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
54
+ <path stroke="#FFF" d="M237.2 66.8l3.4-1.7"/>
55
+ <circle cx="237.2" cy="77.8" r="4" fill="#D1D3D4" fill-rule="nonzero"/>
56
+ <path stroke="#FFF" d="M237.2 77.8l2.3 3"/>
57
+ <path stroke="#231F20" stroke-width="2" d="M230 30v52M184 96v34M207 96v34"/>
58
+ <path fill="#F1F2F2" fill-rule="nonzero" stroke="#231F20" d="M179.5 109.5h9v3h-9zM202.5 125.5h9v4h-9z"/>
59
+ <path stroke="#231F20" stroke-width="2" d="M230 96v34"/>
60
+ <path fill="#F1F2F2" fill-rule="nonzero" stroke="#231F20" d="M225.5 114.5h9v4h-9z"/>
61
+ <circle cx="169.5" cy="35.8" r="5" fill="#F2C994" fill-rule="nonzero" stroke="#231F20" stroke-width="2"/>
62
+ <circle cx="169.5" cy="48.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
63
+ <circle cx="169.5" cy="59.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
64
+ <circle cx="169.5" cy="70.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
65
+ <circle cx="169.5" cy="81.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
66
+ <circle cx="169.5" cy="99.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
67
+ <circle cx="169.5" cy="113.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
68
+ <circle cx="169.5" cy="127.8" r="3" fill="#D1D3D4" fill-rule="nonzero"/>
69
+ <circle cx="194.2" cy="152.8" r="2" fill="#F2C994" fill-rule="nonzero"/>
70
+ <circle cx="210.2" cy="162.8" r="2" fill="#F2C994" fill-rule="nonzero"/>
71
+ <path stroke="#231F20" d="M187 156.5h61"/>
72
+ <circle cx="239.2" cy="159.8" r="2" fill="#F2C994" fill-rule="nonzero"/>
73
+ </g>
74
+ </svg>
assets/js/block-loader.js CHANGED
@@ -1,22 +1,22 @@
1
- var Printful_Block_Loader;
2
-
3
- (function () {
4
- 'use strict';
5
-
6
- Printful_Block_Loader = {
7
- load: function (ajax_url, block) {
8
-
9
- block = jQuery('#' + block);
10
- if (block.length > 0) {
11
-
12
- jQuery.ajax({
13
- type: "GET",
14
- url: ajax_url,
15
- success: function (response) {
16
- block.html(response);
17
- }
18
- });
19
- }
20
- }
21
- };
22
  })();
1
+ var Printful_Block_Loader;
2
+
3
+ (function () {
4
+ 'use strict';
5
+
6
+ Printful_Block_Loader = {
7
+ load: function (ajax_url, block) {
8
+
9
+ block = jQuery('#' + block);
10
+ if (block.length > 0) {
11
+
12
+ jQuery.ajax({
13
+ type: "GET",
14
+ url: ajax_url,
15
+ success: function (response) {
16
+ block.html(response);
17
+ }
18
+ });
19
+ }
20
+ }
21
+ };
22
  })();
assets/js/connect.js CHANGED
@@ -1,60 +1,60 @@
1
- var Printful_Connect;
2
-
3
- (function () {
4
- 'use strict';
5
-
6
- Printful_Connect = {
7
- interval: 0,
8
- ajax_url: '',
9
- init: function (ajax_url) {
10
- this.ajax_url = ajax_url;
11
- this.loader();
12
- this.listen_status();
13
- this.listen_auth_return();
14
- },
15
- loader: function () {
16
- jQuery('.printful-connect-button').click(function () {
17
- jQuery(this).hide();
18
- jQuery(this).siblings('.loader').removeClass('hidden');
19
-
20
- setTimeout(function() {
21
- Printful_Connect.hide_loader();
22
- }, 60000); //hide the loader after a minute, assume failure
23
- });
24
- },
25
- hide_loader: function() {
26
- var button = jQuery('.printful-connect-button');
27
- button.show();
28
- button.siblings('.loader').addClass('hidden');
29
- },
30
- listen_status: function () {
31
- this.interval = setInterval(this.get_status.bind(this), 10000); //check status every 10 secs
32
- },
33
- get_status: function () {
34
- var interval = this.interval;
35
- jQuery.ajax( {
36
- type: "GET",
37
- url: this.ajax_url,
38
- success: function( response ) {
39
- if (response === 'OK') {
40
- clearInterval(interval);
41
- Printful_Connect.send_return_message();
42
- }
43
- }
44
- });
45
- },
46
- listen_auth_return: function () {
47
- var intercom = Intercom.getInstance();
48
- intercom.on('printful-auth', function (data) {
49
- if (data.success === true) {
50
- location.reload();
51
- }
52
- });
53
- },
54
- send_return_message: function () {
55
- var intercom = Intercom.getInstance();
56
- intercom.emit('printful-auth', {success: true});
57
- window.top.close();
58
- }
59
- };
60
  })();
1
+ var Printful_Connect;
2
+
3
+ (function () {
4
+ 'use strict';
5
+
6
+ Printful_Connect = {
7
+ interval: 0,
8
+ ajax_url: '',
9
+ init: function (ajax_url) {
10
+ this.ajax_url = ajax_url;
11
+ this.loader();
12
+ this.listen_status();
13
+ this.listen_auth_return();
14
+ },
15
+ loader: function () {
16
+ jQuery('.printful-connect-button').click(function () {
17
+ jQuery(this).hide();
18
+ jQuery(this).siblings('.loader').removeClass('hidden');
19
+
20
+ setTimeout(function() {
21
+ Printful_Connect.hide_loader();
22
+ }, 60000); //hide the loader after a minute, assume failure
23
+ });
24
+ },
25
+ hide_loader: function() {
26
+ var button = jQuery('.printful-connect-button');
27
+ button.show();
28
+ button.siblings('.loader').addClass('hidden');
29
+ },
30
+ listen_status: function () {
31
+ this.interval = setInterval(this.get_status.bind(this), 10000); //check status every 10 secs
32
+ },
33
+ get_status: function () {
34
+ var interval = this.interval;
35
+ jQuery.ajax( {
36
+ type: "GET",
37
+ url: this.ajax_url,
38
+ success: function( response ) {
39
+ if (response === 'OK') {
40
+ clearInterval(interval);
41
+ Printful_Connect.send_return_message();
42
+ }
43
+ }
44
+ });
45
+ },
46
+ listen_auth_return: function () {
47
+ var intercom = Intercom.getInstance();
48
+ intercom.on('printful-auth', function (data) {
49
+ if (data.success === true) {
50
+ location.reload();
51
+ }
52
+ });
53
+ },
54
+ send_return_message: function () {
55
+ var intercom = Intercom.getInstance();
56
+ intercom.emit('printful-auth', {success: true});
57
+ window.top.close();
58
+ }
59
+ };
60
  })();
assets/js/intercom.min.js CHANGED
@@ -1,12 +1,12 @@
1
- /*! intercom.js | https://github.com/diy/intercom.js | Apache License (v2) */
2
- var Intercom=function(){var g=function(){};g.createInterface=function(b){return{on:function(a,c){"undefined"===typeof this[b]&&(this[b]={});this[b].hasOwnProperty(a)||(this[b][a]=[]);this[b][a].push(c)},off:function(a,c){"undefined"!==typeof this[b]&&this[b].hasOwnProperty(a)&&i.removeItem(c,this[b][a])},trigger:function(a){if("undefined"!==typeof this[b]&&this[b].hasOwnProperty(a))for(var c=Array.prototype.slice.call(arguments,1),e=0;e<this[b][a].length;e++)this[b][a][e].apply(this[b][a][e],c)}}};
3
- var m=g.createInterface("_handlers");g.prototype._on=m.on;g.prototype._off=m.off;g.prototype._trigger=m.trigger;var n=g.createInterface("handlers");g.prototype.on=function(){n.on.apply(this,arguments);Array.prototype.unshift.call(arguments,"on");this._trigger.apply(this,arguments)};g.prototype.off=n.off;g.prototype.trigger=n.trigger;var f=window.localStorage;"undefined"===typeof f&&(f={getItem:function(){},setItem:function(){},removeItem:function(){}});var i={},h=function(){return(65536*(1+Math.random())|
4
- 0).toString(16).substring(1)};i.guid=function(){return h()+h()+"-"+h()+"-"+h()+"-"+h()+"-"+h()+h()+h()};i.throttle=function(b,a){var c=0;return function(){var e=(new Date).getTime();e-c>b&&(c=e,a.apply(this,arguments))}};i.extend=function(b,a){if("undefined"===typeof b||!b)b={};if("object"===typeof a)for(var c in a)a.hasOwnProperty(c)&&(b[c]=a[c]);return b};i.removeItem=function(b,a){for(var c=a.length-1;0<=c;c--)a[c]===b&&a.splice(c,1);return a};var d=function(){var b=this,a=(new Date).getTime();
5
- this.origin=i.guid();this.lastMessage=a;this.bindings=[];this.receivedIDs={};this.previousValues={};a=function(){b._onStorageEvent.apply(b,arguments)};window.attachEvent?document.attachEvent("onstorage",a):window.addEventListener("storage",a,!1)};d.prototype._transaction=function(b){var a=this,c=!1,e=!1,p=null,d=function(){if(!c){var g=(new Date).getTime(),s=parseInt(f.getItem(l)||0);s&&1E3>g-s?(e||(a._on("storage",d),e=!0),p=window.setTimeout(d,20)):(c=!0,f.setItem(l,g),b(),e&&a._off("storage",d),
6
- p&&window.clearTimeout(p),f.removeItem(l))}};d()};d.prototype._cleanup_emit=i.throttle(100,function(){this._transaction(function(){for(var b=(new Date).getTime()-t,a=0,c=JSON.parse(f.getItem(j)||"[]"),e=c.length-1;0<=e;e--)c[e].timestamp<b&&(c.splice(e,1),a++);0<a&&f.setItem(j,JSON.stringify(c))})});d.prototype._cleanup_once=i.throttle(100,function(){var b=this;this._transaction(function(){var a,c=JSON.parse(f.getItem(k)||"{}");(new Date).getTime();var e=0;for(a in c)b._once_expired(a,c)&&(delete c[a],
7
- e++);0<e&&f.setItem(k,JSON.stringify(c))})});d.prototype._once_expired=function(b,a){if(!a||!a.hasOwnProperty(b)||"object"!==typeof a[b])return!0;var c=a[b].ttl||u,e=(new Date).getTime();return a[b].timestamp<e-c};d.prototype._localStorageChanged=function(b,a){if(b&&b.key)return b.key===a;var c=f.getItem(a);if(c===this.previousValues[a])return!1;this.previousValues[a]=c;return!0};d.prototype._onStorageEvent=function(b){var b=b||window.event,a=this;this._localStorageChanged(b,j)&&this._transaction(function(){for(var b=
8
- (new Date).getTime(),e=f.getItem(j),e=JSON.parse(e||"[]"),d=0;d<e.length;d++)if(e[d].origin!==a.origin&&!(e[d].timestamp<a.lastMessage)){if(e[d].id){if(a.receivedIDs.hasOwnProperty(e[d].id))continue;a.receivedIDs[e[d].id]=!0}a.trigger(e[d].name,e[d].payload)}a.lastMessage=b});this._trigger("storage",b)};d.prototype._emit=function(b,a,c){if((c="string"===typeof c||"number"===typeof c?String(c):null)&&c.length){if(this.receivedIDs.hasOwnProperty(c))return;this.receivedIDs[c]=!0}var e={id:c,name:b,origin:this.origin,
9
- timestamp:(new Date).getTime(),payload:a},d=this;this._transaction(function(){var c=f.getItem(j)||"[]",c=[c.substring(0,c.length-1),"[]"===c?"":",",JSON.stringify(e),"]"].join("");f.setItem(j,c);d.trigger(b,a);window.setTimeout(function(){d._cleanup_emit()},50)})};d.prototype.bind=function(b,a){for(var c=0;c<d.bindings.length;c++){var e=d.bindings[c].factory(b,a||null,this);e&&this.bindings.push(e)}};d.prototype.emit=function(b,a){this._emit.apply(this,arguments);this._trigger("emit",b,a)};d.prototype.once=
10
- function(b,a,c){if(d.supported){var e=this;this._transaction(function(){var d=JSON.parse(f.getItem(k)||"{}");e._once_expired(b,d)&&(d[b]={},d[b].timestamp=(new Date).getTime(),"number"===typeof c&&(d[b].ttl=1E3*c),f.setItem(k,JSON.stringify(d)),a(),window.setTimeout(function(){e._cleanup_once()},50))})}};i.extend(d.prototype,g.prototype);d.bindings=[];d.supported="undefined"!==typeof f;var j="intercom",k="intercom_once",l="intercom_lock",t=5E4,u=36E5;d.destroy=function(){f.removeItem(l);f.removeItem(j);
11
- f.removeItem(k)};var q=null;d.getInstance=function(){q||(q=new d);return q};var r=function(b,a,c){a=i.extend({id:null,send:!0,receive:!0},a);if(a.receive){var d=[],f=function(f){-1===d.indexOf(f)&&(d.push(f),b.on(f,function(b){var d="function"===typeof a.id?a.id(f,b):null,e="function"===typeof a.receive?a.receive(f,b):!0;(e||"boolean"!==typeof e)&&c._emit(f,b,d)}))},g;for(g in c.handlers)for(var h=0;h<c.handlers[g].length;h++)f(g,c.handlers[g][h]);c._on("on",f)}a.send&&c._on("emit",function(c,d){var e=
12
  "function"===typeof a.send?a.send(c,d):!0;(e||"boolean"!==typeof e)&&b.emit(c,d)})};r.factory=function(b,a,c){return"undefined"===typeof b.socket?!1:new r(b,a,c)};d.bindings.push(r);return d}();
1
+ /*! intercom.js | https://github.com/diy/intercom.js | Apache License (v2) */
2
+ var Intercom=function(){var g=function(){};g.createInterface=function(b){return{on:function(a,c){"undefined"===typeof this[b]&&(this[b]={});this[b].hasOwnProperty(a)||(this[b][a]=[]);this[b][a].push(c)},off:function(a,c){"undefined"!==typeof this[b]&&this[b].hasOwnProperty(a)&&i.removeItem(c,this[b][a])},trigger:function(a){if("undefined"!==typeof this[b]&&this[b].hasOwnProperty(a))for(var c=Array.prototype.slice.call(arguments,1),e=0;e<this[b][a].length;e++)this[b][a][e].apply(this[b][a][e],c)}}};
3
+ var m=g.createInterface("_handlers");g.prototype._on=m.on;g.prototype._off=m.off;g.prototype._trigger=m.trigger;var n=g.createInterface("handlers");g.prototype.on=function(){n.on.apply(this,arguments);Array.prototype.unshift.call(arguments,"on");this._trigger.apply(this,arguments)};g.prototype.off=n.off;g.prototype.trigger=n.trigger;var f=window.localStorage;"undefined"===typeof f&&(f={getItem:function(){},setItem:function(){},removeItem:function(){}});var i={},h=function(){return(65536*(1+Math.random())|
4
+ 0).toString(16).substring(1)};i.guid=function(){return h()+h()+"-"+h()+"-"+h()+"-"+h()+"-"+h()+h()+h()};i.throttle=function(b,a){var c=0;return function(){var e=(new Date).getTime();e-c>b&&(c=e,a.apply(this,arguments))}};i.extend=function(b,a){if("undefined"===typeof b||!b)b={};if("object"===typeof a)for(var c in a)a.hasOwnProperty(c)&&(b[c]=a[c]);return b};i.removeItem=function(b,a){for(var c=a.length-1;0<=c;c--)a[c]===b&&a.splice(c,1);return a};var d=function(){var b=this,a=(new Date).getTime();
5
+ this.origin=i.guid();this.lastMessage=a;this.bindings=[];this.receivedIDs={};this.previousValues={};a=function(){b._onStorageEvent.apply(b,arguments)};window.attachEvent?document.attachEvent("onstorage",a):window.addEventListener("storage",a,!1)};d.prototype._transaction=function(b){var a=this,c=!1,e=!1,p=null,d=function(){if(!c){var g=(new Date).getTime(),s=parseInt(f.getItem(l)||0);s&&1E3>g-s?(e||(a._on("storage",d),e=!0),p=window.setTimeout(d,20)):(c=!0,f.setItem(l,g),b(),e&&a._off("storage",d),
6
+ p&&window.clearTimeout(p),f.removeItem(l))}};d()};d.prototype._cleanup_emit=i.throttle(100,function(){this._transaction(function(){for(var b=(new Date).getTime()-t,a=0,c=JSON.parse(f.getItem(j)||"[]"),e=c.length-1;0<=e;e--)c[e].timestamp<b&&(c.splice(e,1),a++);0<a&&f.setItem(j,JSON.stringify(c))})});d.prototype._cleanup_once=i.throttle(100,function(){var b=this;this._transaction(function(){var a,c=JSON.parse(f.getItem(k)||"{}");(new Date).getTime();var e=0;for(a in c)b._once_expired(a,c)&&(delete c[a],
7
+ e++);0<e&&f.setItem(k,JSON.stringify(c))})});d.prototype._once_expired=function(b,a){if(!a||!a.hasOwnProperty(b)||"object"!==typeof a[b])return!0;var c=a[b].ttl||u,e=(new Date).getTime();return a[b].timestamp<e-c};d.prototype._localStorageChanged=function(b,a){if(b&&b.key)return b.key===a;var c=f.getItem(a);if(c===this.previousValues[a])return!1;this.previousValues[a]=c;return!0};d.prototype._onStorageEvent=function(b){var b=b||window.event,a=this;this._localStorageChanged(b,j)&&this._transaction(function(){for(var b=
8
+ (new Date).getTime(),e=f.getItem(j),e=JSON.parse(e||"[]"),d=0;d<e.length;d++)if(e[d].origin!==a.origin&&!(e[d].timestamp<a.lastMessage)){if(e[d].id){if(a.receivedIDs.hasOwnProperty(e[d].id))continue;a.receivedIDs[e[d].id]=!0}a.trigger(e[d].name,e[d].payload)}a.lastMessage=b});this._trigger("storage",b)};d.prototype._emit=function(b,a,c){if((c="string"===typeof c||"number"===typeof c?String(c):null)&&c.length){if(this.receivedIDs.hasOwnProperty(c))return;this.receivedIDs[c]=!0}var e={id:c,name:b,origin:this.origin,
9
+ timestamp:(new Date).getTime(),payload:a},d=this;this._transaction(function(){var c=f.getItem(j)||"[]",c=[c.substring(0,c.length-1),"[]"===c?"":",",JSON.stringify(e),"]"].join("");f.setItem(j,c);d.trigger(b,a);window.setTimeout(function(){d._cleanup_emit()},50)})};d.prototype.bind=function(b,a){for(var c=0;c<d.bindings.length;c++){var e=d.bindings[c].factory(b,a||null,this);e&&this.bindings.push(e)}};d.prototype.emit=function(b,a){this._emit.apply(this,arguments);this._trigger("emit",b,a)};d.prototype.once=
10
+ function(b,a,c){if(d.supported){var e=this;this._transaction(function(){var d=JSON.parse(f.getItem(k)||"{}");e._once_expired(b,d)&&(d[b]={},d[b].timestamp=(new Date).getTime(),"number"===typeof c&&(d[b].ttl=1E3*c),f.setItem(k,JSON.stringify(d)),a(),window.setTimeout(function(){e._cleanup_once()},50))})}};i.extend(d.prototype,g.prototype);d.bindings=[];d.supported="undefined"!==typeof f;var j="intercom",k="intercom_once",l="intercom_lock",t=5E4,u=36E5;d.destroy=function(){f.removeItem(l);f.removeItem(j);
11
+ f.removeItem(k)};var q=null;d.getInstance=function(){q||(q=new d);return q};var r=function(b,a,c){a=i.extend({id:null,send:!0,receive:!0},a);if(a.receive){var d=[],f=function(f){-1===d.indexOf(f)&&(d.push(f),b.on(f,function(b){var d="function"===typeof a.id?a.id(f,b):null,e="function"===typeof a.receive?a.receive(f,b):!0;(e||"boolean"!==typeof e)&&c._emit(f,b,d)}))},g;for(g in c.handlers)for(var h=0;h<c.handlers[g].length;h++)f(g,c.handlers[g][h]);c._on("on",f)}a.send&&c._on("emit",function(c,d){var e=
12
  "function"===typeof a.send?a.send(c,d):!0;(e||"boolean"!==typeof e)&&b.emit(c,d)})};r.factory=function(b,a,c){return"undefined"===typeof b.socket?!1:new r(b,a,c)};d.bindings.push(r);return d}();
assets/js/product-size-guide.js ADDED
@@ -0,0 +1,439 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /** Define class */
2
+ var Printful_Product_Size_Guide;
3
+
4
+ (function () {
5
+ 'use strict';
6
+
7
+ /**
8
+ * @type {{modal: null, onSizeGuideClick: onSizeGuideClick, closeModal: closeModal, createModal: (function(string): HTMLDivElement)}}
9
+ */
10
+ Printful_Product_Size_Guide = {
11
+ modal: null,
12
+ modalContentNode: null,
13
+ modalBodyNode: null,
14
+ sizeChartNode: null,
15
+ /**
16
+ * @var {{}}
17
+ * @var {Array<string>} sizeGuideData.availableSizes
18
+ * @var {{}} sizeGuideData.modelMeasurements
19
+ * @var {{}} sizeGuideData.productMeasurements
20
+ */
21
+ sizeGuideData: null,
22
+
23
+ /**
24
+ * Handle click event
25
+ */
26
+ onSizeGuideClick: function () {
27
+ if (!window.pfGlobal || !window.pfGlobal.sg_data_raw) {
28
+ return;
29
+ }
30
+ this.sizeGuideData = JSON.parse(window.pfGlobal.sg_data_raw);
31
+ document.body.appendChild(this.createModal());
32
+ },
33
+
34
+ /**
35
+ * Close the modal element
36
+ */
37
+ closeModal: function () {
38
+ this.removeNode(this.sizeChartNode);
39
+ this.removeNode(this.modalContentNode);
40
+ this.removeNode(this.modalBodyNode);
41
+ this.removeNode(this.modal);
42
+ this.sizeChartNode = null;
43
+ this.modalContentNode = null;
44
+ this.modalBodyNode = null;
45
+ this.modal = null;
46
+ },
47
+
48
+ /**
49
+ * Create modal content
50
+ * @returns {HTMLDivElement}
51
+ */
52
+ createModal: function () {
53
+ // Clear the old one just to be sure
54
+ this.closeModal();
55
+ this.modal = this.buildEl('div', 'pf-size-guide-modal-wrapper');
56
+ this.modalBodyNode = this.buildEl('div', 'pf-size-guide-modal');
57
+ this.modalBodyNode.style.color = this.getTextColor();
58
+ this.modalBodyNode.style.backgroundColor = this.getBackGroundColor();
59
+ this.modalBodyNode.appendChild(this.buildModalHeader());
60
+
61
+ // Render initial active tab
62
+ this.renderModalContent(Printful_Product_Size_Guide.TAB_TYPE_PERSON);
63
+ this.modal.appendChild(this.modalBodyNode);
64
+
65
+ return this.modal;
66
+ },
67
+
68
+ buildEl: function (tagName, className, attributes) {
69
+ attributes = attributes || {};
70
+ var el = document.createElement(tagName);
71
+ if (className) {
72
+ el.className = className;
73
+ }
74
+
75
+ if (attributes.id) {
76
+ el.setAttribute('id', attributes.id);
77
+ }
78
+
79
+ if (attributes.innerHTML) {
80
+ el.innerHTML = attributes.innerHTML;
81
+ }
82
+
83
+ if (attributes.src) {
84
+ el.setAttribute('src', attributes.src);
85
+ }
86
+
87
+ if (attributes.onclick) {
88
+ el.onclick = attributes.onclick;
89
+ }
90
+
91
+ if (['td', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p'].indexOf(tagName) > -1) {
92
+ el.style.backgroundColor = this.getBackGroundColor();
93
+ el.style.color = this.getTextColor();
94
+ }
95
+
96
+ return el;
97
+ },
98
+
99
+ removeNode: function (node) {
100
+ if (node && node.parentNode && node.parentNode.tagName) {
101
+ node.parentNode.removeChild(node);
102
+ }
103
+ },
104
+
105
+ renderModalContent: function (activeTabType) {
106
+ this.removeNode(this.modalContentNode);
107
+ this.modalContentNode = this.buildEl('div', 'pf-size-guide-modal__content');
108
+
109
+ var tabData = this.getSizeGuideDataForTab(activeTabType),
110
+ tabTitle = this.getTabTitle(activeTabType);
111
+
112
+ this.modalContentNode.appendChild(this.buildSizeGuideTabsNode(activeTabType));
113
+ this.modalContentNode.appendChild(this.buildTabContent(tabData, tabTitle));
114
+
115
+ this.modalBodyNode.appendChild(this.modalContentNode);
116
+ },
117
+
118
+ getSizeGuideDataForTab: function (tabType) {
119
+ if (tabType === Printful_Product_Size_Guide.TAB_TYPE_PERSON) {
120
+ return this.sizeGuideData.modelMeasurements;
121
+ }
122
+
123
+ if (tabType === Printful_Product_Size_Guide.TAB_TYPE_PRODUCT) {
124
+ return this.sizeGuideData.productMeasurements;
125
+ }
126
+
127
+ return {};
128
+ },
129
+
130
+ getTabTitle: function (tabType) {
131
+ if (tabType === Printful_Product_Size_Guide.TAB_TYPE_PERSON) {
132
+ return window.pfGlobal && window.pfGlobal.sg_tab_title_person ? window.pfGlobal.sg_tab_title_person : 'Measure yourself';
133
+ }
134
+
135
+ return window.pfGlobal && window.pfGlobal.sg_tab_title_product ? window.pfGlobal.sg_tab_title_product : 'Product measurements';
136
+ },
137
+
138
+ renderSizeChart: function (measurementData, selectedUnit) {
139
+ if (this.sizeChartNode) {
140
+ this.removeNode(this.sizeChartNode.firstChild);
141
+ } else {
142
+ this.sizeChartNode = this.buildEl('div', 'pf-size-guide-modal-size-chart');
143
+ }
144
+
145
+ this.sizeChartNode.appendChild(this.buildSizeChartBlock(measurementData, selectedUnit));
146
+ },
147
+
148
+ buildModalHeader: function () {
149
+ var title = window.pfGlobal && window.pfGlobal.sg_modal_title ? window.pfGlobal.sg_modal_title : 'Size guide',
150
+ wrapper = this.buildEl('div', 'pf-size-guide-modal__header');
151
+
152
+ wrapper.appendChild(this.buildEl('h4', 'pf-size-guide-modal__title', {innerHTML: title}));
153
+
154
+ var closeBtn = this.buildEl('button', 'pf-size-guide-modal__close'),
155
+ img = this.buildEl('img');
156
+
157
+ img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAAAXNSR0IArs4c6QAAAgVJREFUaAXtmU2KwkAQhZ0hQbyDu7mIB3PhwoN5EXfeIYguJo9JQRMydurnNYxTDTH+dNWr71VD2mSzyZEOpAPpQDqQDqQD6UA6kA6kA80dOJ1O++Px+NVKGFrQ9Oh9WoMhPAzD5fF4XFpAQwNa0PRAf1iABXaMle7e+r4/nM/nqyVfLUZgx3nS3etutzuMddxqsfPf1cALsJKTAr0AK3omaPWSfj6f21ERx3zso5f3C1hob6da5nW8/KzuMLJVCgnpNEvDBMyGZsGibjMwC5oJ6waOhmbDhgBHQbeADQP2QreCDQW2QreEDQfWQreGpQCvhcY8bFTGk2wX8ZWMkGu5JCvPrstSmWj+vta9aX5TWGjSgJG8Ao0p80HrrAhRgSGigKbDoh468EroJrCoRf1vCUF/edA7/K+WtAJWFg19adM6XIGVWzPvcVmqweL+F1r6FhuPNbBys08zV9a89xy6pC0AlhgPdBiwp3BPrBY+BDii4Igca+DdwJGFRub6Dd4FzCiQkbOENwMzC2PmNgEzC5JusDTUwKxCBLQ8M7TU/5a6rruPReGYj/B9MDYo065MtqKl5n2qpfyu+l7dYWRceIIYDltWvtBp05ND5DQBI7CA3jKfDUMLo4C+W58N/2RyvAIahThSqEKhBU1VUE5OB9KBdCAdSAfSgXQgHUgH0oEQB74BG1sUIwNoL3cAAAAASUVORK5CYII=";
158
+ closeBtn.appendChild(img);
159
+ closeBtn.onclick = (this.closeModal).bind(this);
160
+
161
+ wrapper.appendChild(closeBtn);
162
+ wrapper.append(this.buildEl('div', 'pf-size-guide-modal-clear'));
163
+
164
+ return wrapper;
165
+ },
166
+
167
+ buildSizeGuideTabsNode: function (activeTabType) {
168
+ var tabs = this.buildEl('ul', 'pf-product-size-guide__tabs');
169
+ tabs.appendChild(this.buildSizeGuideTabNode(this.getTabTitle(Printful_Product_Size_Guide.TAB_TYPE_PERSON), Printful_Product_Size_Guide.TAB_TYPE_PERSON, activeTabType));
170
+ tabs.appendChild(this.buildSizeGuideTabNode(this.getTabTitle(Printful_Product_Size_Guide.TAB_TYPE_PRODUCT), Printful_Product_Size_Guide.TAB_TYPE_PRODUCT, activeTabType));
171
+
172
+ return tabs;
173
+ },
174
+
175
+ buildSizeGuideTabNode: function (title, type, activeType) {
176
+ var className = 'pf-product-size-guide__tab';
177
+
178
+ if (type === activeType) {
179
+ className += ' pf-product-size-guide__tab--active';
180
+ }
181
+
182
+ var tab = this.buildEl('li', className, {
183
+ innerHTML: title,
184
+ onclick: (this.renderModalContent).bind(this, type)
185
+ });
186
+
187
+ if (type === activeType) {
188
+ tab.style.backgroundColor = this.getTabBackGroundColor(true);
189
+ } else {
190
+ tab.style.backgroundColor = this.getTabBackGroundColor(false);
191
+ }
192
+
193
+ return tab;
194
+ },
195
+
196
+ buildTabContent: function (measurementData, title) {
197
+ measurementData = measurementData || {};
198
+ var node = this.buildEl('div');
199
+ node.appendChild(this.buildEl('h4', null, {innerHTML: title}));
200
+ if (measurementData.hasOwnProperty('description')) {
201
+ node.appendChild(this.buildEl('div', null, {innerHTML: measurementData.description}));
202
+ }
203
+
204
+ // Model description
205
+ node.appendChild(this.buildDescriptionBlock(measurementData));
206
+
207
+ // Size table
208
+ this.renderSizeChart(measurementData);
209
+ node.appendChild(this.sizeChartNode);
210
+
211
+ return node;
212
+ },
213
+
214
+ buildDescriptionBlock: function (measurementData) {
215
+ var node = this.buildEl('div', 'pf-size-guide-modal-measurements');
216
+ if (measurementData.hasOwnProperty('imageUrl') && measurementData.imageUrl) {
217
+ var measurementImgNode = this.buildEl('div', 'pf-size-guide-modal-measurements__image');
218
+ measurementImgNode.appendChild(this.buildEl('img', null, {src: measurementData.imageUrl}));
219
+
220
+ if (measurementData.hasOwnProperty('modelDescription')) {
221
+ measurementImgNode.appendChild(this.buildEl('div', null, {innerHTML: measurementData.modelDescription}));
222
+ }
223
+ node.appendChild(measurementImgNode);
224
+ }
225
+
226
+ if (measurementData.hasOwnProperty('imageDescription')) {
227
+ node.appendChild(this.buildEl('div', 'pf-size-guide-modal-measurements__description', {innerHTML: measurementData.imageDescription}));
228
+ }
229
+ node.append(this.buildEl('div', 'pf-size-guide-modal-clear'));
230
+
231
+ return node;
232
+ },
233
+
234
+ /**
235
+ * @param {{}} measurementData
236
+ * @param {string} [selectedUnit]
237
+ * @return {*}
238
+ */
239
+ buildSizeChartBlock: function (measurementData, selectedUnit) {
240
+ var node = this.buildEl('div');
241
+
242
+ if (!measurementData.hasOwnProperty('sizeTableRows') || measurementData.sizeTableRows.length < 1) {
243
+ return node;
244
+ }
245
+ var sizeRows = measurementData.sizeTableRows,
246
+ availableUnits = this.getUniqueUnits(sizeRows);
247
+
248
+ // Selected or first if nothing selected
249
+ selectedUnit = selectedUnit || this.getDefaultUnit(availableUnits);
250
+ node.appendChild(this.buildSizeChartTabsNode(measurementData, availableUnits, selectedUnit));
251
+ node.appendChild(this.buildSizeChartTable(this.getSortedChartRows(sizeRows, selectedUnit)));
252
+
253
+ return node;
254
+ },
255
+
256
+ /**
257
+ * @param {{}} measurementData
258
+ * @param {Array<{key, title}>} availableUnits
259
+ * @param {string} selectedUnit
260
+ * @return {*}
261
+ */
262
+ buildSizeChartTabsNode: function (measurementData, availableUnits, selectedUnit) {
263
+ availableUnits = availableUnits || [];
264
+ var tabsNode = this.buildEl('ul', 'pf-size-guide-modal-size-chart__tabs');
265
+
266
+ availableUnits.map((function (item) {
267
+ var className = 'pf-size-guide-modal-size-chart__tab';
268
+ if (item.key === selectedUnit) {
269
+ className += ' pf-size-guide-modal-size-chart__tab--active';
270
+ }
271
+ tabsNode.appendChild(this.buildEl('li', className, {
272
+ innerHTML: item.title,
273
+ onclick: (this.renderSizeChart).bind(this, measurementData, item.key)
274
+ }));
275
+ }).bind(this));
276
+
277
+ return tabsNode;
278
+ },
279
+
280
+ buildSizeChartTable: function (rows) {
281
+ var tableNode = this.buildEl('table', 'pf-size-guide-modal-size-chart__table'),
282
+ availableSizes = this.sizeGuideData.availableSizes;
283
+
284
+ var tableHeader = this.buildEl('thead'),
285
+ tableHeaderRow = this.buildEl('tr');
286
+
287
+ tableHeaderRow.appendChild(this.buildEl('td', null, {innerHTML: 'Size'}));
288
+ rows.map((function (row) {
289
+ tableHeaderRow.appendChild(this.buildEl('td', null, {innerHTML: row.title}));
290
+ }).bind(this));
291
+
292
+ tableHeader.appendChild(tableHeaderRow);
293
+ tableNode.appendChild(tableHeader);
294
+
295
+ // Loop all available sizes and print out a row for each type
296
+ var tableBody = this.buildEl('tbody');
297
+ availableSizes.map((function (size) {
298
+ var tableBodyRow = this.buildEl('tr');
299
+ tableBodyRow.appendChild(this.buildEl('td', null, {innerHTML: size}));
300
+ rows.map((function (row) {
301
+ tableBodyRow.appendChild(this.buildEl('td', null, {innerHTML: row.sizes[size] || ''}));
302
+ }).bind(this));
303
+
304
+ tableBody.appendChild(tableBodyRow);
305
+ }).bind(this));
306
+
307
+ tableNode.appendChild(tableBody);
308
+
309
+ return tableNode;
310
+ },
311
+
312
+ getSortedChartRows: function (sizeTableRows, selectedUnit) {
313
+ return sizeTableRows
314
+ .filter(function (row) {
315
+ return row.unit === selectedUnit;
316
+ }).map((function (row) {
317
+ var sizes = {};
318
+ for (var k in row.sizes) {
319
+ if (!row.sizes.hasOwnProperty(k)) {
320
+ continue;
321
+ }
322
+
323
+ // Convert to fractions if needed, round and join
324
+ sizes[k] = row.sizes[k].map((function (size) {
325
+ if (row.unit === Printful_Product_Size_Guide.UNIT_INCH) {
326
+ return this.convertToFraction(size);
327
+ } else {
328
+ return size.toFixed(1);
329
+ }
330
+ }).bind(this)).join(' - ');
331
+ }
332
+
333
+ return {
334
+ title: row.title,
335
+ sizes: sizes
336
+ };
337
+ }).bind(this));
338
+ },
339
+
340
+ convertToFraction: function (value) {
341
+ var split = String(value).split('.'),
342
+ integer = parseInt(split[0], 10);
343
+
344
+ if (!split[1]) {
345
+ return value;
346
+ }
347
+
348
+ var decimal = parseFloat('0.' + split[1]),
349
+ fraction = 0;
350
+
351
+ for (var fra in Printful_Product_Size_Guide.FRACTION_MAP) {
352
+ var compareValue = Printful_Product_Size_Guide.FRACTION_MAP[fra];
353
+
354
+ if (decimal < compareValue) {
355
+ fraction = fra;
356
+ break;
357
+ }
358
+ }
359
+
360
+ if (fraction === 0 && integer) {
361
+ integer++;
362
+ fraction = '';
363
+ }
364
+
365
+ return (integer > 0 ? integer + ' ' : '') + fraction;
366
+ },
367
+
368
+ /**
369
+ * @param {Array<{unit, unitName}>}sizeTableRows
370
+ * @return {Array<{key, title}>}
371
+ */
372
+ getUniqueUnits: function (sizeTableRows) {
373
+ var uniqueUnits = {};
374
+ sizeTableRows.map(function (row) {
375
+ // Gather unique units
376
+ if (!uniqueUnits.hasOwnProperty(row.unit)) {
377
+ uniqueUnits[row.unit] = row.unitName;
378
+ }
379
+ });
380
+
381
+ var units = [];
382
+ for (var i in uniqueUnits) {
383
+ if (uniqueUnits.hasOwnProperty(i)) {
384
+ units.push({
385
+ key: i,
386
+ title: uniqueUnits[i]
387
+ });
388
+ }
389
+ }
390
+
391
+ return units;
392
+ },
393
+
394
+ getBackGroundColor: function () {
395
+ return window.pfGlobal && window.pfGlobal.sg_modal_background_color ? window.pfGlobal.sg_modal_background_color : '#FFF';
396
+ },
397
+
398
+ getTextColor: function () {
399
+ return window.pfGlobal && window.pfGlobal.sg_modal_text_color ? window.pfGlobal.sg_modal_text_color : '#000';
400
+ },
401
+
402
+ getTabBackGroundColor: function (isActive) {
403
+ if (isActive) {
404
+ return window.pfGlobal && window.pfGlobal.sg_active_tab_background_color ? window.pfGlobal.sg_active_tab_background_color : '#FFF';
405
+ }
406
+
407
+ return window.pfGlobal && window.pfGlobal.sg_tab_background_color ? window.pfGlobal.sg_tab_background_color : '#EEE';
408
+ },
409
+
410
+ getDefaultUnit: function (availableUnits) {
411
+ availableUnits = availableUnits || [];
412
+ var uniqueUnitTypes = availableUnits.map(function (item) {
413
+ return item.key;
414
+ });
415
+
416
+ var defaultUnit = uniqueUnitTypes[0];
417
+ if (window.pfGlobal && window.pfGlobal.sg_primary_unit && uniqueUnitTypes.indexOf(window.pfGlobal.sg_primary_unit) > -1) {
418
+ return window.pfGlobal.sg_primary_unit;
419
+ }
420
+
421
+ return defaultUnit;
422
+ }
423
+ };
424
+
425
+ Printful_Product_Size_Guide.TAB_TYPE_PERSON = 'person';
426
+ Printful_Product_Size_Guide.TAB_TYPE_PRODUCT = 'product';
427
+ Printful_Product_Size_Guide.UNIT_INCH = 'inch';
428
+
429
+ Printful_Product_Size_Guide.FRACTION_MAP = {
430
+ ' ': 0.0625, //don't add any fraction
431
+ '⅛': 0.187,
432
+ '¼': 0.3125,
433
+ '⅜': 0.4375,
434
+ '½': 0.5625,
435
+ '⅝': 0.6875,
436
+ '¾': 0.8125,
437
+ '⅞': 0.9375
438
+ };
439
+ })();
assets/js/settings.js CHANGED
@@ -1,44 +1,44 @@
1
- var Printful_Settings;
2
-
3
- (function () {
4
- 'use strict';
5
-
6
- Printful_Settings = {
7
- init_submit: function () {
8
-
9
- var form = jQuery('form[name=printful_settings]');
10
- var submit_button = form.find('.woocommerce-save-button');
11
- var loader = form.find('.loader');
12
- var pass = form.find('.loader-wrap .pass');
13
- var fail = form.find('.loader-wrap .fail');
14
-
15
- submit_button.click(function (e) {
16
-
17
- e.preventDefault();
18
- submit_button.attr('disabled', 'disabled');
19
- loader.show();
20
-
21
- jQuery.ajax({
22
- type: "POST",
23
- url: form.attr('action'),
24
- data: form.serialize(),
25
- success: function (response) {
26
- submit_button.removeAttr('disabled');
27
- loader.hide();
28
-
29
- if (response === 'OK') {
30
- pass.show(0).delay(3000).hide(0);
31
- } else {
32
- fail.empty();
33
- fail.append('<span class="dashicons dashicons-no"></span>' + response);
34
- fail.show(0).delay(3000).hide(0);
35
- }
36
- }
37
- });
38
- });
39
- },
40
- enable_submit_btn: function () {
41
- jQuery('.printful-submit input[type=submit]').removeClass('disabled').prop('disabled', false);
42
- }
43
- };
44
  })();
1
+ var Printful_Settings;
2
+
3
+ (function () {
4
+ 'use strict';
5
+
6
+ Printful_Settings = {
7
+ init_submit: function () {
8
+
9
+ var form = jQuery('form[name=printful_settings]');
10
+ var submit_button = form.find('.woocommerce-save-button');
11
+ var loader = form.find('.loader');
12
+ var pass = form.find('.loader-wrap .pass');
13
+ var fail = form.find('.loader-wrap .fail');
14
+
15
+ submit_button.click(function (e) {
16
+
17
+ e.preventDefault();
18
+ submit_button.attr('disabled', 'disabled');
19
+ loader.show();
20
+
21
+ jQuery.ajax({
22
+ type: "POST",
23
+ url: form.attr('action'),
24
+ data: form.serialize(),
25
+ success: function (response) {
26
+ submit_button.removeAttr('disabled');
27
+ loader.hide();
28
+
29
+ if (response === 'OK') {
30
+ pass.show(0).delay(3000).hide(0);
31
+ } else {
32
+ fail.empty();
33
+ fail.append('<span class="dashicons dashicons-no"></span>' + response);
34
+ fail.show(0).delay(3000).hide(0);
35
+ }
36
+ }
37
+ });
38
+ });
39
+ },
40
+ enable_submit_btn: function () {
41
+ jQuery('.printful-submit input[type=submit]').removeClass('disabled').prop('disabled', false);
42
+ }
43
+ };
44
  })();
i18n/languages/printful.pot CHANGED
@@ -2,7 +2,7 @@
2
  msgid ""
3
  msgstr ""
4
  "Project-Id-Version: \n"
5
- "POT-Creation-Date: 2020-03-09 10:02+0200\n"
6
  "PO-Revision-Date: 2018-04-26 13:50+0300\n"
7
  "Last-Translator: \n"
8
  "Language-Team: \n"
@@ -25,23 +25,23 @@ msgstr ""
25
  msgid "Loading your orders..."
26
  msgstr ""
27
 
28
- #: includes/class-printful-admin-settings.php:25
29
  msgid "states where Printful applies sales tax"
30
  msgstr ""
31
 
32
- #: includes/class-printful-admin-settings.php:30
33
  msgid "Printful store API key"
34
  msgstr ""
35
 
36
- #: includes/class-printful-admin-settings.php:33
37
  msgid "Your store's Printful API key. Create it in the Prinful dashboard"
38
  msgstr ""
39
 
40
- #: includes/class-printful-admin-settings.php:37
41
  msgid "Calculate sales tax"
42
  msgstr ""
43
 
44
- #: includes/class-printful-admin-settings.php:40
45
  #, php-format
46
  msgid ""
47
  "Calculated for all products listed on your store (including non-Printful "
@@ -49,42 +49,114 @@ msgid ""
49
  "a seller permit in all these states."
50
  msgstr ""
51
 
52
- #: includes/class-printful-admin-settings.php:46
53
  msgid "Disable SSL"
54
  msgstr ""
55
 
56
- #: includes/class-printful-admin-settings.php:48
57
  msgid ""
58
  "Use HTTP instead of HTTPS to connect to the Printful API (may be required if "
59
  "the plugin does not work for some hosting configurations)"
60
  msgstr ""
61
 
62
- #: includes/class-printful-admin-settings.php:52
63
- #: includes/class-printful-admin-settings.php:54
64
  msgid "Personalization button text"
65
  msgstr ""
66
 
67
- #: includes/class-printful-admin-settings.php:58
68
  msgid "Personalization button color"
69
  msgstr ""
70
 
71
- #: includes/class-printful-admin-settings.php:60
72
  msgid "Personalization button background color"
73
  msgstr ""
74
 
75
- #: includes/class-printful-admin-settings.php:64
76
  msgid "Personalization popup title"
77
  msgstr ""
78
 
79
- #: includes/class-printful-admin-settings.php:66
80
  msgid "Personalization popup title text"
81
  msgstr ""
82
 
83
- #: includes/class-printful-admin-settings.php:159
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  msgid "Shipping Methods"
85
  msgstr ""
86
 
87
- #: includes/class-printful-admin-settings.php:160
88
  msgid ""
89
  "Here you can choose the shipping methods you want Printful to use for "
90
  "shipping your orders.\n"
@@ -93,15 +165,15 @@ msgid ""
93
  "method for each order."
94
  msgstr ""
95
 
96
- #: includes/class-printful-admin-settings.php:166
97
  msgid "You need to be connected to Printful API to edit carrier settings!"
98
  msgstr ""
99
 
100
- #: includes/class-printful-admin-settings.php:307
101
  msgid "International from USA"
102
  msgstr ""
103
 
104
- #: includes/class-printful-admin-settings.php:313
105
  msgid "International from EU"
106
  msgstr ""
107
 
@@ -316,6 +388,7 @@ msgid "Store Identifier"
316
  msgstr ""
317
 
318
  #: includes/class-printful-rest-api-controller.php:55
 
319
  msgid "Unique identifier for the resource."
320
  msgstr ""
321
 
@@ -323,27 +396,31 @@ msgstr ""
323
  msgid "Printful size guide"
324
  msgstr ""
325
 
326
- #: includes/class-printful-rest-api-controller.php:122
 
 
 
 
327
  msgid "No size chart was provided"
328
  msgstr ""
329
 
330
- #: includes/class-printful-rest-api-controller.php:128
331
  msgid "The product ID is invalid"
332
  msgstr ""
333
 
334
- #: includes/class-printful-rest-api-controller.php:134
335
  msgid "The product is not found"
336
  msgstr ""
337
 
338
- #: includes/class-printful-rest-api-controller.php:140
339
  msgid "You do not have permission to edit the size chart"
340
  msgstr ""
341
 
342
- #: includes/class-printful-rest-api-controller.php:211
343
  msgid "Sorry, you cannot list resources."
344
  msgstr ""
345
 
346
- #: includes/class-printful-rest-api-controller.php:228
347
  msgid "Sorry, you are not allowed to edit this resource."
348
  msgstr ""
349
 
@@ -371,15 +448,23 @@ msgstr ""
371
  msgid "Display Printful status messages if rate API request fails"
372
  msgstr ""
373
 
374
- #: includes/class-printful-size-chart-tab.php:27
375
  msgid "Size chart"
376
  msgstr ""
377
 
378
- #: includes/class-printful-size-chart-tab.php:38
379
- #: includes/class-printful-size-chart-tab.php:51
380
  msgid "Size Chart"
381
  msgstr ""
382
 
 
 
 
 
 
 
 
 
383
  #: includes/class-printful-taxes.php:160
384
  msgid "Sales Tax"
385
  msgstr ""
2
  msgid ""
3
  msgstr ""
4
  "Project-Id-Version: \n"
5
+ "POT-Creation-Date: 2020-08-17 14:22+0300\n"
6
  "PO-Revision-Date: 2018-04-26 13:50+0300\n"
7
  "Last-Translator: \n"
8
  "Language-Team: \n"
25
  msgid "Loading your orders..."
26
  msgstr ""
27
 
28
+ #: includes/class-printful-admin-settings.php:35
29
  msgid "states where Printful applies sales tax"
30
  msgstr ""
31
 
32
+ #: includes/class-printful-admin-settings.php:40
33
  msgid "Printful store API key"
34
  msgstr ""
35
 
36
+ #: includes/class-printful-admin-settings.php:43
37
  msgid "Your store's Printful API key. Create it in the Prinful dashboard"
38
  msgstr ""
39
 
40
+ #: includes/class-printful-admin-settings.php:47
41
  msgid "Calculate sales tax"
42
  msgstr ""
43
 
44
+ #: includes/class-printful-admin-settings.php:50
45
  #, php-format
46
  msgid ""
47
  "Calculated for all products listed on your store (including non-Printful "
49
  "a seller permit in all these states."
50
  msgstr ""
51
 
52
+ #: includes/class-printful-admin-settings.php:56
53
  msgid "Disable SSL"
54
  msgstr ""
55
 
56
+ #: includes/class-printful-admin-settings.php:58
57
  msgid ""
58
  "Use HTTP instead of HTTPS to connect to the Printful API (may be required if "
59
  "the plugin does not work for some hosting configurations)"
60
  msgstr ""
61
 
62
+ #: includes/class-printful-admin-settings.php:68
63
+ #: includes/class-printful-admin-settings.php:70
64
  msgid "Personalization button text"
65
  msgstr ""
66
 
67
+ #: includes/class-printful-admin-settings.php:74
68
  msgid "Personalization button color"
69
  msgstr ""
70
 
71
+ #: includes/class-printful-admin-settings.php:76
72
  msgid "Personalization button background color"
73
  msgstr ""
74
 
75
+ #: includes/class-printful-admin-settings.php:80
76
  msgid "Personalization popup title"
77
  msgstr ""
78
 
79
+ #: includes/class-printful-admin-settings.php:82
80
  msgid "Personalization popup title text"
81
  msgstr ""
82
 
83
+ #: includes/class-printful-admin-settings.php:92
84
+ msgid "Size guide popup title"
85
+ msgstr ""
86
+
87
+ #: includes/class-printful-admin-settings.php:94
88
+ msgid "Size guide popup title text"
89
+ msgstr ""
90
+
91
+ #: includes/class-printful-admin-settings.php:98
92
+ #: includes/class-printful-admin-settings.php:100
93
+ msgid "Size guide popup text color"
94
+ msgstr ""
95
+
96
+ #: includes/class-printful-admin-settings.php:104
97
+ #: includes/class-printful-admin-settings.php:106
98
+ msgid "Size guide popup background color"
99
+ msgstr ""
100
+
101
+ #: includes/class-printful-admin-settings.php:110
102
+ #: includes/class-printful-admin-settings.php:112
103
+ msgid "Size guide tab background color"
104
+ msgstr ""
105
+
106
+ #: includes/class-printful-admin-settings.php:116
107
+ #: includes/class-printful-admin-settings.php:118
108
+ msgid "Size guide active tab background color"
109
+ msgstr ""
110
+
111
+ #: includes/class-printful-admin-settings.php:122
112
+ #: includes/class-printful-admin-settings.php:124
113
+ msgid "Size guide button text"
114
+ msgstr ""
115
+
116
+ #: includes/class-printful-admin-settings.php:128
117
+ #: includes/class-printful-admin-settings.php:130
118
+ msgid "Size guide button text color"
119
+ msgstr ""
120
+
121
+ #: includes/class-printful-admin-settings.php:134
122
+ msgid "Primary measurement unit"
123
+ msgstr ""
124
+
125
+ #: includes/class-printful-admin-settings.php:136
126
+ msgid "Primary measurement unit (cm/inch)"
127
+ msgstr ""
128
+
129
+ #: includes/class-printful-admin-settings.php:140
130
+ msgid "Inches"
131
+ msgstr ""
132
+
133
+ #: includes/class-printful-admin-settings.php:141
134
+ msgid "Centimeters"
135
+ msgstr ""
136
+
137
+ #: includes/class-printful-admin-settings.php:185
138
+ msgid "Integration settings"
139
+ msgstr ""
140
+
141
+ #: includes/class-printful-admin-settings.php:189
142
+ msgid "Product personalization settings"
143
+ msgstr ""
144
+
145
+ #: includes/class-printful-admin-settings.php:194
146
+ msgid "Size guide settings"
147
+ msgstr ""
148
+
149
+ #: includes/class-printful-admin-settings.php:195
150
+ msgid ""
151
+ "These settings control how the new size guide will look on your WooCommerce "
152
+ "storefront. Products with an old size guide will not be affected."
153
+ msgstr ""
154
+
155
+ #: includes/class-printful-admin-settings.php:256
156
  msgid "Shipping Methods"
157
  msgstr ""
158
 
159
+ #: includes/class-printful-admin-settings.php:257
160
  msgid ""
161
  "Here you can choose the shipping methods you want Printful to use for "
162
  "shipping your orders.\n"
165
  "method for each order."
166
  msgstr ""
167
 
168
+ #: includes/class-printful-admin-settings.php:263
169
  msgid "You need to be connected to Printful API to edit carrier settings!"
170
  msgstr ""
171
 
172
+ #: includes/class-printful-admin-settings.php:404
173
  msgid "International from USA"
174
  msgstr ""
175
 
176
+ #: includes/class-printful-admin-settings.php:410
177
  msgid "International from EU"
178
  msgstr ""
179
 
388
  msgstr ""
389
 
390
  #: includes/class-printful-rest-api-controller.php:55
391
+ #: includes/class-printful-rest-api-controller.php:75
392
  msgid "Unique identifier for the resource."
393
  msgstr ""
394
 
396
  msgid "Printful size guide"
397
  msgstr ""
398
 
399
+ #: includes/class-printful-rest-api-controller.php:81
400
+ msgid "Advanced Printful size guide"
401
+ msgstr ""
402
+
403
+ #: includes/class-printful-rest-api-controller.php:142
404
  msgid "No size chart was provided"
405
  msgstr ""
406
 
407
+ #: includes/class-printful-rest-api-controller.php:148
408
  msgid "The product ID is invalid"
409
  msgstr ""
410
 
411
+ #: includes/class-printful-rest-api-controller.php:155
412
  msgid "The product is not found"
413
  msgstr ""
414
 
415
+ #: includes/class-printful-rest-api-controller.php:161
416
  msgid "You do not have permission to edit the size chart"
417
  msgstr ""
418
 
419
+ #: includes/class-printful-rest-api-controller.php:241
420
  msgid "Sorry, you cannot list resources."
421
  msgstr ""
422
 
423
+ #: includes/class-printful-rest-api-controller.php:258
424
  msgid "Sorry, you are not allowed to edit this resource."
425
  msgstr ""
426
 
448
  msgid "Display Printful status messages if rate API request fails"
449
  msgstr ""
450
 
451
+ #: includes/class-printful-size-chart-tab.php:32
452
  msgid "Size chart"
453
  msgstr ""
454
 
455
+ #: includes/class-printful-size-chart-tab.php:43
456
+ #: includes/class-printful-size-chart-tab.php:56
457
  msgid "Size Chart"
458
  msgstr ""
459
 
460
+ #: includes/class-printful-size-guide.php:51
461
+ msgid "Measure yourself"
462
+ msgstr ""
463
+
464
+ #: includes/class-printful-size-guide.php:52
465
+ msgid "Product measurements"
466
+ msgstr ""
467
+
468
  #: includes/class-printful-taxes.php:160
469
  msgid "Sales Tax"
470
  msgstr ""
includes/class-printful-admin-settings.php CHANGED
@@ -14,6 +14,16 @@ class Printful_Admin_Settings {
14
  const DEFAULT_PERSONALIZE_BUTTON_COLOR = '#eee';
15
  const DEFAULT_PERSONALIZE_MODAL_TITLE = 'Create a personalized design';
16
 
 
 
 
 
 
 
 
 
 
 
17
  /**
18
  * @return array
19
  */
@@ -48,18 +58,24 @@ class Printful_Admin_Settings {
48
  'label' => __( 'Use HTTP instead of HTTPS to connect to the Printful API (may be required if the plugin does not work for some hosting configurations)', 'printful' ),
49
  'default' => 'no',
50
  ),
51
- 'pfc_button_text' => array(
52
- 'title' => __( 'Personalization button text', 'printful' ),
53
- 'type' => 'text',
54
- 'description' => __( 'Personalization button text', 'printful' ),
55
- 'default' => self::DEFAULT_PERSONALIZE_BUTTON_TEXT
56
- ),
57
- 'pfc_button_color' => array(
58
- 'title' => __( 'Personalization button color', 'printful' ),
59
- 'type' => 'color-picker',
60
- 'description' => __( 'Personalization button background color', 'printful' ),
61
- 'default' => self::DEFAULT_PERSONALIZE_BUTTON_COLOR,
62
- ),
 
 
 
 
 
 
63
  'pfc_modal_title' => array(
64
  'title' => __( 'Personalization popup title', 'printful' ),
65
  'type' => 'text',
@@ -69,6 +85,72 @@ class Printful_Admin_Settings {
69
  );
70
  }
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  /**
73
  * @return Printful_Admin_Settings
74
  */
@@ -99,10 +181,22 @@ class Printful_Admin_Settings {
99
 
100
  echo '<form method="post" name="printful_settings" action="' . esc_url( admin_url( 'admin-ajax.php?action=save_printful_settings' ) ) . '">';
101
 
102
- //integration settings
103
- $integration_settings = $this->setup_integration_fields();
104
  Printful_Admin::load_template( 'setting-group', $integration_settings );
105
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  Printful_Admin::load_template( 'shipping-notification' );
107
 
108
  //carriers settings
@@ -129,25 +223,28 @@ class Printful_Admin_Settings {
129
  exit;
130
  }
131
 
132
- /**
133
- * @return mixed
134
- * @internal param $integration_settings
135
- */
136
- public function setup_integration_fields() {
137
-
138
- $integration_settings = array(
139
- 'title' => 'Integration settings',
140
- 'description' => '',
141
- 'settings' => self::getIntegrationFields(),
142
- );
 
 
 
143
 
144
- foreach ( $integration_settings['settings'] as $key => $setting ) {
145
- if ( $setting['type'] !== 'title' ) {
146
- $integration_settings['settings'][ $key ]['value'] = Printful_Integration::instance()->get_option( $key, $setting['default'] );
147
- }
148
- }
149
 
150
- return $integration_settings;
151
  }
152
 
153
  /**
@@ -253,7 +350,7 @@ class Printful_Admin_Settings {
253
  $options = array();
254
 
255
  //build save options list
256
- foreach ( self::getIntegrationFields() as $key => $field ) {
257
 
258
  if ( $field['type'] == 'checkbox' ) {
259
  if ( isset( $_POST[ $key ] ) ) {
14
  const DEFAULT_PERSONALIZE_BUTTON_COLOR = '#eee';
15
  const DEFAULT_PERSONALIZE_MODAL_TITLE = 'Create a personalized design';
16
 
17
+ // Size guide modal settings
18
+ const DEFAULT_SIZE_GUIDE_BUTTON_TEXT = 'Size Guide';
19
+ const DEFAULT_SIZE_GUIDE_BUTTON_COLOR = '#1164A9';
20
+ const DEFAULT_SIZE_GUIDE_MODAL_TITLE = 'Size guide';
21
+ const DEFAULT_SIZE_GUIDE_MODAL_TEXT_COLOR = '#000';
22
+ const DEFAULT_SIZE_GUIDE_MODAL_BACKGROUND_COLOR = '#fff';
23
+ const DEFAULT_SIZE_GUIDE_TAB_BACKGROUND_COLOR = '#fff';
24
+ const DEFAULT_SIZE_GUIDE_ACTIVE_TAB_BACKGROUND_COLOR = '#fff';
25
+ const DEFAULT_SIZE_GUIDE_UNIT = 'inch';
26
+
27
  /**
28
  * @return array
29
  */
58
  'label' => __( 'Use HTTP instead of HTTPS to connect to the Printful API (may be required if the plugin does not work for some hosting configurations)', 'printful' ),
59
  'default' => 'no',
60
  ),
61
+ );
62
+ }
63
+
64
+ public static function getPersonalizationFields()
65
+ {
66
+ return array(
67
+ 'pfc_button_text' => array(
68
+ 'title' => __( 'Personalization button text', 'printful' ),
69
+ 'type' => 'text',
70
+ 'description' => __( 'Personalization button text', 'printful' ),
71
+ 'default' => self::DEFAULT_PERSONALIZE_BUTTON_TEXT
72
+ ),
73
+ 'pfc_button_color' => array(
74
+ 'title' => __( 'Personalization button color', 'printful' ),
75
+ 'type' => 'color-picker',
76
+ 'description' => __( 'Personalization button background color', 'printful' ),
77
+ 'default' => self::DEFAULT_PERSONALIZE_BUTTON_COLOR,
78
+ ),
79
  'pfc_modal_title' => array(
80
  'title' => __( 'Personalization popup title', 'printful' ),
81
  'type' => 'text',
85
  );
86
  }
87
 
88
+ public static function getSizeGuideFields()
89
+ {
90
+ return array(
91
+ 'pfsg_modal_title' => array(
92
+ 'title' => __( 'Size guide popup title', 'printful' ),
93
+ 'type' => 'text',
94
+ 'description' => __( 'Size guide popup title text', 'printful' ),
95
+ 'default' => self::DEFAULT_SIZE_GUIDE_MODAL_TITLE,
96
+ ),
97
+ 'pfsg_modal_text_color' => array(
98
+ 'title' => __( 'Size guide popup text color', 'printful' ),
99
+ 'type' => 'color-picker',
100
+ 'description' => __( 'Size guide popup text color', 'printful' ),
101
+ 'default' => self::DEFAULT_SIZE_GUIDE_MODAL_TEXT_COLOR,
102
+ ),
103
+ 'pfsg_modal_background_color' => array(
104
+ 'title' => __( 'Size guide popup background color', 'printful' ),
105
+ 'type' => 'color-picker',
106
+ 'description' => __( 'Size guide popup background color', 'printful' ),
107
+ 'default' => self::DEFAULT_SIZE_GUIDE_MODAL_BACKGROUND_COLOR,
108
+ ),
109
+ 'pfsg_tab_background_color' => array(
110
+ 'title' => __( 'Size guide tab background color', 'printful' ),
111
+ 'type' => 'color-picker',
112
+ 'description' => __( 'Size guide tab background color', 'printful' ),
113
+ 'default' => self::DEFAULT_SIZE_GUIDE_TAB_BACKGROUND_COLOR,
114
+ ),
115
+ 'pfsg_active_tab_background_color' => array(
116
+ 'title' => __( 'Size guide active tab background color', 'printful' ),
117
+ 'type' => 'color-picker',
118
+ 'description' => __( 'Size guide active tab background color', 'printful' ),
119
+ 'default' => self::DEFAULT_SIZE_GUIDE_ACTIVE_TAB_BACKGROUND_COLOR,
120
+ ),
121
+ 'pfsg_button_text' => array(
122
+ 'title' => __( 'Size guide button text', 'printful' ),
123
+ 'type' => 'text',
124
+ 'description' => __( 'Size guide button text', 'printful' ),
125
+ 'default' => self::DEFAULT_SIZE_GUIDE_BUTTON_TEXT,
126
+ ),
127
+ 'pfsg_button_color' => array(
128
+ 'title' => __( 'Size guide button text color', 'printful' ),
129
+ 'type' => 'color-picker',
130
+ 'description' => __( 'Size guide button text color', 'printful' ),
131
+ 'default' => self::DEFAULT_SIZE_GUIDE_BUTTON_COLOR,
132
+ ),
133
+ 'pfsg_primary_unit' => array(
134
+ 'title' => __( 'Primary measurement unit', 'printful' ),
135
+ 'type' => 'dropdown',
136
+ 'description' => __( 'Primary measurement unit (cm/inch)', 'printful' ),
137
+ 'default' => self::DEFAULT_SIZE_GUIDE_UNIT,
138
+ 'selected' => Printful_Integration::instance()->get_option( 'pfsg_primary_unit' ),
139
+ 'items' => [
140
+ 'inch' => __('Inches', 'printful'),
141
+ 'centimeter' => __('Centimeters', 'printful'),
142
+ ],
143
+ ),
144
+ );
145
+ }
146
+
147
+ /**
148
+ * @return array
149
+ */
150
+ public static function getAllFields() {
151
+ return array_merge(self::getIntegrationFields(), self::getPersonalizationFields(), self::getSizeGuideFields());
152
+ }
153
+
154
  /**
155
  * @return Printful_Admin_Settings
156
  */
181
 
182
  echo '<form method="post" name="printful_settings" action="' . esc_url( admin_url( 'admin-ajax.php?action=save_printful_settings' ) ) . '">';
183
 
184
+ // Integration settings
185
+ $integration_settings = $this->setup_fields( __('Integration settings', 'printful'), '', self::getIntegrationFields() );
186
  Printful_Admin::load_template( 'setting-group', $integration_settings );
187
 
188
+ // Product personalization settings
189
+ $personalization_settings = $this->setup_fields( __('Product personalization settings', 'printful'), '', self::getPersonalizationFields() );
190
+ Printful_Admin::load_template( 'setting-group', $personalization_settings );
191
+
192
+ // Size guide settings
193
+ $size_guide_settings = $this->setup_fields(
194
+ __('Size guide settings', 'printful'),
195
+ __('These settings control how the new size guide will look on your WooCommerce storefront. Products with an old size guide will not be affected.', 'printful'),
196
+ self::getSizeGuideFields()
197
+ );
198
+ Printful_Admin::load_template( 'setting-group', $size_guide_settings );
199
+
200
  Printful_Admin::load_template( 'shipping-notification' );
201
 
202
  //carriers settings
223
  exit;
224
  }
225
 
226
+ /**
227
+ * @param string $title Settings section title
228
+ * @param string $description Section description
229
+ * @param array $fields
230
+ *
231
+ * @return array
232
+ */
233
+ public function setup_fields($title, $description = '', $fields = [])
234
+ {
235
+ $fieldGroup = array(
236
+ 'title' => $title,
237
+ 'description' => $description,
238
+ 'settings' => $fields,
239
+ );
240
 
241
+ foreach ( $fieldGroup['settings'] as $key => $setting ) {
242
+ if ( $setting['type'] !== 'title' ) {
243
+ $fieldGroup['settings'][ $key ]['value'] = Printful_Integration::instance()->get_option( $key, $setting['default'] );
244
+ }
245
+ }
246
 
247
+ return $fieldGroup;
248
  }
249
 
250
  /**
350
  $options = array();
351
 
352
  //build save options list
353
+ foreach ( self::getAllFields() as $key => $field ) {
354
 
355
  if ( $field['type'] == 'checkbox' ) {
356
  if ( isset( $_POST[ $key ] ) ) {
includes/class-printful-admin.php CHANGED
@@ -1,172 +1,172 @@
1
- <?php
2
- if ( ! defined( 'ABSPATH' ) ) exit;
3
-
4
- class Printful_Admin {
5
-
6
- const MENU_TITLE_TOP = 'Printful';
7
- const PAGE_TITLE_DASHBOARD = 'Dashboard';
8
- const MENU_TITLE_DASHBOARD = 'Dashboard';
9
- const MENU_SLUG_DASHBOARD = 'printful-dashboard';
10
- const CAPABILITY = 'manage_options';
11
-
12
- public static function init() {
13
- $admin = new self;
14
- $admin->register_admin();
15
- }
16
-
17
- /**
18
- * Register admin scripts
19
- */
20
- public function register_admin() {
21
-
22
- add_action( 'admin_menu', array( $this, 'register_admin_menu_page' ) );
23
- add_action( 'admin_enqueue_scripts', array( $this, 'add_admin_styles' ) );
24
- add_action( 'admin_enqueue_scripts', array( $this, 'add_admin_scripts' ) );
25
- add_action( 'wp_enqueue_scripts', array( $this, 'add_global_style' ) );
26
- add_action( 'admin_bar_menu', array( $this, 'add_printful_status_toolbar' ), 999 );
27
- }
28
-
29
- /**
30
- * Loads stylesheets used in printful admin pages
31
- * @param $hook
32
- */
33
- public function add_admin_styles($hook) {
34
-
35
- wp_enqueue_style( 'printful-global', plugins_url( '../assets/css/global.css', __FILE__ ) );
36
-
37
- if ( strpos( $hook, 'printful-dashboard' ) !== false ) {
38
- wp_enqueue_style( 'wp-color-picker' );
39
- wp_enqueue_style( 'printful-dashboard', plugins_url( '../assets/css/dashboard.css', __FILE__ ) );
40
- wp_enqueue_style( 'printful-status', plugins_url( '../assets/css/status.css', __FILE__ ) );
41
- wp_enqueue_style( 'printful-support', plugins_url( '../assets/css/support.css', __FILE__ ) );
42
- wp_enqueue_style( 'printful-settings', plugins_url( '../assets/css/settings.css', __FILE__ ) );
43
- }
44
- }
45
-
46
- /**
47
- * Loads stylesheet for printful toolbar element
48
- */
49
- public function add_global_style() {
50
- if ( is_user_logged_in() ) {
51
- wp_enqueue_style( 'printful-global', plugins_url( '../assets/css/global.css', __FILE__ ) );
52
- }
53
- }
54
-
55
- /**
56
- * Loads scripts used in printful admin pages
57
- * @param $hook
58
- */
59
- public function add_admin_scripts($hook) {
60
- if ( strpos( $hook, 'printful-dashboard' ) !== false ) {
61
- wp_enqueue_script( 'wp-color-picker' );
62
- wp_enqueue_script( 'printful-settings', plugins_url( '../assets/js/settings.js', __FILE__ ) );
63
- wp_enqueue_script( 'printful-connect', plugins_url( '../assets/js/connect.js', __FILE__ ) );
64
- wp_enqueue_script( 'printful-block-loader', plugins_url( '../assets/js/block-loader.js', __FILE__ ) );
65
- wp_enqueue_script( 'printful-intercom', plugins_url( '../assets/js/intercom.min.js', __FILE__ ) );
66
- }
67
- }
68
-
69
- /**
70
- * Register admin menu pages
71
- */
72
- public function register_admin_menu_page() {
73
-
74
- add_menu_page(
75
- __( 'Dashboard', 'printful' ),
76
- self::MENU_TITLE_TOP,
77
- self::CAPABILITY,
78
- self::MENU_SLUG_DASHBOARD,
79
- array( 'Printful_Admin', 'route' ),
80
- Printful_Base::get_asset_url() . 'images/printful-menu-icon.png',
81
- 58
82
- );
83
- }
84
-
85
- /**
86
- * Route the tabs
87
- */
88
- public static function route() {
89
-
90
- $tabs = array(
91
- 'dashboard' => 'Printful_Admin_Dashboard',
92
- 'settings' => 'Printful_Admin_Settings',
93
- 'status' => 'Printful_Admin_Status',
94
- 'support' => 'Printful_Admin_Support',
95
- );
96
-
97
- $tab = ( ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'dashboard' );
98
- if ( ! empty( $tabs[ $tab ] ) ) {
99
- call_user_func( array( $tabs[ $tab ], 'view' ) );
100
- }
101
- }
102
-
103
- /**
104
- * Get the tabs used in printful admin pages
105
- * @return array
106
- * @throws PrintfulException
107
- */
108
- public static function get_tabs() {
109
-
110
- $tabs = array(
111
- array( 'name' => __( 'Settings', 'printful' ), 'tab_url' => 'settings' ),
112
- array( 'name' => __( 'Status', 'printful' ), 'tab_url' => 'status' ),
113
- array( 'name' => __( 'Support', 'printful' ), 'tab_url' => 'support' ),
114
- );
115
-
116
- if ( Printful_Integration::instance()->is_connected() ) {
117
- array_unshift( $tabs, array( 'name' => __( 'Dashboard', 'printful' ), 'tab_url' => false ) );
118
- } else {
119
- array_unshift( $tabs, array( 'name' => __( 'Connect', 'printful' ), 'tab_url' => false ) );
120
- }
121
-
122
- return $tabs;
123
- }
124
-
125
- /**
126
- * Create the printful toolbar
127
- * @param $wp_admin_bar
128
- */
129
- public function add_printful_status_toolbar( $wp_admin_bar ) {
130
-
131
- $issueCount = get_transient( Printful_Admin_Status::PF_STATUS_ISSUE_COUNT );
132
-
133
- if ( $issueCount ) {
134
- //Add top level menu item
135
- $args = array(
136
- 'id' => 'printful_toolbar',
137
- 'title' => 'Printful Integration' . ( $issueCount > 0 ? ' <span class="printful-toolbar-issues">' . esc_attr( $issueCount ) . '</span>' : '' ),
138
- 'href' => get_admin_url( null, 'admin.php?page=' . Printful_Admin::MENU_SLUG_DASHBOARD ),
139
- 'meta' => array( 'class' => 'printful-toolbar' ),
140
- );
141
- $wp_admin_bar->add_node( $args );
142
-
143
- //Add status
144
- $args = array(
145
- 'id' => 'printful_toolbar_status',
146
- 'parent' => 'printful_toolbar',
147
- 'title' => 'Integration status' . ( $issueCount > 0 ? ' (' . esc_attr( $issueCount ) . _n( ' issue', ' issues', $issueCount ) . ')' : '' ),
148
- 'href' => get_admin_url( null, 'admin.php?page=' . Printful_Admin::MENU_SLUG_DASHBOARD . '&tab=status' ),
149
- 'meta' => array( 'class' => 'printful-toolbar-status' ),
150
- );
151
- $wp_admin_bar->add_node( $args );
152
- }
153
- }
154
-
155
- /**
156
- * Load a template file. Extract any variables that are passed
157
- * @param $name
158
- * @param array $variables
159
- */
160
- public static function load_template( $name, $variables = array() ) {
161
-
162
- if ( ! empty( $variables ) ) {
163
- extract( $variables );
164
- }
165
-
166
- $filename = plugin_dir_path( __FILE__ ) . 'templates/' . $name . '.php';
167
- if ( file_exists( $filename ) ) {
168
- include( $filename );
169
- }
170
- }
171
-
172
  }
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) exit;
3
+
4
+ class Printful_Admin {
5
+
6
+ const MENU_TITLE_TOP = 'Printful';
7
+ const PAGE_TITLE_DASHBOARD = 'Dashboard';
8
+ const MENU_TITLE_DASHBOARD = 'Dashboard';
9
+ const MENU_SLUG_DASHBOARD = 'printful-dashboard';
10
+ const CAPABILITY = 'manage_options';
11
+
12
+ public static function init() {
13
+ $admin = new self;
14
+ $admin->register_admin();
15
+ }
16
+
17
+ /**
18
+ * Register admin scripts
19
+ */
20
+ public function register_admin() {
21
+
22
+ add_action( 'admin_menu', array( $this, 'register_admin_menu_page' ) );
23
+ add_action( 'admin_enqueue_scripts', array( $this, 'add_admin_styles' ) );
24
+ add_action( 'admin_enqueue_scripts', array( $this, 'add_admin_scripts' ) );
25
+ add_action( 'wp_enqueue_scripts', array( $this, 'add_global_style' ) );
26
+ add_action( 'admin_bar_menu', array( $this, 'add_printful_status_toolbar' ), 999 );
27
+ }
28
+
29
+ /**
30
+ * Loads stylesheets used in printful admin pages
31
+ * @param $hook
32
+ */
33
+ public function add_admin_styles($hook) {
34
+
35
+ wp_enqueue_style( 'printful-global', plugins_url( '../assets/css/global.css', __FILE__ ) );
36
+
37
+ if ( strpos( $hook, 'printful-dashboard' ) !== false ) {
38
+ wp_enqueue_style( 'wp-color-picker' );
39
+ wp_enqueue_style( 'printful-dashboard', plugins_url( '../assets/css/dashboard.css', __FILE__ ) );
40
+ wp_enqueue_style( 'printful-status', plugins_url( '../assets/css/status.css', __FILE__ ) );
41
+ wp_enqueue_style( 'printful-support', plugins_url( '../assets/css/support.css', __FILE__ ) );
42
+ wp_enqueue_style( 'printful-settings', plugins_url( '../assets/css/settings.css', __FILE__ ) );
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Loads stylesheet for printful toolbar element
48
+ */
49
+ public function add_global_style() {
50
+ if ( is_user_logged_in() ) {
51
+ wp_enqueue_style( 'printful-global', plugins_url( '../assets/css/global.css', __FILE__ ) );
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Loads scripts used in printful admin pages
57
+ * @param $hook
58
+ */
59
+ public function add_admin_scripts($hook) {
60
+ if ( strpos( $hook, 'printful-dashboard' ) !== false ) {
61
+ wp_enqueue_script( 'wp-color-picker' );
62
+ wp_enqueue_script( 'printful-settings', plugins_url( '../assets/js/settings.js', __FILE__ ) );
63
+ wp_enqueue_script( 'printful-connect', plugins_url( '../assets/js/connect.js', __FILE__ ) );
64
+ wp_enqueue_script( 'printful-block-loader', plugins_url( '../assets/js/block-loader.js', __FILE__ ) );
65
+ wp_enqueue_script( 'printful-intercom', plugins_url( '../assets/js/intercom.min.js', __FILE__ ) );
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Register admin menu pages
71
+ */
72
+ public function register_admin_menu_page() {
73
+
74
+ add_menu_page(
75
+ __( 'Dashboard', 'printful' ),
76
+ self::MENU_TITLE_TOP,
77
+ self::CAPABILITY,
78
+ self::MENU_SLUG_DASHBOARD,
79
+ array( 'Printful_Admin', 'route' ),
80
+ Printful_Base::get_asset_url() . 'images/printful-menu-icon.png',
81
+ 58
82
+ );
83
+ }
84
+
85
+ /**
86
+ * Route the tabs
87
+ */
88
+ public static function route() {
89
+
90
+ $tabs = array(
91
+ 'dashboard' => 'Printful_Admin_Dashboard',
92
+ 'settings' => 'Printful_Admin_Settings',
93
+ 'status' => 'Printful_Admin_Status',
94
+ 'support' => 'Printful_Admin_Support',
95
+ );
96
+
97
+ $tab = ( ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'dashboard' );
98
+ if ( ! empty( $tabs[ $tab ] ) ) {
99
+ call_user_func( array( $tabs[ $tab ], 'view' ) );
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Get the tabs used in printful admin pages
105
+ * @return array
106
+ * @throws PrintfulException
107
+ */
108
+ public static function get_tabs() {
109
+
110
+ $tabs = array(
111
+ array( 'name' => __( 'Settings', 'printful' ), 'tab_url' => 'settings' ),
112
+ array( 'name' => __( 'Status', 'printful' ), 'tab_url' => 'status' ),
113
+ array( 'name' => __( 'Support', 'printful' ), 'tab_url' => 'support' ),
114
+ );
115
+
116
+ if ( Printful_Integration::instance()->is_connected() ) {
117
+ array_unshift( $tabs, array( 'name' => __( 'Dashboard', 'printful' ), 'tab_url' => false ) );
118
+ } else {
119
+ array_unshift( $tabs, array( 'name' => __( 'Connect', 'printful' ), 'tab_url' => false ) );
120
+ }
121
+
122
+ return $tabs;
123
+ }
124
+
125
+ /**
126
+ * Create the printful toolbar
127
+ * @param $wp_admin_bar
128
+ */
129
+ public function add_printful_status_toolbar( $wp_admin_bar ) {
130
+
131
+ $issueCount = get_transient( Printful_Admin_Status::PF_STATUS_ISSUE_COUNT );
132
+
133
+ if ( $issueCount ) {
134
+ //Add top level menu item
135
+ $args = array(
136
+ 'id' => 'printful_toolbar',
137
+ 'title' => 'Printful Integration' . ( $issueCount > 0 ? ' <span class="printful-toolbar-issues">' . esc_attr( $issueCount ) . '</span>' : '' ),
138
+ 'href' => get_admin_url( null, 'admin.php?page=' . Printful_Admin::MENU_SLUG_DASHBOARD ),
139
+ 'meta' => array( 'class' => 'printful-toolbar' ),
140
+ );
141
+ $wp_admin_bar->add_node( $args );
142
+
143
+ //Add status
144
+ $args = array(
145
+ 'id' => 'printful_toolbar_status',
146
+ 'parent' => 'printful_toolbar',
147
+ 'title' => 'Integration status' . ( $issueCount > 0 ? ' (' . esc_attr( $issueCount ) . _n( ' issue', ' issues', $issueCount ) . ')' : '' ),
148
+ 'href' => get_admin_url( null, 'admin.php?page=' . Printful_Admin::MENU_SLUG_DASHBOARD . '&tab=status' ),
149
+ 'meta' => array( 'class' => 'printful-toolbar-status' ),
150
+ );
151
+ $wp_admin_bar->add_node( $args );
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Load a template file. Extract any variables that are passed
157
+ * @param $name
158
+ * @param array $variables
159
+ */
160
+ public static function load_template( $name, $variables = array() ) {
161
+
162
+ if ( ! empty( $variables ) ) {
163
+ extract( $variables );
164
+ }
165
+
166
+ $filename = plugin_dir_path( __FILE__ ) . 'templates/' . $name . '.php';
167
+ if ( file_exists( $filename ) ) {
168
+ include( $filename );
169
+ }
170
+ }
171
+
172
  }
includes/class-printful-client.php CHANGED
@@ -1,192 +1,192 @@
1
- <?php
2
- if ( ! defined( 'ABSPATH' ) ) exit;
3
-
4
- /**
5
- * Printful API client
6
- */
7
- class Printful_Client {
8
-
9
- private $key = false;
10
- private $lastResponseRaw;
11
- private $lastResponse;
12
- private $userAgent = 'Printful WooCommerce Plugin';
13
- private $apiUrl;
14
-
15
- /**
16
- * @param string $key Printful Store API key
17
- * @param bool|string $disable_ssl Force HTTP instead of HTTPS for API requests
18
- *
19
- * @throws PrintfulException if the library failed to initialize
20
- */
21
- public function __construct( $key = '', $disable_ssl = false ) {
22
-
23
- $key = (string) $key;
24
-
25
- $this->userAgent .= ' ' . Printful_Base::VERSION . ' (WP ' . get_bloginfo( 'version' ) . ' + WC ' . WC()->version . ')';
26
-
27
- if ( ! function_exists( 'json_decode' ) || ! function_exists( 'json_encode' ) ) {
28
- throw new PrintfulException( 'PHP JSON extension is required for the Printful API library to work!' );
29
- }
30
- if ( strlen( $key ) < 32 ) {
31
- throw new PrintfulException( 'Missing or invalid Printful store key!' );
32
- }
33
- $this->key = $key;
34
-
35
- if ( $disable_ssl ) {
36
- $this->apiUrl = str_replace( 'https://', 'http://', $this->apiUrl );
37
- }
38
-
39
- //setup api host
40
- $this->apiUrl = Printful_Base::get_printful_api_host();
41
- }
42
-
43
- /**
44
- * Returns total available item count from the last request if it supports paging (e.g order list) or null otherwise.
45
- *
46
- * @return int|null Item count
47
- */
48
- public function getItemCount() {
49
- return isset( $this->lastResponse['paging']['total'] ) ? $this->lastResponse['paging']['total'] : null;
50
- }
51
-
52
- /**
53
- * Perform a GET request to the API
54
- * @param string $path Request path (e.g. 'orders' or 'orders/123')
55
- * @param array $params Additional GET parameters as an associative array
56
- * @return mixed API response
57
- * @throws PrintfulApiException if the API call status code is not in the 2xx range
58
- * @throws PrintfulException if the API call has failed or the response is invalid
59
- */
60
- public function get( $path, $params = array() ) {
61
- return $this->request( 'GET', $path, $params );
62
- }
63
-
64
- /**
65
- * Perform a DELETE request to the API
66
- * @param string $path Request path (e.g. 'orders' or 'orders/123')
67
- * @param array $params Additional GET parameters as an associative array
68
- * @return mixed API response
69
- * @throws PrintfulApiException if the API call status code is not in the 2xx range
70
- * @throws PrintfulException if the API call has failed or the response is invalid
71
- */
72
- public function delete( $path, $params = array() ) {
73
- return $this->request( 'DELETE', $path, $params );
74
- }
75
-
76
- /**
77
- * Perform a POST request to the API
78
- * @param string $path Request path (e.g. 'orders' or 'orders/123')
79
- * @param array $data Request body data as an associative array
80
- * @param array $params Additional GET parameters as an associative array
81
- * @return mixed API response
82
- * @throws PrintfulApiException if the API call status code is not in the 2xx range
83
- * @throws PrintfulException if the API call has failed or the response is invalid
84
- */
85
- public function post( $path, $data = array(), $params = array() ) {
86
- return $this->request( 'POST', $path, $params, $data );
87
- }
88
- /**
89
- * Perform a PUT request to the API
90
- * @param string $path Request path (e.g. 'orders' or 'orders/123')
91
- * @param array $data Request body data as an associative array
92
- * @param array $params Additional GET parameters as an associative array
93
- * @return mixed API response
94
- * @throws PrintfulApiException if the API call status code is not in the 2xx range
95
- * @throws PrintfulException if the API call has failed or the response is invalid
96
- */
97
- public function put( $path, $data = array(), $params = array() ) {
98
- return $this->request( 'PUT', $path, $params, $data );
99
- }
100
-
101
-
102
- /**
103
- * Perform a PATCH request to the API
104
- * @param string $path Request path
105
- * @param array $data Request body data as an associative array
106
- * @param array $params
107
- * @return mixed API response
108
- * @throws PrintfulApiException if the API call status code is not in the 2xx range
109
- * @throws PrintfulException if the API call has failed or the response is invalid
110
- */
111
- public function patch( $path, $data = array(), $params = array() )
112
- {
113
- return $this->request( 'PATCH', $path, $params, $data );
114
- }
115
-
116
- /**
117
- * Return raw response data from the last request
118
- * @return string|null Response data
119
- */
120
- public function getLastResponseRaw() {
121
- return $this->lastResponseRaw;
122
- }
123
- /**
124
- * Return decoded response data from the last request
125
- * @return array|null Response data
126
- */
127
- public function getLastResponse() {
128
- return $this->lastResponse;
129
- }
130
-
131
- /**
132
- * Internal request implementation
133
- *
134
- * @param $method
135
- * @param $path
136
- * @param array $params
137
- * @param null $data
138
- *
139
- * @return
140
- * @throws PrintfulApiException
141
- * @throws PrintfulException
142
- */
143
- private function request( $method, $path, array $params = array(), $data = null ) {
144
-
145
- $this->lastResponseRaw = null;
146
- $this->lastResponse = null;
147
-
148
- $url = trim( $path, '/' );
149
-
150
- if ( ! empty( $params ) ) {
151
- $url .= '?' . http_build_query( $params );
152
- }
153
-
154
- $request = array(
155
- 'timeout' => 10,
156
- 'user-agent' => $this->userAgent,
157
- 'method' => $method,
158
- 'headers' => array( 'Authorization' => 'Basic ' . base64_encode( $this->key ) ),
159
- 'body' => $data !== null ? json_encode( $data ) : null,
160
- );
161
-
162
- $result = wp_remote_get( $this->apiUrl . $url, $request );
163
-
164
- //allow other methods to hook in on the api result
165
- $result = apply_filters( 'printful_api_result', $result, $method, $this->apiUrl . $url, $request );
166
-
167
- if ( is_wp_error( $result ) ) {
168
- throw new PrintfulException( "API request failed - " . $result->get_error_message() );
169
- }
170
- $this->lastResponseRaw = $result['body'];
171
- $this->lastResponse = $response = json_decode( $result['body'], true );
172
-
173
- if ( ! isset( $response['code'], $response['result'] ) ) {
174
- throw new PrintfulException( 'Invalid API response' );
175
- }
176
- $status = (int) $response['code'];
177
- if ( $status < 200 || $status >= 300 ) {
178
- throw new PrintfulApiException( (string) $response['result'], $status );
179
- }
180
-
181
- return $response['result'];
182
- }
183
- }
184
-
185
- /**
186
- * Class PrintfulException Generic Printful exception
187
- */
188
- class PrintfulException extends Exception {}
189
- /**
190
- * Class PrintfulException Printful exception returned from the API
191
- */
192
  class PrintfulApiException extends PrintfulException {}
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) exit;
3
+
4
+ /**
5
+ * Printful API client
6
+ */
7
+ class Printful_Client {
8
+
9
+ private $key = false;
10
+ private $lastResponseRaw;
11
+ private $lastResponse;
12
+ private $userAgent = 'Printful WooCommerce Plugin';
13
+ private $apiUrl;
14
+
15
+ /**
16
+ * @param string $key Printful Store API key
17
+ * @param bool|string $disable_ssl Force HTTP instead of HTTPS for API requests
18
+ *
19
+ * @throws PrintfulException if the library failed to initialize
20
+ */
21
+ public function __construct( $key = '', $disable_ssl = false ) {
22
+
23
+ $key = (string) $key;
24
+
25
+ $this->userAgent .= ' ' . Printful_Base::VERSION . ' (WP ' . get_bloginfo( 'version' ) . ' + WC ' . WC()->version . ')';
26
+
27
+ if ( ! function_exists( 'json_decode' ) || ! function_exists( 'json_encode' ) ) {
28
+ throw new PrintfulException( 'PHP JSON extension is required for the Printful API library to work!' );
29
+ }
30
+ if ( strlen( $key ) < 32 ) {
31
+ throw new PrintfulException( 'Missing or invalid Printful store key!' );
32
+ }
33
+ $this->key = $key;
34
+
35
+ if ( $disable_ssl ) {
36
+ $this->apiUrl = str_replace( 'https://', 'http://', $this->apiUrl );
37
+ }
38
+
39
+ //setup api host
40
+ $this->apiUrl = Printful_Base::get_printful_api_host();
41
+ }
42
+
43
+ /**
44
+ * Returns total available item count from the last request if it supports paging (e.g order list) or null otherwise.
45
+ *
46
+ * @return int|null Item count
47
+ */
48
+ public function getItemCount() {
49
+ return isset( $this->lastResponse['paging']['total'] ) ? $this->lastResponse['paging']['total'] : null;
50
+ }
51
+
52
+ /**
53
+ * Perform a GET request to the API
54
+ * @param string $path Request path (e.g. 'orders' or 'orders/123')
55
+ * @param array $params Additional GET parameters as an associative array
56
+ * @return mixed API response
57
+ * @throws PrintfulApiException if the API call status code is not in the 2xx range
58
+ * @throws PrintfulException if the API call has failed or the response is invalid
59
+ */
60
+ public function get( $path, $params = array() ) {
61
+ return $this->request( 'GET', $path, $params );
62
+ }
63
+
64
+ /**
65
+ * Perform a DELETE request to the API
66
+ * @param string $path Request path (e.g. 'orders' or 'orders/123')
67
+ * @param array $params Additional GET parameters as an associative array
68
+ * @return mixed API response
69
+ * @throws PrintfulApiException if the API call status code is not in the 2xx range
70
+ * @throws PrintfulException if the API call has failed or the response is invalid
71
+ */
72
+ public function delete( $path, $params = array() ) {
73
+ return $this->request( 'DELETE', $path, $params );
74
+ }
75
+
76
+ /**
77
+ * Perform a POST request to the API
78
+ * @param string $path Request path (e.g. 'orders' or 'orders/123')
79
+ * @param array $data Request body data as an associative array
80
+ * @param array $params Additional GET parameters as an associative array
81
+ * @return mixed API response
82
+ * @throws PrintfulApiException if the API call status code is not in the 2xx range
83
+ * @throws PrintfulException if the API call has failed or the response is invalid
84
+ */
85
+ public function post( $path, $data = array(), $params = array() ) {
86
+ return $this->request( 'POST', $path, $params, $data );
87
+ }
88
+ /**
89
+ * Perform a PUT request to the API
90
+ * @param string $path Request path (e.g. 'orders' or 'orders/123')
91
+ * @param array $data Request body data as an associative array
92
+ * @param array $params Additional GET parameters as an associative array
93
+ * @return mixed API response
94
+ * @throws PrintfulApiException if the API call status code is not in the 2xx range
95
+ * @throws PrintfulException if the API call has failed or the response is invalid
96
+ */
97
+ public function put( $path, $data = array(), $params = array() ) {
98
+ return $this->request( 'PUT', $path, $params, $data );
99
+ }
100
+
101
+
102
+ /**
103
+ * Perform a PATCH request to the API
104
+ * @param string $path Request path
105
+ * @param array $data Request body data as an associative array
106
+ * @param array $params
107
+ * @return mixed API response
108
+ * @throws PrintfulApiException if the API call status code is not in the 2xx range
109
+ * @throws PrintfulException if the API call has failed or the response is invalid
110
+ */
111
+ public function patch( $path, $data = array(), $params = array() )
112
+ {
113
+ return $this->request( 'PATCH', $path, $params, $data );
114
+ }
115
+
116
+ /**
117
+ * Return raw response data from the last request
118
+ * @return string|null Response data
119
+ */
120
+ public function getLastResponseRaw() {
121
+ return $this->lastResponseRaw;
122
+ }
123
+ /**
124
+ * Return decoded response data from the last request
125
+ * @return array|null Response data
126
+ */
127
+ public function getLastResponse() {
128
+ return $this->lastResponse;
129
+ }
130
+
131
+ /**
132
+ * Internal request implementation
133
+ *
134
+ * @param $method
135
+ * @param $path
136
+ * @param array $params
137
+ * @param null $data
138
+ *
139
+ * @return
140
+ * @throws PrintfulApiException
141
+ * @throws PrintfulException
142
+ */
143
+ private function request( $method, $path, array $params = array(), $data = null ) {
144
+
145
+ $this->lastResponseRaw = null;
146
+ $this->lastResponse = null;
147
+
148
+ $url = trim( $path, '/' );
149
+
150
+ if ( ! empty( $params ) ) {
151
+ $url .= '?' . http_build_query( $params );
152
+ }
153
+
154
+ $request = array(
155
+ 'timeout' => 10,
156
+ 'user-agent' => $this->userAgent,
157
+ 'method' => $method,
158
+ 'headers' => array( 'Authorization' => 'Basic ' . base64_encode( $this->key ) ),
159
+ 'body' => $data !== null ? json_encode( $data ) : null,
160
+ );
161
+
162
+ $result = wp_remote_get( $this->apiUrl . $url, $request );
163
+
164
+ //allow other methods to hook in on the api result
165
+ $result = apply_filters( 'printful_api_result', $result, $method, $this->apiUrl . $url, $request );
166
+
167
+ if ( is_wp_error( $result ) ) {
168
+ throw new PrintfulException( "API request failed - " . $result->get_error_message() );
169
+ }
170
+ $this->lastResponseRaw = $result['body'];
171
+ $this->lastResponse = $response = json_decode( $result['body'], true );
172
+
173
+ if ( ! isset( $response['code'], $response['result'] ) ) {
174
+ throw new PrintfulException( 'Invalid API response' );
175
+ }
176
+ $status = (int) $response['code'];
177
+ if ( $status < 200 || $status >= 300 ) {
178
+ throw new PrintfulApiException( (string) $response['result'], $status );
179
+ }
180
+
181
+ return $response['result'];
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Class PrintfulException Generic Printful exception
187
+ */
188
+ class PrintfulException extends Exception {}
189
+ /**
190
+ * Class PrintfulException Printful exception returned from the API
191
+ */
192
  class PrintfulApiException extends PrintfulException {}
includes/class-printful-integration.php CHANGED
@@ -1,165 +1,165 @@
1
- <?php
2
- if ( ! defined( 'ABSPATH' ) ) exit;
3
-
4
- class Printful_Integration
5
- {
6
- const PF_API_CONNECT_STATUS = 'printful_api_connect_status';
7
- const PF_CONNECT_ERROR = 'printful_connect_error';
8
-
9
- public static $_instance;
10
-
11
- public static function instance() {
12
- if ( is_null( self::$_instance ) ) {
13
- self::$_instance = new self();
14
- }
15
-
16
- return self::$_instance;
17
- }
18
-
19
- public function __construct() {
20
- self::$_instance = $this;
21
- }
22
-
23
- /**
24
- * @return Printful_Client
25
- * @throws PrintfulException
26
- */
27
- public function get_client() {
28
-
29
- require_once 'class-printful-client.php';
30
- $client = new Printful_Client( $this->get_option( 'printful_key' ), $this->get_option( 'disable_ssl' ) == 'yes' );
31
-
32
- return $client;
33
- }
34
-
35
- /**
36
- * Check if the connection to printful is working
37
- * @param bool $force
38
- * @return bool
39
- * @throws PrintfulException
40
- */
41
- public function is_connected( $force = false ) {
42
-
43
- $api_key = $this->get_option( 'printful_key' );
44
-
45
- //dont need to show error - the plugin is simply not setup
46
- if ( empty( $api_key ) ) {
47
- return false;
48
- }
49
-
50
- //validate length, show error
51
- if ( strlen( $api_key ) != 36 ) {
52
- $message = 'Invalid API key - the key must be 36 characters long. Please ensure that your API key in <a href="%s">Settings</a> matches the one in your <a href="%s">Printful dashboard</a>.';
53
- $settings_url = admin_url( 'admin.php?page=printful-dashboard&tab=settings' );
54
- $printful_url = Printful_Base::get_printful_host() . 'dashboard/';
55
- $this->set_connect_error(sprintf( $message, $settings_url, $printful_url ) );
56
-
57
- return false;
58
- }
59
-
60
- //show connect status from cache
61
- if ( ! $force ) {
62
- $connected = get_transient( self::PF_API_CONNECT_STATUS );
63
- if ( $connected && $connected['status'] == 1 ) {
64
- $this->clear_connect_error();
65
-
66
- return true;
67
- } else if ( $connected && $connected['status'] == 0 ) { //try again in a minute
68
- return false;
69
- }
70
- }
71
-
72
- $client = $this->get_client();
73
- $response = false;
74
-
75
- //attempt to connect to printful to verify the API key
76
- try {
77
- $storeData = $client->get( 'store' );
78
- if ( ! empty( $storeData ) && $storeData['type'] == 'woocommerce') {
79
- $response = true;
80
- $this->clear_connect_error();
81
- set_transient( self::PF_API_CONNECT_STATUS, array( 'status' => 1 ) ); //no expiry
82
- } elseif ( $storeData['type'] != 'woocommerce' ) {
83
- $message = 'Invalid API key. This API key belongs to a ' . ucfirst( $storeData['type'] ) . ' store. Please copy the correct key from <a href="%s">Printful store settings</a> and enter it in the <a href="%s">Printful plugin settings</a>';
84
- $settings_url = admin_url( 'admin.php?page=printful-dashboard&tab=settings' );
85
- $printful_url = Printful_Base::get_printful_host() . 'dashboard/store';
86
- $this->set_connect_error( sprintf( $message, $settings_url, $printful_url ) );
87
- set_transient( self::PF_API_CONNECT_STATUS, array( 'status' => 0 ), MINUTE_IN_SECONDS ); //try again in 1 minute
88
- }
89
- } catch ( Exception $e ) {
90
-
91
- if ( $e->getCode() == 401 ) {
92
- $message = 'Invalid API key. Please ensure that your API key in <a href="%s">Printful plugin settings</a> matches the one in your <a href="%s">Printful store settings</a>.';
93
- $settings_url = admin_url( 'admin.php?page=printful-dashboard&tab=settings' );
94
- $printful_url = Printful_Base::get_printful_host() . 'dashboard/store';
95
- $this->set_connect_error( sprintf( $message, $settings_url, $printful_url ) );
96
- set_transient( self::PF_API_CONNECT_STATUS, array( 'status' => 0 ), MINUTE_IN_SECONDS ); //try again in 1 minute
97
- } else {
98
- $this->set_connect_error( 'Could not connect to Printful API. Please try again later. (Error ' . $e->getCode() . ': ' . $e->getMessage() . ')' );
99
- }
100
-
101
- //do nothing
102
- set_transient( self::PF_API_CONNECT_STATUS, array( 'status' => 0 ), MINUTE_IN_SECONDS ); //try again in 1 minute
103
- }
104
-
105
- return $response;
106
- }
107
-
108
- /**
109
- * Update connect error message
110
- * @param string $error
111
- */
112
- public function set_connect_error($error = '') {
113
- update_option( self::PF_CONNECT_ERROR, $error );
114
- }
115
-
116
- /**
117
- * Get current connect error message
118
- */
119
- public function get_connect_error() {
120
- return get_option( self::PF_CONNECT_ERROR, false );
121
- }
122
-
123
- /**
124
- * Remove option used for storing current connect error
125
- */
126
- public function clear_connect_error() {
127
- delete_option( self::PF_CONNECT_ERROR );
128
- }
129
-
130
- /**
131
- * AJAX call endpoint for connect status check
132
- * @throws PrintfulException
133
- */
134
- public static function ajax_force_check_connect_status() {
135
- if ( Printful_Integration::instance()->is_connected( true ) ) {
136
- die( 'OK' );
137
- }
138
-
139
- die( 'FAIL' );
140
- }
141
-
142
- /**
143
- * Wrapper method for getting an option
144
- * @param $name
145
- * @param array $default
146
- * @return bool
147
- */
148
- public function get_option( $name, $default = array() ) {
149
- $options = get_option( 'woocommerce_printful_settings', $default );
150
- if ( ! empty( $options[ $name ] ) ) {
151
- return $options[ $name ];
152
- }
153
-
154
- return false;
155
- }
156
-
157
- /**
158
- * Save the setting
159
- * @param $settings
160
- */
161
- public function update_settings( $settings ) {
162
- delete_transient( self::PF_API_CONNECT_STATUS ); //remove the successful API status since API key could have changed
163
- update_option( 'woocommerce_printful_settings', $settings );
164
- }
165
  }
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) exit;
3
+
4
+ class Printful_Integration
5
+ {
6
+ const PF_API_CONNECT_STATUS = 'printful_api_connect_status';
7
+ const PF_CONNECT_ERROR = 'printful_connect_error';
8
+
9
+ public static $_instance;
10
+
11
+ public static function instance() {
12
+ if ( is_null( self::$_instance ) ) {
13
+ self::$_instance = new self();
14
+ }
15
+
16
+ return self::$_instance;
17
+ }
18
+
19
+ public function __construct() {
20
+ self::$_instance = $this;
21
+ }
22
+
23
+ /**
24
+ * @return Printful_Client
25
+ * @throws PrintfulException
26
+ */
27
+ public function get_client() {
28
+
29
+ require_once 'class-printful-client.php';
30
+ $client = new Printful_Client( $this->get_option( 'printful_key' ), $this->get_option( 'disable_ssl' ) == 'yes' );
31
+
32
+ return $client;
33
+ }
34
+
35
+ /**
36
+ * Check if the connection to printful is working
37
+ * @param bool $force
38
+ * @return bool
39
+ * @throws PrintfulException
40
+ */
41
+ public function is_connected( $force = false ) {
42
+
43
+ $api_key = $this->get_option( 'printful_key' );
44
+
45
+ //dont need to show error - the plugin is simply not setup
46
+ if ( empty( $api_key ) ) {
47
+ return false;
48
+ }
49
+
50
+ //validate length, show error
51
+ if ( strlen( $api_key ) != 36 ) {
52
+ $message = 'Invalid API key - the key must be 36 characters long. Please ensure that your API key in <a href="%s">Settings</a> matches the one in your <a href="%s">Printful dashboard</a>.';
53
+ $settings_url = admin_url( 'admin.php?page=printful-dashboard&tab=settings' );
54
+ $printful_url = Printful_Base::get_printful_host() . 'dashboard/';
55
+ $this->set_connect_error(sprintf( $message, $settings_url, $printful_url ) );
56
+
57
+ return false;
58
+ }
59
+
60
+ //show connect status from cache
61
+ if ( ! $force ) {
62
+ $connected = get_transient( self::PF_API_CONNECT_STATUS );
63
+ if ( $connected && $connected['status'] == 1 ) {
64
+ $this->clear_connect_error();
65
+
66
+ return true;
67
+ } else if ( $connected && $connected['status'] == 0 ) { //try again in a minute
68
+ return false;
69
+ }
70
+ }
71
+
72
+ $client = $this->get_client();
73
+ $response = false;
74
+
75
+ //attempt to connect to printful to verify the API key
76
+ try {
77
+ $storeData = $client->get( 'store' );
78
+ if ( ! empty( $storeData ) && $storeData['type'] == 'woocommerce') {
79
+ $response = true;
80
+ $this->clear_connect_error();
81
+ set_transient( self::PF_API_CONNECT_STATUS, array( 'status' => 1 ) ); //no expiry
82
+ } elseif ( $storeData['type'] != 'woocommerce' ) {
83
+ $message = 'Invalid API key. This API key belongs to a ' . ucfirst( $storeData['type'] ) . ' store. Please copy the correct key from <a href="%s">Printful store settings</a> and enter it in the <a href="%s">Printful plugin settings</a>';
84
+ $settings_url = admin_url( 'admin.php?page=printful-dashboard&tab=settings' );
85
+ $printful_url = Printful_Base::get_printful_host() . 'dashboard/store';
86
+ $this->set_connect_error( sprintf( $message, $settings_url, $printful_url ) );
87
+ set_transient( self::PF_API_CONNECT_STATUS, array( 'status' => 0 ), MINUTE_IN_SECONDS ); //try again in 1 minute
88
+ }
89
+ } catch ( Exception $e ) {
90
+
91
+ if ( $e->getCode() == 401 ) {
92
+ $message = 'Invalid API key. Please ensure that your API key in <a href="%s">Printful plugin settings</a> matches the one in your <a href="%s">Printful store settings</a>.';
93
+ $settings_url = admin_url( 'admin.php?page=printful-dashboard&tab=settings' );
94
+ $printful_url = Printful_Base::get_printful_host() . 'dashboard/store';
95
+ $this->set_connect_error( sprintf( $message, $settings_url, $printful_url ) );
96
+ set_transient( self::PF_API_CONNECT_STATUS, array( 'status' => 0 ), MINUTE_IN_SECONDS ); //try again in 1 minute
97
+ } else {
98
+ $this->set_connect_error( 'Could not connect to Printful API. Please try again later. (Error ' . $e->getCode() . ': ' . $e->getMessage() . ')' );
99
+ }
100
+
101
+ //do nothing
102
+ set_transient( self::PF_API_CONNECT_STATUS, array( 'status' => 0 ), MINUTE_IN_SECONDS ); //try again in 1 minute
103
+ }
104
+
105
+ return $response;
106
+ }
107
+
108
+ /**
109
+ * Update connect error message
110
+ * @param string $error
111
+ */
112
+ public function set_connect_error($error = '') {
113
+ update_option( self::PF_CONNECT_ERROR, $error );
114
+ }
115
+
116
+ /**
117
+ * Get current connect error message
118
+ */
119
+ public function get_connect_error() {
120
+ return get_option( self::PF_CONNECT_ERROR, false );
121
+ }
122
+
123
+ /**
124
+ * Remove option used for storing current connect error
125
+ */
126
+ public function clear_connect_error() {
127
+ delete_option( self::PF_CONNECT_ERROR );
128
+ }
129
+
130
+ /**
131
+ * AJAX call endpoint for connect status check
132
+ * @throws PrintfulException
133
+ */
134
+ public static function ajax_force_check_connect_status() {
135
+ if ( Printful_Integration::instance()->is_connected( true ) ) {
136
+ die( 'OK' );
137
+ }
138
+
139
+ die( 'FAIL' );
140
+ }
141
+
142
+ /**
143
+ * Wrapper method for getting an option
144
+ * @param $name
145
+ * @param array $default
146
+ * @return bool
147
+ */
148
+ public function get_option( $name, $default = array() ) {
149
+ $options = get_option( 'woocommerce_printful_settings', $default );
150
+ if ( ! empty( $options[ $name ] ) ) {
151
+ return $options[ $name ];
152
+ }
153
+
154
+ return false;
155
+ }
156
+
157
+ /**
158
+ * Save the setting
159
+ * @param $settings
160
+ */
161
+ public function update_settings( $settings ) {
162
+ delete_transient( self::PF_API_CONNECT_STATUS ); //remove the successful API status since API key could have changed
163
+ update_option( 'woocommerce_printful_settings', $settings );
164
+ }
165
  }
includes/class-printful-request-log.php CHANGED
@@ -1,151 +1,151 @@
1
- <?php
2
- if ( ! defined( 'ABSPATH' ) ) exit;
3
-
4
- class Printful_Request_log {
5
-
6
- const PF_USER_AGENT = 'Printful WooCommerce Integration';
7
- const PF_OPTION_LAST_API_RESPONSE = 'printful_last_api_response';
8
- const PF_OPTION_INCOMING_API_REQUEST_LOG = 'printful_incoming_api_request_log';
9
- const PF_OPTION_OUTGOING_API_REQUEST_LOG = 'printful_outgoing_api_request_log';
10
- const PF_INCOMING_API_ERRORS = 'printful-incoming-api-errors';
11
- const PF_OUTGOING_API_ERRORS = 'printful-outgoing-api-errors';
12
-
13
- public static function init() {
14
- $printful_log = new self;
15
- add_filter( 'woocommerce_api_serve_request', array( $printful_log, 'log_incoming_printful_api_requests' ), 10, 3 );
16
- add_filter( 'printful_api_result', array( $printful_log, 'log_outgoing_printful_api_requests'), 10, 4 );
17
- }
18
-
19
- /**
20
- * Log printful API errors and save the last 20 API responses
21
- * @param $served
22
- * @param $result
23
- * @param $request
24
- *
25
- * @return mixed
26
- */
27
- public function log_incoming_printful_api_requests( $served, $result, $request ) {
28
-
29
- if ( ! $this->isPrintfulApiRequest( $request ) ) {
30
- return $served;
31
- }
32
-
33
- $last_api_response = get_option( self::PF_OPTION_LAST_API_RESPONSE, false );
34
- $response_hash = md5( serialize( array( 'request' => $request, 'results' => $result ) ) );
35
-
36
- if ( $last_api_response == $response_hash ) { //do not allow the same response to be logged twice
37
- return $served;
38
- }
39
-
40
- //save full error to WC logs
41
- if ( ! empty( $result['errors'] ) ) {
42
- $this->save_to_wc_log( $request, $result, self::PF_INCOMING_API_ERRORS );
43
- }
44
-
45
- //save summary in database to be easily accessible for status page
46
- $this->save_to_printful_log( $request->method . ' ' . $request->path, $result, $response_hash, self::PF_OPTION_INCOMING_API_REQUEST_LOG );
47
-
48
- return $served; //we avoid changing the result
49
- }
50
-
51
-
52
- /**
53
- * @param $result
54
- * @param $method
55
- * @param $url
56
- * @param $request
57
- * @return array|mixed|object
58
- */
59
- public function log_outgoing_printful_api_requests($result, $method, $url, $request) {
60
-
61
- $original_result = $result;
62
- $request['path'] = $url;
63
- $params_set = null;
64
- $code_success = null;
65
-
66
- if ( ! is_wp_error( $result ) ) {
67
- $result = json_decode( $result['body'], true );
68
- $params_set = ! isset( $result['code'], $result['result'] );
69
- $status = (int) $result['code'];
70
- $code_success = ( $status < 200 || $status >= 300 );
71
- }
72
-
73
- //if the request contains error, log it
74
- if ( is_wp_error($result) || $params_set || $code_success ) {
75
- $this->save_to_wc_log( $request, $result, self::PF_OUTGOING_API_ERRORS );
76
- }
77
-
78
- $response_hash = md5( serialize( array( 'request' => $request, 'results' => $result ) ) );
79
-
80
- //save summary in database to be easily accessible for status page
81
- $this->save_to_printful_log( $method . ' ' . $url, $result, $response_hash, self::PF_OPTION_OUTGOING_API_REQUEST_LOG );
82
-
83
- return $original_result; //don't change the result
84
- }
85
-
86
- /**
87
- * Write Printful API request errors to log
88
- *
89
- * @param $request
90
- * @param $result
91
- * @param string $context
92
- * @return bool
93
- */
94
- private function save_to_wc_log( $request, $result, $context ) {
95
-
96
- if ( ! function_exists( 'wc_get_logger' ) ) {
97
- return false;
98
- }
99
-
100
- $logger = wc_get_logger();
101
- $context = array( 'source' => $context );
102
- $log_item = array(
103
- 'request' => (array) $request,
104
- 'results' => (array) $result,
105
- );
106
- $logger->error( wc_print_r( $log_item, true ), $context );
107
-
108
- return true;
109
- }
110
-
111
- /**
112
- * Save 20 lasts requests in easily accessible location
113
- *
114
- * @param $request_title
115
- * @param $result
116
- * @param $response_hash
117
- * @param $log
118
- */
119
- private function save_to_printful_log( $request_title, $result, $response_hash, $log ) {
120
-
121
- $request_log = get_option( $log, array() );
122
- if ( count( $request_log ) > 20 ) {
123
- $request_log = array_slice( $request_log, 1, 19 ); //if there are more than 20, remove the first entry
124
- }
125
-
126
- $is_error = is_wp_error($result) || !empty( $result['errors'] );
127
-
128
- $request_log[] = array(
129
- 'date' => date( 'Y-m-d H:i:s' ),
130
- 'request' => $request_title,
131
- 'result' => ( $is_error ? 'ERROR' : 'OK' ),
132
- );
133
-
134
- update_option( $log, $request_log );
135
- update_option( self::PF_OPTION_LAST_API_RESPONSE, $response_hash );
136
- }
137
-
138
- /**
139
- * Check requests header for indications that this is a printful api request
140
- * @param $request
141
- * @return bool
142
- */
143
- private function isPrintfulApiRequest( $request ) {
144
-
145
- if ( ! empty( $request->headers ) && ! empty( $request->headers['USER_AGENT'] ) && $request->headers['USER_AGENT'] == self::PF_USER_AGENT ) {
146
- return true;
147
- }
148
-
149
- return false;
150
- }
151
  }
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) exit;
3
+
4
+ class Printful_Request_log {
5
+
6
+ const PF_USER_AGENT = 'Printful WooCommerce Integration';
7
+ const PF_OPTION_LAST_API_RESPONSE = 'printful_last_api_response';
8
+ const PF_OPTION_INCOMING_API_REQUEST_LOG = 'printful_incoming_api_request_log';
9
+ const PF_OPTION_OUTGOING_API_REQUEST_LOG = 'printful_outgoing_api_request_log';
10
+ const PF_INCOMING_API_ERRORS = 'printful-incoming-api-errors';
11
+ const PF_OUTGOING_API_ERRORS = 'printful-outgoing-api-errors';
12
+
13
+ public static function init() {
14
+ $printful_log = new self;
15
+ add_filter( 'woocommerce_api_serve_request', array( $printful_log, 'log_incoming_printful_api_requests' ), 10, 3 );
16
+ add_filter( 'printful_api_result', array( $printful_log, 'log_outgoing_printful_api_requests'), 10, 4 );
17
+ }
18
+
19
+ /**
20
+ * Log printful API errors and save the last 20 API responses
21
+ * @param $served
22
+ * @param $result
23
+ * @param $request
24
+ *
25
+ * @return mixed
26
+ */
27
+ public function log_incoming_printful_api_requests( $served, $result, $request ) {
28
+
29
+ if ( ! $this->isPrintfulApiRequest( $request ) ) {
30
+ return $served;
31
+ }
32
+
33
+ $last_api_response = get_option( self::PF_OPTION_LAST_API_RESPONSE, false );
34
+ $response_hash = md5( serialize( array( 'request' => $request, 'results' => $result ) ) );
35
+
36
+ if ( $last_api_response == $response_hash ) { //do not allow the same response to be logged twice
37
+ return $served;
38
+ }
39
+
40
+ //save full error to WC logs
41
+ if ( ! empty( $result['errors'] ) ) {
42
+ $this->save_to_wc_log( $request, $result, self::PF_INCOMING_API_ERRORS );
43
+ }
44
+
45
+ //save summary in database to be easily accessible for status page
46
+ $this->save_to_printful_log( $request->method . ' ' . $request->path, $result, $response_hash, self::PF_OPTION_INCOMING_API_REQUEST_LOG );
47
+
48
+ return $served; //we avoid changing the result
49
+ }
50
+
51
+
52
+ /**
53
+ * @param $result
54
+ * @param $method
55
+ * @param $url
56
+ * @param $request
57
+ * @return array|mixed|object
58
+ */
59
+ public function log_outgoing_printful_api_requests($result, $method, $url, $request) {
60
+
61
+ $original_result = $result;
62
+ $request['path'] = $url;
63
+ $params_set = null;
64
+ $code_success = null;
65
+
66
+ if ( ! is_wp_error( $result ) ) {
67
+ $result = json_decode( $result['body'], true );
68
+ $params_set = ! isset( $result['code'], $result['result'] );
69
+ $status = (int) $result['code'];
70
+ $code_success = ( $status < 200 || $status >= 300 );
71
+ }
72
+
73
+ //if the request contains error, log it
74
+ if ( is_wp_error($result) || $params_set || $code_success ) {
75
+ $this->save_to_wc_log( $request, $result, self::PF_OUTGOING_API_ERRORS );
76
+ }
77
+
78
+ $response_hash = md5( serialize( array( 'request' => $request, 'results' => $result ) ) );
79
+
80
+ //save summary in database to be easily accessible for status page
81
+ $this->save_to_printful_log( $method . ' ' . $url, $result, $response_hash, self::PF_OPTION_OUTGOING_API_REQUEST_LOG );
82
+
83
+ return $original_result; //don't change the result
84
+ }
85
+
86
+ /**
87
+ * Write Printful API request errors to log
88
+ *
89
+ * @param $request
90
+ * @param $result
91
+ * @param string $context
92
+ * @return bool
93
+ */
94
+ private function save_to_wc_log( $request, $result, $context ) {
95
+
96
+ if ( ! function_exists( 'wc_get_logger' ) ) {
97
+ return false;
98
+ }
99
+
100
+ $logger = wc_get_logger();
101
+ $context = array( 'source' => $context );
102
+ $log_item = array(
103
+ 'request' => (array) $request,
104
+ 'results' => (array) $result,
105
+ );
106
+ $logger->error( wc_print_r( $log_item, true ), $context );
107
+
108
+ return true;
109
+ }
110
+
111
+ /**
112
+ * Save 20 lasts requests in easily accessible location
113
+ *
114
+ * @param $request_title
115
+ * @param $result
116
+ * @param $response_hash
117
+ * @param $log
118
+ */
119
+ private function save_to_printful_log( $request_title, $result, $response_hash, $log ) {
120
+
121
+ $request_log = get_option( $log, array() );
122
+ if ( count( $request_log ) > 20 ) {
123
+ $request_log = array_slice( $request_log, 1, 19 ); //if there are more than 20, remove the first entry
124
+ }
125
+
126
+ $is_error = is_wp_error($result) || !empty( $result['errors'] );
127
+
128
+ $request_log[] = array(
129
+ 'date' => date( 'Y-m-d H:i:s' ),
130
+ 'request' => $request_title,
131
+ 'result' => ( $is_error ? 'ERROR' : 'OK' ),
132
+ );
133
+
134
+ update_option( $log, $request_log );
135
+ update_option( self::PF_OPTION_LAST_API_RESPONSE, $response_hash );
136
+ }
137
+
138
+ /**
139
+ * Check requests header for indications that this is a printful api request
140
+ * @param $request
141
+ * @return bool
142
+ */
143
+ private function isPrintfulApiRequest( $request ) {
144
+
145
+ if ( ! empty( $request->headers ) && ! empty( $request->headers['USER_AGENT'] ) && $request->headers['USER_AGENT'] == self::PF_USER_AGENT ) {
146
+ return true;
147
+ }
148
+
149
+ return false;
150
+ }
151
  }
includes/class-printful-rest-api-controller.php CHANGED
@@ -64,6 +64,26 @@ class Printful_REST_API_Controller extends WC_REST_Controller
64
  )
65
  ) );
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  register_rest_route( $this->namespace, '/' . $this->rest_base . '/version', array(
68
  array(
69
  'methods' => WP_REST_Server::READABLE,
@@ -142,7 +162,16 @@ class Printful_REST_API_Controller extends WC_REST_Controller
142
  }
143
 
144
  //lets do this
145
- update_post_meta( $product->get_id(), 'pf_size_chart', htmlspecialchars( $data['size_chart'] ) );
 
 
 
 
 
 
 
 
 
146
 
147
  return array(
148
  'product' => $product,
64
  )
65
  ) );
66
 
67
+ register_rest_route( $this->namespace, '/' . $this->rest_base . '/products/(?P<product_id>\d+)/advanced-size-chart', array(
68
+ array(
69
+ 'methods' => WP_REST_Server::EDITABLE,
70
+ 'callback' => array( $this, 'post_size_guide' ),
71
+ 'permission_callback' => array( $this, 'update_item_permissions_check' ),
72
+ 'show_in_index' => false,
73
+ 'args' => array(
74
+ 'product_id' => array(
75
+ 'description' => __( 'Unique identifier for the resource.', 'printful' ),
76
+ 'type' => 'integer',
77
+ ),
78
+ 'size_chart' => array(
79
+ 'required' => true,
80
+ 'type' => 'object',
81
+ 'description' => __( 'Advanced Printful size guide', 'printful' ),
82
+ )
83
+ )
84
+ )
85
+ ) );
86
+
87
  register_rest_route( $this->namespace, '/' . $this->rest_base . '/version', array(
88
  array(
89
  'methods' => WP_REST_Server::READABLE,
162
  }
163
 
164
  //lets do this
165
+ if( is_array( $data['size_chart']) ) {
166
+ // Advanced size guide
167
+ $payload = addslashes( json_encode( Printful_Size_Guide::process_size_guide_for_storage($data['size_chart'], $product_id ) ) );
168
+ $metaKey = 'pf_advanced_size_chart';
169
+ } else {
170
+ $payload = htmlspecialchars( $data['size_chart'] );
171
+ $metaKey = 'pf_size_chart';
172
+ }
173
+
174
+ update_post_meta( $product->get_id(), $metaKey, $payload );
175
 
176
  return array(
177
  'product' => $product,
includes/class-printful-size-chart-tab.php CHANGED
@@ -1,124 +1,129 @@
1
- <?php
2
- if ( ! defined( 'ABSPATH' ) ) exit;
3
-
4
- class Printful_Size_Chart_Tab {
5
-
6
- /**
7
- * Is meta boxes saved once?
8
- *
9
- * @var boolean
10
- */
11
- private static $saved_meta_boxes = false;
12
-
13
- /**
14
- * Printful_Size_Chart_Tab constructor.
15
- */
16
- public static function init() {
17
- $size_chart = new self;
18
- add_filter( 'woocommerce_product_tabs', array( $size_chart, 'init_size_chart_tab' ) );
19
- add_action( 'add_meta_boxes', array( $size_chart, 'init_metabox' ) );
20
- add_action( 'save_post', array( $size_chart, 'save_size_chart' ), 1, 2 );
21
- }
22
-
23
- /**
24
- * Initialize meta boxes
25
- */
26
- public function init_metabox() {
27
- add_meta_box( 'pf_size_chart', __( 'Size chart', 'printful' ), array( $this, 'size_chart_metabox' ), 'product', 'normal' );
28
- }
29
-
30
- /**
31
- * @param $tabs
32
- *
33
- * @return mixed
34
- */
35
- public function init_size_chart_tab( $tabs ) {
36
- if ( strlen( $this->get_size_chart_content() ) > 0 ) {
37
- $tabs['size_chart'] = array(
38
- 'title' => __( 'Size Chart', 'printful' ),
39
- 'priority' => 50,
40
- 'callback' => array( $this, 'size_chart_tab_content' ),
41
- );
42
- }
43
-
44
- return $tabs;
45
- }
46
-
47
- /**
48
- * Display the size chart content
49
- */
50
- public function size_chart_tab_content() {
51
- echo '<h2>' . esc_html__( 'Size Chart', 'printful' ) . '</h2>';
52
- echo $this->get_size_chart_content();
53
- }
54
-
55
- /**
56
- * @return mixed
57
- */
58
- public function get_size_chart_content() {
59
- global $post;
60
-
61
- return htmlspecialchars_decode(get_post_meta( $post->ID, 'pf_size_chart', true ));
62
- }
63
-
64
- /**
65
- * @param $meta_id
66
- */
67
- public function size_chart_metabox( $meta_id ) {
68
- $settings = array(
69
- 'textarea_name' => 'pf_size_chart',
70
- 'tinymce' => array(
71
- 'theme_advanced_buttons1' => 'bold,italic,strikethrough,separator,bullist,numlist,separator,blockquote,separator,justifyleft,justifycenter,justifyright,separator,link,unlink,separator,undo,redo,separator',
72
- 'theme_advanced_buttons2' => '',
73
- ),
74
- 'editor_css' => '<style>#wp-pf_size_chart-editor-container .wp-editor-area{height:175px; width:100%;} .wp-editor-area{height:175px; width:100%;}</style>',
75
- );
76
-
77
- $content = get_post_meta( $meta_id->ID, 'pf_size_chart', true );
78
-
79
- wp_editor( htmlspecialchars_decode( $content ), 'pf_size_chart_editor', apply_filters( 'woocommerce_product_short_description_editor_settings', $settings ) );
80
- }
81
-
82
- /**
83
- * @param $post_id
84
- * @param $post
85
- */
86
- public function save_size_chart( $post_id, $post ) {
87
-
88
- // $post_id and $post are required
89
- if ( empty( $post_id ) || empty( $post ) || self::$saved_meta_boxes ) {
90
- return;
91
- }
92
-
93
- // Dont' save meta boxes for revisions or autosaves
94
- if ( defined( 'DOING_AUTOSAVE' ) || is_int( wp_is_post_revision( $post ) ) || is_int( wp_is_post_autosave( $post ) ) ) {
95
- return;
96
- }
97
-
98
- // Check the nonce
99
- if ( empty( $_POST['woocommerce_meta_nonce'] ) || ! wp_verify_nonce( $_POST['woocommerce_meta_nonce'], 'woocommerce_save_data' ) ) {
100
- return;
101
- }
102
-
103
- // Check the post being saved == the $post_id to prevent triggering this call for other save_post events
104
- if ( empty( $_POST['post_ID'] ) || $_POST['post_ID'] != $post_id ) {
105
- return;
106
- }
107
-
108
- // Check user has permission to edit
109
- if ( ! current_user_can( 'edit_post', $post_id ) ) {
110
- return;
111
- }
112
-
113
- // Check the post type
114
- if ( $post->post_type != 'product' ) {
115
- return;
116
- }
117
-
118
- // We need this save event to run once to avoid potential endless loops.
119
- self::$saved_meta_boxes = true;
120
-
121
- //save
122
- update_post_meta($post_id, 'pf_size_chart', htmlspecialchars($_POST['pf_size_chart']));
123
- }
 
 
 
 
 
124
  }
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) exit;
3
+
4
+ class Printful_Size_Chart_Tab {
5
+
6
+ /**
7
+ * Is meta boxes saved once?
8
+ *
9
+ * @var boolean
10
+ */
11
+ private static $saved_meta_boxes = false;
12
+
13
+ /**
14
+ * Printful_Size_Chart_Tab constructor.
15
+ */
16
+ public static function init() {
17
+ $size_chart = new self;
18
+ add_filter( 'woocommerce_product_tabs', array( $size_chart, 'init_size_chart_tab' ) );
19
+ add_action( 'add_meta_boxes', array( $size_chart, 'init_metabox' ) );
20
+ add_action( 'save_post', array( $size_chart, 'save_size_chart' ), 1, 2 );
21
+ }
22
+
23
+ /**
24
+ * Initialize meta boxes
25
+ */
26
+ public function init_metabox() {
27
+ global $post;
28
+ // If product has advanced size chart we don't show basic size chart metabox at all
29
+ if (Printful_Size_Guide::get_size_guide_for_product($post)) {
30
+ return;
31
+ }
32
+ add_meta_box( 'pf_size_chart', __( 'Size chart', 'printful' ), array( $this, 'size_chart_metabox' ), 'product', 'normal' );
33
+ }
34
+
35
+ /**
36
+ * @param $tabs
37
+ *
38
+ * @return mixed
39
+ */
40
+ public function init_size_chart_tab( $tabs ) {
41
+ if ( strlen( $this->get_size_chart_content() ) > 0 ) {
42
+ $tabs['size_chart'] = array(
43
+ 'title' => __( 'Size Chart', 'printful' ),
44
+ 'priority' => 50,
45
+ 'callback' => array( $this, 'size_chart_tab_content' ),
46
+ );
47
+ }
48
+
49
+ return $tabs;
50
+ }
51
+
52
+ /**
53
+ * Display the size chart content
54
+ */
55
+ public function size_chart_tab_content() {
56
+ echo '<h2>' . esc_html__( 'Size Chart', 'printful' ) . '</h2>';
57
+ echo $this->get_size_chart_content();
58
+ }
59
+
60
+ /**
61
+ * @return mixed
62
+ */
63
+ public function get_size_chart_content() {
64
+ global $post;
65
+
66
+ return htmlspecialchars_decode(get_post_meta( $post->ID, 'pf_size_chart', true ));
67
+ }
68
+
69
+ /**
70
+ * @param $meta_id
71
+ */
72
+ public function size_chart_metabox( $meta_id ) {
73
+ $settings = array(
74
+ 'textarea_name' => 'pf_size_chart',
75
+ 'tinymce' => array(
76
+ 'theme_advanced_buttons1' => 'bold,italic,strikethrough,separator,bullist,numlist,separator,blockquote,separator,justifyleft,justifycenter,justifyright,separator,link,unlink,separator,undo,redo,separator',
77
+ 'theme_advanced_buttons2' => '',
78
+ ),
79
+ 'editor_css' => '<style>#wp-pf_size_chart-editor-container .wp-editor-area{height:175px; width:100%;} .wp-editor-area{height:175px; width:100%;}</style>',
80
+ );
81
+
82
+ $content = get_post_meta( $meta_id->ID, 'pf_size_chart', true );
83
+
84
+ wp_editor( htmlspecialchars_decode( $content ), 'pf_size_chart_editor', apply_filters( 'woocommerce_product_short_description_editor_settings', $settings ) );
85
+ }
86
+
87
+ /**
88
+ * @param $post_id
89
+ * @param $post
90
+ */
91
+ public function save_size_chart( $post_id, $post ) {
92
+
93
+ // $post_id and $post are required
94
+ if ( empty( $post_id ) || empty( $post ) || self::$saved_meta_boxes ) {
95
+ return;
96
+ }
97
+
98
+ // Dont' save meta boxes for revisions or autosaves
99
+ if ( defined( 'DOING_AUTOSAVE' ) || is_int( wp_is_post_revision( $post ) ) || is_int( wp_is_post_autosave( $post ) ) ) {
100
+ return;
101
+ }
102
+
103
+ // Check the nonce
104
+ if ( empty( $_POST['woocommerce_meta_nonce'] ) || ! wp_verify_nonce( $_POST['woocommerce_meta_nonce'], 'woocommerce_save_data' ) ) {
105
+ return;
106
+ }
107
+
108
+ // Check the post being saved == the $post_id to prevent triggering this call for other save_post events
109
+ if ( empty( $_POST['post_ID'] ) || $_POST['post_ID'] != $post_id ) {
110
+ return;
111
+ }
112
+
113
+ // Check user has permission to edit
114
+ if ( ! current_user_can( 'edit_post', $post_id ) ) {
115
+ return;
116
+ }
117
+
118
+ // Check the post type
119
+ if ( $post->post_type != 'product' ) {
120
+ return;
121
+ }
122
+
123
+ // We need this save event to run once to avoid potential endless loops.
124
+ self::$saved_meta_boxes = true;
125
+
126
+ //save
127
+ update_post_meta($post_id, 'pf_size_chart', htmlspecialchars($_POST['pf_size_chart']));
128
+ }
129
  }
includes/class-printful-size-guide.php ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ class Printful_Size_Guide {
5
+ public static function init() {
6
+ $sizeGuide = new self();
7
+
8
+ $sizeGuide->hook_actions();
9
+ }
10
+
11
+ public function hook_actions() {
12
+ add_action( 'wp_enqueue_scripts', array( $this, 'load_size_guide_scripts' ) );
13
+ }
14
+
15
+ public function load_size_guide_scripts() {
16
+ global $post;
17
+
18
+ $sizeGuideData = self::get_size_guide_for_product( $post );
19
+ if ( ! $sizeGuideData ) {
20
+ return;
21
+ }
22
+
23
+ $handle = 'printful-product-size-guide';
24
+ wp_enqueue_script( $handle, plugins_url( '../assets/js/product-size-guide.js', __FILE__ ) );
25
+ wp_localize_script(
26
+ $handle,
27
+ 'pfGlobal',
28
+ array(
29
+ 'sg_modal_title' => Printful_Integration::instance()
30
+ ->get_option( 'pfsg_modal_title', Printful_Admin_Settings::DEFAULT_SIZE_GUIDE_MODAL_TITLE ),
31
+ 'sg_modal_text_color' => Printful_Integration::instance()
32
+ ->get_option( 'pfsg_modal_text_color', Printful_Admin_Settings::DEFAULT_SIZE_GUIDE_MODAL_TEXT_COLOR ),
33
+ 'sg_modal_background_color' => Printful_Integration::instance()
34
+ ->get_option(
35
+ 'pfsg_modal_background_color',
36
+ Printful_Admin_Settings::DEFAULT_SIZE_GUIDE_MODAL_BACKGROUND_COLOR
37
+ ),
38
+ 'sg_tab_background_color' => Printful_Integration::instance()
39
+ ->get_option(
40
+ 'pfsg_tab_background_color',
41
+ Printful_Admin_Settings::DEFAULT_SIZE_GUIDE_TAB_BACKGROUND_COLOR
42
+ ),
43
+ 'sg_active_tab_background_color' => Printful_Integration::instance()
44
+ ->get_option(
45
+ 'pfsg_active_tab_background_color',
46
+ Printful_Admin_Settings::DEFAULT_SIZE_GUIDE_ACTIVE_TAB_BACKGROUND_COLOR
47
+ ),
48
+ 'sg_primary_unit' => Printful_Integration::instance()
49
+ ->get_option( 'pfsg_primary_unit', Printful_Admin_Settings::DEFAULT_SIZE_GUIDE_UNIT ),
50
+ 'sg_data_raw' => json_encode( $sizeGuideData ),
51
+ 'sg_tab_title_person' => __( 'Measure yourself', 'printful' ),
52
+ 'sg_tab_title_product' => __( 'Product measurements', 'printful' ),
53
+ )
54
+ );
55
+ wp_register_style( $handle, plugins_url( '../assets/css/size-guide.css', __FILE__ ) );
56
+ wp_enqueue_style( $handle );
57
+ }
58
+
59
+ /**
60
+ * @param WP_Post $product
61
+ *
62
+ * @return array|null
63
+ */
64
+ public static function get_size_guide_for_product( $product = null ) {
65
+ if ( ! $product || ! get_post_meta( $product->ID, 'pf_advanced_size_chart', true ) ) {
66
+ return null;
67
+ }
68
+
69
+ // Size guide data should be a valid JSON
70
+ $size_guide_data = json_decode( get_post_meta( $product->ID, 'pf_advanced_size_chart', true ), true );
71
+
72
+ if ( ! self::is_size_guide_valid( $size_guide_data ) ) {
73
+ return null;
74
+ }
75
+
76
+ // URLs to size guide images
77
+ if ( isset( $size_guide_data['modelMeasurements']['imageId'] ) ) {
78
+ $size_guide_data['modelMeasurements']['imageUrl'] = self::get_attached_image_src( $size_guide_data['modelMeasurements']['imageId'] );
79
+ }
80
+
81
+ if ( isset( $size_guide_data['productMeasurements']['imageId'] ) ) {
82
+ $size_guide_data['productMeasurements']['imageUrl'] = self::get_attached_image_src( $size_guide_data['productMeasurements']['imageId'] );
83
+ }
84
+
85
+ return $size_guide_data;
86
+ }
87
+
88
+ /**
89
+ * @param array $size_guide
90
+ *
91
+ * @return bool
92
+ */
93
+ public static function is_size_guide_valid( $size_guide = null ) {
94
+ if ( empty( $size_guide ) || ! is_array( $size_guide ) ) {
95
+ return false;
96
+ }
97
+
98
+ // Size guide should have at least model or product measurement data
99
+ if ( ( empty( $size_guide['modelMeasurements'] ) && empty( $size_guide['productMeasurements'] ) )
100
+ || empty( $size_guide['availableSizes'] ) ) {
101
+ return false;
102
+ }
103
+
104
+ // Validate size guide rows
105
+ $rows = [];
106
+ if ( ! empty( $size_guide['modelMeasurements']['sizeTableRows'] ) ) {
107
+ $rows += $size_guide['modelMeasurements']['sizeTableRows'];
108
+ }
109
+
110
+ if ( ! empty( $size_guide['productMeasurements']['sizeTableRows'] ) ) {
111
+ $rows += $size_guide['productMeasurements']['sizeTableRows'];
112
+ }
113
+
114
+ foreach ( $rows as $row ) {
115
+ if ( empty( $row['unit'] ) || empty( $row['sizes'] ) ) {
116
+ return false;
117
+ }
118
+ }
119
+
120
+ return true;
121
+ }
122
+
123
+ /**
124
+ * @param int $attachment_id
125
+ *
126
+ * @return string|null
127
+ */
128
+ public static function get_attached_image_src( $attachment_id ) {
129
+ $attachment = wp_get_attachment_image_src( $attachment_id, 'full' );
130
+
131
+ if ( ! is_array( $attachment ) ) {
132
+ return null;
133
+ }
134
+
135
+ return current( $attachment );
136
+ }
137
+
138
+ /**
139
+ * Creates file from given url and stores as post attachment
140
+ *
141
+ * @param string $url
142
+ * @param string $file_name
143
+ *
144
+ * @return int|WP_Error
145
+ */
146
+ public static function save_image( $url, $file_name ) {
147
+ if ( ! $url ) {
148
+ return null;
149
+ }
150
+
151
+ require_once ABSPATH . 'wp-admin/includes/file.php';
152
+
153
+ // Download size guide img to temp file
154
+ $temp_file_name = download_url( $url, 20 );
155
+ if ( is_wp_error( $temp_file_name ) ) {
156
+ return $temp_file_name;
157
+ }
158
+
159
+ // Validate image readability
160
+ require_once ABSPATH . 'wp-admin/includes/image.php';
161
+ if ( ! file_is_displayable_image( $temp_file_name ) ) {
162
+ @unlink( $temp_file_name );
163
+
164
+ return new WP_Error( 'pf_size_guide_img', 'Size guide attachment is not a valid image' );
165
+ }
166
+
167
+ $file_data = array(
168
+ 'name' => $file_name,
169
+ 'tmp_name' => $temp_file_name,
170
+ );
171
+
172
+ // Handle side load and attach to a post
173
+ require_once ABSPATH . 'wp-admin/includes/media.php';
174
+ $id = media_handle_sideload( $file_data );
175
+
176
+ @unlink( $temp_file_name );
177
+
178
+ if ( is_wp_error( $id ) ) {
179
+ return $id;
180
+ }
181
+
182
+ return $id;
183
+ }
184
+
185
+ /**
186
+ * @param array $size_guide_data
187
+ * @param int $product_id
188
+ *
189
+ * @return array
190
+ */
191
+ public static function process_size_guide_for_storage( $size_guide_data, $product_id ) {
192
+ $modelImageUrl = ! empty( $size_guide_data['modelMeasurements']['imageUrl'] )
193
+ ? $size_guide_data['modelMeasurements']['imageUrl'] : null;
194
+
195
+ $productImageUrl = ! empty( $size_guide_data['productMeasurements']['imageUrl'] )
196
+ ? $size_guide_data['productMeasurements']['imageUrl'] : null;
197
+
198
+ // Remove original URLs
199
+ unset( $size_guide_data['modelMeasurements']['imageUrl'] );
200
+ unset( $size_guide_data['productMeasurements']['imageUrl'] );
201
+
202
+ $modelImageId = self::save_image( $modelImageUrl, $product_id . '_model_size_guide.png' );
203
+ $productImageId = self::save_image( $productImageUrl, $product_id . '_product_size_guide.png' );
204
+
205
+ // Link size guide image attachments to size guide
206
+ $size_guide_data['modelMeasurements']['imageId'] = ! is_wp_error( $modelImageId ) ? $modelImageId : null;
207
+ $size_guide_data['productMeasurements']['imageId'] = ! is_wp_error( $productImageId ) ? $productImageId : null;
208
+
209
+ return $size_guide_data;
210
+ }
211
+ }
includes/class-printful-taxes.php CHANGED
@@ -1,189 +1,189 @@
1
- <?php
2
- if ( ! defined( 'ABSPATH' ) ) exit;
3
-
4
- class Printful_Taxes {
5
-
6
- /**
7
- * Initialize the values, hooks and actions
8
- */
9
- public static function init() {
10
- if ( Printful_Integration::instance()->get_option( 'calculate_tax' ) == 'yes' ) {
11
- //Update tax options if taxes are enabled
12
- if ( get_option( 'woocommerce_calc_taxes' ) != 'yes' ) {
13
- update_option( 'woocommerce_calc_taxes', 'yes' );
14
- }
15
- if ( get_option( 'woocommerce_tax_based_on' ) != 'shipping' ) {
16
- update_option( 'woocommerce_tax_based_on', 'shipping' );
17
- }
18
-
19
- //Override tax rates calculated by Woocommerce
20
- $taxes = new self;
21
- add_filter( 'woocommerce_matched_tax_rates', array( $taxes, 'calculate_tax' ), 10, 6 );
22
- }
23
- }
24
-
25
- /**
26
- * @param $matched_tax_rates
27
- * @param $country
28
- * @param $state
29
- * @param $postcode
30
- * @param $city
31
- * @param $tax_class
32
- *
33
- * @return mixed
34
- */
35
- public function calculate_tax( $matched_tax_rates, $country, $state, $postcode, $city, $tax_class ) {
36
- //if a tax rate is already matched, avoid adding extra one
37
- if ( ! empty( $matched_tax_rates ) ) {
38
- return $matched_tax_rates;
39
- }
40
-
41
- $countries = $this->get_tax_countries();
42
- if ( isset( $countries[ $country ][ $state ] ) && !empty($postcode) ) { //only make the request if country, state and zip are set
43
- $key = 'printful_tax_rate_' . $country . '-' . $state . '-' . $city . '-' . $postcode;
44
- $rate = get_transient( $key );
45
- $response = null;
46
-
47
- if ( $rate === false ) {
48
- try {
49
- $client = Printful_Integration::instance()->get_client();
50
- $response = $client->post( 'tax/rates', array(
51
- 'recipient' => array(
52
- 'country_code' => $country,
53
- 'state_code' => $state,
54
- 'city' => $city,
55
- 'zip' => $postcode,
56
- ),
57
- ) );
58
- } catch ( Exception $e ) {
59
- }
60
-
61
- if ( isset( $response['rate'] ) ) {
62
- $rate = $response;
63
- } else {
64
- $rate = array(
65
- 'required' => false,
66
- 'rate' => 0,
67
- 'shipping_taxable' => false,
68
- );
69
- }
70
- set_transient( $key, $rate, 1800 );
71
- }
72
-
73
- if ( $rate['required'] ) {
74
- $rate_item = array(
75
- 'rate' => $rate['rate'] * 100,
76
- 'label' => 'Sales Tax',
77
- 'shipping' => $rate['shipping_taxable'] ? 'yes' : 'no',
78
- 'compound' => 'no',
79
- );
80
-
81
- if ( $this->isRateUnique( $rate_item, $matched_tax_rates ) ) {
82
- $id = $this->get_printful_rate_id( $country, $state, $rate['shipping_taxable'] );
83
- $matched_tax_rates[ $id ] = $rate_item;
84
- }
85
- }
86
- }
87
-
88
- return $matched_tax_rates;
89
- }
90
-
91
- /**
92
- * Checks if a equal tax rate is not already set
93
- *
94
- * @param $rate
95
- * @param $matched_tax_rates
96
- *
97
- * @return bool
98
- */
99
- private function isRateUnique( $rate, $matched_tax_rates ) {
100
- if ( empty( $matched_tax_rates ) ) {
101
- return true;
102
- }
103
-
104
- foreach ( $matched_tax_rates as $mr ) {
105
- if ( floatval( $mr['rate'] ) == floatval( $rate['rate'] ) && $mr['shipping'] == $rate['shipping'] ) {
106
- return false;
107
- }
108
- }
109
-
110
- return true;
111
- }
112
-
113
- /**
114
- * Gets list of countries and states where Printful needs to calculate sales tax
115
- * @return array|mixed
116
- */
117
- private function get_tax_countries() {
118
- $countries = get_transient( 'printful_tax_countries' );
119
-
120
- if ( ! $countries ) {
121
- $countries = array();
122
-
123
- try {
124
- $client = Printful_Integration::instance()->get_client();
125
- $list = $client->get( 'tax/countries' );
126
-
127
- foreach ( $list as $country ) {
128
- foreach ( $country['states'] as $state ) {
129
- $countries[ $country['code'] ][ $state['code'] ] = 1;
130
- }
131
- }
132
-
133
- if ( ! empty( $countries ) ) {
134
- set_transient( 'printful_tax_countries', $countries, 6 * 3600 );
135
- }
136
- } catch ( Exception $e ) {
137
- //Default to CA if can't get the actual state list
138
- return array( 'US' => array( 'CA' => 1 ) );
139
- }
140
- }
141
-
142
- return $countries;
143
- }
144
-
145
- /**
146
- * Creates dummy tax rate ID to display Printful tax rates in the cart summary.
147
- *
148
- * @param $cc
149
- * @param $state
150
- * @param bool $includeShipping
151
- *
152
- * @return int|null|string
153
- */
154
- private function get_printful_rate_id( $cc, $state, $includeShipping = false ) {
155
- global $wpdb;
156
-
157
- $includeShipping = (int) $includeShipping;
158
-
159
- $states = WC()->countries->get_states( $cc );
160
- $tax_title = ( isset( $states[ $state ] ) ? $states[ $state ] . ' ' : '' ) . __( 'Sales Tax', 'printful' );
161
- $id = $wpdb->get_var(
162
- $wpdb->prepare( "SELECT tax_rate_id FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_class='printful'
163
- and tax_rate_country = %s AND tax_rate_state = %s AND tax_rate_shipping = %s LIMIT 1",
164
- $cc,
165
- $state,
166
- $includeShipping
167
- ) );
168
- if ( empty( $id ) ) {
169
- $wpdb->insert(
170
- $wpdb->prefix . "woocommerce_tax_rates",
171
- array(
172
- 'tax_rate_country' => $cc,
173
- 'tax_rate_state' => $state,
174
- 'tax_rate' => 0,
175
- 'tax_rate_name' => $tax_title,
176
- 'tax_rate_priority' => 1,
177
- 'tax_rate_compound' => 0,
178
- 'tax_rate_shipping' => $includeShipping,
179
- 'tax_rate_class' => 'printful',
180
- )
181
- );
182
- $id = $wpdb->insert_id;
183
- }
184
-
185
- return $id;
186
- }
187
-
188
-
189
  }
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) exit;
3
+
4
+ class Printful_Taxes {
5
+
6
+ /**
7
+ * Initialize the values, hooks and actions
8
+ */
9
+ public static function init() {
10
+ if ( Printful_Integration::instance()->get_option( 'calculate_tax' ) == 'yes' ) {
11
+ //Update tax options if taxes are enabled
12
+ if ( get_option( 'woocommerce_calc_taxes' ) != 'yes' ) {
13
+ update_option( 'woocommerce_calc_taxes', 'yes' );
14
+ }
15
+ if ( get_option( 'woocommerce_tax_based_on' ) != 'shipping' ) {
16
+ update_option( 'woocommerce_tax_based_on', 'shipping' );
17
+ }
18
+
19
+ //Override tax rates calculated by Woocommerce
20
+ $taxes = new self;
21
+ add_filter( 'woocommerce_matched_tax_rates', array( $taxes, 'calculate_tax' ), 10, 6 );
22
+ }
23
+ }
24
+
25
+ /**
26
+ * @param $matched_tax_rates
27
+ * @param $country
28
+ * @param $state
29
+ * @param $postcode
30
+ * @param $city
31
+ * @param $tax_class
32
+ *
33
+ * @return mixed
34
+ */
35
+ public function calculate_tax( $matched_tax_rates, $country, $state, $postcode, $city, $tax_class ) {
36
+ //if a tax rate is already matched, avoid adding extra one
37
+ if ( ! empty( $matched_tax_rates ) ) {
38
+ return $matched_tax_rates;
39
+ }
40
+
41
+ $countries = $this->get_tax_countries();
42
+ if ( isset( $countries[ $country ][ $state ] ) && !empty($postcode) ) { //only make the request if country, state and zip are set
43
+ $key = 'printful_tax_rate_' . $country . '-' . $state . '-' . $city . '-' . $postcode;
44
+ $rate = get_transient( $key );
45
+ $response = null;
46
+
47
+ if ( $rate === false ) {
48
+ try {
49
+ $client = Printful_Integration::instance()->get_client();
50
+ $response = $client->post( 'tax/rates', array(
51
+ 'recipient' => array(
52
+ 'country_code' => $country,
53
+ 'state_code' => $state,
54
+ 'city' => $city,
55
+ 'zip' => $postcode,
56
+ ),
57
+ ) );
58
+ } catch ( Exception $e ) {
59
+ }
60
+
61
+ if ( isset( $response['rate'] ) ) {
62
+ $rate = $response;
63
+ } else {
64
+ $rate = array(
65
+ 'required' => false,
66
+ 'rate' => 0,
67
+ 'shipping_taxable' => false,
68
+ );
69
+ }
70
+ set_transient( $key, $rate, 1800 );
71
+ }
72
+
73
+ if ( $rate['required'] ) {
74
+ $rate_item = array(
75
+ 'rate' => $rate['rate'] * 100,
76
+ 'label' => 'Sales Tax',
77
+ 'shipping' => $rate['shipping_taxable'] ? 'yes' : 'no',
78
+ 'compound' => 'no',
79
+ );
80
+
81
+ if ( $this->isRateUnique( $rate_item, $matched_tax_rates ) ) {
82
+ $id = $this->get_printful_rate_id( $country, $state, $rate['shipping_taxable'] );
83
+ $matched_tax_rates[ $id ] = $rate_item;
84
+ }
85
+ }
86
+ }
87
+
88
+ return $matched_tax_rates;
89
+ }
90
+
91
+ /**
92
+ * Checks if a equal tax rate is not already set
93
+ *
94
+ * @param $rate
95
+ * @param $matched_tax_rates
96
+ *
97
+ * @return bool
98
+ */
99
+ private function isRateUnique( $rate, $matched_tax_rates ) {
100
+ if ( empty( $matched_tax_rates ) ) {
101
+ return true;
102
+ }
103
+
104
+ foreach ( $matched_tax_rates as $mr ) {
105
+ if ( floatval( $mr['rate'] ) == floatval( $rate['rate'] ) && $mr['shipping'] == $rate['shipping'] ) {
106
+ return false;
107
+ }
108
+ }
109
+
110
+ return true;
111
+ }
112
+
113
+ /**
114
+ * Gets list of countries and states where Printful needs to calculate sales tax
115
+ * @return array|mixed
116
+ */
117
+ private function get_tax_countries() {
118
+ $countries = get_transient( 'printful_tax_countries' );
119
+
120
+ if ( ! $countries ) {
121
+ $countries = array();
122
+
123
+ try {
124
+ $client = Printful_Integration::instance()->get_client();
125
+ $list = $client->get( 'tax/countries' );
126
+
127
+ foreach ( $list as $country ) {
128
+ foreach ( $country['states'] as $state ) {
129
+ $countries[ $country['code'] ][ $state['code'] ] = 1;
130
+ }
131
+ }
132
+
133
+ if ( ! empty( $countries ) ) {
134
+ set_transient( 'printful_tax_countries', $countries, 6 * 3600 );
135
+ }
136
+ } catch ( Exception $e ) {
137
+ //Default to CA if can't get the actual state list
138
+ return array( 'US' => array( 'CA' => 1 ) );
139
+ }
140
+ }
141
+
142
+ return $countries;
143
+ }
144
+
145
+ /**
146
+ * Creates dummy tax rate ID to display Printful tax rates in the cart summary.
147
+ *
148
+ * @param $cc
149
+ * @param $state
150
+ * @param bool $includeShipping
151
+ *
152
+ * @return int|null|string
153
+ */
154
+ private function get_printful_rate_id( $cc, $state, $includeShipping = false ) {
155
+ global $wpdb;
156
+
157
+ $includeShipping = (int) $includeShipping;
158
+
159
+ $states = WC()->countries->get_states( $cc );
160
+ $tax_title = ( isset( $states[ $state ] ) ? $states[ $state ] . ' ' : '' ) . __( 'Sales Tax', 'printful' );
161
+ $id = $wpdb->get_var(
162
+ $wpdb->prepare( "SELECT tax_rate_id FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_class='printful'
163
+ and tax_rate_country = %s AND tax_rate_state = %s AND tax_rate_shipping = %s LIMIT 1",
164
+ $cc,
165
+ $state,
166
+ $includeShipping
167
+ ) );
168
+ if ( empty( $id ) ) {
169
+ $wpdb->insert(
170
+ $wpdb->prefix . "woocommerce_tax_rates",
171
+ array(
172
+ 'tax_rate_country' => $cc,
173
+ 'tax_rate_state' => $state,
174
+ 'tax_rate' => 0,
175
+ 'tax_rate_name' => $tax_title,
176
+ 'tax_rate_priority' => 1,
177
+ 'tax_rate_compound' => 0,
178
+ 'tax_rate_shipping' => $includeShipping,
179
+ 'tax_rate_class' => 'printful',
180
+ )
181
+ );
182
+ $id = $wpdb->insert_id;
183
+ }
184
+
185
+ return $id;
186
+ }
187
+
188
+
189
  }
includes/class-printful-template.php CHANGED
@@ -22,6 +22,8 @@ class Printful_Template {
22
  add_action( 'woocommerce_after_add_to_cart_button', array( $this, 'printful_template_customize_button' ), 20 );
23
  // add a hidden input field
24
  add_action( 'woocommerce_before_add_to_cart_button', array( $this, 'printful_customizer_hash_field' ), 11 );
 
 
25
  }
26
 
27
  /**
@@ -42,11 +44,34 @@ class Printful_Template {
42
  }
43
  }
44
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  /**
46
  * Add hidden customizer hash ID field to form
47
  */
48
  public static function printful_customizer_hash_field()
49
  {
50
- Printful_Admin::load_template( 'customizer-hidden-input' );
 
 
 
 
51
  }
52
  }
22
  add_action( 'woocommerce_after_add_to_cart_button', array( $this, 'printful_template_customize_button' ), 20 );
23
  // add a hidden input field
24
  add_action( 'woocommerce_before_add_to_cart_button', array( $this, 'printful_customizer_hash_field' ), 11 );
25
+ // Hook size guide button
26
+ add_action( 'woocommerce_before_single_variation', array( $this, 'printful_size_guide_button' ), 20 );
27
  }
28
 
29
  /**
44
  }
45
  }
46
 
47
+ /**
48
+ * Hook callback for size guide button render within product page
49
+ */
50
+ public static function printful_size_guide_button()
51
+ {
52
+ global $post;
53
+
54
+ $sizeGuideData = Printful_Size_Guide::get_size_guide_for_product($post);
55
+ if (!$sizeGuideData) {
56
+ return;
57
+ }
58
+
59
+ // Load size guide button template
60
+ Printful_Admin::load_template( 'size-guide-button', array(
61
+ 'size_guide_button_color' => Printful_Integration::instance()->get_option( 'pfsg_button_color' ) ?: Printful_Admin_Settings::DEFAULT_SIZE_GUIDE_BUTTON_COLOR,
62
+ 'size_guide_button_text' => Printful_Integration::instance()->get_option( 'pfsg_button_text' ) ?: Printful_Admin_Settings::DEFAULT_SIZE_GUIDE_BUTTON_TEXT,
63
+ ) );
64
+ }
65
+
66
  /**
67
  * Add hidden customizer hash ID field to form
68
  */
69
  public static function printful_customizer_hash_field()
70
  {
71
+ global $post;
72
+
73
+ if ( $post && get_post_meta( $post->ID, 'pf_customizable', true ) ) {
74
+ Printful_Admin::load_template('customizer-hidden-input');
75
+ }
76
  }
77
  }
includes/templates/ajax-loader.php CHANGED
@@ -1,11 +1,11 @@
1
- <div id="loader-block-<?php echo esc_attr($action); ?>">
2
- <div class="block-loader loader-wrap">
3
- <img src="<?php echo esc_url( admin_url( 'images/spinner-2x.gif' ) ) ?>" class="loader" width="20px" height="20px" alt="loader"/>
4
- <span class="message"><?php echo esc_html($message); ?></span>
5
- </div>
6
- </div>
7
- <script type="text/javascript">
8
- jQuery(document).ready(function () {
9
- Printful_Block_Loader.load('<?php echo esc_url(admin_url( 'admin-ajax.php?action=' . $action )); ?>', 'loader-block-<?php echo esc_attr($action); ?>');
10
- });
11
  </script>
1
+ <div id="loader-block-<?php echo esc_attr($action); ?>">
2
+ <div class="block-loader loader-wrap">
3
+ <img src="<?php echo esc_url( admin_url( 'images/spinner-2x.gif' ) ) ?>" class="loader" width="20px" height="20px" alt="loader"/>
4
+ <span class="message"><?php echo esc_html($message); ?></span>
5
+ </div>
6
+ </div>
7
+ <script type="text/javascript">
8
+ jQuery(document).ready(function () {
9
+ Printful_Block_Loader.load('<?php echo esc_url(admin_url( 'admin-ajax.php?action=' . $action )); ?>', 'loader-block-<?php echo esc_attr($action); ?>');
10
+ });
11
  </script>
includes/templates/connect.php CHANGED
@@ -1,40 +1,40 @@
1
- <div class="printful-connect">
2
-
3
- <div class="printful-connect-inner">
4
-
5
- <h1><?php esc_html_e('Connect to Printful', 'printful'); ?></h1>
6
-
7
- <img src=" <?php echo esc_url(Printful_Base::get_asset_url() . 'images/connect.svg'); ?>" class="connect-image" alt="connect to printful">
8
-
9
- <?php
10
- if ( ! empty( $issues ) ) {
11
- ?>
12
- <p><?php esc_html_e('To connect your store to Printful, fix the following errors:', 'printful'); ?></p>
13
- <div class="printful-notice">
14
- <ul>
15
- <?php
16
- foreach ( $issues as $issue ) {
17
- echo '<li>' . wp_kses_post( $issue ) . '</li>';
18
- }
19
- ?>
20
- </ul>
21
- </div>
22
- <?php
23
- $url = '#';
24
- } else {
25
- ?><p class="connect-description"><?php esc_html_e('You\'re almost done! Just 2 more steps to have your WooCommerce store connected to Printful for automatic order fulfillment.', 'printful'); ?></p><?php
26
- $url = Printful_Base::get_printful_host() . 'dashboard/woocommerce/plugin-connect?website=' . urlencode( trailingslashit( get_home_url() ) ) . '&key=' . urlencode( $consumer_key ) . '&returnUrl=' . urlencode( get_admin_url( null,'admin.php?page=' . Printful_Admin::MENU_SLUG_DASHBOARD ) );
27
- }
28
-
29
- echo '<a href="' . esc_url($url) . '" class="button button-primary printful-connect-button ' . ( ! empty( $issues ) ? 'disabled' : '' ) . '" target="_blank">' . esc_html__('Connect', 'printful') . '</a>';
30
- ?>
31
-
32
- <img src="<?php echo esc_url( admin_url( 'images/spinner-2x.gif' ) ) ?>" class="loader hidden" width="20px" height="20px" alt="loader"/>
33
-
34
- <script type="text/javascript">
35
- jQuery(document).ready(function () {
36
- Printful_Connect.init('<?php echo esc_url( admin_url( 'admin-ajax.php?action=ajax_force_check_connect_status' ) ); ?>');
37
- });
38
- </script>
39
- </div>
40
  </div>
1
+ <div class="printful-connect">
2
+
3
+ <div class="printful-connect-inner">
4
+
5
+ <h1><?php esc_html_e('Connect to Printful', 'printful'); ?></h1>
6
+
7
+ <img src=" <?php echo esc_url(Printful_Base::get_asset_url() . 'images/connect.svg'); ?>" class="connect-image" alt="connect to printful">
8
+
9
+ <?php
10
+ if ( ! empty( $issues ) ) {
11
+ ?>
12
+ <p><?php esc_html_e('To connect your store to Printful, fix the following errors:', 'printful'); ?></p>
13
+ <div class="printful-notice">
14
+ <ul>
15
+ <?php
16
+ foreach ( $issues as $issue ) {
17
+ echo '<li>' . wp_kses_post( $issue ) . '</li>';
18
+ }
19
+ ?>
20
+ </ul>
21
+ </div>
22
+ <?php
23
+ $url = '#';
24
+ } else {
25
+ ?><p class="connect-description"><?php esc_html_e('You\'re almost done! Just 2 more steps to have your WooCommerce store connected to Printful for automatic order fulfillment.', 'printful'); ?></p><?php
26
+ $url = Printful_Base::get_printful_host() . 'dashboard/woocommerce/plugin-connect?website=' . urlencode( trailingslashit( get_home_url() ) ) . '&key=' . urlencode( $consumer_key ) . '&returnUrl=' . urlencode( get_admin_url( null,'admin.php?page=' . Printful_Admin::MENU_SLUG_DASHBOARD ) );
27
+ }
28
+
29
+ echo '<a href="' . esc_url($url) . '" class="button button-primary printful-connect-button ' . ( ! empty( $issues ) ? 'disabled' : '' ) . '" target="_blank">' . esc_html__('Connect', 'printful') . '</a>';
30
+ ?>
31
+
32
+ <img src="<?php echo esc_url( admin_url( 'images/spinner-2x.gif' ) ) ?>" class="loader hidden" width="20px" height="20px" alt="loader"/>
33
+
34
+ <script type="text/javascript">
35
+ jQuery(document).ready(function () {
36
+ Printful_Connect.init('<?php echo esc_url( admin_url( 'admin-ajax.php?action=ajax_force_check_connect_status' ) ); ?>');
37
+ });
38
+ </script>
39
+ </div>
40
  </div>
includes/templates/header.php CHANGED
@@ -1,20 +1,20 @@
1
- <div class="wrap">
2
-
3
- <?php
4
- $base_url = '?page=printful-dashboard';
5
- ?>
6
-
7
- <h2 class="nav-tab-wrapper printful-tabs">
8
- <?php foreach ($tabs as $tab) : ?>
9
- <?php
10
- $active = '';
11
- if ( ! empty( $_GET['tab'] ) && $_GET['tab'] == $tab['tab_url'] ) {
12
- $active = 'nav-tab-active';
13
- }
14
- if ( empty( $_GET['tab'] ) && $tab['tab_url'] == '' ) {
15
- $active = 'nav-tab-active';
16
- }
17
- ?>
18
- <a href="<?php echo esc_url($base_url . ($tab['tab_url'] ? '&tab=' . $tab['tab_url'] : '') ); ?>" class="nav-tab <?php echo esc_attr($active);?>"><?php echo esc_html($tab['name']); ?></a>
19
- <?php endforeach; ?>
20
  </h2>
1
+ <div class="wrap">
2
+
3
+ <?php
4
+ $base_url = '?page=printful-dashboard';
5
+ ?>
6
+
7
+ <h2 class="nav-tab-wrapper printful-tabs">
8
+ <?php foreach ($tabs as $tab) : ?>
9
+ <?php
10
+ $active = '';
11
+ if ( ! empty( $_GET['tab'] ) && $_GET['tab'] == $tab['tab_url'] ) {
12
+ $active = 'nav-tab-active';
13
+ }
14
+ if ( empty( $_GET['tab'] ) && $tab['tab_url'] == '' ) {
15
+ $active = 'nav-tab-active';
16
+ }
17
+ ?>
18
+ <a href="<?php echo esc_url($base_url . ($tab['tab_url'] ? '&tab=' . $tab['tab_url'] : '') ); ?>" class="nav-tab <?php echo esc_attr($active);?>"><?php echo esc_html($tab['name']); ?></a>
19
+ <?php endforeach; ?>
20
  </h2>
includes/templates/inline-script.php CHANGED
@@ -1,3 +1,3 @@
1
- <script type="text/javascript">
2
- <?php echo esc_js( $script ); ?>
3
  </script>
1
+ <script type="text/javascript">
2
+ <?php echo esc_js( $script ); ?>
3
  </script>
includes/templates/order-table.php CHANGED
@@ -1,69 +1,69 @@
1
-
2
- <h2>Printful product orders</h2>
3
-
4
- <?php if ( ! empty( $orders ) && $orders['count'] > 0 ): ?>
5
-
6
- <table class="wp-list-table widefat fixed striped printful-latest-orders">
7
- <thead>
8
- <tr>
9
- <th class="col-order"><?php esc_html_e('Order', 'printful'); ?></th>
10
- <th class="col-date"><?php esc_html_e('Date', 'printful'); ?></th>
11
- <th class="col-from"><?php esc_html_e('From', 'printful'); ?></th>
12
- <th class="col-status"><?php esc_html_e('Status', 'printful'); ?></th>
13
- <th class="col-total"><?php esc_html_e('Total', 'printful'); ?></th>
14
- <th class="col-actions"><?php esc_html_e('Actions', 'printful'); ?></th>
15
- </tr>
16
- </thead>
17
- <tbody>
18
-
19
- <?php foreach ( $orders['results'] as $order ) : ?>
20
-
21
- <tr>
22
- <td>
23
- <?php
24
- if ( $order['external_id'] ) {
25
- echo '<a href="' . esc_url( get_edit_post_link( $order['external_id'] ) ) . '">';
26
- echo '#' . esc_html( $order['external_id'] );
27
- echo '</a>';
28
- } else {
29
- echo '#' . esc_html( $order['id'] );
30
- }
31
- ?>
32
- </td>
33
- <td>
34
- <?php echo esc_html( date('Y-m-d', $order['created']) ); ?>
35
- </td>
36
- <td>
37
- <?php echo esc_html( $order['recipient']['name'] ); ?>
38
- </td>
39
- <td>
40
- <?php echo esc_html( ucfirst($order['status']) ); ?>
41
- </td>
42
- <td>
43
- $<?php echo esc_html( $order['costs']['total'] ); ?>
44
- </td>
45
- <td>
46
- <a href="<?php echo esc_url(Printful_Base::get_printful_host()); ?>dashboard?order_id=<?php echo esc_attr($order['id']); ?>" target="_blank"><?php esc_html_e('Open in Printful', 'printful'); ?></a>
47
- </td>
48
- </tr>
49
-
50
- <?php endforeach; ?>
51
-
52
- </tbody>
53
- <tfoot>
54
- <tr>
55
- <th class="col-order"><?php esc_html_e('Order', 'printful'); ?></th>
56
- <th class="col-date"><?php esc_html_e('Date', 'printful'); ?></th>
57
- <th class="col-from"><?php esc_html_e('From', 'printful'); ?></th>
58
- <th class="col-status"><?php esc_html_e('Status', 'printful'); ?></th>
59
- <th class="col-total"><?php esc_html_e('Total', 'printful'); ?></th>
60
- <th class="col-actions"><?php esc_html_e('Actions', 'printful'); ?></th>
61
- </tr>
62
- </tfoot>
63
- </table>
64
-
65
- <?php else: ?>
66
- <div class="printful-latest-orders">
67
- <p><?php esc_html_e('Once your store gets some Printful product orders, they will be shown here!', 'printful'); ?></p>
68
- </div>
69
  <?php endif; ?>
1
+
2
+ <h2>Printful product orders</h2>
3
+
4
+ <?php if ( ! empty( $orders ) && $orders['count'] > 0 ): ?>
5
+
6
+ <table class="wp-list-table widefat fixed striped printful-latest-orders">
7
+ <thead>
8
+ <tr>
9
+ <th class="col-order"><?php esc_html_e('Order', 'printful'); ?></th>
10
+ <th class="col-date"><?php esc_html_e('Date', 'printful'); ?></th>
11
+ <th class="col-from"><?php esc_html_e('From', 'printful'); ?></th>
12
+ <th class="col-status"><?php esc_html_e('Status', 'printful'); ?></th>
13
+ <th class="col-total"><?php esc_html_e('Total', 'printful'); ?></th>
14
+ <th class="col-actions"><?php esc_html_e('Actions', 'printful'); ?></th>
15
+ </tr>
16
+ </thead>
17
+ <tbody>
18
+
19
+ <?php foreach ( $orders['results'] as $order ) : ?>
20
+
21
+ <tr>
22
+ <td>
23
+ <?php
24
+ if ( $order['external_id'] ) {
25
+ echo '<a href="' . esc_url( get_edit_post_link( $order['external_id'] ) ) . '">';
26
+ echo '#' . esc_html( $order['external_id'] );
27
+ echo '</a>';
28
+ } else {
29
+ echo '#' . esc_html( $order['id'] );
30
+ }
31
+ ?>
32
+ </td>
33
+ <td>
34
+ <?php echo esc_html( date('Y-m-d', $order['created']) ); ?>
35
+ </td>
36
+ <td>
37
+ <?php echo esc_html( $order['recipient']['name'] ); ?>
38
+ </td>
39
+ <td>
40
+ <?php echo esc_html( ucfirst($order['status']) ); ?>
41
+ </td>
42
+ <td>
43
+ $<?php echo esc_html( $order['costs']['total'] ); ?>
44
+ </td>
45
+ <td>
46
+ <a href="<?php echo esc_url(Printful_Base::get_printful_host()); ?>dashboard?order_id=<?php echo esc_attr($order['id']); ?>" target="_blank"><?php esc_html_e('Open in Printful', 'printful'); ?></a>
47
+ </td>
48
+ </tr>
49
+
50
+ <?php endforeach; ?>
51
+
52
+ </tbody>
53
+ <tfoot>
54
+ <tr>
55
+ <th class="col-order"><?php esc_html_e('Order', 'printful'); ?></th>
56
+ <th class="col-date"><?php esc_html_e('Date', 'printful'); ?></th>
57
+ <th class="col-from"><?php esc_html_e('From', 'printful'); ?></th>
58
+ <th class="col-status"><?php esc_html_e('Status', 'printful'); ?></th>
59
+ <th class="col-total"><?php esc_html_e('Total', 'printful'); ?></th>
60
+ <th class="col-actions"><?php esc_html_e('Actions', 'printful'); ?></th>
61
+ </tr>
62
+ </tfoot>
63
+ </table>
64
+
65
+ <?php else: ?>
66
+ <div class="printful-latest-orders">
67
+ <p><?php esc_html_e('Once your store gets some Printful product orders, they will be shown here!', 'printful'); ?></p>
68
+ </div>
69
  <?php endif; ?>
includes/templates/personalize-button.php CHANGED
@@ -1,12 +1,12 @@
1
- <?php
2
- /**
3
- * @var string $pfc_button_color
4
- * @var string $site_url
5
- * @var string $pfc_button_text
6
- */
7
- ?>
8
- <a class="button"
9
- style="background-color: <?php esc_attr_e($pfc_button_color); ?>"
10
- onclick="Printful_Product_Customizer.onCustomizeClick( '<?php echo esc_url($site_url); ?>')">
11
- <?php esc_html_e($pfc_button_text, 'printful'); ?>
12
  </a>
1
+ <?php
2
+ /**
3
+ * @var string $pfc_button_color
4
+ * @var string $site_url
5
+ * @var string $pfc_button_text
6
+ */
7
+ ?>
8
+ <a class="button"
9
+ style="background-color: <?php esc_attr_e($pfc_button_color); ?>"
10
+ onclick="Printful_Product_Customizer.onCustomizeClick( '<?php echo esc_url($site_url); ?>')">
11
+ <?php esc_html_e($pfc_button_text, 'printful'); ?>
12
  </a>
includes/templates/setting-group.php CHANGED
@@ -73,7 +73,18 @@
73
 
74
  <?php elseif ($setting['type'] == 'color-picker') : ?>
75
  <label for="<?php esc_attr_e($key); ?>">
76
- <input type="text" name="<?php esc_attr_e($key); ?>" class="<?php esc_attr_e($key); ?>" value="<?php esc_attr_e($setting['value'] ?: $setting['default']); ?>" data-default-color="<?php esc_attr_e($setting['default']); ?>" />
 
 
 
 
 
 
 
 
 
 
 
77
  <?php endif; ?>
78
 
79
  </fieldset>
@@ -89,6 +100,6 @@
89
  </div>
90
  <script>
91
  jQuery(document).ready(function($){
92
- $('.pfc_button_color').wpColorPicker();
93
  });
94
  </script>
73
 
74
  <?php elseif ($setting['type'] == 'color-picker') : ?>
75
  <label for="<?php esc_attr_e($key); ?>">
76
+ <input type="text" name="<?php esc_attr_e($key); ?>" class="<?php esc_attr_e($key); ?> pf-color-picker" value="<?php esc_attr_e($setting['value'] ?: $setting['default']); ?>" data-default-color="<?php esc_attr_e($setting['default']); ?>" />
77
+
78
+ <?php elseif ($setting['type'] == 'dropdown') : ?>
79
+ <label for="<?php esc_attr_e($key); ?>">
80
+ <select name="<?php esc_attr_e($key); ?>" id="<?php esc_attr_e($key); ?>">
81
+ <?php foreach ( $setting['items'] as $selectionKey => $selectionValue ) : ?>
82
+ <option <?php echo $setting['selected'] === $selectionKey ? 'selected' : ''; ?>
83
+ value="<?php esc_attr_e($selectionKey); ?>">
84
+ <?php esc_attr_e($selectionValue); ?>
85
+ </option>
86
+ <?php endforeach; ?>
87
+ </select>
88
  <?php endif; ?>
89
 
90
  </fieldset>
100
  </div>
101
  <script>
102
  jQuery(document).ready(function($){
103
+ $('.pf-color-picker').wpColorPicker();
104
  });
105
  </script>
includes/templates/setting-submit.php CHANGED
@@ -1,24 +1,24 @@
1
- <?php
2
- /**
3
- * @var bool $disabled
4
- */
5
- ?>
6
- <p class="printful-submit">
7
- <input name="save" class="button-primary woocommerce-save-button <?php if($disabled) { echo 'disabled'; } ?>" type="submit" value="<?php esc_attr_e('Save changes', 'printful'); ?>" <?php if($disabled) { echo 'disabled'; } ?>/>
8
- <input type="hidden" id="_wpnonce" name="_wpnonce" value="<?php echo esc_attr($nonce); ?>">
9
- <?php wp_referer_field(true); ?>
10
- <span class="loader-wrap">
11
- <img src="<?php echo esc_url( admin_url( 'images/spinner-2x.gif' ) ) ?>" class="loader" width="20px" height="20px" alt="loader"/>
12
- <span class="pass">
13
- <span class="dashicons dashicons-yes"></span>
14
- <?php esc_html_e('Saved successfully', 'printful'); ?>
15
- </span>
16
- <span class="fail">
17
- </span>
18
- </span>
19
- </p>
20
- <script type="text/javascript">
21
- jQuery(document).ready(function () {
22
- Printful_Settings.init_submit();
23
- });
24
  </script>
1
+ <?php
2
+ /**
3
+ * @var bool $disabled
4
+ */
5
+ ?>
6
+ <p class="printful-submit">
7
+ <input name="save" class="button-primary woocommerce-save-button <?php if($disabled) { echo 'disabled'; } ?>" type="submit" value="<?php esc_attr_e('Save changes', 'printful'); ?>" <?php if($disabled) { echo 'disabled'; } ?>/>
8
+ <input type="hidden" id="_wpnonce" name="_wpnonce" value="<?php echo esc_attr($nonce); ?>">
9
+ <?php wp_referer_field(true); ?>
10
+ <span class="loader-wrap">
11
+ <img src="<?php echo esc_url( admin_url( 'images/spinner-2x.gif' ) ) ?>" class="loader" width="20px" height="20px" alt="loader"/>
12
+ <span class="pass">
13
+ <span class="dashicons dashicons-yes"></span>
14
+ <?php esc_html_e('Saved successfully', 'printful'); ?>
15
+ </span>
16
+ <span class="fail">
17
+ </span>
18
+ </span>
19
+ </p>
20
+ <script type="text/javascript">
21
+ jQuery(document).ready(function () {
22
+ Printful_Settings.init_submit();
23
+ });
24
  </script>
includes/templates/shipping-notification.php CHANGED
@@ -1,4 +1,4 @@
1
- <div class="printful-setting-group">
2
- <h2><?php esc_html_e('Printful Shipping', 'printful'); ?></h2>
3
- <p><?php esc_html_e('To enable/disable Printful shipping for your store go to', 'printful'); ?> <a href="<?php echo esc_url( admin_url( 'admin.php?page=wc-settings&tab=shipping&section=printful_shipping' ) ); ?>"><?php esc_html_e('WooCommerce Shipping settings', 'printful'); ?></a>.</p>
4
  </div>
1
+ <div class="printful-setting-group">
2
+ <h2><?php esc_html_e('Printful Shipping', 'printful'); ?></h2>
3
+ <p><?php esc_html_e('To enable/disable Printful shipping for your store go to', 'printful'); ?> <a href="<?php echo esc_url( admin_url( 'admin.php?page=wc-settings&tab=shipping&section=printful_shipping' ) ); ?>"><?php esc_html_e('WooCommerce Shipping settings', 'printful'); ?></a>.</p>
4
  </div>
includes/templates/size-guide-button.php ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * @var string $size_guide_button_color
4
+ * @var string $size_guide_button_text
5
+ */
6
+ ?>
7
+ <a href="javascript:" style="color: <?php esc_attr_e($size_guide_button_color); ?>"
8
+ onclick="Printful_Product_Size_Guide.onSizeGuideClick()">
9
+ <?php esc_html_e($size_guide_button_text, 'printful'); ?>
10
+ </a>
includes/templates/stats.php CHANGED
@@ -1,44 +1,44 @@
1
- <div class="printful-stats">
2
- <div class="printful-stats-item">
3
- <h4><?php echo esc_html(get_woocommerce_currency_symbol($stats['currency'])) . ' ' . esc_html($stats['orders_today']['total']); ?></h4>
4
- <b>
5
- <?php
6
- echo esc_html($stats['orders_today']['orders']);
7
- echo ' ' . _n('ORDER', 'ORDERS', $stats['orders_today']['orders'], 'printful' );
8
- ?>
9
- </b>
10
- <?php esc_html_e('today', 'printful'); ?>
11
- </div>
12
- <div class="printful-stats-item">
13
- <h4>
14
- <?php echo esc_html(get_woocommerce_currency_symbol($stats['currency'])) . ' ' . esc_html($stats['orders_last_7_days']['total']); ?>
15
- <?php echo '<span class="dashicons dashicons-arrow-' . esc_attr($stats['orders_last_7_days']['trend']) .'-alt"></span>'; ?>
16
- </h4>
17
- <b>
18
- <?php
19
- echo esc_html($stats['orders_last_7_days']['orders']);
20
- echo ' ' . _n( 'ORDER', 'ORDERS', $stats['orders_last_7_days']['orders'], 'printful' );
21
- ?>
22
- </b>
23
- <?php esc_html_e('last 7 days', 'printful'); ?>
24
- </div>
25
- <div class="printful-stats-item">
26
- <h4>
27
- <?php echo esc_html(get_woocommerce_currency_symbol($stats['currency'])) . ' ' . esc_html($stats['orders_last_28_days']['total']); ?>
28
- <?php echo '<span class="dashicons dashicons-arrow-' . esc_attr($stats['orders_last_28_days']['trend']) .'-alt"></span>'; ?>
29
- </h4>
30
- <b>
31
- <?php
32
- echo esc_html($stats['orders_last_28_days']['orders']);
33
- echo ' ' . _n( 'ORDER', 'ORDERS', $stats['orders_last_28_days']['orders'], 'printful' );
34
- ?>
35
- </b> <?php esc_html_e('last 28 days', 'printful'); ?>
36
- </div>
37
- <div class="printful-stats-item">
38
- <h4>
39
- <?php echo esc_html(get_woocommerce_currency_symbol($stats['currency'])) . ' ' . esc_attr($stats['profit_last_28_days']); ?>
40
- <?php echo '<span class="dashicons dashicons-arrow-' . esc_attr($stats['profit_trend_last_28_days']) .'-alt"></span>'; ?>
41
- </h4>
42
- <b><?php esc_html_e('PROFIT', 'printful'); ?></b> <?php esc_html_e('last 28 days', 'printful'); ?>
43
- </div>
44
  </div>
1
+ <div class="printful-stats">
2
+ <div class="printful-stats-item">
3
+ <h4><?php echo esc_html(get_woocommerce_currency_symbol($stats['currency'])) . ' ' . esc_html($stats['orders_today']['total']); ?></h4>
4
+ <b>
5
+ <?php
6
+ echo esc_html($stats['orders_today']['orders']);
7
+ echo ' ' . _n('ORDER', 'ORDERS', $stats['orders_today']['orders'], 'printful' );
8
+ ?>
9
+ </b>
10
+ <?php esc_html_e('today', 'printful'); ?>
11
+ </div>
12
+ <div class="printful-stats-item">
13
+ <h4>
14
+ <?php echo esc_html(get_woocommerce_currency_symbol($stats['currency'])) . ' ' . esc_html($stats['orders_last_7_days']['total']); ?>
15
+ <?php echo '<span class="dashicons dashicons-arrow-' . esc_attr($stats['orders_last_7_days']['trend']) .'-alt"></span>'; ?>
16
+ </h4>
17
+ <b>
18
+ <?php
19
+ echo esc_html($stats['orders_last_7_days']['orders']);
20
+ echo ' ' . _n( 'ORDER', 'ORDERS', $stats['orders_last_7_days']['orders'], 'printful' );
21
+ ?>
22
+ </b>
23
+ <?php esc_html_e('last 7 days', 'printful'); ?>
24
+ </div>
25
+ <div class="printful-stats-item">
26
+ <h4>
27
+ <?php echo esc_html(get_woocommerce_currency_symbol($stats['currency'])) . ' ' . esc_html($stats['orders_last_28_days']['total']); ?>
28
+ <?php echo '<span class="dashicons dashicons-arrow-' . esc_attr($stats['orders_last_28_days']['trend']) .'-alt"></span>'; ?>
29
+ </h4>
30
+ <b>
31
+ <?php
32
+ echo esc_html($stats['orders_last_28_days']['orders']);
33
+ echo ' ' . _n( 'ORDER', 'ORDERS', $stats['orders_last_28_days']['orders'], 'printful' );
34
+ ?>
35
+ </b> <?php esc_html_e('last 28 days', 'printful'); ?>
36
+ </div>
37
+ <div class="printful-stats-item">
38
+ <h4>
39
+ <?php echo esc_html(get_woocommerce_currency_symbol($stats['currency'])) . ' ' . esc_attr($stats['profit_last_28_days']); ?>
40
+ <?php echo '<span class="dashicons dashicons-arrow-' . esc_attr($stats['profit_trend_last_28_days']) .'-alt"></span>'; ?>
41
+ </h4>
42
+ <b><?php esc_html_e('PROFIT', 'printful'); ?></b> <?php esc_html_e('last 28 days', 'printful'); ?>
43
+ </div>
44
  </div>
includes/templates/status-report.php CHANGED
@@ -1,23 +1,23 @@
1
- <div class="support-report-wrap">
2
- <p>
3
- <?php esc_html_e('Copy the box content below and add it to your support message', 'printful'); ?>
4
- <br/>
5
- <?php esc_html_e('Note: this status report may not include an error log. Contact your hosting provider if you need help with acquiring error logs.'); ?>
6
- </p>
7
- <textarea class="support-report"><?php echo esc_html($status_report); ?></textarea>
8
- <button class="button button-primary button-large support-report-btn"><?php esc_html_e('Copy', 'printful'); ?></button>
9
- <script type="text/javascript">
10
- var copyTextareaBtn = document.querySelector('.support-report-btn');
11
-
12
- copyTextareaBtn.addEventListener('click', function() {
13
- var copyTextarea = document.querySelector('.support-report');
14
- copyTextarea.select();
15
-
16
- try {
17
- document.execCommand('copy');
18
- } catch (err) {
19
- //do nothing
20
- }
21
- });
22
- </script>
23
  </div>
1
+ <div class="support-report-wrap">
2
+ <p>
3
+ <?php esc_html_e('Copy the box content below and add it to your support message', 'printful'); ?>
4
+ <br/>
5
+ <?php esc_html_e('Note: this status report may not include an error log. Contact your hosting provider if you need help with acquiring error logs.'); ?>
6
+ </p>
7
+ <textarea class="support-report"><?php echo esc_html($status_report); ?></textarea>
8
+ <button class="button button-primary button-large support-report-btn"><?php esc_html_e('Copy', 'printful'); ?></button>
9
+ <script type="text/javascript">
10
+ var copyTextareaBtn = document.querySelector('.support-report-btn');
11
+
12
+ copyTextareaBtn.addEventListener('click', function() {
13
+ var copyTextarea = document.querySelector('.support-report');
14
+ copyTextarea.select();
15
+
16
+ try {
17
+ document.execCommand('copy');
18
+ } catch (err) {
19
+ //do nothing
20
+ }
21
+ });
22
+ </script>
23
  </div>
includes/templates/support-info.php CHANGED
@@ -1,27 +1,27 @@
1
- <div class="support-info-wrap">
2
-
3
- <div class="support-info-block">
4
- <h3><?php esc_html_e('Need help? Get in touch!', 'printful') ?></h3>
5
- <p><?php esc_html_e('Email, call, text, or visit us. We\'ve got support teams in the USA and Europe to make sure we\'re there when you need us.', 'printful') ?><br/><?php esc_html_e('We\'d love to hear from you.', 'printful') ?></p>
6
- <div class="btn-wrap">
7
- <a href="<?php echo esc_url(Printful_Base::get_printful_host()); ?>contacts" class="button button-primary button-large" target="_blank"><?php esc_html_e('Contact support', 'printful') ?></a>
8
- </div>
9
- </div>
10
-
11
- <div class="support-info-block">
12
- <h3><?php esc_html_e('Read our FAQs', 'printful') ?></h3>
13
- <p><?php esc_html_e('Getting started made easy – read the FAQs to jumpstart your business.', 'printful') ?><br/><?php esc_html_e('Whether you\'re a video tutorial fan or prefer the written answers – we\'ve got it covered!', 'printful') ?></p>
14
- <div class="btn-wrap">
15
- <a href="<?php echo esc_url(Printful_Base::get_printful_host()); ?>faq" class="button button-primary button-large" target="_blank"><?php esc_html_e('See Printful FAQ', 'printful') ?></a>
16
- </div>
17
- </div>
18
-
19
- <div class="support-info-block">
20
- <h3><?php esc_html_e('Integration help', 'printful') ?></h3>
21
- <p><?php esc_html_e('Are you experiencing technical issues? Solve them on your own by reading these helpful guides and video tutorials.', 'printful') ?></p>
22
- <div class="btn-wrap">
23
- <a href="<?php echo esc_url(Printful_Base::get_printful_host()); ?>faq/integrations/woocommerce" class="button button-primary button-large" target="_blank"><?php esc_html_e('Integration help', 'printful') ?></a>
24
- </div>
25
- </div>
26
-
27
  </div>
1
+ <div class="support-info-wrap">
2
+
3
+ <div class="support-info-block">
4
+ <h3><?php esc_html_e('Need help? Get in touch!', 'printful') ?></h3>
5
+ <p><?php esc_html_e('Email, call, text, or visit us. We\'ve got support teams in the USA and Europe to make sure we\'re there when you need us.', 'printful') ?><br/><?php esc_html_e('We\'d love to hear from you.', 'printful') ?></p>
6
+ <div class="btn-wrap">
7
+ <a href="<?php echo esc_url(Printful_Base::get_printful_host()); ?>contacts" class="button button-primary button-large" target="_blank"><?php esc_html_e('Contact support', 'printful') ?></a>
8
+ </div>
9
+ </div>
10
+
11
+ <div class="support-info-block">
12
+ <h3><?php esc_html_e('Read our FAQs', 'printful') ?></h3>
13
+ <p><?php esc_html_e('Getting started made easy – read the FAQs to jumpstart your business.', 'printful') ?><br/><?php esc_html_e('Whether you\'re a video tutorial fan or prefer the written answers – we\'ve got it covered!', 'printful') ?></p>
14
+ <div class="btn-wrap">
15
+ <a href="<?php echo esc_url(Printful_Base::get_printful_host()); ?>faq" class="button button-primary button-large" target="_blank"><?php esc_html_e('See Printful FAQ', 'printful') ?></a>
16
+ </div>
17
+ </div>
18
+
19
+ <div class="support-info-block">
20
+ <h3><?php esc_html_e('Integration help', 'printful') ?></h3>
21
+ <p><?php esc_html_e('Are you experiencing technical issues? Solve them on your own by reading these helpful guides and video tutorials.', 'printful') ?></p>
22
+ <div class="btn-wrap">
23
+ <a href="<?php echo esc_url(Printful_Base::get_printful_host()); ?>faq/integrations/woocommerce" class="button button-primary button-large" target="_blank"><?php esc_html_e('Integration help', 'printful') ?></a>
24
+ </div>
25
+ </div>
26
+
27
  </div>
printful-shipping.php CHANGED
@@ -3,13 +3,13 @@
3
  Plugin Name: Printful Integration for WooCommerce
4
  Plugin URI: https://wordpress.org/plugins/printful-shipping-for-woocommerce/
5
  Description: Calculate correct shipping and tax rates for your Printful-Woocommerce integration.
6
- Version: 2.1.13
7
  Author: Printful
8
  Author URI: http://www.printful.com
9
  License: GPL2 http://www.gnu.org/licenses/gpl-2.0.html
10
  Text Domain: printful
11
  WC requires at least: 3.0.0
12
- WC tested up to: 4.3
13
  */
14
 
15
  if ( ! defined( 'ABSPATH' ) ) exit;
@@ -20,7 +20,7 @@ if ( ! defined( 'PF_PLUGIN_FILE' ) ) {
20
 
21
  class Printful_Base {
22
 
23
- const VERSION = '2.1.13';
24
  const PF_HOST = 'https://www.printful.com/';
25
  const PF_API_HOST = 'https://api.printful.com/';
26
 
@@ -59,6 +59,7 @@ class Printful_Base {
59
  require_once 'includes/class-printful-size-chart-tab.php';
60
  require_once 'includes/class-printful-template.php';
61
  require_once 'includes/class-printful-customizer.php';
 
62
 
63
  //launch init
64
  Printful_Taxes::init();
@@ -68,6 +69,7 @@ class Printful_Base {
68
  Printful_Size_Chart_Tab::init();
69
  Printful_Template::init();
70
  Printful_Customizer::init();
 
71
 
72
  //hook ajax callbacks
73
  add_action( 'wp_ajax_save_printful_settings', array( 'Printful_Admin_Settings', 'save_printful_settings' ) );
3
  Plugin Name: Printful Integration for WooCommerce
4
  Plugin URI: https://wordpress.org/plugins/printful-shipping-for-woocommerce/
5
  Description: Calculate correct shipping and tax rates for your Printful-Woocommerce integration.
6
+ Version: 2.1.14
7
  Author: Printful
8
  Author URI: http://www.printful.com
9
  License: GPL2 http://www.gnu.org/licenses/gpl-2.0.html
10
  Text Domain: printful
11
  WC requires at least: 3.0.0
12
+ WC tested up to: 4.4
13
  */
14
 
15
  if ( ! defined( 'ABSPATH' ) ) exit;
20
 
21
  class Printful_Base {
22
 
23
+ const VERSION = '2.1.14';
24
  const PF_HOST = 'https://www.printful.com/';
25
  const PF_API_HOST = 'https://api.printful.com/';
26
 
59
  require_once 'includes/class-printful-size-chart-tab.php';
60
  require_once 'includes/class-printful-template.php';
61
  require_once 'includes/class-printful-customizer.php';
62
+ require_once 'includes/class-printful-size-guide.php';
63
 
64
  //launch init
65
  Printful_Taxes::init();
69
  Printful_Size_Chart_Tab::init();
70
  Printful_Template::init();
71
  Printful_Customizer::init();
72
+ Printful_Size_Guide::init();
73
 
74
  //hook ajax callbacks
75
  add_action( 'wp_ajax_save_printful_settings', array( 'Printful_Admin_Settings', 'save_printful_settings' ) );
readme.txt CHANGED
@@ -2,9 +2,9 @@
2
  Contributors: girts_u, kievins, kberzins
3
  Tags: woocommerce, printful, drop shipping, shipping, shipping rates, fulfillment, printing, fedex, carriers, checkout, t-shirts
4
  Requires at least: 4.9
5
- Tested up to: 5.4
6
  Requires PHP: 5.6
7
- Stable tag: 2.1.13
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -65,6 +65,9 @@ Go to https://www.printful.com/dashboard/store , select your WooCommerce store,
65
 
66
  == Upgrade Notice ==
67
 
 
 
 
68
  = 2.1.13 =
69
  Woocommerce compatibility raised to 4.3
70
 
@@ -181,6 +184,9 @@ First release
181
 
182
  == Changelog ==
183
 
 
 
 
184
  = 2.1.13 =
185
  * Improved support for WooCommerce 4.3
186
 
2
  Contributors: girts_u, kievins, kberzins
3
  Tags: woocommerce, printful, drop shipping, shipping, shipping rates, fulfillment, printing, fedex, carriers, checkout, t-shirts
4
  Requires at least: 4.9
5
+ Tested up to: 5.5
6
  Requires PHP: 5.6
7
+ Stable tag: 2.1.14
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
65
 
66
  == Upgrade Notice ==
67
 
68
+ = 2.1.14 =
69
+ Advanced size guide feature
70
+
71
  = 2.1.13 =
72
  Woocommerce compatibility raised to 4.3
73
 
184
 
185
  == Changelog ==
186
 
187
+ = 2.1.14 =
188
+ Advanced size guide feature
189
+
190
  = 2.1.13 =
191
  * Improved support for WooCommerce 4.3
192